var cScrollers = new Array();

function cScroller(AAreaID, ADirection, AButtonLowIDs, AButtonHighIDs, ABarID, ASliderID, AClassNameActive, AClassNameInactive, AClassNameDisabled)
{
  this.areaID = AAreaID;
  this.barID = ABarID;
  this.buttonHighIDs;
  this.buttonLowIDs;
  this.sliderID = ASliderID;
  this.sliderTrackID = null;

  if (typeof(AButtonHighIDs) == 'string')
    this.buttonHighIDs = [AButtonHighIDs];
  else
    this.buttonHighIDs = AButtonHighIDs;

  if (typeof(AButtonLowIDs) == 'string')
    this.buttonLowIDs = [AButtonLowIDs];
  else
    this.buttonLowIDs = AButtonLowIDs;

  this.className_Active = AClassNameActive;
  this.className_Disabled = AClassNameDisabled;
  this.className_Inactive = AClassNameInactive;

  this.areaBlockNoFrom = 1;
  this.areaBlockCount = 1;
  this.areaBlockLength = 0;
  this.areaBlocksLoad = null;
  this.areaMaxLength = 0;
  this.areaOnScroll = null;
  this.areaScrollStep = 50;
  this.initialScrollPositionN = null;

  this.marginLow = 0;
  this.marginHigh = 0;

  this.sliderIsResizable = false;

// private
  this.area = null;
  this.areaChildNode = null;
  this.areaClientLength = 0;
  this.areaPosition = 0;
  this.areaPositionTotal = 0;
  this.areaScrollShift = 0;
  this.areaScrollShiftTotal = 0;

  this.areaScroll = cScroller__areaScroll;
  this.areaBlocksCheckLoad = cScroller__areaBlocksCheckLoad;
  this.doAreaBlockNoFromChanged = cScroller__doAreaBlockNoFromChanged;
  this.init = cScroller__init;
  this.initScrollbar = cScroller__initScrollbar;
  this.finalize = cScroller__finalize;
  this.resizeInternal = cScroller__resizeInternal;
  this.resize = cScroller__resize;

  this.areaPositionSet = function (ANewPosition, AElementNoFrom)
  {
    var LElementNoFromOld = this.areaBlockNoFrom;
    this.areaBlockNoFrom = AElementNoFrom;

    this.areaPositionTotal = ANewPosition;
    this.areaPosition = (this.areaBlockNoFrom - 1) * this.areaBlockLength + this.areaPositionTotal;

    if (ADirection == csdVertical)
      this.areaChildNode.style.top = this.areaPosition - this.marginLow + 'px';
    else
      this.areaChildNode.style.left = this.areaPosition - this.marginLow + 'px';

    if (LElementNoFromOld != this.areaBlockNoFrom)
      this.doAreaBlockNoFromChanged(LElementNoFromOld);
  };

  if ((ADirection != csdHorizontal) && (ADirection != csdVertical))
    ADirection = csdHorizontal;
  this.directionGet = function () {return ADirection};

  this.id = cScrollers.length;
  cScrollers[this.id] = this;
}

function cScroller__areaBlocksCheckLoad(ANewPosition)
{
  var LNoScrollTo = this.areaBlockNoFrom;
  if (((this.areaBlockCount != 1) || (this.areaBlockNoFrom != 1)) && (this.areaBlockLength > 0) && this.areaBlocksLoad)
  {
    var LClientLength = this.areaClientLength + this.marginLow + this.marginHigh;
    var LCountMax = Math.floor((LClientLength - this.areaScrollShift - this.areaBlockLength)/ this.areaBlockLength + 1);

    LNoScrollTo = Math.floor((-ANewPosition) / this.areaBlockLength + 1);
    if (LNoScrollTo < 1)
      LNoScrollTo = 1;

    var LDoLoad = (
      (LNoScrollTo < this.areaBlockNoFrom)
       || ((LNoScrollTo + LCountMax - 1) > LCountMax)
     );
    if ((LNoScrollTo > 1) && ((LNoScrollTo + LCountMax - 1) > this.areaBlockCount))
      LNoScrollTo = this.areaBlockCount - LCountMax + 1;

    if (LDoLoad)
      this.areaBlocksLoad(LNoScrollTo, this);
  }

  return LNoScrollTo;
}

