// ///////////////////////////////////////////////////////////////
// =========================================================================
//
// the xml recordset light version
// Date:		August 22, 2006
// Created by:	Yi Zhang
// 
//  Version 3.1                                
// =========================================================================
//SQL sample: select clientid, firstname, lastname, createddate from client for xml auto
//xml sample: 
//<client clientid="1" firstname="Jun" lastname="Yang" createddate="2006-06-28T12:58:00" />
//<client clientid="2" firstname="Taha" lastname="Ismail" createddate="2000-06-22T00:00:00" />
//<client clientid="3" firstname="Spot" lastname="Test" createddate="2006-07-07T11:44:00" />
//<client clientid="4" firstname="Rhodora" lastname="De Chavez" createddate="2006-07-26T11:30:00" />
//<client clientid="5" firstname="Jing" lastname="Zhang" createddate="2006-07-26T12:03:00" />
//<client clientid="6" firstname="Antonio" lastname="Pavi" createddate="2006-07-26T12:17:00" />
//<client clientid="7" firstname="Antonio" lastname="Esquillo" createddate="2006-07-26T15:25:00" />
//<client clientid="8" firstname="Jesus Antonio" lastname="Pavi" createddate="2006-07-26T15:36:00" />
//<client clientid="11" firstname="Edgardo" lastname="Maglanque" createddate="2006-07-26T16:05:00" />
//<client clientid="12" firstname="Daniel Boone" lastname="Calud" createddate="2006-07-26T16:12:00" />        
// ///////////////////////////////////////////////////////////////
//SQL read XML sample
//DECLARE @idoc int
//DECLARE @doc varchar(1000)
//SET @doc ='
//<ROOT>
//<client clientid="1" firstname="Jun" lastname="Yang" createddate="2006-06-28T12:58:00" />
//</ROOT>
//'
//--Create an internal representation of the XML document.
//EXEC sp_xml_preparedocument @idoc OUTPUT, @doc
//-- Execute a SELECT statement that uses the OPENXML rowset provider.
//SELECT * FROM OPENXML (@idoc, '/ROOT/client',1)
            //WITH (clientid  int, firstname varchar(20), lastname varchar(20), createddate datetime)
//EXEC sp_xml_removedocument @idoc

// CLASS Recordset EXPOSES THE FOLLOWING INTERFACE:

// columns()                          returns table (2-dimensional array) of column names, types, and maxlengths

// EXAMPLE XML RECORDSET DOCUMENT

// setup new function on string object
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/, ''); }
String.prototype.ltrim = function () { return this.replace(/^\s+/, ''); }
String.prototype.rtrim = function () { return this.replace(/\s+$/, ''); }
String.prototype.isNumeric = function () { 	var IsNumber = false; if( this.search(/[^0-9.]/g) == -1 ){ IsNumber = true; } return IsNumber; }

function Recordset() {
	this.tablename;		//string
	this.columns;		//arrary
	this.rows;			//arrary
	this.deletedrows;	//arrary
	this.maxid;
	this.load = rs_xml_init;
	this.currentRecord = 0;
	this.recordCount  = 0;
	
	// move record, return -1 if error or not found
	this.moveFirst = rs_mv_first;
	this.movePrevious = rs_mv_prev;
	this.moveNext = rs_mv_next;
	this.moveLast = rs_mv_last;
	this.moveTo = rs_mv_to;
	
	// find a record
	this.filter = '';
	this.rs_seek_Index = 0;
	this.seekFirst = rs_seek_first;	
	this.seekNext = rs_seek_next;
	
	// management data value
	this.addRow = rs_add_row;
	this.addCol = rs_add_col;
	this.getValue = rs_get_value;
	this.setValue = rs_set_value;
	this.deleteRow = rs_delete_row;
	
	this.xmlData = rs_get_xmldata;
	
	// 
	if ( Recordset.arguments.length > 0 )
	{
		this.load( Recordset.arguments[0] );
	}
 
	// _row_id: row index
	// _row_status: unchanged, addnew, deleted, changed
}

