﻿////////////////////////////////////////////////////
// XML
   
// var xmlDoc;

	////////////////////////////////////////////////////
	// Main

	// ------------------------------
	// RemoveWhiteSpaces will remove all whitespaces from the xml tree.
	// Parameters: Node - root node of xml tree.
	function RemoveWhiteSpaces(Node)
	{
		return; // NOTE: Function works to slowly, temporary disabling it. However without this function HTML page formatting is fine.
		
		if (!Node.hasChildNodes())
			return;
		var NodesToRemove = new Array();
		var nNodeIndex;
		for (nNodeIndex = 0; nNodeIndex < Node.childNodes.length; nNodeIndex++)
		{
			if (Node.childNodes[nNodeIndex].nodeType == 3 /* NODE_TEXT */ && Node.childNodes[nNodeIndex].nodeValue.replace(/\n|\r|\t/g, "").length == 0)
			{
				NodesToRemove[NodesToRemove.length] = Node.childNodes[nNodeIndex];
			}	
			else
				RemoveWhiteSpaces(Node.childNodes[nNodeIndex]);
		}
		for (nNodeIndex = 0; nNodeIndex < NodesToRemove.length; nNodeIndex++)
		{
			Node.removeChild(NodesToRemove[nNodeIndex]);
		}
	}
	
	function GetReadyStateAsString(readyState)
	{
		var states = new Array();
		states[states.length] = "uninitialized";
		states[states.length] = "loading";
		states[states.length] = "loaded";
		states[states.length] = "interactive";
		states[states.length] = "complete";
		if (readyState < 0 || readyState >= states.length)
			return "unknown";
		return states[readyState];
	}
	
	function LoadXml(sXml, onLoaded)
	{
		var xhttp;
		if (window.XMLHttpRequest)
		{
			xhttp = new XMLHttpRequest();
		}
		else
		{
			xhttp = new ActiveXObject("Microsoft.XMLHTTP");
		}
		xhttp.open("GET", sXml, true);
		xhttp.onreadystatechange =
			function()
			{
				ShowTickCount(sXml + ": onreadystatechange " + GetReadyStateAsString(xhttp.readyState) + " (readyState " + xhttp.readyState + ")");
				if (xhttp.readyState == 4)
				{
					ShowTickCount(sXml + ": onreadystatechange: Completed");
					ShowTickCount(sXml + ": onreadystatechange: 1");
					var xmlDoc = xhttp.responseXML;
					ShowTickCount(sXml + ": onreadystatechange: 2");
					RemoveWhiteSpaces(xmlDoc);
					ShowTickCount(sXml + ": onreadystatechange: 3");
					onLoaded(xmlDoc);
				}
			}
		xhttp.send(null);
	}
	
	var g_categoriesXmlDoc = null;
	var g_itemsXmlDoc = null;
	
	function InitializeStore(onCategoriesLoaded, onItemsLoaded)
	{
		LoadXml(
				"categories.xml", 
				function(xmlDoc) 
				{ 
					ShowTickCount("InitializeStore: categories loaded");
					g_categoriesXmlDoc = xmlDoc;
					if (onCategoriesLoaded)
					{
						ShowTickCount("InitializeStore: calling onCategoriesLoaded");
						onCategoriesLoaded(xmlDoc);
					}
					ShowTickCount("InitializeStore: loading items");
					LoadXml(
							"items.xml", 
							function(xmlDoc2)
							{
								ShowTickCount("InitializeStore: items loaded");
								g_itemsXmlDoc = xmlDoc2;
								if (onItemsLoaded)
								{
									ShowTickCount("InitializeStore: loading items");
									onItemsLoaded(xmlDoc2, g_categoriesXmlDoc);
								}
							}
						);					
				}
			);
	}
	
	////////////////////////////////////////////////////
	// Child Nodes and Attributes

	// TODO: Replace all getElementsByTagName functions with GetChildNodesByTagName

	// GetChildNodesByTagNames function is equal to xmlNode.getElementsByTagName except that
	// this function will return only first-child elements.
	function GetChildNodesByTagName(xmlNode, sNodeName)
	{
		var xmlResultNodes = new Array();
		var xmlSupposedNodesOwner = xmlNode.nodeType == 9 /*NODE_DOCUMENT*/ ? xmlNode.documentElement : xmlNode;
		// SUGG: If getElementsByTagName will work too slow try to use selectNodes function
		var xmlNodes = xmlNode.getElementsByTagName(sNodeName);
		for (var nIndex = 0; nIndex < xmlNodes.length; nIndex++)
			if (xmlNodes[nIndex].parentNode == xmlSupposedNodesOwner)
				xmlResultNodes[xmlResultNodes.length] = xmlNodes[nIndex];
		return xmlResultNodes;
	}
	
	function GetAttribute(xmlNode, sAttributeName)
	{
		var xmlAttribute = xmlNode.attributes.getNamedItem(sAttributeName);
		return xmlAttribute ? xmlAttribute.nodeValue : '';
	}
	
	function GetAttributeEx(xmlNodes, sField, sAttributeName)
	{
		var xmlFields = xmlNodes.getElementsByTagName(sField);
		//var xmlFields = xmlNodes.selectNodes('./' + sField);
		if (xmlFields.length > 1)
			alert('GetAttributeEx::ambigious attribute access. sField \"' + sField + '\", sAttributeName \"' + sAttributeName + "\"");
		if (xmlFields.length == 0)
			return '';
		return xmlFields[0].attributes.getNamedItem(sAttributeName).nodeValue;
	}
	
	function GetElementByAttribute(xmlNodes, sAttributeName, sAttributeValue)
	{
		// TODO: Rework, we can get attribute by getNamedItem function
		var nIndex, nAttributeIndex;
		for (nIndex = 0; nIndex < xmlNodes.length; nIndex++)
			for (nAttributeIndex = 0; nAttributeIndex < xmlNodes[nIndex].attributes.length; nAttributeIndex++)
				if (xmlNodes[nIndex].attributes[nAttributeIndex].name == sAttributeName && xmlNodes[nIndex].attributes[nAttributeIndex].value == sAttributeValue)
					return xmlNodes[nIndex];
		return null;
	}
	
	function GetTextElementByAttribute(xmlNodes, sAttributeName, sAttributeValue)
	{
		var xmlNode = GetElementByAttribute(xmlNodes, sAttributeName, sAttributeValue);
		// TODO: Rework
		if (!xmlNode)
		{
			alert('Could not get node by attribute');
			return '';
		}
		// TODO: check xmlNode that it contains only one child
		return xmlNode.childNodes[0].nodeValue;
	}
	
	function GetCategorizedNodes(xmlNodes, sCategoryName)
	{
		var CategorizedItems = new Array();
		var nIndex, nCategoryIndex;
		for (nIndex = 0; nIndex < xmlNodes.length; nIndex++)
		{
			var Categories = new Array();
			Categories = xmlNodes[nIndex].attributes.getNamedItem('category').nodeValue.split(',');
			for (nCategoryIndex = 0; nCategoryIndex < Categories.length; nCategoryIndex++)
				if (Categories[nCategoryIndex] == sCategoryName)
				{
					CategorizedItems[CategorizedItems.length] = xmlNodes[nIndex];
					break;
				}
		}
		return CategorizedItems;
	}
	
	function GetCategorizedItems(xmlCategoriesDoc, xmlItemsDoc, sCategoryName)
	{
		var xmlItems = xmlItemsDoc.getElementsByTagName('item');
		if (sCategoryName == 'all')
			return xmlItems;
		var xmlCategorizedItems = GetCategorizedNodes(xmlItems, sCategoryName);
		var xmlSubcategories = xmlCategoriesDoc.getElementsByTagName('subcategory');
		var xmlCategorizedSubcategories = GetCategorizedNodes(xmlSubcategories, sCategoryName);
		for (var nIndex = 0; nIndex < xmlCategorizedSubcategories.length; nIndex++)
		{
			var sSubcategoryId = xmlCategorizedSubcategories[nIndex].attributes.getNamedItem('id').nodeValue;
			var xmlSubcategorizedItems = GetCategorizedNodes(xmlItems, sSubcategoryId);
			for (var nSubIndex = 0; nSubIndex < xmlSubcategorizedItems.length; nSubIndex++)
			{
				var bItemFounded = false;
				for (var nCurrentIndex = 0; !bItemFounded && nCurrentIndex < xmlCategorizedItems.length; nCurrentIndex++)
					if (xmlCategorizedItems[nCurrentIndex].attributes.getNamedItem('id').nodeValue == xmlSubcategorizedItems[nSubIndex].attributes.getNamedItem('id').nodeValue)
						bItemFounded = true;
				if (!bItemFounded)
					xmlCategorizedItems[xmlCategorizedItems.length] = xmlSubcategorizedItems[nSubIndex];
			}
		}
		return xmlCategorizedItems;
	}
	
	////////////////////////////////////////////////////
	// Node's properties
	
	
	function GetTitle(xmlNode)
	{
		return GetTextElementByAttribute(GetChildNodesByTagName(xmlNode, 'title'), 'lang', GetLanguage());
	}
	
	////////////////////////////////////////////////////
	// Description parsing 
	
	function GetDescription(xmlNode)
	{
		return GetTextElementByAttribute(GetChildNodesByTagName(xmlNode, 'description'), 'lang', GetLanguage());
	}
	
	function CDesriptionLineElement(sText, sType, sHref /*Used only in link element*/)
	{
		this.sText = sText;
		this.sType = sType;
		this.sHref = sHref;
		
		this.InitializeAsText = CDesriptionLineElement_InitializeAsText;
		this.InitializeAsBold = CDesriptionLineElement_InitializeAsBold;
		this.InitializeAsStriked = CDesriptionLineElement_InitializeAsStriked;
		this.InitializeAsLink = CDesriptionLineElement_InitializeAsLink;
	}
		// Text
		function CDesriptionLineElement_InitializeAsText(sText)
		{
			this.sText = sText;
			this.Add = CDesriptionLineElement_Text_Add;
		}
		function CDesriptionLineElement_Text_Add(RootElement)
		{
			RootElement.appendChild(document.createTextNode(this.sText));
		}
		// Bold
		function CDesriptionLineElement_InitializeAsBold(sText)
		{
			this.sText = sText;
			this.Add = CDesriptionLineElement_Bold_Add;
		}
		function CDesriptionLineElement_Bold_Add(RootElement)
		{
			var BElement = document.createElement('b');
			BElement.appendChild(document.createTextNode(this.sText));
			RootElement.appendChild(BElement);
		}
		// Striked		
		function CDesriptionLineElement_InitializeAsStriked(sText)
		{
			this.sText = sText;
			this.Add = CDesriptionLineElement_Striked_Add;
		}
		function CDesriptionLineElement_Striked_Add(RootElement)
		{
			var SpanElement = document.createElement('span');
			SpanElement.style.textDecoration = "line-through";
			SpanElement.appendChild(document.createTextNode(this.sText));
			RootElement.appendChild(SpanElement);
		}
		// Link
		function CDesriptionLineElement_InitializeAsLink(sHref, sText, nWidth, nHeight)
		{
			this.sHref = sHref;
			this.sText = sText;
			this.nWidth = nWidth ? nWidth : 0;
			this.nHeight = nHeight ? nHeight : 0;
			this.Add = CDesriptionLineElement_Link_Add;
		}
		function CDesriptionLineElement_Link_OpenInNewWindow(sUrl, nWidth, nHeight)
		{
			nWidth += 32;
			nHeight += 96;
			var Window = window.open(
				sUrl, 
				'Link', 
				'width=' + nWidth + ', height=' + nHeight + ', ' +
				'location=no, menubar=no, ' +
				'status=no, toolbar=no, scrollbars=no, resizable=no');
			Window.resizeTo(nWidth, nHeight);
			Window.focus();
		}
		function CDesriptionLineElement_Link_Add(RootElement)
		{
			var AElement = document.createElement('a');
			if (this.nWidth == 0 && this.nHeight == 0)
			{
				AElement.setAttribute('href', this.sHref);
			}
			else
			{
				AElement.setAttribute(
					'href', 
					'javascript:CDesriptionLineElement_Link_OpenInNewWindow("' + this.sHref + '",' + this.nWidth + ',' + this.nHeight + ')');
			}	
			AElement.appendChild(document.createTextNode(this.sText));
			RootElement.appendChild(AElement);
		}
	
	function ParseDescriptionLine(sLineArg)
	{
		var DescriptionLineElementArray = new Array;
		var sLine = sLineArg;
		while (sLine.length)
		{
			var nBPosition = sLine.search(/\[b\]/i);
			if (nBPosition == 0)
			{
				// Get bold text
				var nBClosePosition = sLine.search(/\[\/b\]/i);
				var sBoldText = sLine.substring(nBPosition + 3, nBClosePosition);
				var DescriptionLineElement = new CDesriptionLineElement();
				DescriptionLineElement.InitializeAsBold(sBoldText);
				DescriptionLineElementArray[DescriptionLineElementArray.length] = DescriptionLineElement;
				sLine = sLine.substring(nBClosePosition + 4);
				continue;
			}
			var nLinkPosition = sLine.search(/\[link\]/i);
			if (nLinkPosition == 0)
			{
				// Get link
				var nLinkClosePosition = sLine.search(/\[\/link\]/i);
				var LinkArguments = sLine.substring(nLinkPosition + 6, nLinkClosePosition).split(',');
				if (LinkArguments.length != 2 && LinkArguments.length != 4)
					alert('Unable to parse link in description, in line: ' + sLineArg);
				var nWidth = LinkArguments.length == 4 ? parseInt(LinkArguments[2]) : 0;
				var nHeight = LinkArguments.length == 4 ? parseInt(LinkArguments[3]) : 0;
				var DescriptionLineElement = new CDesriptionLineElement();
				DescriptionLineElement.InitializeAsLink(LinkArguments[0], LinkArguments[1], nWidth, nHeight);
				DescriptionLineElementArray[DescriptionLineElementArray.length] = DescriptionLineElement;			
				sLine = sLine.substring(nLinkClosePosition + 7);
				continue;
			}
			var nStrikedPosition = sLine.search(/\[s\]/i);
			if (nStrikedPosition == 0)
			{
				// Get striked text
				var nStrikedClosePosition = sLine.search(/\[\/s\]/i);
				var sStrikedText = sLine.substring(nStrikedPosition + 3, nStrikedClosePosition);
				var DescriptionLineElement = new CDesriptionLineElement();
				DescriptionLineElement.InitializeAsStriked(sStrikedText);
				DescriptionLineElementArray[DescriptionLineElementArray.length] = DescriptionLineElement;
				sLine = sLine.substring(nStrikedClosePosition + 4);
				continue;
			}
			// Get raw text			
			var Positions = new Array;
			Positions[Positions.length] = nBPosition < 0 ? sLine.length : nBPosition;
			Positions[Positions.length] = nLinkPosition < 0 ? sLine.length : nLinkPosition;
			Positions[Positions.length] = nStrikedPosition < 0 ? sLine.length : nStrikedPosition;
			var nTextUntilPosition = Positions[0];
			for (var nIndex = 0; nIndex < Positions.length; nIndex++)
				if (nTextUntilPosition > Positions[nIndex])
					nTextUntilPosition = Positions[nIndex];
			//var nTextUntilPosition = nBPosition < nLinkPosition ? nBPosition : nLinkPosition;
			var sText = sLine.substring(0, nTextUntilPosition);
			var DescriptionLineElement = new CDesriptionLineElement();
			DescriptionLineElement.InitializeAsText(sText);
			DescriptionLineElementArray[DescriptionLineElementArray.length] = DescriptionLineElement;
			sLine = sLine.substring(nTextUntilPosition);
		}
		return DescriptionLineElementArray;
	}
	
	function GetDescriptionLines(sDescriptionArg)
	{
		return sDescriptionArg.replace(/\[br\]\[br\]/gi, '[br]\u00a0[br]').split(/\[br\]/gi);	
	}
	
	function AddHtmlFormatedText(RootElement, sText)
	{
		var Lines = GetDescriptionLines(sText);
		for (var nIndex = 0; nIndex < Lines.length; nIndex++)
		{
			var sLine = Lines[nIndex];
			if (!sLine.length) 
			{
				RootElement.appendChild(document.createTextNode(' '));
			} else 
			{	
				var DescriptionLineElementArray = ParseDescriptionLine(sLine);
				for (var nElementIndex = 0; nElementIndex < DescriptionLineElementArray.length; nElementIndex++)
				{
					var Element = DescriptionLineElementArray[nElementIndex];
					Element.Add(RootElement);
				}
			}
			if (nIndex + 1 < Lines.length)
				RootElement.appendChild(document.createElement('br'));
		}
	}
	
	////////////////////////////////////////////////////
	// Search
	
	function XmlIsItemMatchSearch(xmlItem, sSearchReques)
	{
		var Languages = new Array();
		Languages[Languages.length] = 'ru';
		Languages[Languages.length] = 'lv';
		var SearchWords = sSearchReques.split(/\s/g);
		var SearchRegExp = new Array();
		for (var nIndex = 0; nIndex < SearchWords.length; nIndex++)
			SearchRegExp[SearchRegExp.length] = new RegExp(SearchWords[nIndex], 'i');
		for (var nLanguageIndex = 0; nLanguageIndex < Languages.length; nLanguageIndex++)
		{
			var FieldsToSearch = new Array();
			FieldsToSearch[FieldsToSearch.length] = GetTextElementByAttribute(xmlItem.getElementsByTagName('title'), 'lang', Languages[nLanguageIndex]);
			FieldsToSearch[FieldsToSearch.length] = GetTextElementByAttribute(xmlItem.getElementsByTagName('description'), 'lang', Languages[nLanguageIndex]);
			for (var nIndex = 0; nIndex < FieldsToSearch.length; nIndex++)
				for (var nSearchRegExpIndex = 0; nSearchRegExpIndex < SearchRegExp.length; nSearchRegExpIndex++)
					if (FieldsToSearch[nIndex].search(SearchRegExp[nSearchRegExpIndex]) >= 0)
						return true;
		}
		return false;
	}
	function XmlSearchItems(xmlRoot, sSearchRequest)
	{
		var xmlItems = xmlRoot.getElementsByTagName('item');
		var xmlResultItems = new Array();
		for (var nIndex = 0; nIndex < xmlItems.length; nIndex++)
			if (XmlIsItemMatchSearch(xmlItems[nIndex], sSearchRequest))
				xmlResultItems[xmlResultItems.length] = xmlItems[nIndex];
		return xmlResultItems;		
	}
