function Recordset(arrFieldNames,arrFieldTypes,arrData) {

	//Variables

	this.Columns = arrFieldNames;

	this.Types = arrFieldTypes;

	this.Data = arrData;

	this.Columns.Count = this.Columns.length;

	this.Record = 0;

	this.AbsolutePosition = 1;

	this.EOF = false;

	this.BOF = false;

	this.RecordCount = arrData.length;

	this.SortColumns = Array();

	//Functions

	this.RowId = Recordset_RowId;

	this.Fields = Recordset_Fields;

	this.Move = Recordset_Move;

	this.MoveTo = Recordset_MoveTo;

	this.MoveFirst = Recordset_MoveFirst;

	this.MoveNext = Recordset_MoveNext;

	this.MovePrevious = Recordset_MovePrevious;

	this.MoveLast = Recordset_MoveLast;

	this.AddNew = Recordset_AddNew;

	this.Update = Recordset_Update;

	this.Delete = Recordset_Delete;

	this.Sort = Recordset_Sort;

	this.Distinct = Recordset_Distinct;

	this.Filter = Recordset_Filter;

	this.Clone = Recordset_Clone;

	this.ColumnIndex = Recordset_ColumnIndex;

	this.CheckPosition = Recordset_CheckPosition;

	//Initialize

	for(var rowcount=0;rowcount<this.Data.length;rowcount++)

		this.Data[rowcount][this.Columns.length]=rowcount;

	this.CheckPosition();

}

	

	function Recordset_RowId(){

		return this.Data[this.Record][this.Columns.length];

	}

	

	function Recordset_Fields(x, debug){

		this.Record = this.AbsolutePosition-1;

		if (isNaN(x)&&x) {

			for (var i=0; i<this.Columns.length; i++) if (this.Columns[i].toUpperCase() == x.toUpperCase()) return (new Field(this.Columns[i], this.Types[i], this.Data[this.Record][i]));

		} else if (x < this.Data[this.Record].length) {

			return (new Field(this.Columns[x], this.Types[x], this.Data[this.Record][x]));

		}

		alert("Client Recordset Error: Field [" + x + "] not found.");

		return (new Field(x,-1,""));

	}



	function Recordset_Move(i){

		this.Record += i-1;

		this.AbsolutePosition += i;

		this.CheckPosition();

	}



	function Recordset_MoveTo(i){

		this.Record = i;

		this.AbsolutePosition = i+1;

		this.CheckPosition();

	}



	function Recordset_MoveFirst(){

		this.Record = 0;

		this.AbsolutePosition = 1;

		this.EOF = false;

		this.BOF = false;

	}



	function Recordset_MoveNext(){

		this.Record++;

		this.AbsolutePosition++;

		this.CheckPosition();

	}



	function Recordset_MovePrevious(){

		this.Record--;

		this.AbsolutePosition--;

		this.CheckPosition();

	}



	function Recordset_MoveLast(){

		this.Record = this.Data.length-1;

		this.AbsolutePosition = this.Data.length;

		this.EOF = false;

		this.BOF = false;

	}



	function Recordset_AddNew(xFields, xValues){

		this.Record = this.Data.length;

		this.AbsolutePosition = this.Data.length+1;

		this.Data[this.Data.length] = new Array(this.Columns.length);

		this.RecordCount = this.Data.length;

		if (xFields) this.Update(xFields, xValues);

	}



	function Recordset_Update(xFields, xValues){

		this.Record = this.AbsolutePosition-1;

		if (typeof(xFields) == "string") {

			for (var i=0; i<this.Columns.length; i++) {

				if (this.Columns[i].toUpperCase() == xFields.toUpperCase()) {

					this.Data[this.Record][i] = xValues + "";

					return true;

				}

			}

			throw "JS Recordset error: Item (" + xFields + ") not found.";

		} else {

			if (xFields.length) for (var i=0; i<xFields.length; i++) this.Update(xFields[i], xValues[i]);

			return true;

	}}



	function Recordset_Delete(){

		this.Data = RemoveItem(this.Data,this.Record);

		this.RecordCount = this.Data.length;

		this.CheckPosition();

	}



	function Field(strName, intType, strValue){

		this.Name = strName;

		this.Type = intType;

		this.Value = strValue;

	}

	

	/* Sort

		f: Sql type sort expression or a 2d array [column][direction].SQL in the form "[FieldName1] ASC,[FieldName2] desc,[FieldName3]"

		if ASC or DESC is left off, default is ASC for ascending.

	*/			

	function Recordset_Sort(f){

		if(typeof(f)=='string')for(i=0,f=f.split(",");i<f.length;i++)f[i]=Array(f[i].match(/[^\s]*/).toString(),f[i].search(/\sdesc/i)/Math.abs(f[i].search(/\sdesc/i)));

		for(j=f.length-1;j>-1;j--){fld=f[j][0];sdir=f[j][1];idx=this.ColumnIndex(f[j][0]);typ="|"+this.Types[this.ColumnIndex(f[j][0])]+"|";

			this.Data.sort(function(rowA,rowB){

					if("|2|3|4|5|6|14|17|18|19|20|21|".indexOf(typ)>-1) return sdir*(rowA[idx]-rowB[idx]);

					else if("|7|133|134|135|".indexOf(typ)>-1) return sdir*((Date(rowA[idx])>=Date(rowB[idx]))*2-1);

					else if("|8|11|12|129|130|136|200|201|130|202|203|204|205|".indexOf(typ)>-1) return sdir*(LexComp(rowA[idx].toUpperCase(),rowB[idx].toUpperCase(),0));

					else return 0;});}

	}

	

	function LexComp(str1,str2,intChr){

		var ret=(str2+"").charCodeAt(intChr)-(str1+"").charCodeAt(intChr);

		if(ret)return ret;

		else if(!(intChr==str1.length||intChr==str2.length)) return LexComp(str1,str2,++intChr);

		else return 0;

	}

	

	function Recordset_Distinct(iField){

		for(var j=1;j<this.Data.length;j++){

			if(iField!=null)

				equal=(this.Data[j][iField]==this.Data[j-1][iField]);

			else

				for(var idx=0,equal=true;equal&&idx<this.Columns.length;idx++)

					equal=(this.Data[j][idx]==this.Data[j-1][idx]);

			if(equal){RemoveItem(this.Data,j);j--;this.RecordCount--;}

		}

	}

	

	function Recordset_Filter(strClause){

		var re=/(NOT)?(\s*\(*)\s*(\w+)\s*(=|<>|<|>|LIKE|IN)\s*(\(([^\)]*)\)|'([^']*)'|(-?\d*\.?\d+))(\s*\)*\s*)(AND|OR)?/gi;

		for(a=new Array(),j=0;ok=re.exec(strClause);j++){for(a[j]=new Array(),i=0;i<ok.length;i++)a[j][i]=ok[i];if(!(re.lastIndex))strClause=strClause.replace(ok[0],"");}

		for(this.MoveFirst(),strJ='';!this.EOF;strJ=''){

			for(j=0;j<a.length;j++)strJ+=SQL2JS(a[j][1])+SQL2JS(a[j][4],a[j][2],this.Fields(a[j][3]).Value,a[j][6]+a[j][7]+a[j][8],a[j][9])+SQL2JS(a[j][10]);

			eval(strJ)?this.MoveNext():this.Delete();

		}this.MoveFirst();this.CheckPosition();

	}

	

	function SQL2JS(sT,sB1,sV,sC,sB2){

		sM="NOT:!%AND:&&%OR:||%=:"+sB1+"'"+sV+"'=='"+sC+"'"+sB2+"%<>:"+sB1+"'"+sV+"'!='"+sC+"'"+sB2+"%LIKE:"+sB1+"'"+sV+"'.indexOf('"+sC+"')>-1"+sB2+"%IN:"+sB1+"'"+sC+",'.indexOf(',"+sV+",')>-1"+sB2+"%";

		return sT?sM.substring(v=sM.indexOf(sT+":")+sT.length+1,sM.indexOf("%",v)):"";

	}

	

	function Recordset_ColumnIndex(xField){

		for(i=0;i<this.Columns.Count;i++)

			if(this.Columns[i].toUpperCase()==xField.toUpperCase())return i;

		return -1;

	}

	

	function Recordset_Clone() {

       	return new Recordset(CopyArray(this.Columns),CopyArray(this.Types),CopyArray(this.Data))

   	}



   	function Recordset_CheckPosition() {

		this.EOF = (this.Record == this.Data.length || this.Data.length == 0);

		this.BOF = (this.Record == -1 || this.Data.length == 0);

	}

	

	function CopyArray(aryWhat) {

	    aryReturn=new Array();

	    for(i=0;i<aryWhat.length;i++)aryReturn[i]=aryWhat[i];

	    return aryReturn;

	}



	function RemoveItem(aryWhat,intItem){

		 for(var i=intItem;i<aryWhat.length-1;i++)aryWhat[i]=aryWhat[i+1];

		 aryWhat.length-=1;return aryWhat;    	

	}