function cScroller__areaScroll()
{
  var LValue = -this.scrollbar.scrollValue;
  this.areaPositionSet(LValue, this.areaBlocksCheckLoad(LValue));

  if (this.areaOnScroll)
  {
    var LTotalLength = (this.areaClientLength - this.areaScrollShiftTotal + this.marginLow + this.marginHigh);

    this.areaOnScroll(((-this.areaPositionTotal + this.marginLow + this.marginHigh) / LTotalLength), ((this.areaClientLength - this.areaPositionTotal + this.marginLow) / LTotalLength));
  }
}

function cScroller__finalize()
{
  if (this.scrollbar)
    this.scrollbar.finalize();
  cScrollers[this.id] = null;
//!!OKO think about empty indexes
}

function cScroller__doAreaBlockNoFromChanged(AOldValue)
{
  if (typeof(this.areaBlockNoFromOnChange) == 'function')
    this.areaBlockNoFromOnChange(AOldValue);
}

function cScroller__init()
{
  this.area = ElementGet(this.areaID);
  if (!this.area)
  {
    alert('cScroller: area[' + this.areaID + '] is not found');
    return;
  }
  this.area.style.overflow = 'hidden';

  for (var i = 0; i < this.area.childNodes.length; i++)
    if (this.area.childNodes[i].style)
    {
      this.areaChildNode = this.area.childNodes[i];
      this.areaChildNode.style.position = 'relative';
      break;
    }

  this.initScrollbar();
  this.resizeInternal();

  if (this.initialScrollPositionN)
  {
    this.scrollbar.scrollValue = ((this.areaClientLength - this.areaScrollShiftTotal) * this.initialScrollPositionN);
  }
  else
    this.scrollbar.scrollValue = 0;

  this.scrollbar.init();
}

function  cScroller__initScrollbar()
{
  var LScroller = this;
  function LScrollbarOnScroll()
  {
    LScroller.areaScroll();
  }

  this.scrollbar = new cScrollbar(this.directionGet(), this.buttonLowIDs, this.buttonHighIDs, this.barID, this.sliderID, this.sliderTrackID,
    this.className_Active, this.className_Inactive, this.className_Disabled);
  this.scrollbar.onScroll = LScrollbarOnScroll;
  this.scrollbar.scrollStep = this.areaScrollStep;
  this.scrollbar.sliderIsResizable = this.sliderIsResizable;
}

function  cScroller__resize()
{
  this.resizeInternal();
  this.scrollbar.scroll(0);
}

function cScroller__resizeInternal()
{
  var LDirection = this.directionGet();
  if (this.areaMaxLength > 0)
  {
    if (LDirection == csdVertical)
      this.area.style.height = (this.areaChildNode.offsetHeight > this.areaMaxLength) ? this.areaMaxLength : this.areaChildNode.offsetHeight;
    else
      this.area.style.width = (this.areaChildNode.offsetWidth > this.areaMaxLength) ? this.areaMaxLength : this.areaChildNode.offsetWidth;
  }

  if (this.areaChildNode)
  {
    if (LDirection == csdVertical)
    {
      this.areaClientLength = this.area.clientHeight;
      this.areaScrollShift = this.areaClientLength - this.areaChildNode.offsetHeight;
    }
    else
    {
      this.areaClientLength = this.area.clientWidth;
      this.areaScrollShift = this.areaClientLength - this.areaChildNode.offsetWidth;
    }
  }
  else
    this.areaScrollShift = 0;

  this.areaScrollShift = this.areaScrollShift + this.marginLow + this.marginHigh;
  if (this.areaScrollShift > 0)
    this.areaScrollShift = 0;

  if (this.areaBlockCount > 1)
    this.areaScrollShiftTotal = -this.areaBlockCount * this.areaBlockLength + this.areaClientLength  + this.marginLow + this.marginHigh;
  else
    this.areaScrollShiftTotal = this.areaScrollShift;
    
  if (this.areaScrollShiftTotal > 0)
    this.areaScrollShiftTotal = 0;

  this.scrollbar.sliderResizeCoefficient = this.areaClientLength / (this.areaClientLength - this.areaScrollShiftTotal);
  this.scrollbar.scrollMin = 0;
  this.scrollbar.scrollMax = -this.areaScrollShiftTotal;
  this.scrollbar.resizeInternal();
  
}

