// JavaScript Document

/*
 *Vector in public/Utils.js
 *Map in public/Utils.js
*/

/*
 *@param name
 *@param type:0 is String/1 is Integer/2 is Float/4 is html;
 *@param length
 *@param scale
 *@param keyed
 */
function Field(name,type,length,scale,keyed,exotic) {
  this.name=name;
  this.type=type;
  this.length=length;
  this.scale=scale;
  this.keyed=keyed;
  this.exotic=exotic;
}

Field.prototype.getName=function() {
  return this.name;
}

Field.prototype.getType=function() {
  return this.type;
}

Field.prototype.getLength=function() {
  return this.length;
}

Field.prototype.getScale=function() {
  return this.scale;
}

Field.prototype.isKey=function() {
  return this.keyed;
}

Field.prototype.isExotic=function() {
  return this.exotic==true;
}

///////////////////////////////////
/**
 *@param fields:array of Field
 */
function FieldSet(fields) {
  this.fields=fields;
}

FieldSet.prototype.getFieldByName=function(name) {
  for (var i=0;i<this.fields.length;i++)
    if (this.fields[i].getName()==name) return this.fields[i];
  alert("The "+name+" field is not exist!");
  return null;
}

FieldSet.prototype.getFieldByIndex=function(index) {
  return this.fields[index];
}

FieldSet.prototype.getFieldIndex=function(field) {
  for (var i=0;i<this.fields.length;i++)
    if (this.fields[i]==field) {
		return i;
	}
  return -1;
}

FieldSet.prototype.getFieldCount=function() {
  return this.fields.length;
}

///////////////////////////////////

function RecordField(field,value) {
	this.field=field;
	this.oldValue=value;
	this.lastValue=value;
	this.setValue(value);
}

RecordField.prototype.getField=function() {
  return this.field;
}

RecordField.prototype.getValue=function() {
  return this.value;
}

RecordField.prototype.setValue=function(value) {
  var tempValue=null;
  if (this.field.getType()==1 || this.field.getType()==2) {
	 if (!value || value.toString()=="") {
		 tempValue=0;
	 } else {
		if (this.field.getType()==1) tempValue=xRound(value,0);
		else tempValue=xRound(value,this.field.getScale());
	 }
  } else tempValue=value;
  this.value=tempValue;
}

RecordField.prototype.isChanged=function() {
	return this.value!=this.oldValue;
}

RecordField.prototype.getOldValue=function() {
	return this.oldValue;
}

RecordField.prototype.post=function() {
	this.lastValue=this.value;
}

///////////////////////////////////
/**
 *@param fields:FieldSet
 *@param values:array of value
 */
function Record(fields,values) {
  this.fields=fields;
  this.rs=new Array();
  this.newRecord=false;
  this.status=this.NORMAL_STATUS;
  for (var i=0;i<fields.getFieldCount();i++) {
    if (values)	 {
		if (values[i])
		  this.rs[i]=new RecordField(fields.getFieldByIndex(i),values[i]);
		else this.rs[i]=new RecordField(fields.getFieldByIndex(i),null);
	}
	else this.rs[i]=new RecordField(fields.getFieldByIndex(i),null);
  }
  this.listeners=new EventListenerList();
}

Record.prototype.isNew=function() {
	return this.newRecord==true;
}

Record.prototype.PROPERTY_CHANGE="PROPERTY_CHANGE";
Record.prototype.VALUE_CHANGE="VALUE_CHANGE";
Record.prototype.BEFORE_POST="BEFORE_POST";
Record.prototype.AFTER_POST="AFTER_POST";
Record.prototype.NORMAL_STATUS=0;
Record.prototype.UPDATE_STATUS=1;

Record.prototype.addPropertyChangeListener=function(ls) {
	this.listeners.add(this.PROPERTY_CHANGE,ls);
}
Record.prototype.removePropertyChangeListener=function(ls) {
	this.listeners.remove(this.PROPERTY_CHANGE,ls);
}

