/*
 * @Copyright Art.Lebedev Studio | http://www.artlebedev.ru
 *
 * @Author Denis Khripkov | denisx@design.ru | www.denisx.ru
 */
var oXmlValidator = {
	oTab: new RegExp(/[\n\t\r]+/g),
	oCommentAndCdata: new RegExp(/<!(?:--(?:[^-]|-[^-])*--|\[CDATA\[(?:[^\]]|\][^\]]|\]+[^\>\]])*]{2,})>/g), 
	oInstruction: new RegExp(/<\?.*?\?>/),
	oDocType: new RegExp(/<\!DocType.*?>/i),
	oOutTagTextBegin: new RegExp(/^\s*[^<\s]+/),
	oEntityFull: new RegExp(/&(?:#(?:x[a-f\d]{1,4}|\d{2,5})|[a-z][\w\-]*);/gi),
	oAttribute: new RegExp(/(<[a-z_][\w:-]*)(?:\s+[a-z_][\w:-]*\s*=\s*(?:'[^<>']*'|"[^<>"]*"))*\s*(\/?>)/gi),
	oSingleTag: new RegExp(/<[a-z_][\w:-]*\/>/gi),
	oDoubleTag: new RegExp(/<([a-zA-Z_][\w:-]*)>[^<]*<\/\1\s*>/g),	
	sMessage: new Array(
		'Все верно', // 0
		'Нет корневой ноды', // 1
		'Незакрытый комментарий', // 2
		'Незакрытый блок CDATA', // 3	
		'Неожиданный Instruction', // 4	
		'Неожиданный DocType', // 5	
		'Текст в начале строки выходит за пределы тега', // 6	
		'Текст в конце строки выходит за пределы тега', // 7
		'Неожиданная entity', // 8	
		'Больше одной корневой ноды или неожиданный тег', // 9	
		'Незакрытый тег') // 10	
};
oXmlValidator.Object = function(sValue){
	this.sValue = sValue;	
	this.nCode = 0;
	this.nBugPlace = 0;
	this.sMessage = oXmlValidator.sMessage[0];
	this.hParams = {
		bFragment: false // true - проверяемый код не целый xml, а только его часть
	}
};
oXmlValidator.Object.prototype = {
	valid: function(){
		var sValue = this.sValue;
		var nCode = this.nCode;
		var hParams = this.hParams;
		if ( sValue ){
			// вырезаем табуляцию и переносы строк     
			sValue = sValue.replace( oXmlValidator.oTab, ' ' );
			// вырезаем комменты и CDATA
			sValue = sValue.replace( oXmlValidator.oCommentAndCdata, '' );
			if ( sValue.indexOf( '<!--' ) != -1 ) {
				this.nCode = 2; 
				this.sMessage = oXmlValidator.sMessage[2];
				return false;
			}	
			if ( sValue.indexOf( ']]>' ) != -1 ) {
				this.nCode = 3; 
				this.sMessage = oXmlValidator.sMessage[3];
				return false;
			}
			// вырезаем инструкции
			if ( !hParams.bFragment ) 
				sValue = sValue.replace( oXmlValidator.oInstruction, '' );
				if ( sValue.search( oXmlValidator.oInstruction ) != -1 ) {
					this.nCode = 4; 
					this.sMessage = oXmlValidator.sMessage[4];
					return false;
				}
			// вырезаем DocType
			if ( !hParams.bFragment )
				sValue = sValue.replace( oXmlValidator.oDocType, '' );
			if ( sValue.search( oXmlValidator.oDocType ) != -1 ) {
				this.nCode = 5;
				this.sMessage = oXmlValidator.sMessage[5];
				return false;
			}
			// ищем текст в начале и в конце строки, выходящий за пределы тегов
			if ( !hParams.bFragment ) {
				if ( sValue.search( oXmlValidator.oOutTagTextBegin ) != -1 ) {
					this.nCode = 6;
					this.sMessage = oXmlValidator.sMessage[6];
					return false;
				}
				// конец строки.
				var nValueLength = sValue.length;
				var bIsSpace = true;
				do {
					nValueLength--;
					if ( sValue.charAt( nValueLength ) != ' ' ) bIsSpace = false;
				} while ( bIsSpace && nValueLength > 0 )
				if ( !bIsSpace && sValue.charAt( nValueLength ) != '>' ){
					this.nCode = 7;
					this.sMessage = oXmlValidator.sMessage[7];
					return false;
				}
				else if ( nValueLength == 0 ){
					this.nCode = 1;
					this.sMessage = oXmlValidator.sMessage[1];
					return false;
				}
			}
			// вырезаем Entities
			sValue = sValue.replace( oXmlValidator.oEntityFull, '' );
			if ( sValue.indexOf( '&' ) != -1 ){
				this.nCode = 8;
				this.sMessage = oXmlValidator.sMessage[8];
				return false;
			}
			// вырезаем аттрибуты
			sValue = sValue.replace( oXmlValidator.oAttribute, '$1$2' );
			// параметр для вырезания тэгов
			var sTagReplaceTo = '';
			if ( !hParams.bFragment ) sTagReplaceTo = '&';
			// вырезаем одинарные тэги
			sValue = sValue.replace( oXmlValidator.oSingleTag, sTagReplaceTo );
			// вырезаем двойные тэги 
			var nPrevLen; var nLen = 0;
			do {
				nPrevLen = nLen;
				sValue = sValue.replace( oXmlValidator.oDoubleTag, sTagReplaceTo );
				nLen = sValue.length;
			} while ( nLen != nPrevLen );
			if ( !hParams.bFragment ) {
				if ( sValue.indexOf(sTagReplaceTo) != sValue.lastIndexOf(sTagReplaceTo) ) {
					this.nCode = 9;
					this.sMessage = oXmlValidator.sMessage[9];
					return false;
				}
			}
			if( sValue.indexOf( '<' ) != -1 ){
				this.nCode = 10;
				this.sMessage = oXmlValidator.sMessage[10];
				return false;
			}
			this.nCode = 0;
			this.sMessage = oXmlValidator.sMessage[0];
			return true;
		} else { // пустая строка
			if ( !hParams.bFragment ){
				this.nCode = 1;
				this.sMessage = oXmlValidator.sMessage[1];
				return false;
			}
			else {
				this.nCode = 0;
				this.sMessage = oXmlValidator.sMessage[0];
				return true;
			}
		}
	}
};