
	function createXMLHttpRequest()
	{
		if(window.XMLHttpRequest) return new XMLHttpRequest();
		else if(window.ActiveXObject)
		{
			try { return new ActiveXObject('Msxml2.XMLHTTP'); }
			catch (e) { return new ActiveXObject('Microsoft.XMLHTTP'); }
		}
	}

	function createDOMDocument()
	{
		if(window.ActiveXObject)
		{
			try { return new ActiveXObject('Microsoft.XMLDOM'); }
			catch (e) { return new ActiveXObject('Msxml2.DOMDocument'); }
		}
		return document.implementation.createDocument("", "", null);
	}

	function cloneDocument(doc)
	{
		return loadXmlFromString(saveXml(doc));
	}

	function getFrameDocument(frame)
	{
		if(frame.contentWindow.document) return frame.contentWindow.document;
		else if(frame.contentDocument) return frame.contentDocument;
		return frame.document;
	}

	function getFrameWindow(frame)
	{
		if(frame.contentWindow) return frame.contentWindow;
		return frame.window;
	}

//  *** XSLT transformations ***

	function importChilds(element, doc, importNamespace)
	{
		var fragment = doc.createDocumentFragment();
		for(var i = 0; i < element.childNodes.length; i++)
		{
			var childNode = element.childNodes[i];
			switch(childNode.nodeType)
			{
				case 1: // ELEMENT_NODE
					if(childNode.nodeName.indexOf('/') < 0) // IE!
						fragment.appendChild(importElement(childNode, doc, importNamespace));
					break;
				case 3:  // TEXT_NODE
					fragment.appendChild(doc.createTextNode(childNode.nodeValue));
					break;
				case 4:  // CDATA_SECTION_NODE
					fragment.appendChild(doc.createCDATASection(childNode.data));
					break;
				case 5:  // ENTITY_REFERENCE_NODE
					fragment.appendChild(doc.createComment('ENTITY_REFERENCE_NODE'));
					break;
				case 6:  // ENTITY_NODE
					fragment.appendChild(doc.createComment('ENTITY_NODE'));
					break;
				case 7:  // PROCESSING_INSTRUCTION_NODE
					fragment.appendChild(doc.createComment('PROCESSING_INSTRUCTION_NODE'));
					break;
				case 8:  // COMMENT_NODE
					fragment.appendChild(doc.createComment(childNode.data));
					break;
			}
		}

		return fragment;
	}

	function importElement(element, doc, importNamespace)
	{
		if(importNamespace == undefined) importNamespace = true;
		
		try { var imported = importNamespace && element.namespaceURI ? doc.createElementNS(element.namespaceURI, element.nodeName/*.toLowerCase()*/) : doc.createElement(element.nodeName/*.toLowerCase()*/); }
		catch(e) { var imported = doc.createElement(element.nodeName/*.toLowerCase()*/); }

		for(var i = 0; i < element.attributes.length; i++)
		{
			var attribute = element.attributes[i];
			if(attribute.value != 'null' && attribute.value != '')
			{
				imported.setAttribute(attribute.nodeName, attribute.value);
			}
		}

		var fragment = importChilds(element, doc, importNamespace);
		
		if(fragment.childNodes.length) imported.appendChild(fragment);

		return imported;
	}

	function transform(doc, stylesheetName)
	{
		// TODO: optimization - async loading
		if(window.XSLTProcessor)
		{
			var xsltProcessor = new XSLTProcessor();
			xsltProcessor.importStylesheet(loadXml(stylesheetName));

			/*
			try { xsltProcessor.importStylesheet(loadXml(stylesheetName)); }
			catch(e) {
				
				var tmp = loadNormalizedXslt(stylesheetName);
				alert(saveXml(tmp));
				xsltProcessor.importStylesheet(loadNormalizedXslt(stylesheetName));
			
			} // Opera
			*/
			return xsltProcessor.transformToDocument(doc);

			//// TODO: INCLUDES
		}
		else if(window.ActiveXObject)
		{
			var result = createDOMDocument();
			result.async = false;
			result.validateOnParse = true;

			var xsltDoc = createDOMDocument();
			try
			{
				xsltDoc.resolveExternals = true;
				xsltDoc.setProperty("AllowDocumentFunction", true);
			}
			catch(e) {}
            xsltDoc.async = false;
            xsltDoc.load(stylesheetName);

			var xslOutput = selectNode('xsl:output[@doctype-public]', xsltDoc.documentElement);
			if(xslOutput != null) xslOutput.removeAttribute('doctype-public');
			var xslOutput = selectNode('xsl:output[@doctype-system]', xsltDoc.documentElement);
			if(xslOutput != null) xslOutput.removeAttribute('doctype-system');

			doc.transformNodeToObject(xsltDoc, result);
			
			return result;
		}
	}

	// for Opera
	function loadNormalizedXslt(fileName)
	{
		var cacheKey = 'norm-xslt:' + fileName;
		if(filesCache[cacheKey] !== undefined) return filesCache[cacheKey];

		var doc = loadXml(fileName);
		var node = doc.documentElement.firstChild;
		while(node)
		{
			if(node.nodeType == 1 /* ELEMENT_NODE */ && node.nodeName.match(/(:|^)(include|import)$/i) && node.namespaceURI == 'http://www.w3.org/1999/XSL/Transform')
			{
				var includeElement = node;
				var ref = includeElement.getAttribute('href');

				if(fileName.lastIndexOf('/'))
				{
					var base = fileName.substr(0, fileName.lastIndexOf('/'));

					while(ref.indexOf('../') == 0)
					{
						base = base.substr(0, base.lastIndexOf('/'));
						ref = ref.substr(3);
					}

					ref = base + '/' + ref;
				}

				var childDoc = loadNormalizedXslt(ref);
				doc.documentElement.insertBefore(importChilds(childDoc.documentElement, doc), includeElement);
				node = includeElement.nextSibling;
				doc.documentElement.removeChild(includeElement);
			}
			else node = node.nextSibling;
		}

		return doc;
	}