function  ScrollersInit()
{
  for (var i = 0; i < cScrollers.length; i++)
  {
    var LScroller = cScrollers[i];
    if (LScroller)
      LScroller.init();
  }
}

function cDEScrollerBase(AAreaID, ADirection, AButtonLowIDs, AButtonHighIDs, ABarID, ASliderID, AClassNameActive, AClassNameInactive, AClassNameDisabled, ARecordNavigatorID)
{
  var self = new cScroller(AAreaID, ADirection, AButtonLowIDs, AButtonHighIDs, ABarID, ASliderID, AClassNameActive, AClassNameInactive, AClassNameDisabled);

  self.GRC = 1;
  self.MRP = 1;

  self.groupCountVisible = 1;
  self.recordCount = 1;
  self.recordNoCurrent = 1;
  self.recordNoFrom = 1;

  self.recordNavigator = null;
  self.recordNavigatorID = ARecordNavigatorID;
  self.recordNavigator_recordNoCurrentHTMLGet = null;

  self.timeoutID = null;

  function LRaiseENotImplemented(AMethodName)
  {
    alert('cDEScrollerBase.' + AMethodName + ' is not implemented');
  }

  self.init_cScroller = self.init;
  self.init = cDEScrollerBase__init;

  self.areaOnScroll = cDEScrollerBase__areaOnScroll;
  self.areaBlockNoFromOnChange = cDEScrollerBase__areaBlockNoFromOnChange;
  self.areaBlocksLoad = cDEScrollerBase__recordsLoad;
  self.recordCountSetInternal = cDEScrollerBase__recordCountSetInternal;
  self.recordCountSet = cDEScrollerBase__recordCountSet;
  self.recordsInit = cDEScrollerBase__recordsInit;
  self.recordsLoadFromServer = cDEScrollerBase__recordsLoadFromServer;


// abstract
  self.recordCheckLoad = function (ARecNo, AIndex)
  {
    LRaiseENotImplemented('recordCheckLoad');
  };
  self.recordHide = function(ARecIndex)
  {
    LRaiseENotImplemented('recordHide');
  };
  self.recordsLoadComplete = function (AFrom, ATo)
  {
    LRaiseENotImplemented('recordsLoadComplete');
  };
  self.recordStubLoad = function(ARecIndex)
  {
    LRaiseENotImplemented('recordStubLoad');
  }
  self.recordIsLoadedCheck = function (ARecNo)
  {
    LRaiseENotImplemented('recordIsLoadedCheck');
  };
  self.recordShow = function(ARecIndex)
  {
    LRaiseENotImplemented('recordShow');
  };
  self.recordsLoadFromServerInternal = function(APageNo, APageCount, AFrom, ATo)
  {
    LRaiseENotImplemented('recordsLoadFromServerInternal');
  };

  return self;
}

function cDEScrollerBase__areaOnScroll(AFrom, ATo)
{
  var LFrom = (Math.floor(this.areaBlockCount * AFrom + 1) - 1) * this.GRC + 1;
  var LTo   = Math.round(this.areaBlockCount * ATo + 0.499) * this.GRC;
  if (LTo > this.recordCount)
    LTo = this.recordCount;

  if (this.recordNavigator)
  {
    var LRecords = new Array();
    for (var i = LFrom; i <= LTo; i++)
    {
      var LRecNo = i;
      if ((LRecNo == this.recordNoCurrent)
        && (typeof(this.recordNavigator_recordNoCurrentHTMLGet) == 'function')
      )
        LRecNo = this.recordNavigator_recordNoCurrentHTMLGet(LRecNo);
      LRecords[i - LFrom] = LRecNo;
    }

    this.recordNavigator.innerHTML = LRecords.join('<span>|</span>') + ' of ' + this.recordCount;
  }
}

function  cDEScrollerBase__areaBlockNoFromOnChange(AOldValue)
{
  this.recordNoFrom = (this.areaBlockNoFrom - 1) * this.GRC + 1;
}

function cDEScrollerBase__init()
{
  if (this.recordNavigatorID)
    this.recordNavigator = ElementGet(this.recordNavigatorID);

  var LCurrentElementNo = Math.floor((this.recordNoCurrent - 1)/ this.GRC) + 1;
  if (this.recordNoCurrent > this.groupCountVisible)
  {
    this.initialScrollPositionN = (((Math.floor((LCurrentElementNo - 1)/ this.groupCountVisible) * this.groupCountVisible)/* - 1*/) / this.areaBlockCount);
  }

  this.init_cScroller();
}

