function db_escape(str){	
	if(typeof str !=='string') return str;
//	str=str.replace(/\\/g,'\\'+'\\');
//	str=str.replace(new RegExp("'",'g'),"\\'");
//	str=str.replace(/\r/g,'<n'+'l>'); 
	str=str.replace(";", "\;");
	return str;
}
function db_unescape(str){
	if(typeof str !=='string') return str;
	str=str.replace(/\;/gi,';');
//	str=str.replace(/<n\l>/gi,'\n');
//	str=str.replace(/&lt/gi,'<');
//	str=str.replace(new RegExp("\\"+"\\",'g'),"\\") 
//	str=str.replace(new RegExp("\\'",'g'),"'");
//	str=str.replace(/<n\l>/gi,'\r\n'); 
	return str;
}

/* Peter Tracey peter@levelthreesolutions.com
It takes an array of fieldnames
Example:  ["field1", "field2"]
and a 2-dimensional array of the dataset data
Example: [["field1row1data", "field2row1data"], ["field1row2data", "field2row2data"]]
*/

function datasetEditFlag(keyVal){
	if(keyVal==undefined) keyVal=undefined
	this.keyVal=(keyVal) //this.keyVal=db_escape(keyVal)
	this.isNew=false;
	this.dirties = new Array();
}
function datasetField(arrDef) {
	var idx=0;
	this.alias 	= (arrDef[idx]!==undefined) ? arrDef[idx] : '-?'  ; idx++;
	this.caption	= (arrDef[idx]!==undefined) ? arrDef[idx] : '-?'  ; idx++;
	this.type 	= (arrDef[idx]!==undefined) ? arrDef[idx] : 's'	; idx++;
	this.align 	= (arrDef[idx]!==undefined) ? arrDef[idx] : 'left'	; idx++;
	this.updatable 	= (arrDef[idx]!==undefined) ? arrDef[idx] : 0	; idx++;
	this.format	= (arrDef[idx]) ? arrDef[idx] : '2.,'  	; idx++;
	this.width	= (arrDef[idx]!==undefined) ? arrDef[idx] : 0	; idx++;	
	this.hidden	= (arrDef[idx]!==undefined) ? arrDef[idx] : false	; idx++;
	this.defaultVal	= (arrDef[idx]!==undefined) ? arrDef[idx] : ''	; idx++;
	this.stampVal 	= (arrDef[idx]!==undefined) ? arrDef[idx] : ''	; idx++;
	this.dropdown 	= (arrDef[idx]!==undefined) ? arrDef[idx] : ''	; idx++;
	this.dropdownCol= (arrDef[idx]!==undefined) ? arrDef[idx] : 0 	; idx++;		
	this.onchange	= '';
	this.required	= false;
	this.height	 	= 0;
//	_log(arrDef,this.alias , this.format)
	this.value 		= '';
	
	
//	this.align = this.type.match(/i|n/gi) ? 'right' :'left' ;
	this.toString=function() {    	
    	if(this.type=='s') return this.value;
    	if(this.type=='b') return this.value;
    	if(this.type=='i') {
    		return this.value;
    	}
    	if(this.type=='n') {    	
//   			format =this.format;    			
//    		return number_format(this.value, format[0], format[1], format[2]);
		return  this.value;
    	}
    	if(this.type=='d') {
   			if (this.format=='') this.format=xDateFormat;
    		return cDate(this.value, this.format );    	
    	}
		return  this.value;
        
    }		
	
}


