// supported column types (magic values grabbed from datalist/datalist.h)
var CT_UNDEFINED=0x7FFFFFFF;// undefined
var CT_DECIMAL=3; // Decimal
var CT_INTEGER=4; // int
var CT_DOUBLE=8; // double
var CT_DATETIME=9; // Time
var CT_STRING=12; // string
var CT_INT64=-5; // int64_t
// Column Sizes for different types
var CS_INT64=9;
var CS_INTEGER=5;
var CS_STRING=5;
/////////////////////////////////////////////////////////////////////////////////////
// DataSetFeederIF class
/////////////////////////////////////////////////////////////////////////////////////
var DataSetFeederIF = Class.create({
initialize: function() {
this.sContName_ = "";
this.sObjName_ = "";
},
Serialize: function(oS) {
oS.BeginType("DataSetFeederIF");
oS.Serialize(this.sContName_);
oS.Serialize(this.sObjName_);
oS.EndType("DataSetFeederIF");
// throw new Error("Cannot serialize DataSetFeederIF. ");
},
Restore: function(oR) {
oR.BeginType("DataSetFeederIF");
this.sContName_ = oR.Restore();
this.sObjName_ = oR.Restore();
oR.EndType("DataSetFeederIF");
},
ContName: function() { return this.sContName_; },
ObjName: function() { return this.sObjName_; },
SetContName: function(sContName) { this.sContName_ = sContName; },
SetObjName: function(sObjName) { this.sObjName_ = sObjName; },
toString: function() { return "C:"+this.sContName_+",O:"+this.sObjName_; }
});
/////////////////////////////////////////////////////////////////////////////////////
// ADataSource class
/////////////////////////////////////////////////////////////////////////////////////
var ADataSource = Class.create({
initialize: function() {
this.sModule_ = "";
this.sComponent_ = "";
this.sActionSegment_ = "";
this.oParams_ = new Object();
this.oFeeder_ = new Object();
this.aHeader_ = [];
this.nRowCount_ = 0;
},
Serialize: function(oS) {
oS.BeginType("ADataSource");
oS.Serialize(this.sModule_);
oS.Serialize(this.sComponent_);
oS.Serialize(this.sActionSegment_);
oS.Serialize(this.oParams_);
oS.Serialize(this.oFeeder_);
oS.Serialize(this.aHeader_);
oS.Serialize(this.nRowCount_);
oS.EndType("ADataSource");
},
Restore: function(oR) {
oR.BeginType("ADataSource");
this.sModule_ = oR.Restore();
this.sComponent_ = oR.Restore();
this.sActionSegment_ = oR.Restore();
this.oParams_ = oR.Restore();
this.oFeeder_ = oR.Restore();
this.aHeader_ = oR.Restore();
this.nRowCount_ = oR.Restore().Value();
oR.EndType("ADataSource");
},
// getters
Module: function() { return this.sModule_; },
Component: function() { return this.sComponent_; },
ActionSegment: function() { return this.sActionSegment_; },
Params: function() { return this.oParams_; },
Feeder: function() { return this.oFeeder_; },
Header: function() { return this.aHeader_; },
RowCount: function() { return this.nRowCount_; },
CallURL: function() { return "/srv/"+this.sModule_+"/"+this.sComponent_+"/"+this.sActionSegment_; },
// setters
SetFeeder: function(oFeeder) { this.oFeeder_ = oFeeder; }
});
function CompareDataSources(oDSF, oDSS) {
var oSFParams = URLSerialize(oDSF.Params());
var oSSParams = URLSerialize(oDSS.Params());
return (oDSF.Module() == oDSS.Module() &&
oDSF.Component() == oDSS.Component() &&
oDSF.ActionSegment() == oDSS.ActionSegment() &&
oSFParams == oSSParams);
}
var IntRange = Class.create({
initialize: function(oValue) {
if (typeof(oValue) == 'string') this.convertFromString(oValue);
else this.convertFromMinMax(oValue);
},
convertFromMinMax: function(oMM) {
this.nMin = null;
this.nMax = null;
this.nVal = null;
if (oMM === null || (typeof(oMM["Min"]) == 'undefined' && typeof(oMM["Max"]) == 'undefined') ) {
this.nVal = oMM;
return;
}
// this is MinMax
if (typeof(oMM["Max"]) != 'undefined') this.nMax = oMM["Max"];
if (typeof(oMM["Min"]) != 'undefined') this.nMin = oMM["Min"];
},
convertFromString: function(sR) {
// TODO: replace all bad chars???
if (!sR) {
this.nVal = null;
this.nMin = null;
this.nMax = null;
return;
}
var aR=sR.split(/\.{2,}/); // two or more dots
this.nMin = null;
this.nMax = null;
if (aR.length == 1) {
var v = (aR[0]) ? parseInt(aR[0]) : null;
this.nVal = this.convert(v);
this.nMin = null;
this.nMax = null;
return;
}
else if (aR.length != 2) {
alert('IntRange: Bad range spec');
return;
}
// this is a true range
var v = (aR[0]) ? parseInt(aR[0]) : null;
this.nMin = this.convert(v);
v = (aR[1]) ? parseInt(aR[1]) : null;
this.nMax = this.convert(v);
this.nVal = null;
},
Val: function() { return this.nVal; },
MinVal: function() { return this.nMin; },
MaxVal: function() { return this.nMax; },
toString: function() {
if (this.nVal !== null) {
return this.nVal.toString();
}
if (this.nMin == null && this.nMax == null) return '';
var sR = '';
if (this.nMin !== null) sR += this.nMin;
sR += '...';
if (this.nMax !== null) sR += this.nMax;
return sR;
},
ToMinMax: function() {
var oValue = new Object();
if (this.nVal !== null) {
oValue = this.nVal;
} else {
if (this.nMin !== null) oValue["Min"] = this.nMin;
if (this.nMax !== null) oValue["Max"] = this.nMax;
if (this.nMin === null && this.nMax === null) oValue = null;
}
return oValue;
},
convert: function(v) { return (v===null||isNaN(v)) ? null : int(v); }
});
var Int64Range = Class.create(IntRange, {
convert: function(v) { return (v===null||isNaN(v)) ? null : int64(v); }
});
//{FirstRow='10', Size='10'
/////////////////////////////////////////////////////////////////////////////////////
// RowBatch class
/////////////////////////////////////////////////////////////////////////////////////
var RowBatch = Class.create({
initialize: function() {
this.rowIndex_ = 0;
this.colTypes_ = [];
this.indices_ = [];
this.rows_ = 0;
this.data_ = null;
},
Serialize: function(oS) {
LogE("RowBatch serializer is not implemented. ");
},
Restore: function(oR) {
oR.BeginType("RowBatch");
/*this.length=*/oR.Restore().Value(); // unsigned int was there (not used)
this.data_=oR.Restore();
oR.BeginType("RowIndex");
this.rows_=oR.Restore().Value(); // unsigned int was there
this.indices_=[];
for(var i=0; i= this.rows_) return false;
this.colIndex_ = 0; // reset column index
this.index_ = this.indices_[this.rowIndex_];
this.rowIndex_++;
return true;
},
Read: function() {
var nType = this.colTypes_[this.colIndex_];
this.colIndex_++;
var oVal = null;
switch (nType) {
case CT_INTEGER: return this.getInt();
case CT_INT64: return this.getInt64();
case CT_STRING: return this.getString();
case CT_DATETIME: return this.getDateTime();
case CT_DOUBLE: return this.getDouble();
case CT_DECIMAL: return this.getDecimal();
default: // nothing to do
alert("RowBatch: Unknown column type: "+nType);
this.rowIndex_ = this.rows_; // to stop fetching
}
return oVal;
},
getInt: function() {
if(this.data_.charCodeAt(this.index_)!=0) {
this.index_ += CS_INTEGER;
return null; // NULL
}
var res=0;
if(this.data_.charCodeAt(this.index_+4)<128) { // positive
var pow=1;
for(var i=1; i<=4; ++i) {
var val=this.data_.charCodeAt(this.index_+i);
res+=val*pow;
pow*=256;
}
}
else { // negative
var pow=1;
for(var i=1; i<=4; ++i) {
var val=this.data_.charCodeAt(this.index_+i);
val=(~val)&255;
res+=val*pow;
pow*=256;
}
res+=1;
res=-res;
}
this.index_ += CS_INTEGER;
// TODO: optimization for Int
return res;
},
getInt64: function() {
// TODO: will give wrong results on real Int64 values (of order 2^60)
if(this.data_.charCodeAt(this.index_)!=0) {
this.index_ += CS_INT64;
return null; // NULL
}
var res=0;
if(this.data_.charCodeAt(this.index_+8)<128) { // positive
var pow=1;
for(var i=1; i<9; ++i) {
var val=this.data_.charCodeAt(this.index_+i);
res+=val*pow;
pow*=256;
}
}
else { // negative
var pow=1;
for(var i=1; i<9; ++i) {
var val=this.data_.charCodeAt(this.index_+i);
val=(~val)&255;
res+=val*pow;
pow*=256;
}
res+=1;
res=-res;
}
this.index_ += CS_INT64;
return new Int64(res);
},
getString: function() {
if(this.data_.charCodeAt(this.index_)!=0) {
this.index_ += CS_STRING;
return null; // NULL
}
var len=0;
var pow=1;
for(var i=1; i<5; ++i) {
var val=this.data_.charCodeAt(this.index_+i);
len+=val*pow;
pow*=256;
}
this.index_ += CS_STRING;
var val=this.data_.substr(this.index_, len-1);
val=Encoder.UTF8Decode(val);
this.index_+=len;
return val;
},
getDateTime: function() {
if(this.data_.charCodeAt(this.index_)!=0) { // NULL
this.index_++;
return null;
}
this.index_++;
var start=this.index_;
var len=0;
for(; this.data_.charCodeAt(this.index_+len)!=0; ++len);
var raw=this.data_.substr(start, len);
var val = new Time(raw);
this.index_+=16; // aka datalist.cc::IsoTimeStringSize
return val;
},
getDouble: function() {
if(this.data_.charCodeAt(this.index_)!=0) { // NULL
this.index_+= 9; // StatusByte + sizeof(double)
return null; // will not work (check NaN instead)
}
this.index_++;
var sBytes = this.data_.substr(this.index_,8);
this.index_+=8;
var val = parseBytesAsDouble(sBytes);
if (isNaN(val)) return null;
return new Double(val);
},
getDecimal: function() {
if(this.data_.charCodeAt(this.index_)!=0) { // NULL
this.index_+= 17; // StatusByte + sizeof(Decimal)
return decimal();
}
this.index_++;
var sBytes=this.data_.substr(this.index_,16);
this.index_+=16;
var d=new CDecimal({binary: sBytes});
return new Decimal(d.toString());
}
});
// Column order
var CO_SORTUP = 1;
var CO_SORTDOWN = 2;
var CO_UNSORTED = 3;
var ColSortElem = Class.create({
initialize: function(nColIndex, nSortDir) {
this.nColIndex_ = nColIndex;
this.nSortDir_ = nSortDir ? nSortDir : CO_SORTUP;
////LogL("ColSortElem created: "+nColIndex);
},
Touch: function() {
this.nSortDir_ = this.nSortDir_ % 2 + 1; // NOTE: CO_UNSORTED is disabled !!!
////LogL("ColSortElem ["+this.nColIndex_+"] touched! "+this.nSortDir_);
},
Index: function() { return this.nColIndex_; },
Dir: function() { return this.nSortDir_; },
SetDir: function(nSortDir) { this.nSortDir_ = nSortDir; },
SetIndex: function(nColIndex) { this.nColIndex = nColIndex; },
Get: function() {
switch (this.nSortDir_) {
case CO_SORTUP: return (this.nColIndex_+1);
case CO_SORTDOWN: return -(this.nColIndex_+1);
case CO_UNSORTED: return null;
}
LogE('ColSortElem::Get: Invalid sort direction! ');
return null;
}
});
var ColSort = Class.create({
initialize: function() {
this.aOrder_ = new Array();
},
Touch: function(nIndex, bMultiSort) {
if (typeof(bMultiSort)!='boolean') bMultiSort = false;
if (!bMultiSort) {
if (this.aOrder_.length) {
// use first element as previous state (if indexes match)
var oPrevElem = this.aOrder_[0];
if (oPrevElem.Index() == nIndex) {
oPrevElem.Touch();
this.aOrder_ = [oPrevElem];
} else {
this.aOrder_ = [new ColSortElem(nIndex)];
}
} else {
this.aOrder_ = [new ColSortElem(nIndex)];
}
}
else {
var bFound = false;
for (var i = 0; i < this.aOrder_.length; ++i) {
var oElem = this.aOrder_[i];
if (oElem.Index() == nIndex) {
oElem.Touch();
bFound = true;
}
}
if (!bFound) {
this.aOrder_.push(new ColSortElem(nIndex));
}
}
},
Find: function(nIndex) {
for (var i = 0; i < this.aOrder_.length; ++i) {
var oElem = this.aOrder_[i];
if (oElem.Index() == nIndex) return {idx: i, elem: oElem};
}
return null;
},
Get: function() {
var aRes = new Array();
for (var i = 0; i < this.aOrder_.length; ++i) {
var nI = this.aOrder_[i].Get();
if (nI) aRes.push(nI);
}
return aRes;
},
Set: function(aSort) {
this.Flush();
// length = column count
var nL = aSort.length;
// first, collect elements
var aOrder = [];
for (var i = 0; i < nL; ++i) {
var nSO = aSort[i];
if (!nSO) continue; // skip non-sorted columns
var nSI = Math.abs(nSO);
var nDir = ((nSO > 0) ? CO_SORTUP : CO_SORTDOWN);
aOrder[nSI] = new ColSortElem(i, nDir);
}
// second, eliminate gaps in array
for (var i = 0; i < aOrder.length; ++i) {
if (typeof(aOrder[i]) != 'undefined') {
this.aOrder_.push(aOrder[i]);
}
}
},
Flush: function() { this.aOrder_.clear(); }
});