function cDEScrollerBase__recordCountSet(ARecCount)
{
  if (this.recordCount == ARecCount)
    return;
    
  var LRecCountOld = this.recordCount;

  this.recordCountSetInternal(ARecCount);

  var
    LFrom = 0,
    LTo = 0;

  if ((ARecCount > LRecCountOld) && (LRecCountOld < this.MRP))
  {
    LFrom = (Math.floor((LRecCountOld - 1) / this.GRC) + 1) * this.GRC;
    if (this.MRP > ARecCount)
      LTo = (Math.floor((ARecCount - 1) / this.GRC) + 1) * this.GRC;
    else
      LTo = this.MRP;

    for (var i = LFrom; i < LTo; i++)
      this.recordShow(i);
  }
  else
  if ((ARecCount < LRecCountOld) && (ARecCount <= this.MRP))
  {
    LFrom = (Math.floor((ARecCount - 1) / this.GRC) + 1) * this.GRC;
    if (this.MRP > LRecCountOld)
      LTo = (Math.floor((LRecCountOld - 1) / this.GRC) + 1) * this.GRC;
    else
      LTo = this.MRP;

    for (var i = LTo - 1; i >= LFrom; i--)
      this.recordHide(i);
  }

  for (var i = ARecCount; i < LTo; i++)
    this.recordStubLoad(i + 1, i);

  this.resize();
}

function cDEScrollerBase__recordCountSetInternal(ARecCount)
{
  if (isNaN(parseInt(ARecCount)))
    this.recordCount = 1
  else
  if (ARecCount < 0)
    this.recordCount = 0;
  else
    this.recordCount = ARecCount;
  this.areaBlockCount = Math.floor((this.recordCount - 1) / this.GRC) + 1;
}

function cDEScrollerBase__recordsInit(ARecCount, ARecLength, AMRP, AGRC, ARecNoFrom)
{
  this.GRC = (AGRC) ? AGRC : 1;
  this.MRP = (AMRP) ? AMRP : 1;
  this.recordNoFrom = (ARecNoFrom) ? ARecNoFrom : 1;
  this.areaBlockLength = (ARecLength) ? ARecLength : 0;
  this.areaBlockNoFrom = Math.floor((this.recordNoFrom - 1)/ this.GRC) + 1;
  this.recordCountSetInternal(ARecCount);
}

function cDEScrollerBase__recordsLoad(AFrom, AScrollBar)
{
  if (this.timeoutID != null)
    clearTimeout(this.timeoutID);

  var LPageElementCount = this.MRP / this.GRC;

  var LTo = AFrom + LPageElementCount - 1;
  if (LTo > this.areaBlockCount)
    LTo = this.areaBlockCount;
  var LFrom = LTo - LPageElementCount + 1;
  if (LFrom < 1)
    LFrom = 1;

  var LLoadFrom = 0;
  var LLoadTo = 0;

  var LRecIndex = 0;
  for (var i = LFrom; i <= LTo; i++)
    for (var j = 1; j <= this.GRC; j++)
    {
      var LRecNo = (i - 1) * this.GRC + j;
      if (LRecNo > this.recordCount)
        this.recordStubLoad(LRecNo, LRecIndex);
      else
        if (!this.recordCheckLoad(LRecNo, LRecIndex))
        {
          if (LLoadFrom == 0)
          {
            LLoadFrom = LRecNo;
            LLoadTo = i;
          }
          else
            LLoadTo = LRecNo;
        }
      LRecIndex++;
    }

  if (LLoadFrom > 0)
    this.timeoutID = setTimeout('cScrollers[' + this.id.toString() + '].recordsLoadFromServer(' + LLoadFrom.toString() + ', ' + LLoadTo.toString() + ')', 500);
}

function cDEScrollerBase__recordsLoadFromServer(AFrom, ATo)
{
  var LPageNo, LPageCount;
  var LPageFrom = this.recordIsLoadedCheck(AFrom) ? 0 : (Math.floor((AFrom - 1) / this.MRP) + 1);
  var LPageTo = this.recordIsLoadedCheck(ATo) ? 0 :(Math.floor((ATo - 1) / this.MRP) + 1);

  if ((LPageFrom == 0) && (LPageTo == 0))
  {
    this.recordsLoadComplete(AFrom, ATo);
    return;
  }

  if (LPageFrom == 0)
  {
    LPageNo = LPageTo;
    LPageCount = 1;
  }
  else
  {
    LPageNo = LPageFrom;
    LPageCount = (LPageTo == 0) ? 1 : (LPageTo - LPageFrom + 1);
  }

  this.recordsLoadFromServerInternal(LPageNo, LPageCount, AFrom, ATo);
}

