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.RowIndexOf = Recordset_RowIndexOf;
	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.splice(this.Record,1);
		this.RecordCount--;
		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. Distinct: boolean to filter similar rows.
	*/			
	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){this.Data.splice(j,1);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]?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_RowIndexOf(xField,sFind,iStart){
		if(!sFind) return -1;
		col=this.ColumnIndex(xField);
		for(var j1=iStart?iStart:0;j1<this.Data.length;j1++){
			//alert((j1+1)+" of "+this.Data.length+":"+this.Data[j1][col]+"\n\n"+sFind+":"+this.Data[j1][col].indexOf(sFind))
			if(this.Data[j1][col].indexOf(sFind)>-1)
				return j1;
		}
		return -1;
	}
	
	function Recordset_ColumnIndex(xField){
		if(!isNaN(xField))return 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;
	}