function rs_get_value(fieldname)
{
	var strResult = '';
	if ( this.currentRecord > -1 )
	{
		strResult = this.rows[this.currentRecord][fieldname];
	}	
	return strResult;
}

function rs_set_value(fieldname, value)
{
	if ( this.currentRecord > -1 )
	{
		this.rows[this.currentRecord][fieldname] = value;
		if ( this.rows[this.currentRecord]['row_status'] != 'addnew' )
		{
			this.rows[this.currentRecord]['row_status'] = 'changed';
		}
	}	

}

function rs_add_row()
{
	var i;
	var attrs = new Array();
	
	for ( i = 0; i < this.columns.length; i++ )
	{
		attrs[this.columns[i]] = '';
	}
	// assign a uniqe number to _row_id for the new record
	this.maxid ++;
	attrs['_row_id'] = this.maxid;
	attrs['_row_status'] = 'addnew';
	this.rows.push(attrs);
	this.currentRecord = this.rows.length - 1;
	this.recordCount = this.rows.length;
}

function rs_delete_row()
{
	var attrs;
	if (rs_delete_row.arguments.length > 0 )
	{
		this.seekFirst('_row_id = ' + rs_delete_row.arguments[0].toString());
	}
	
	if ( this.currentRecord > -1 )
	{
		if ( this.rows[this.currentRecord]['_row_status'] != 'addnew' )
		{
			attrs = this.rows.splice(this.currentRecord, 1);
			attrs[0]['_row_status'] = 'deleted';
			this.deletedrows.push(attrs[0]);
			this.currentRecord = ( this.currentRecord < this.rows.length ) ? this.currentRecord : -1;
		}
	}	
	this.recordCount = this.rows.length;
}

function rs_add_col(colname)
{
	this.columns.push( colname );
}