function cDEScroller(AAreaID, ADirection, AButtonLowIDs, AButtonHighIDs, ABarID, ASliderID, AClassNameActive, AClassNameInactive, AClassNameDisabled, ARecordNavigatorID)
{
  var self = new cDEScrollerBase(AAreaID, ADirection, AButtonLowIDs, AButtonHighIDs, ABarID, ASliderID, AClassNameActive, AClassNameInactive, AClassNameDisabled, ARecordNavigatorID);

  self.areaIsFilledIn = false;
  self.recordInfoBad = null;
  self.recordInfoList = new Array();

  self.areaFillIn = cDEScroller__areaFillIn;
  self.recordAdd = cDEScroller__recordAdd;
  self.recordCheckLoad = cDEScroller__recordCheckLoad;
  self.recordHide = cDEScroller__recordHide;
  self.recordIsLoadedCheck = cDEScroller__recordIsLoadedCheck;
  self.recordShow = cDEScroller__recordShow;
  self.recordsLoadComplete = cDEScroller__recordsLoadComplete;
  self.recordStubLoad = cDEScroller__recordStubLoad;

  self.init_cDEScrollerBase = self.init;
  self.init = cDEScroller__init;
  
// abstract;
  self.areaFillInInternal = null; // function(ARecNoFrom, ARecCount)
  self.recordContainerGet_ByIndex = function (ARecordIndex)
  {
    alert('cDEScroller.recordContainerGet_ByIndex is not implemented');
    return null;
  }
  self.recordContainerGet_ByRecNo = function (ARecordNo)
  {
    alert('cDEScroller.recordContainerGet_ByRecNo is not implemented');
    return null;
  }
  self.recordProcess = function (ARecNo, ARecordInfo, ARecordContainer)
  {
    alert('cDEScroller.recordProcess is not implemented');
    return '';
  }

  return self;
}

function cDEScroller__areaFillIn()
{
  if (this.areaIsFilledIn || (typeof(this.areaFillInInternal) != 'function'))
    return;

  var LCount = (this.recordCount < this.MRP) ? this.recordCount : this.MRP;
  this.areaFillInInternal(this.recordNoFrom, LCount);

  this.areaIsFilledIn = true;
}

function cDEScroller__init()
{
  this.areaFillIn();
  this.init_cDEScrollerBase();
}

function cDEScroller__recordAdd(ARecNo, ARecordInfo)
{
  if ((ARecNo < 0) || (ARecNo > this.recordCount))
    return;

  this.recordInfoList[ARecNo] = ARecordInfo;
}

function cDEScroller__recordCheckLoad(ARecNo, ARecordIndex)
{
  var LRecordInfo = this.recordInfoList[ARecNo];
  var LResult = (!LRecordInfo) ? false : true;
  if (!LResult)
    LRecordInfo = this.recordInfoBad;

  this.recordProcess(ARecNo, LRecordInfo, this.recordContainerGet_ByIndex(ARecordIndex));
  return LResult;
}

function cDEScroller__recordHide(ARecIndex)
{
  var LContainer = this.recordContainerGet_ByIndex(ARecIndex);
  if (!LContainer)
    return;

  this.recordProcess(ARecIndex + 1, this.recordInfoBad, LContainer);
  LContainer.style.display = 'none';
}

function cDEScroller__recordIsLoadedCheck(ARecNo)
{
  return ((this.recordInfoList[ARecNo]) ? true : false);
}

function cDEScroller__recordShow(ARecIndex)
{
  var LContainer = this.recordContainerGet_ByIndex(ARecIndex);
  if (!LContainer)
  {
    alert('cDEScroller__recordShow is not implemented');
    return;
  }

  this.recordProcess(ARecIndex + 1, this.recordInfoList[ARecIndex], LContainer);
  LContainer.style.display = '';
}