Record.prototype.addBeforePostListener=function(ls) {
	this.listeners.add(this.BEFORE_POST,ls);
}
Record.prototype.removeBeforePostListener=function(ls) {
	this.listeners.remove(this.BEFORE_POST,ls);
}

Record.prototype.addAfterPostListener=function(ls) {
	this.listeners.add(this.AFTER_POST,ls);
}
Record.prototype.removeAfterPostListener=function(ls) {
	this.listeners.remove(this.AFTER_POST,ls);
}

Record.prototype.getValueByName=function(name) {
   var index=this.getFieldSet().getFieldIndex(this.getFieldSet().getFieldByName(name));
   return this.getValueByIndex(index);
}

Record.prototype.getValueByIndex=function(index) {
   return this.rs[index].getValue();
}

Record.prototype.setValueByName=function(name,value) {
	var index=this.getFieldSet().getFieldIndex(this.getFieldSet().getFieldByName(name));
    this.setValueByIndex(index,value);
}

Record.prototype.setValueByIndex=function(index,value) {
	if (this.getValueByIndex(index)==value) return;
	var e={};
	e.source=this;
	e.propertyName=this.VALUE_CHANGE;
	e.oldValue=this.getValueByIndex(index);
	e.value=value;
	e.index=index;
	this.rs[index].setValue(value);
	this.setStatus(this.UPDATE_STATUS);
	this.processEvent(this.PROPERTY_CHANGE,e);
}

Record.prototype.getStatus=function() {
	return this.status;
}
Record.prototype.setStatus=function(status) {
	this.status=status;
}

Record.prototype.post=function() {
	if (this.status==this.NORMAL_STATUS && !this.isNew()) return;
	var e={};
	e.source=this;
	e.interrupted=false;
	this.processEvent(this.BEFORE_POST,e);
	if (e.interrupted==true) return false;
	for (var i=0;i<this.rs.length;i++) this.rs[i].post();
	this.processEvent(this.AFTER_POST,e);
	this.setStatus(this.NORMAL_STATUS);
	return e.interrupted==false;
}

Record.prototype.getFieldSet=function() {
  return this.fields;
}

Record.prototype.isChanged=function() {
  for (var i=0;i<this.rs.length;i++) 
    if (this.isFieldChanged(i)) return true;
  return false;
}

Record.prototype.isFieldChanged=function(index) {
  return this.rs[index].isChanged();
}


Record.prototype.processEvent=function(which,e) {
  var lses=this.listeners.get(which);
  if (lses) {
     for (var i=0;i<lses.length;i++) lses[i](e);
  }
}

///////////////////////////////////
/**
 *@param fs:FieldSet
 *@param values:array of array of value
 */
function DataTable(fs,values) {
  this.listeners=new EventListenerList();
  this.reset(fs,values);
  var ds=this;
  this.defaultRecordPropertyChangeListener=function(e) {
	 e.dataTable=ds;
	 e.rowIndex=ds.getRecordIndex(e.source);
	 ds.processEvent(ds.PROPERTY_CHANGE,e); 
  }
  this.defaultBeforePostListener=function(e) {
	 e.dataTable=ds;
	 ds.processEvent(ds.BEFORE_POST,e); 
  }
  this.defaultAfterPostListener=function(e) {
	 e.dataTable=ds;
	 ds.processEvent(ds.AFTER_POST,e); 
  }
}

DataTable.prototype.BEFORE_SCROLL="BEFORE_SCROLL";
DataTable.prototype.AFTER_SCROLL="AFTER_SCROLL";
DataTable.prototype.BEFORE_POST="BEFORE_POST";
DataTable.prototype.AFTER_POST="AFTER_POST";
DataTable.prototype.DELETE_RECORD="DELETE_RECORD";
DataTable.prototype.INSERT_RECORD="INSERT_RECORD";
DataTable.prototype.PROPERTY_CHANGE="PROPERTY_CHANGE";
DataTable.prototype.PARTIAL_LOCATE="0";
DataTable.prototype.FULL_LOCATE="1";