function dataset(arrColumns,arrData) {	
	if(arrColumns==undefined)	arrColumns=[];
	if(arrData==undefined)		arrData=[1,[]];
	
	this.columns = arrColumns;	
	this.data =  arrData[1];
	this.pageCount= arrData[0];
	this.record = 0;
	this.position = 1;
	this.pageNo=1;	
	this.eof = false;
	this.bof = false;	
	this.callUpdate=null;
	this.dontCallback=false
	
	this.initialize=function(oid){
		this.columns.count = this.columns.length;
		this.recordCount = this.data.length;
		this.unescapeData();
		this._deletedRows = new Array(0);
		this.addFlags();
		this.fields= new Array();
		for (var i=0; i<this.columns.length; i++) {
			var fld = new datasetField(this.columns[i])
			this.fields.push(fld);
		}
		this.reposition();
	}

	this._flag = function (r){
		if (r==undefined) r = this.record;		
		try { return this.data[r][this.flagIdx]
		 }catch(e){
		 alert('flag err row:'+r)} 
	}

	
	this.addFlags = function() {
		this.flagIdx = this.columns.count + 1;		
		var keyVal=undefined;
		for (var i = 0; i < this.data.length; i++){
			if(this.keyIndex>=0) keyVal=this.data[i][this.keyIndex]
			this.data[i][this.flagIdx]=new datasetEditFlag(keyVal)
		} 
	}	
	this.unescapeData=function(){
return;
		for (var r = 0; r < this.data.length; r++){
			for (var c = 0; c < this.columns.count; c++){
				this.data[r][c] = db_unescape(this.data[r][c])
			}
		}
	}
	

	this.colIndex = function (x) {
		if (isNaN(x*1)) {
			for (var i=0; i<this.columns.length; i++) 
				if (this.columns[i][0].toUpperCase() == x.toUpperCase()) return i;
		} else if (x < this.columns.count && x>=0) {
			return x;
		}
		
		if(this.columns.length)_log("dataSet.colIndex error: Field item (" + x + ") not found.");
		return -1;
		
	}

	this.field = function (x)	{
		i= this.colIndex(x);		
		return this.fields[i] 
	}

	this.move 	= function (i)	{this.record += i-1;		this.reposition();	}
	this.moveTo = function (i)	{this.record = i-1;			this.reposition();	}
	this.first 	= function ()	{	this.record = 0;			this.reposition();		}
	this.next 	= function ()	{this.record++;				this.reposition(0);	}
	this.previous =function ()	{this.record--;				this.reposition(1);	}
	this.last 	= function () 	{this.record = this.data.length-1;	this.reposition();}
	
	this.reposition = 	function (iDirection) {
//		if(this.record < 0) this.record = -1;
		if(this.record < 0) this.record = 0;
		if(this.record > this.data.length) this.record = this.data.length;	
		this.position = this.record +1;		
		switch(iDirection) {
		case 0 : // forward - check eof
			if (this.record == this.data.length) this.eof = true;
			else this.eof = false;
			break;
		case 1 : // backward - check bof
			if (this.record == -1) this.bof = true;
			else this.bof = false;
			break;		
		default :
			if (this.record == this.data.length || this.data.length == 0) this.eof = true;
			else this.eof = false;
			if (this.record == -1 || this.data.length == 0) this.bof = true;
			else this.bof = false;
		}
		
		this.readRow();	
		if(!this.dontCallback && this.callReposition) eval(this.callReposition);
	}
	this.readRow = function(){
		for (var i=0; i<this.columns.length; i++) {
			try { fldVal=this.data[this.record][i];	 }catch(e){fldVal=''} 
			this.fields[i].value=fldVal;
		}

	}
	this.addNew = function (xFields, xValues) {		
		this.__newRow = new Array(this.columns.length);
		for (var i = 0; i < this.__newRow.length; i++) this.__newRow[i]='' ; 
		this.__newRow[this.flagIdx]=new datasetEditFlag();
		this.__newRow[this.flagIdx].isNew=true;
		
		this.data[this.data.length] =this.__newRow;
		this.recordCount = this.data.length;		
		
		this.dontCallback=true;
		this.last();
		this.dontCallback=false;
		
		if(this.callAddnew) eval(this.callAddnew);
		if (xFields || xFields===0) this.update(xFields, xValues);
		for (var i=0; i<this.columns.length; i++) {			
			if(this.field(i).defaultVal!==''){
				if(this.field(i).value=='') this.update(i, this.field(i).defaultVal)
			}
			if(this.field(i).stampVal!=='' ){
				this.update(i, this.field(i).stampVal)
			}
		}
		
		
	}
	this.update = function (xFields, xValues) {
		if(this.recordCount==0 ){
			this.addNew (xFields, xValues);
			return
		} 
		if(this.eof || this.bof) return;
		this.record = this.position-1;
		
		if ( (typeof(xFields) == "string")|| (typeof(xFields) == "number")) {
			i= this.colIndex(xFields);
			if(i==undefined){alert('dataset.field ' + xFields +' not found!');return}
			if(i>-1){			
				if(this.field(i).stampVal) {
					xValues =this.field(i).stampVal; 
				}else{
					if(this.field(i).type=='s' && typeof(xFields) == "string" && typeof(xValues) == "string"  ) xValues = xValues.removeHTMLent() ;  
				}	
				this.data[this.record][i] = xValues + "";
				this._flag().dirties.uniqueAdd(i);
				
				this.readRow();
				if(this.field(i).onchange) 
					try { if(!this.field(i)._calcOnchange){
							this.field(i)._calcOnchange=true;
							eval(this.field(i).onchange);
							this.field(i)._calcOnchange=false;
						  }
					 }catch(e){_log('onChange Error:',xFields,this.field(i).onchange)} 
	 
				if(this.field(i).hasTotal) this.calcTotal(i);
				if(this.callUpdate) eval(this.callUpdate);
			}
		 }else {
			if (xFields.length) for (var i=0; i<xFields.length; i++) this.update(xFields[i], xValues[i]);
			this._flag().dirties.uniqueAdd(i);
			//readRow and callUpdate executed from first if branch
		}
		return true;
	}
	this.calcTotal = function(col){	
		t=0;
		for (var r = 0; r < this.data.length; r++){			
			t+=xval(this.data[r][col]) 
		} 		
		this.field(i).total=t;		
	}	
	this.deleteRow = function () {
		this._deletedRows.push(this.data[this.record]);
		this.data.remove(this.record);
		this.recordCount = this.data.length;
		this.record--;
		this.position--;
		this.reposition();
	}

	this.deleteRows = function (aRows) {
		for (var i = 0; i < aRows.length; i++){
		 var _record = aRows[i]-1
		 this._deletedRows.push(this.data[_record]);
		 this.data[_record].deleted=true
		} 
		var newData=[]
		for (var i = 0; i < this.data.length; i++){
		 if(!this.data[i].deleted) newData.push(this.data[i])
		}
		this.data=newData;
		this.recordCount = this.data.length;
		this.record--;
		this.position--;
		this.reposition();
	}
	
	this.findFirst = function(fld,str){
		col= this.colIndex(fld);
		for (var i = 0; i < this.data.length; i++){
			if((this.data[i][col]+'').like(str)){
				this.moveTo(i+1);
				return i+1;
				break;
			}
		} 		
		return false;
	}
	this.findNext = function(fld,str){
		col= this.colIndex(fld);
		for (var i = this.record+1; i < this.data.length; i++){
			if((this.data[i][col]+'').like(str)){
				this.moveTo(i+1);
				return i+1;
				break;
			}
		}
		return false;
	}
	this.findPrev = function(fld,str){
		col= this.colIndex(fld);
		for (var i = this.record; i >=0; i--){
			if((this.data[i][col]+'').like(str)){
				this.moveTo(i+1);
				return i+1;
				break;
			}
		}
		return false;
	}

	
	
	this.sort = function (index, sortasc){
		if(sortasc==undefined) sortasc=true;
		var i= this.colIndex(index);
		if(i<0)return
		var asDate= (this.field(i).type=='d')
		var asString= (this.field(i).type=='s') 
		this.data.multiSort(i,sortasc,asDate,asString );

	}

	this.refreshFields = function (){
		srv = new clsAjaxCall('database.cls', this.oid, 'getFields()')
		srv.runAJAX(); 

		var newFields= new Array();		
		var allColumns = eval(srv.response);
		for (var i=0; i<allColumns.length; i++) {
			var fName = allColumns[i][0];
			var fld = null;
			for (var f=0; f<this.columns.length; f++) {
				if (this.columns[f][0].toUpperCase() == fName.toUpperCase()) {
					fld = this.field(fName)
					fld.type=allColumns[i][2];
					fld.align = fld.type.match(/i|n/gi) ? 'right' :'left' ;
					continue;
				}
			}
			if(fld==undefined) 	fld = new datasetField(allColumns[i]);			
			newFields.push(fld);			
		}
		this.fields= newFields;
		this.columns = allColumns
		this.columns.count = allColumns.length;		
	}
	
	this._refresh_old = function (){
	
		srv = new clsAjaxCall('database.cls', this.oid, 'refresh()')
		srv.setVar ("pageNo",this.pageNo)
		srv.runAJAX(); 
		var arrData = eval(srv.response);
		this.pageCount= arrData[0];
		this.data =  arrData[1];
		if(this.pageNo>this.pageCount) this.pageNo=this.pageCount;

		//check fields
		if(this.data.length){
			if(this.data[0].length!==this.columns.count) this.refreshFields();			
		}
		this.unescapeData();
		
		this._deletedRows = new Array(0);
		this.addFlags();				

		this.recordCount = this.data.length;
		this.moveTo(this.position);
		if(this.eof)this.last();
		
		return true;	
	}

	this.refresh = function (callBack,sync){
	if(sync==undefined)sync=false
		srv = new clsAjaxCall('database.cls', this.oid, 'refresh()')
		srv.setVar ("pageNo",this.pageNo)
		srv.async=!sync
		srv.callBack = callBack
		srv._ds=this
		srv.onCompletion=function(){
		
			var arrData = eval(this.response);
			this._ds.pageCount= arrData[0];
			this._ds.data =  arrData[1];
			if(this._ds.pageNo>this._ds.pageCount) this._ds.pageNo=this._ds.pageCount;
			if(this._ds.pageNo==0 )this._ds.pageNo=1 
	
			//check fields
			if(this._ds.data.length){
				if(this._ds.data[0].length!==this._ds.columns.count) this._ds.refreshFields();
			}
			this._ds.unescapeData();
			
			this._ds._deletedRows = new Array(0);
			this._ds.addFlags();				
	
			this._ds.recordCount = this._ds.data.length;
			this._ds.moveTo(this._ds.position);
			if(this._ds.eof)this._ds.last();
			
			if(this.callBack)callBack();
		}
		srv.runAJAX(); 
		
	}
	
	this.applyFilter = function (fld, fldVal, reset){	
		if(reset==undefined) reset=true;
		srv = new clsAjaxCall('database.cls', this.oid, 'applyFilter()')
		srv.setVar ("fld",fld)
		srv.setVar ("fldVal",fldVal)
		srv.setVar ("reset",reset)		
		srv.runAJAX();		
	}
	this.check = function (){	
		srv = new clsAjaxCall('database.cls', this.oid, 'check()')
		srv.runAJAX(); 		
	}
	
	this.save = function (doExecute){
		if(doExecute == undefined)doExecute=true
		
		if(this.keyIndex<0) {
			_log( 'dataset.Save: keyIndex<0');
			return false;
		}
		
		this.dontCallback=true;
		var crtPosition= this.position

		
		var arrUpdate = new Array();
  		var arrAddNew = new Array();		

		this.first();
        while (!this.eof)        {
        	var dirties=this._flag().dirties;
        	if(dirties.length){        		
		        var record = {};var hasData=false;
		        for(var i=0; i<this.columns.count; i++)     {
				if((this.field(i).updatable && dirties.uniqueExists(i)) ){
			        	fVal = (this.field(i).value); //fVal = db_escape(this.field(i).value);
			        	hasData = hasData || (this.field(i).value && this.field(i).value!==this.field(i).defaultVal && this.field(i).value!==this.field(i).stampVal);
			        	if(this.field(i).type== 'n') fVal=xval(fVal);
			        	if(this.field(i).type== 'b') fVal=strBool(fVal); 
					record[i]=fVal
			        }
		        }
		        if(this._flag().isNew){
		        	if(hasData) arrAddNew.push(record);
		        }else{
				record[-1]=this._flag().keyVal
		        	arrUpdate.push(record);
		        }
	        }
	        this.next();	        
        }  
        
        this.moveTo(crtPosition);
        this.dontCallback=false
        
		arrDelete = new Array();
		for (var r = 0 ; r< this._deletedRows.length;r++){			
			if(this._deletedRows[r][this.flagIdx].isNew) continue;
	        	fVal = this._deletedRows[r][this.flagIdx].keyVal;
        		arrDelete.push(fVal);
		}
		
		if(!(arrUpdate.length || arrAddNew.length || arrDelete.length)) return true;
		
		var func = doExecute ?  'saveData()' :  'setSQLsaveData()';
		srv = new clsAjaxCall( 'database.cls', this.oid, func)
		srv.setVar ( "update",json_encode(arrUpdate ))
		srv.setVar ( "addnew",json_encode(arrAddNew ))
		srv.setVar ( "delete",json_encode(arrDelete ))
		
		srv.runAJAX(); 
		
		if(srv.response.match(/error/gi)) {
			return false
		}else{ 
			if(doExecute){
			this._deletedRows = new Array(0);
			this.addFlags();
			}
			return true;
		}	
	}

	this.requiredMissing=function(){
		var requires=[];
		for (var i = 0; i < this.fields.length; i++){
			if(this.field(i).required) requires.push(i)
		}
		if(requires.length==0) return -1
		
		this.first();
		while (!this.eof){
			for (var i = 0; i < requires.length; i++){				
				if( this.field(requires[i]).value=='' ){					
					return requires[i]
				}
			}
			this.next();
		}
		
		return -1

	}
	
//	this.initialize();
}