//  *** XML loading and saving ***

	var filesCache = new Object();

	function loadXmlFromString(string)
	{
		if(window.DOMParser)
		{
			var parser = new DOMParser();
			return parser.parseFromString(string, "text/xml");
		}
		else if(window.ActiveXObject)
			try
			{
				var doc = createDOMDocument();
				doc.async = false;
				doc.loadXML(string);
				return doc;
			}
			catch(e) {}
	}

	function loadXml(url, postData, options) //TEMPORARY
	{
		if(options == undefined) options = {};
		if(options.success != undefined)
		{
			$.ajax({
				type: (postData == undefined ? 'GET' : 'POST'),
				url: url,
				data: postData,
				dataType: 'xml',
				contentType: 'application/x-www-form-urlencoded',
				async: true,
				success: function(result) { if(options.success != undefined) options.success(result) },
				error: function() { if(options.error != undefined) options.error() }
			});
		}
		else
			 return $.ajax({
				url: url,
				type: (postData == undefined ? 'GET' : 'POST'),
				data: postData,
				dataType: 'xml',
				async: false
			}).responseXML;
	}


	function saveXml(doc)
	{
		if(window.XMLSerializer)
		{
			var serializer = new XMLSerializer();
			try{ return XML(serializer.serializeToString(doc)).toXMLString(); }
			catch(e) { return serializer.serializeToString(doc); }
		}
		else return doc.xml
	}

//  *** XPath ***
	//depricated
	function selectNode(xpath, contextNode)
	{
		//alert('DEPRICATED: selectNode('+xpath+')');
		if(window.ActiveXObject)
			try
			{
				contextNode.ownerDocument.setProperty("SelectionLanguage", "XPath");
				return contextNode.selectSingleNode(xpath);
			}
			catch(e)
			{
				// Using ajaxslt modules
				return xpathParse(xpath).evaluate(new ExprContext(contextNode)).nodeSetValue()[0];
			}

		var result = contextNode.ownerDocument.evaluate(xpath, contextNode, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
		return result.singleNodeValue;
	}
	
	function selectNodes(xpath, contextNode)
	{
		//alert('DEPRICATED: selectNode('+xpath+')');
		if(window.ActiveXObject)
			try
			{
				//contextNode.ownerDocument.setProperty("SelectionLanguage", "XPath");
				return contextNode.selectNodes(xpath);
			}
			catch(e)
			{
				// Using ajaxslt modules
				return xpathParse(xpath).evaluate(new ExprContext(contextNode)).nodeSetValue();
			}
		
		var iterator = contextNode.ownerDocument.evaluate(xpath, contextNode, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
		var result = [];
		var node = iterator.iterateNext();
		while(node)
		{
			result.push(node);
			node = iterator.iterateNext();
		}
		return result;
	}
	
	function getScrollOffset() 
	{
		var top = 0, left = 0;
		if(typeof( window.pageYOffset ) == 'number' ) //Netscape compliant
		{
			top = window.pageYOffset;
			left = window.pageXOffset;
		} 
		else if(document.body && (document.body.scrollLeft || document.body.scrollTop))  //DOM compliant
		{
			top = document.body.scrollTop;
			left = document.body.scrollLeft;
		} 
		else if(document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) 
		{
			//IE6 standards compliant mode
			top = document.documentElement.scrollTop;
			left = document.documentElement.scrollLeft;
		  }
		return {left: left, top: top};
	}	

	// classes
	function checkClass(className, element)
	{
		//alert('DEPRICATED: checkClass('+className+')');
		var pattern = new RegExp(className);
		return element.className.search(pattern) >= 0;
	}

	function setClass(className, element)
	{
		//alert('DEPRICATED: setClass('+className+')');
		if(!checkClass(className, element)) element.className += ' ' + className;
	}

	function clearClass(className, element)
	{
		//alert('DEPRICATED: clearClass('+className+')');
		var pattern = new RegExp(className + ' ?', 'g');
		if(checkClass(className, element))  element.className = element.className.replace(pattern, '');
	}

	function toggleClass(className, element)
	{
		var pattern = new RegExp(className + ' ?', 'g');
		if(checkClass(className, element)) element.className = element.className.replace(pattern, '');
		else  element.className += ' ' + className;
	}

	$(document).ready( function() { $("body").addClass('js') });