DataTable.prototype.reset=function(fs,values) {
   this.fs=fs;
   this.deletedRecords=new Vector();
   this.rs=new Vector();
   if (values) {
      for (var i=0;i<values.length;i++) {
         this.rs[i]=new Record(fs,values[i]);
		 this.addDefaultRecordListener(this.rs[i]);
	  }
   }
   this.crIndex=0;
   var e={};
   e.source=this;
   e.propertyName="ALL_CHANGE";
   this.processEvent(this.PROPERTY_CHANGE,e);
}

DataTable.prototype.getFieldSet=function() {
    return this.fs;
}

DataTable.prototype.getCurrentRecord=function() {
	if (this.bof() || this.eof()) return null;
	return this.rs[this.crIndex];
}

DataTable.prototype.getRecordCount=function() {
    return this.rs.length;
}

DataTable.prototype.getRecord=function(index) {
    return this.rs[index];
}

DataTable.prototype.newRecord=function() {
    var rd=new Record(this.getFieldSet(),new Array());
	rd.newRecord=true;
	return rd;
}

DataTable.prototype.getRecordIndex=function(rd) {
	for (var i=0;i<this.rs.length;i++)
	   if (this.rs[i]==rd) return i;
    return -1;
}

DataTable.prototype.insertAtCurrent=function() {
	var index=0;
	if (this.bof()) index=0;
	else if (this.eof()) return this.append();
	else index=this.crIndex;
	return this.insert(index);
}

DataTable.prototype.insertRecordAtCurrent=function(rd) {
	var index=0;
	if (this.bof()) index=0;
	else if (this.eof()) {
		this.appendRecord(rd);
		return;
	}
	else index=this.crIndex;
	this.insertRecord(index,rd);
}

DataTable.prototype.deleteCurrent=function() {
	if (this.bof() || this.eof()) return;
	this.deleteByIndex(this.crIndex);
}

DataTable.prototype.insert=function(index) {
	if (this.beforeFirst()==false) return null;
    var rd=this.newRecord();
    this.insertRecord(index,rd);
	this.setCurrentIndex(index);
    return rd;
}

DataTable.prototype.append=function() {
    return this.insert(this.getRecordCount());
}

DataTable.prototype.deleteByIndex=function(index) {
	var rd=this.getRecord(index);
    this.rs.remove(index);
	if (!rd.isNew()) {
		this.deletedRecords.add(rd);
	}
    this.removeDefaultRecordListener(rd);
	var e={};
	e.source=this;
	e.index=index;
	this.processEvent(this.DELETE_RECORD,e);
	if (this.crIndex==index) {
		if (this.eof()) this.setCurrentIndex(this.crIndex-1);
		else {
	      this.crIndex=this.crIndex-1;
	      this.setCurrentIndex(index);
		}
	}
}

DataTable.prototype.insertRecord=function(index,rd) {
  if (rd.getFieldSet()!=this.getFieldSet()) return;
  this.rs.insert(index,rd);
  if (this.crIndex>=index) this.crIndex=this.crIndex+1;
  this.addDefaultRecordListener(rd);
  var e={};
  e.source=this;
  e.index=index;
  this.processEvent(this.INSERT_RECORD,e);
}

DataTable.prototype.addDefaultRecordListener=function(rd) {
  rd.addPropertyChangeListener(this.defaultRecordPropertyChangeListener);
  rd.addBeforePostListener(this.defaultBeforePostListener);
  rd.addAfterPostListener(this.defaultAfterPostListener);
}
DataTable.prototype.removeDefaultRecordListener=function(rd) {
	rd.removePropertyChangeListener(this.defaultRecordPropertyChangeListene);
	rd.removeBeforePostListener(this.defaultBeforePostListener);
    rd.removeAfterPostListener(this.defaultAfterPostListener);
}

DataTable.prototype.appendRecord=function(rd) {
  this.insertRecord(this.getRecordCount(),rd);
}

DataTable.prototype.deleteRecord=function(rd) {
  var index=this.getRecordIndex(rd);
  if (index>=0) this.deleteByIndex(index);
}