function rs_xml_init(xmlStr) 
{
	var i, j;
	this.columns = new Array();
	this.rows = new Array();
	this.deletedrows = new Array();
	this.columns.push('_row_id'); // add row_id field start from 0
	this.columns.push('_row_status'); // add row_status field
	
	try
	{
/*	
	    var obj_xml_doc;
	    var obj_xml_nodeRoot;
	    var obj_xml_node;
	    
		obj_xml_doc = new ActiveXObject("Msxml2.DOMDocument");

		if ( obj_xml_doc.loadXML("<root>" + xmlStr + "</root>") )
		{
		
		    //obj_xml_node = obj_xml_doc.selectNodes("root")[0];
		    obj_xml_nodeRoot = obj_xml_doc.selectSingleNode("root");
    		
		    for ( i = 0; i < obj_xml_nodeRoot.childNodes.length; i ++ )
		    {
		        obj_xml_node = obj_xml_nodeRoot.childNodes[i];
    		    
		        if ( i == 0 )
		        {
		            this.tablename = obj_xml_node.nodeName;
		        }
    		        
		        var attrs = new Array();
			    attrs['_row_id'] = i;
			    attrs['_row_status'] = 'unchanged';
    		    
    		    for ( j = 0; j < obj_xml_node.attributes.length; j ++ )
	    	    {
				    if ( i == 0 ) // only create column list for the first row
				    {
					    this.columns.push(obj_xml_node.attributes[j].nodeName);
				    }
    	    	
	    	        attrs[obj_xml_node.attributes[j].nodeName] = obj_xml_node.attributes[j].nodeValue;
	    	    }
    	    	
			    this.maxid = i;
			    this.rows.push(attrs);
			}
		}
		
		this.recordCount = this.rows.length;
		return;
*/
		
	}
	catch( ex )
	{
		var ss;//obj_xml_doc = new window.ActiveXObject("Msxml2.XMLParser.2.6");
		ss = ex.message;
	}	
	
	// self made parser
	var strTemp;
	var arrRows, arrCols, arrTemp;

	xmlStr = xmlStr.trim();
		
	i = xmlStr.indexOf('<') + 1;
	j = xmlStr.indexOf(' ');
	if ( i > -1 && j > -1 && j > i )
	{
		this.tablename = xmlStr.slice(i, j);
	}
	
	//var arrRows = xmlStr.split(/\s*<[a-z\d]+\s+|\s+\/>\s*/);
	var arrRows = xmlStr.split(/\s*<[a-z\d]+\s+|\s*\/>\s*/i)
	//var arrRows = xmlStr.split(/\s*\/>/);
	
	this.columns.length = 0;
	this.rows.length = 0;

	for ( i = 0; i < arrRows.length; i++ )	// loop for rows
	{
		var attrs = new Array();
		
		strTemp = arrRows[i];

		//strTemp = strTemp.replace('<' + this.tablename, '');
		//strTemp = strTemp.trim();

		if ( strTemp != '' )
		{
			arrCols = strTemp.split('" ');
			
			// assign _row_id = i and row_status = unchanged;	addnew, deleted, changed
			attrs['_row_id'] = i;
			attrs['_row_status'] = 'unchanged';
			
			// parse column data
			for( j = 0; j < arrCols.length; j++ )	// loop for columns
			{
				strTemp = arrCols[j];
				if( strTemp != '' )
				{
					arrTemp = strTemp.split('=');
					if( arrTemp.length > 1 )
					{
						attrs[arrTemp[0]] = arrTemp[1].replace(/"/g, '');
						
						if ( i == 0 ) // only create column list for the first row
						{
							this.columns.push(arrTemp[0]);
						}
					}
				}
			}
			
			this.maxid = i;
			this.rows.push(attrs);
		}		
		
	}
	
	this.recordCount = this.rows.length;
}

function rs_get_xmldata()
{
	var strXmlData = ''
	var i, j;
	for ( i = 0; i < this.rows.length; i++ )
	{
		strXmlData += '<' + this.tablename;
		for ( j = 0; j < this.columns.length; j++ )
		{
			strXmlData += ' ' + this.columns[j] + '="' + this.rows[i][this.columns[j]] + '"';
		}
		strXmlData += '/>\n';
	}

	for ( i = 0; i < this.deletedrows.length; i++ )
	{
		strXmlData += '<' + this.tablename;
		for ( j = 0; j < this.columns.length; j++ )
		{
			strXmlData += ' ' + this.columns[j] + '="' + this.deletedrows[i][this.columns[j]] + '"';
		}
		strXmlData += '/>\n';
	}	
	return strXmlData;
}

function rs_mv_first() 
{
	this.currentRecord = 0;
}

function rs_mv_prev() 
{
	this.currentRecord = (this.currentRecord == 0) ? - 1 : this.currentRecord - 1;
}

function rs_mv_next() 
{
	this.currentRecord = (this.currentRecord == this.rows.length - 1) ? -1 : this.currentRecord + 1;
}

function rs_mv_last() 
{
	this.currentRecord = this.rows.length - 1;
}

function rs_mv_to(idx) 
{
	this.currentRecord = (idx < this.rows.length) ? idx : -1;
}

function rs_seek_first(strfilter)
{
	this.filter = strfilter.trim();
	this.moveFirst();
	this.rs_seek_Index = 0;
	return this.seekNext();
}

function rs_seek_next()
{
	if ( this.filter != '' && this.filter.indexOf('=') > -1 )
	{
		var key = this.filter.split(/\s*=\s*/)[0];
		var value = this.filter.split(/\s*=\s*/)[1];
		//value = value.replace(/^\'+|^\"+|\'+$|\"+$/, "");

		value = value.replace(/^'+/, '');
		value = value.replace(/'+$/, '');
		
		value = value.replace(/^"+/, '');
		value = value.replace(/"+$/, '');
		
		for ( var i = this.currentRecord + this.rs_seek_Index; i < this.rows.length; i++ )
		{
			if ( this.rows[i][key] == value )
			{
				break;
			}
		}
		
		this.rs_seek_Index = 1;
		this.currentRecord = ( i >= this.rows.length ) ? -1 : i; 

	}
}