function cDEScroller__recordsLoadComplete(ARecNoFrom, ARecNoTo)
{
  for (var i = ARecNoFrom; i <= ARecNoTo; i++)
  {
    var LContainer = this.recordContainerGet_ByRecNo(i);
    if (!LContainer)
      continue;

    var LRecordInfo = this.recordInfoList[i];
    if (!LRecordInfo)
      continue;
    this.recordProcess(i, LRecordInfo, LContainer);
  }
}

function cDEScroller__recordStubLoad(ARecNo, ARecordIndex)
{
  this.recordProcess(ARecNo, null, this.recordContainerGet_ByIndex(ARecordIndex));
}

function cDEScrollerT(AAreaID, ADirection, AButtonLowIDs, AButtonHighIDs, ABarID, ASliderID, AClassNameActive, AClassNameInactive, AClassNameDisabled, ARecordNavigatorID, ATableID)
{
  var self = new cDEScroller(AAreaID, ADirection, AButtonLowIDs, AButtonHighIDs, ABarID, ASliderID, AClassNameActive, AClassNameInactive, AClassNameDisabled, ARecordNavigatorID);

  self.table = null;
  self.tableID = ATableID;

  self.areaFillInInternal = cDEScrollerT__areaFillInInternal;

  self.recordContainerGet_ByIndex = cDEScrollerT__recordContainerGet_ByIndex;
  self.recordContainerGet_ByRecNo = cDEScrollerT__recordContainerGet_ByRecNo;
  self.recordProcess = cDEScrollerT__recordProcess;

  self.recordStubHTMLGet = function (ARecNo, AContainer)
  {
    alert('cDEScrollerT.recordStubHTMLGet is not implemented');
  }
  self.recordToHTML = function (ARecNo, ARecordInfo, AContainer)
  {
    alert('cDEScrollerT.recordToHTML is not implemented');
  }

  return self;
}

function cDEScrollerT__areaFillInInternal(AFrom, ACount)
{
  var LTbl = ElementGet(this.tableID);
  if (!LTbl)
    alert('Ivalid table[' + this.tableID + ']');
  this.table = LTbl;

  var LRowCount, LCellCount, LRowMultiply, LCellMultiply;
  if (this.directionGet() == csdHorizontal)
  {
    LRowCount = this.GRC;
    LRowMultiply = 1;
//    LCellCount = Math.floor((ACount - 1) / this.GRC) + 1;
    LCellCount= this.MRP / this.GRC;
    LCellMultiply = this.GRC;
  }
  else
  {
    LRowCount = this.MRP / this.GRC;
//    LRowCount = Math.floor((ACount - 1) / this.GRC) + 1;
    LRowMultiply = this.GRC;
    LCellCount = this.GRC;
    LCellMultiply = 1;
  }

  var LRowHTML = '';
  var LRowCellInitCount = 0;
  if (LTbl.rows.length == 1)
  {
    var LRow = LTbl.rows[0];
    LRowHTML = LRow.innerHTML;
    LRowCellInitCount = LRow.cells.length;
  }
  for (var i = LTbl.rows.length; i < LRowCount; i++)
  {
    LTbl.insertRow(-1);
  }

  var LToMax = this.MRP;
  var LTo = AFrom + ACount - 1;
  if (LTo > this.recordCount)
    LTo = ((this.recordCount - 1) / this.GRC + 1) * this.GRC;

  if (LTo > LToMax)
    LToMax = LTo;
    
  for (var i = 0; i < LRowCount; i++)
  {
    var LRow = LTbl.rows[LRowCount - i - 1];
    var LRecNo;
    for (var j = 0; j < LCellCount; j++)
    {
      var LTD = LRow.insertCell(0);
      
      LRecNo = LToMax - i * LRowMultiply - j * LCellMultiply;
      this.recordProcess(LRecNo, this.recordInfoList[LRecNo], LTD);
    }
  }

  for (var i = LToMax - 1; i >= LTo; i--)
    this.recordHide(i);
}

function cDEScrollerT__recordContainerGet_ByIndex(AIndex)
{
  var LGroupIndex = Math.floor(AIndex / this.GRC);
  var LRecordIndex = AIndex - LGroupIndex * this.GRC;

  var LCellIndex, LRowIndex;
  if (this.directionGet() == csdHorizontal)
  {
    LCellIndex = LGroupIndex;
    LRowIndex = LRecordIndex;
  }
  else
  {
    LCellIndex = LRecordIndex;
    LRowIndex = LGroupIndex;
  }

  if ((LRowIndex < this.table.rows.length)
    && (LCellIndex < this.table.rows[LRowIndex].cells.length)
  )
    return this.table.rows[LRowIndex].cells[LCellIndex];
  else
    return null;
}