DataTable.prototype.getCurrentIndex=function() {
  return this.crIndex;
}

DataTable.prototype.getValueByName=function(rowIndex,colName) {
   return this.getRecord(rowIndex).getValueByName(colName);
}

DataTable.prototype.getValueByIndex=function(rowIndex,colIndex) {
   return this.getRecord(rowIndex).getValueByIndex(colIndex);
}

DataTable.prototype.setValueByName=function(rowIndex,colName,value) {
	this.getRecord(rowIndex).setValueByName(colName,value);
}

DataTable.prototype.setValueByIndex=function(rowIndex,colIndex,value) {
	this.getRecord(rowIndex).setValueByIndex(colIndex,value);
}

DataTable.prototype.getCurrentValueByName=function(colName) {
	if (this.bof() || this.eof()) return null;
    return this.getValueByName(this.crIndex,colName);
}

DataTable.prototype.getCurrentValueByIndex=function(colIndex) {
	if (this.bof() || this.eof()) return null;
    return this.getValueByIndex(this.crIndex,colIndex);
}

DataTable.prototype.setCurrentValueByName=function(colName,value) {
	if (this.bof() || this.eof()) return;
	this.setValueByName(this.crIndex,colName,value);
}

DataTable.prototype.setCurrentValueByIndex=function(colIndex,value) {
	if (this.bof() || this.eof()) return null;
	this.setValueByIndex(this.crIndex,colIndex,value);
}

DataTable.prototype.setCurrentIndex=function(index) {
	if (this.crIndex==index) return;
	var rd=this.getCurrentRecord();
	if (rd) {
		if (rd.post()==false) {
			return false;
		}
	}
	var e={};
	e.source=this;
	e.fromRowIndex=this.getCurrentIndex();
	e.toRowIndex=index;
	this.processEvent(this.BEFORE_SCROLL,e);
    this.crIndex=index;
	this.processEvent(this.AFTER_SCROLL,e);
	return true;
}

DataTable.prototype.isEmpty=function() {
    var count=this.getRecordCount();
    return count<=0;
}

DataTable.prototype.bof=function() {
    if (this.crIndex<0 || this.isEmpty()) return true;
    return false;
}

DataTable.prototype.eof=function() {
    if (this.crIndex>=this.getRecordCount() || this.isEmpty()) return true;
    return false;
}

DataTable.prototype.beforeFirst=function() {
	return this.setCurrentIndex(-1);
}

DataTable.prototype.first=function() {
  if (this.isEmpty()) return true;
  return this.setCurrentIndex(0);
}

DataTable.prototype.prior=function() {
  if (this.isEmpty() || this.bof()) return true;
  return this.setCurrentIndex(this.getCurrentIndex()-1);
}

DataTable.prototype.next=function() {
  if (this.isEmpty() || this.eof()) return true;
  return this.setCurrentIndex(this.getCurrentIndex()+1);
}

DataTable.prototype.last=function() {
  if (this.isEmpty()) return true;
  return this.setCurrentIndex(this.getRecordCount()-1);
}

DataTable.prototype.afterLast=function() {
	return this.setCurrentIndex(this.getRecordCount());
}

DataTable.prototype.sort=function(fieldName,order) {
	var dt=this;
	function sortFunction(one,two) {
	   var result=0;
	   var valueOne=one.getValueByName(fieldName);
	   var valueTwo=two.getValueByName(fieldName);
	   if (valueOne<valueTwo) result=-1;
	   else if (valueOne>valueTwo) result=1;
       if (order==false) result=-result;
	   return result;
	}
	var cr=this.getCurrentRecord();
	this.rs.sort(sortFunction);
	if (cr) {
	   for (var i=0;i<this.rs.length;i++)
	       if (this.getRecord(i)==cr) {
			   this.crIndex=i;
			   break;
		   }
	}
	var e={};
	e.source=this;
	e.propertyName="ALL_CHANGE";
	this.processEvent(this.PROPERTY_CHANGE,e);
}