function collection() {
this.keys=[];this.length =0
     this.add = function (key,newItem) {
          if (newItem == null) return;
          this.keys[this.keys.length] = key;
          this[key]=newItem;
          this.length ++
     }
     this.insertBefore = function (key,newItem, refKey) {
          if (newItem == null) return;
		  var idxRef=this.getIndex(refKey);
		  this.keys.splice(idxRef,0,key);
		  
          this[key]=newItem;
          this.length ++
     }
     this.insertAfter = function (key,newItem, refKey) {
          if (newItem == null) return;
		  var idxRef=this.getIndex(refKey)+1;
		  this.keys.splice(idxRef,0,key);
		  
          this[key]=newItem;
          this.length ++
     }
     this.remove = function(kindex){
     	  key	=this.getKey(kindex)
          index	=this.getIndex(kindex)       
          this[key] = null;
          for (var i = index; i < this.keys.length; i++) this.keys[i] = this.keys[i + 1];
          this.keys.length--
          this.length --
          
     }
     this.getIndex=function(key){
     	if((key+'').isNumber()) return key;
     	for (var i = 0; i < this.keys.length; i++) if(this.keys[i]==key) return i
     	return null
     }
     this.getKey=function(idx){
     	if(!(idx+'').isNumber()) return idx;
     	if (idx < 0 || idx > this.keys.length ) return null;
     	return this.keys[idx]     	
     }   
     this.clear = function(){
     	for (var i = 0; i < this.keys.length; i++)  this[this.keys[i]]=null
     	this.keys=[];
     	this.length =0;
    }
     this.item = function(kindex){
     	key	= this.getKey(kindex)
     	return this[key]
     }   
}