function cDEScrollerT__recordContainerGet_ByRecNo(ARecNo)
{
  return ElementGet(this.tableID + '_' + ARecNo);
}

function cDEScrollerT__recordProcess(ARecNo, ARecordInfo, AContainer)
{
  var LHTML;

  if (ARecordInfo)
    LHTML = this.recordToHTML(ARecNo, ARecordInfo, AContainer);
  else
    if (ARecNo > this.recordCount)
      LHTML = this.recordStubHTMLGet(ARecNo, AContainer)
    else
      LHTML =  this.recordToHTML(ARecNo, this.recordInfoBad, AContainer);

  if (AContainer)
  {
    AContainer.id = this.tableID + '_' + ARecNo;
    AContainer.innerHTML = LHTML;
  }
}

// use AJAX to load records
function DEScroller_AJAXLoaderInit(AScroller, ASection, AAction, AParams)
{
  AScroller.urlAction = AAction;
  AScroller.urlParams = AParams;
  AScroller.urlSection = ASection;
  AScroller.urlGet = cDEScroller_AJAXLoader__urlGet;

  AScroller.isLoading = false;
  AScroller.stateIdent = null;
  AScroller.stateIdents = [];
  AScroller.states = [];

  AScroller.load = cDEScroller_AJAXLoader__load;
  AScroller.loadNew = cDEScroller_AJAXLoader__loadNew;
  AScroller.loadNewComplete = cDEScroller_AJAXLoader__loadNewComplete;
  AScroller.stateByIdentGet = cDEScroller_AJAXLoader__stateByIdentGet;
  AScroller.stateLoad = cDEScroller_AJAXLoader__stateLoad;
  AScroller.stateSave = cDEScroller_AJAXLoader__stateSave;
  AScroller.stateSet = cDEScroller_AJAXLoader__stateSet;

  AScroller.ajaxLoadFailed = cDEScroller_AJAXLoader__ajaxLoadFailed;
  AScroller.ajaxLoadComplete = cDEScroller_AJAXLoader__ajaxLoadComplete;
  AScroller.recordsLoadFromServerInternal = cDEScroller_AJAXLoader__ajaxLoad;
}

function cDEScroller_AJAXLoader__ajaxLoadFailed(AErrorMsg)
{
  //!! think about propper error handling
  return true;
}

function cDEScroller_AJAXLoader__ajaxLoad(APageNo, APageCount, ARecNoFrom, ARecNoTo)
{
   var LAJAXLoader = new cAJAX_Loader();
   LAJAXLoader.onError = [this, 'ajaxLoadFailed'];
   LAJAXLoader.get(this.urlGet(APageNo, APageCount),
    [this, 'ajaxLoadComplete'], {recNoFrom: ARecNoFrom, recNoTo: ARecNoTo});
}

function cDEScroller_AJAXLoader__ajaxLoadComplete(AResponse, AContext)
{
  var LResponseProcess = function (AScroller)
  {
    eval(AResponse.contentHTML);
  };
  LResponseProcess(this);
  this.recordsLoadComplete(AContext.recNoFrom, AContext.recNoTo);
}


function cDEScroller_AJAXLoader__load(AIdent, ASection, AAction, AParams_Init, AParams_Scroll)
{
  this.stateSave();
  this.stateLoad(AIdent, ASection, AAction, AParams_Init, AParams_Scroll);
}

function cDEScroller_AJAXLoader__loadNew(AURL, AScrollValue)
{
  this.isLoading = true;
  (new cAJAX_Loader()).get(AURL,
    [this, 'loadNewComplete'], {scrollValue: AScrollValue});
}

function cDEScroller_AJAXLoader__loadNewComplete(AResponse, AContext)
{
  var LResponseProcess = function (AScroller)
  {
    eval(AResponse.contentHTML);
  };
  LResponseProcess(this);
  this.scrollbar.scrollValueSet(AContext.scrollValue);
  this.recordsLoadComplete(this.recordNoFrom, this.MRP);
  this.isLoading = false;
}