DataTable.prototype.getChangedData=function() {
	var result=new Vector();
	var count=0;
	for (var i=0;i<this.deletedRecords.length;i++) {
		result[count]=new Array();
		result[count][0]=0;//"DELETE";
		result[count][1]=new Map();
		var rd=this.deletedRecords[i];
		for (var j=0;j<rd.getFieldSet().getFieldCount();j++) {
			var field=rd.getFieldSet().getFieldByIndex(j);
			if (field.isKey()) {
			  var colName=field.getName();
		      result[count][1].add(colName,rd.getValueByName(colName));
			}
		}
		count=count+1;
	}
	
    for (var i=0;i<this.getRecordCount();i++) {
		var rd=this.getRecord(i);
		if (!rd.isNew() && !rd.isChanged()) continue;
		result[count]=new Array();
		var allField=false;
		if (rd.isNew()) {
			allField=true;
			result[count][0]=1;//"INSERT";
		} else result[count][0]=2;//"UPDATE";
		result[count][1]=new Map();
		for (var j=0;j<rd.getFieldSet().getFieldCount();j++) {
			var field=rd.getFieldSet().getFieldByIndex(j);
			if ((field.isKey() || allField || rd.isFieldChanged(j)) && !field.isExotic()) {
			  var colName=field.getName();
			  var value=rd.getValueByName(colName);
			  if ((value!=null && value.toString!="") || field.isKey() || !allField)
		         result[count][1].add(colName,value);
			}
		}
		count=count+1;
	}
	
	return result;
}

DataTable.prototype.locate=function(fieldNames,values,type) {
	var self=this;
	function equal(index) {
		for (var i=0;i<fieldNames.length;i++) {
		   if (!values[i]) continue;
		   var v=self.getValueByName(index,fieldNames[i]);
		   if (!v) return false;
		   var vStr=v.toString();
		   var cStr=values[i].toString();
		   
		   if (type==self.PARTIAL_LOCATE) {
		      if (vStr.indexOf(cStr)<0) return false;
		   } else if (type==self.FULL_LOCATE) {
			  if (vStr!=cStr) return false;
		   } else return false;
		}
		return true;
	}
	var index=-1;
	for (var i=0;i<this.getRecordCount();i++)
	  if (equal(i)==true) {
		  index=i;
		  break;
	  }
	  
	if (index>=0) {
	   this.setCurrentIndex(index);
	}
}

DataTable.prototype.processEvent=function(which,e) {
  var lses=this.listeners.get(which);
  if (lses) {
     for (var i=0;i<lses.length;i++) {
		lses[i](e);
	 }
  }
}

DataTable.prototype.addBeforeScrollListener=function(ls) {
  this.listeners.add(this.BEFORE_SCROLL,ls);
}
DataTable.prototype.removeBeforeScrollListener=function(ls) {
  this.listeners.remove(this.BEFORE_SCROLL,ls);
}

DataTable.prototype.addAfterScrollListener=function(ls) {
  this.listeners.add(this.AFTER_SCROLL,ls);
}
DataTable.prototype.removeAfterScrollListener=function(ls) {
  this.listeners.remove(this.AFTER_SCROLL,ls);
}

DataTable.prototype.addBeforePostListener=function(ls) {
  this.listeners.add(this.BEFORE_POST,ls);
}
DataTable.prototype.removeBeforePostListener=function(ls) {
	this.listeners.remove(this.BEFORE_POST,ls);
}

DataTable.prototype.addAfterPostListener=function(ls) {
    this.listeners.add(this.AFTER_POST,ls);
}
DataTable.prototype.removeAfterPostListener=function(ls) {
	this.listeners.remove(this.AFTER_POST,ls);
}

DataTable.prototype.addPropertyChangeListener=function(ls) {
    this.listeners.add(this.PROPERTY_CHANGE,ls);
}
DataTable.prototype.removePropertyChangeListener=function(ls) {
	this.listeners.remove(this.PROPERTY_CHANGE,ls);
}