function cDEScroller_AJAXLoader__stateByIdentGet(AIdent)
{
  if (!AIdent)
    return null;

  var LIndex = this.stateIdents.indexOf(AIdent);
  if (LIndex >= 0)
    return this.states[LIndex];
  else
    return null;
}

function cDEScroller_AJAXLoader__stateLoad(AIdent, ASection, AAction, AParams_Init, AParams_Scroll)
{
  this.stateIdent = AIdent;
  this.recordInfoList = [];

  var LRecordCount = this.MRP;
  var LRecordNoFrom = 1;
  var LScrollValue = 0;

  var LState = this.stateByIdentGet(AIdent);
  if (!LState)
  {
    this.urlSection = ASection;
    this.urlAction = AAction;
    this.urlParams = (AParams_Scroll) ? AParams_Scroll : [];
    this.urlParamsInit = (AParams_Init) ? AParams_Init : [];
    this.recordNoCurrent = 0;
  }
  else
  {
    LScrollValue = LState.scrollValue;
    LRecordCount = LState.recordCount;
    this.recordNoCurrent = LState.recordNoCurrent;
    LRecordNoFrom = LState.recordNoFrom;
  //  this.recordInfoList: LState.recordInfoList;
    this.urlAction = LState.urlAction;
    this.urlSection = LState.urlSection;
    this.urlParams = LState.urlParams ? LState.urlParams : [];
    this.urlParamsInit = LState.urlParamsInit ? LState.urlParamsInit : [];
  }

//!!OKO should be checked/optimaized;
  this.recordCountSet(LRecordCount);
  this.scrollbar.scrollValueSet(LScrollValue);
  for (var i = 0; i < (LRecordCount > this.MRP ? this.MRP : LRecordCount); i++)
    this.recordCheckLoad(i + 1, i);
//!!OKO

  var LPageNo = Math.floor((LRecordNoFrom - 1) / this.MRP) + 1;
  this.loadNew(WSA_URLGet(this.urlSection, this.urlAction,
  [
    'Mode',   'AJAX',
    'PageNo',  LPageNo
  ].append(this.urlParamsInit)), LScrollValue);
}

function cDEScroller_AJAXLoader__stateSave()
{
  if (!this.stateIdent)
    return;

  var LIndex = this.stateIdents.indexOf(this.stateIdent);
  if (LIndex == -1)
  {
    LIndex = this.stateIdents.length;
    this.stateIdents[LIndex] = this.stateIdent;
  }
  this.states[LIndex] = {
    scrollValue: this.scrollbar.scrollValue,
    recordCount: this.recordCount,
    recordNoCurrent: this.recordNoCurrent,
    recordNoFrom: this.recordNoFrom,
//    recordInfoList: this.recordInfoList,
    urlAction: this.urlAction,
    urlSection: this.urlSection,
    urlParams: this.urlParams,
    urlParamsInit: this.urlParamsInit
  }
}

function cDEScroller_AJAXLoader__stateSet(ARecordCount, AParams)
{
  function  LParamsAdjust(AParamsToAdjust)
  {
    if (!AParamsToAdjust)
      return AParams;

    for (var i = 0; i < AParams.length; i += 2)
    {
      var LIndex = AParamsToAdjust.indexOf(AParams[i]);
      if ((LIndex != -1) && ((Math.floor(LIndex / 2) * 2) == LIndex))
        AParamsToAdjust[LIndex + 1] = AParams[i + 1];
      else
        AParamsToAdjust.append([AParams[i], AParams[i + 1]]);
    }

    return AParamsToAdjust;
  }

  if (!this.isLoading)
    return;

  if ((AParams) && (AParams.length > 0))
  {
    this.urlParamsInit = LParamsAdjust(this.urlParamsInit);
    this.urlParams = LParamsAdjust(this.urlParams);
  }

  this.recordCountSet(ARecordCount);
}

function cDEScroller_AJAXLoader__urlGet(APageNo, APageCount)
{
  var LParams = [
    'Mode',      'AJAX',
    'PageNo',    APageNo,
    'PageCount', APageCount
  ];
  if (this.urlParams)
    LParams.append(this.urlParams);

  return WSA_URLGet(this.urlSection, this.urlAction, LParams);
}

function Scroller_StateSet(AScroller, ARecordCount, AParams)
{
  if ((!AScroller) || (typeof(AScroller.stateSet) != 'function'))
    return;
  AScroller.stateSet(ARecordCount, AParams);
}