DataTable.prototype.addDeleteRecordListener=function(ls) {
    this.listeners.add(this.DELETE_RECORD,ls);
}
DataTable.prototype.removeDeleteRecordListener=function(ls) {
	this.listeners.remove(this.DELETE_RECORD,ls);
}

DataTable.prototype.addInsertRecordListener=function(ls) {
    this.listeners.add(this.INSERT_RECORD,ls);
}
DataTable.prototype.removeInsertRecordListener=function(ls) {
	this.listeners.remove(this.INSERT_RECORD,ls);
}

/*
//////////////////////////////////
/**
 *@param dt:DataTable
 *
function DataSource(dt) {
	this.listeners=new EventListenerList();
	this.setDataTable(dt);
}

DataSource.prototype.DATA_TABLE_CHANGE="DATA_TABLE_CHANGE";

DataSource.prototype.getDataTable=function() {return this.dt;}
DataSource.prototype.setDataTable=function(dt) {
   if (!dt) throw "The data table must not be null";   
   if (this.dt==dt) return;
   var e={};
   e.oldDataTable=this.dt;
   e.dataTable=dt;
   this.dt=dt;
   this.processEvent(this.DATA_TABLE_CHANGE,e);
}

DataSource.prototype.addDataTableChangeListener=function(ls) {
    this.listeners.add(this.DATA_TABLE_CHANGE,ls);
}
DataSource.prototype.removeDataTableChangeListener=function(ls) {
	this.listeners.remove(this.DATA_TABLE_CHANGE,ls);
}

DataSource.prototype.processEvent=function(which,e) {
  var lses=this.listeners.get(which);
  if (lses) {
     for (var i=0;i<lses.length;i++) {
		lses[i](e);
	 }
  }
}

/////////////////////
*/

DataTable.prototype.getDataByXmlURL=function(url) {
	var XMLDoc=new ActiveXObject("MSXML.DOMDocument");
	XMLDoc.async=false;
	XMLDoc.load(url);
	this.getDataByXml(XMLDoc);
}

DataTable.prototype.getDataByXml=function(XMLDoc) {
	var rootNode=XMLDoc.documentElement;
	var fieldNodes=rootNode.selectSingleNode("FieldSet").selectNodes("Field");
	var f=new Array();
	for (var i=0;i<fieldNodes.length;i++) {
		var fieldName=fieldNodes[i].getAttribute("name");
		var fieldType=parseInt(fieldNodes[i].getAttribute("type"));
		var fieldLength=parseInt(fieldNodes[i].getAttribute("length"));
		var fieldScale=parseInt(fieldNodes[i].getAttribute("scale"));
		var fieldKeyed=false;
		if (fieldNodes[i].getAttribute("key")==true || fieldNodes[i].getAttribute("key")=="true") fieldKeyed=true;
		f[i]=new Field(fieldName,fieldType,fieldLength,fieldScale,fieldKeyed);
	}
	var fs=new FieldSet(f);

	var values=new Array();
	var rowNodes=rootNode.selectSingleNode("RowSet").selectNodes("Row");
	for (var i=0;i<rowNodes.length;i++) {
		values[i]=new Array();
		var valueNodes=rowNodes[i].selectNodes("Field");
		for (var j=0;j<valueNodes.length;j++) {
			values[i][j]=valueNodes[j].text;
		}
	}
	
	delete XMLDoc;
	this.reset(fs,values);
}

function encodeChangedDataToXML(map) {
	var result="<?xml version=\"1.0\" ?>\n"+
	           "<OperationSet>";
	for (var i=0;i<map.length;i++) {
		var line="<Operation type=\""+map[i][0]+"\" id=\"\">\n";
		var names=map[i][1].getNameList();
		for (var j=0;j<names.length;j++) {
			line=line+"<"+names[j]+">"+formatStringForXML(map[i][1].getValue(names[j]))+"</"+names[j]+">";
		}
		line=line+"</Operation>";
		result=result+line+"\n";
	}
	result=result+"</OperationSet>";
	return result;
}
