{***************************************************************************}
{ TMS IntraWeb Component Pack Pro                                           }
{ for Delphi & C++Builder                                                   }
{                                                                           }
{ written by TMS Software                                                   }
{            copyright © 2012 - 2013                                        }
{            Email : info@tmssoftware.com                                   }
{            Web : http://www.tmssoftware.com                               }
{                                                                           }
{ The source code is given as is. The author is not responsible             }
{ for any possible damage done due to the use of this code.                 }
{ The component can be freely used in any application. The complete         }
{ source code remains property of the author and may not be distributed,    }
{ published, given or sold in any form as such. No parts of the source      }
{ code can be included in any other component or application without        }
{ written authorization of the author.                                      }
{***************************************************************************}

unit IWContinuousScroll;

{$I TMSDEFS.INC}

interface

uses
  Windows, Graphics, Controls, Forms,
  {$IFDEF DELPHI6_LVL}
  Types,
  {$ENDIF}
  Classes,
  IWControl, IWTypes, IWRenderContext, IWHTMLTag, IWXMLTag, IWTMSBase, IWColor,
  IWFont;

type

  TScrollElement = (seWindow, seControl);
  TLoadItemsType = (liOnScroll, liOnButtonClick, liNever);
  TPosition = (pTop, pBottom);
  TButtonStyle = (bsButton, bsLabel);
  TAlign = (aLeft, aRight, aCenter);

  TIWOnNeedItemEvent = procedure (Sender: TObject; ItemIndex: integer;
    Var ItemContent: string) of object;
  TIWOnNeedItemDataEvent = procedure (Sender: TObject; ItemIndex: integer;
    FieldName: string; Var Value: string) of object;
  TIWAsyncControlEvent = procedure (Sender: TObject; EventParams: TStringList;
    EventType, ControlID: string; ItemIndex: integer) of object;

  TLoadItemsButton = class(TPersistent)
  private
    FBGColor: TIWColor;
    FBGHoverColor: TIWColor;
    FBGDownColor: TIWColor;
    FCaption: string;
    FPosition: TPosition;
    FFontHoverColor: TIWColor;
    FFontDownColor: TIWColor;
    FFont: TIWFont;
    FVisible: boolean;
    FCss: string;
    FStyle: TButtonStyle;
    FAlign: TAlign;
  public
    constructor Create;
    destructor Destroy; override;
  published
    property Align: TAlign read FAlign write FAlign default ALeft;
    property BGColor: TIWColor read FBGColor write FBGColor default clNone;
    property BGHoverColor: TIWColor read FBGHoverColor write FBGHoverColor default clNone;
    property BGDownColor: TIWColor read FBGDownColor write FBGDownColor default clNone;
    property Caption: string read FCaption write FCaption;
    property Css: string read FCss write FCss;
    property Font: TIWFont read FFont write FFont;
    property FontHoverColor: TIWColor read FFontHoverColor write FFontHoverColor default clWebBlack;
    property FontDownColor: TIWColor read FFontDownColor write FFontDownColor default clWebBlack;
    property Position: TPosition read FPosition write FPosition default pBottom;
    property Visible: boolean read FVisible write FVisible default true;
    property Style: TButtonStyle read FStyle write FStyle default bsLabel;
  end;

  TTIWContinuousScroll = class(TIWControl)
  private
    FEventType: string;
    FControlID: string;
    FControlIndex: integer;
    FItemCount: integer;
    FItemDelta: integer;
    FScrollTop: integer;
    FScrollEnd: integer;
    FScrollMax: integer;
    FScrollElement: TScrollElement;
    FOnAsyncScroll: TIWAsyncEvent;
    FOnNeedItem: TIWOnNeedItemEvent;
    FOnAsyncControlEvent: TIWAsyncControlEvent;
    FLoadItems: TLoadItemsType;
    FOnAsyncLoadItems: TIWAsyncEvent;
    FBGColor: TIWColor;
    FMaxItemCount: integer;
    FLoadItemsButton: TLoadItemsButton;
    FItemTemplate: string;
    FOnNeedItemData: TIWOnNeedItemDataEvent;
    FBorderColor: TIWColor;
    FBGColorTo: TIWColor;
    FBorderWidth: integer;
    FLoadScrollPercent: integer;
    FBGGradientDirection: TTIWGradientDirection;
    procedure DoOnAsyncScroll(AParams: TStringList);
    procedure DoOnAsyncLoadItems(AParams: TStringList);
    procedure DoOnAsyncControlEvent(AParams: TStringList);
    procedure SetItemTemplate(const Value: string);
    procedure SetLoadScrollPercent(const Value: integer);
    procedure SetBGColor(const Value: TIWColor);
    procedure SetBGColorTo(const Value: TIWColor);
    function SetBGGradientDirection: TTIWGradientDirection;
    procedure SetBorderColor(const Value: TIWColor);
    procedure SetBorderWidth(const Value: integer);
    procedure SetItemCount(const Value: integer);
    procedure SetItemDelta(const Value: integer);
    procedure SetLoadItems(const Value: TLoadItemsType);
    procedure SetMaxItemCount(const Value: integer);
    procedure SetScrollElement(const Value: TScrollElement);
  protected
    procedure SetValue(const value:string); override;
    procedure Submit(const AValue: string); override;
    procedure AsyncAddItems(Position: TPosition);
    function HTMLReplace(s: string; ItemIndex: integer):string;
    function RenderItems(BeginIndex: integer): string;
    function RenderLoadItemsButton(Position: TPosition): string;
  public
    procedure IWPaint; override;
    procedure AsyncInsertItemsAfter();
    procedure AsyncInsertItemsBefore();
    function RenderHTML(AContext: TIWBaseComponentContext): TIWHTMLTag; override;
    function RenderAsync(AContext: TIWBaseHTMLComponentContext): TIWXMLTag; override;
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Enabled;
    property BGColor: TIWColor read FBGColor write SetBGColor default clNone;
    property BGColorTo: TIWColor read FBGColorTo write SetBGColorTo default clNone;
    property BGGradientDirection: TTIWGradientDirection read SetBGGradientDirection write FBGGradientDirection default gdVertical;
    property BorderColor: TIWColor read FBorderColor write SetBorderColor default clNone;
    property BorderWidth: integer read FBorderWidth write SetBorderWidth default 0;
    property InitialItemCount: integer read FItemCount write SetItemCount default 30;
    property ItemDelta: integer read FItemDelta write SetItemDelta default 5;
    property ItemTemplate: string read FItemTemplate write SetItemTemplate;
    property LoadItems: TLoadItemsType read FLoadItems write SetLoadItems default liOnScroll;
    property LoadItemsButton: TLoadItemsButton read FLoadItemsButton write FLoadItemsButton;
    property LoadScrollPercent: integer read FLoadScrollPercent write SetLoadScrollPercent default 80;
    property MaxItemCount: integer read FMaxItemCount write SetMaxItemCount default 100;
    property ScrollElement: TScrollElement read FScrollElement write SetScrollElement default seWindow;
    property OnAsyncScroll: TIWAsyncEvent read FOnAsyncScroll write FOnAsyncScroll;
    property OnAsyncLoadItems: TIWAsyncEvent read FOnAsyncLoadItems write FOnAsyncLoadItems;
    property OnAsyncControlEvent: TIWAsyncControlEvent read FOnAsyncControlEvent write FOnAsyncControlEvent;
    property OnNeedItem: TIWOnNeedItemEvent read FOnNeedItem write FOnNeedItem;
    property OnNeedItemData: TIWOnNeedItemDataEvent read FOnNeedItemData write FOnNeedItemData;
  end;


implementation

uses
  ImgList, ShellAPI, CommCtrl,
  IWApplication, IWServerControllerBase, TMSIWStrings, TMSIWSystem, IWAppForm, SysUtils, IWServer;

{ TTIWContinuousScroll }
procedure TTIWContinuousScroll.DoOnAsyncControlEvent(AParams: TStringList);
begin
  if Assigned(OnAsyncControlEvent) then
    OnAsyncControlEvent(Self, AParams, FEventType, FControlID, FControlIndex);
end;

procedure TTIWContinuousScroll.DoOnAsyncLoadItems(AParams: TStringList);
begin
  AsyncAddItems(LoadItemsButton.Position);

  if Assigned(OnAsyncLoadItems) then
    OnAsyncLoadItems(Self, AParams);
end;

procedure TTIWContinuousScroll.DoOnAsyncScroll(AParams: TStringList);
begin
//  if (FScrollTop > FScrollMax) then
  if (FScrollTop > FScrollMax)
    and (FScrollTop >= (FScrollEnd * (FLoadScrollPercent/100))) then
  begin
    FScrollMax := FScrollTop;

//    OutputDebugString(pchar('load on scroll'));

    AsyncAddItems(pBottom);
  end;

  if Assigned(OnAsyncScroll) then
    OnAsyncScroll(Self, AParams);
end;

function TTIWContinuousScroll.RenderAsync(
  AContext: TIWBaseHTMLComponentContext): TIWXMLTag;
var
  js: string;
begin
  Result := TIWXMLTag.CreateTag('control');
  try
    Result.AddStringParam('id', HTMLName);
    Result.AddStringParam('type', 'TTIWCONTINUOUSSCROLL');
    RenderAsyncCommonProperties(AContext, Result);

//   if Enabled then
//     js := js + HTMLName + 'Enabled = true;'#13
//   else
//     js := js + HTMLName + 'Enabled = false;'#13;

    GGetWebApplicationThreadVar.CallBackResponse.AddJavaScriptToExecute('<![CDATA['+ js +']]>');
  except
    FreeAndNil(Result);
  end;
end;

procedure TTIWContinuousScroll.AsyncAddItems(Position: TPosition);
var
  js, html: string;
  previtemcount: integer;
begin
  if (FItemCount < FMaxItemCount) then
  begin
    previtemcount := FItemCount;
    FItemCount := FItemCount + FItemDelta;

    if FItemCount >= FMaxItemCount then
    begin
      FItemCount := FMaxItemCount;

      js := 'var el = document.getElementById("' + HTMLName + 'load");'
      + 'if (el) {';
      js := js + 'el.style.visibility = "hidden";';
      js := js + '}';
    end;

    html := RenderItems(previtemcount);
    html := StringReplace(html, '''', '&#34;', [rfReplaceAll]);
    html := StringReplace(html, '"', '''', [rfReplaceAll]);

    js := js + 'var el = document.getElementById("' + HTMLName + 'list");'
    + 'if (el) {';

    if Position = pBottom then
      js := js + 'el.innerHTML = el.innerHTML + "' + html + '";'
    else
      js := js + 'el.innerHTML = "' + html + '" + el.innerHTML;';

    js := js + '}';

    if (LoadItems = liOnButtonClick) and (LoadItemsButton.Position = pBottom) then
      js := js + HTMLName + 'ScrollToBottom();';

//    js := js + 'Initialize();';

    GGetWebApplicationThreadVar.CallBackResponse.AddJavaScriptToExecute('<![CDATA['+ js +']]>');
  end;
end;

procedure TTIWContinuousScroll.AsyncInsertItemsAfter;
begin
  AsyncAddItems(pBottom);
end;

procedure TTIWContinuousScroll.AsyncInsertItemsBefore;
begin
  AsyncAddItems(pTop);
end;

constructor TTIWContinuousScroll.Create(AOwner: TComponent);
begin
  inherited;
  {$IFNDEF TMSIW121}
  SetRenderSize(true);
  {$ENDIF}
  Enabled := true;

  FBGColor := clNone;
  FBGColorTo := clNone;
  FBGGradientDirection := gdVertical;
  FBorderColor := clNone;
  FBorderWidth := 0;
  FEventType := '';
  FControlID := '';
  FControlIndex := -1;
  FItemCount := 30;
  FMaxItemCount := 100;
  FItemDelta := 5;
  FScrollMax := 0;
  FScrollEnd := 0;
  FScrollTop := 0;
  FScrollElement := seWindow;
  FLoadItems := liOnScroll;
  FLoadItemsButton := TLoadItemsButton.Create;
  FLoadScrollPercent := 80;

  Height := 250;
  Width := 300;
end;

destructor TTIWContinuousScroll.Destroy;
begin
  inherited;
  FLoadItemsButton.Free;
end;

procedure TTIWContinuousScroll.IWPaint;
var
  r : TRect;
begin
  Canvas.Font.Name := Font.FontName;
  Canvas.Font.Size := Font.Size;
  Canvas.Font.Color := Font.Color;
  Canvas.Font.Style := Font.Style;

  with Canvas do
  begin

    r.Left := 0;
    r.Top := 0;
    r.Right := Width;
    r.Bottom := Height;

//    if BGGradientDirection = gdVertical then
//      DrawGradient(Canvas,FBGColor, FBGColorTo,80,r,False)
//    else
//      DrawGradient(Canvas,FBGColor, FBGColorTo,80,r,True);

    if FBorderColor <> clNone then
    begin
      Pen.Color := FBorderColor;
      Pen.Width := FBorderWidth;
      Rectangle(0,0, Width,Height);
    end
    else
    begin
      Pen.Color := clWebLightGray;
      Pen.Width := 1;
      Rectangle(0,0, Width,Height);
    end;

    Brush.Style := bsClear;
    TextOut(Width div 2 - TextWidth(Name) div 2,Height div 2 - TextHeight(Name) div 2,Name);
  end;
end;         

procedure TTIWContinuousScroll.SetBGColor(const Value: TIWColor);
begin
  FBGColor := Value;
  Invalidate;
end;

procedure TTIWContinuousScroll.SetBGColorTo(const Value: TIWColor);
begin
  FBGColorTo := Value;
  Invalidate;
end;

function TTIWContinuousScroll.SetBGGradientDirection: TTIWGradientDirection;
begin
  Result := FBGGradientDirection;
  Invalidate;
end;

procedure TTIWContinuousScroll.SetBorderColor(const Value: TIWColor);
begin
  FBorderColor := Value;
  Invalidate;
end;

procedure TTIWContinuousScroll.SetBorderWidth(const Value: integer);
begin
  FBorderWidth := Value;
  Invalidate;
end;

procedure TTIWContinuousScroll.SetItemCount(const Value: integer);
begin
  FItemCount := Value;
  Invalidate;
end;

procedure TTIWContinuousScroll.SetItemDelta(const Value: integer);
begin
  FItemDelta := Value;
  Invalidate;
end;

procedure TTIWContinuousScroll.SetItemTemplate(const Value: string);
begin
  FItemTemplate := Value;
  Invalidate;
end;

procedure TTIWContinuousScroll.SetLoadItems(const Value: TLoadItemsType);
begin
  FLoadItems := Value;
  Invalidate;
end;

procedure TTIWContinuousScroll.SetLoadScrollPercent(const Value: integer);
begin
  if (Value <> FLoadScrollPercent) and (Value > 0) and (Value <= 100) then
  begin
    FLoadScrollPercent := Value;
    Invalidate;
  end;
end;

procedure TTIWContinuousScroll.SetMaxItemCount(const Value: integer);
begin
  FMaxItemCount := Value;
  Invalidate;
end;

procedure TTIWContinuousScroll.SetScrollElement(const Value: TScrollElement);
begin
  FScrollElement := Value;
  Invalidate;
end;

procedure TTIWContinuousScroll.SetValue(const value: string);
var
  A: TStringList;
begin
  inherited;
  if value <> '' then
  begin
    if Pos('^', value) > 0 then
    begin
     A := TStringList.Create;
     try
       IWTMSBase.Split('^', value, A);
       //EventType + "^" + elem.ID + "^" + Index;'#13
       FEventType := A[0];
       FControlID := A[1];
       FControlIndex := StrToInt(A[2]);
     finally
      A.Free;
     end;
    end
    else if Pos(':', value) > 0 then
    begin
     A := TStringList.Create;
     try
       IWTMSBase.Split(':', value, A);
       //ScrollTop + ":" + (ScrollHeight - Height);'#13
       FScrollTop := StrToInt(A[0]);
       FScrollEnd := StrToInt(A[1]);
     finally
      A.Free;
     end;
    end
    else
      FScrollTop := StrToInt(value);
  end;
end;

procedure TTIWContinuousScroll.Submit(const AValue: string);
begin
  inherited;
end;

function TTIWContinuousScroll.RenderHTML(AContext: TIWBaseComponentContext): TIWHTMLTag;
var
  htmlres, ajaxcall, ajaxcallcontrol, ajaxload: string;
begin

  Acontext.WebApplication.RegisterCallBack(HTMLName+'.DoOnAsyncScroll', DoOnAsyncScroll);
  Acontext.WebApplication.RegisterCallBack(HTMLName+'.DoOnAsyncLoadItems', DoOnAsyncLoadItems);
  Acontext.WebApplication.RegisterCallBack(HTMLName+'.DoOnAsyncControlEvent', DoOnAsyncControlEvent);

  ajaxcall := 'processAjaxEvent(''onAsyncScroll'', '
    + HTMLControlImplementation.IWCLName
    + ',''' + HTMLName + '.' + 'DoOnAsyncScroll' + ''','
    + 'true' + ', null, '
    + 'true' + ');';

  ajaxload := 'processAjaxEvent(''onAsyncLoadItems'', '
    + HTMLControlImplementation.IWCLName
    + ',''' + HTMLName + '.' + 'DoOnAsyncLoadItems' + ''','
    + 'true' + ', null, '
    + 'true' + ');';

  if Assigned(FOnAsyncControlEvent) then
  begin
    ajaxcallcontrol := 'processAjaxEvent(''onAsyncControlEvent'', '
      + HTMLControlImplementation.IWCLName
      + ',''' + HTMLName + '.' + 'DoOnAsyncControlEvent' + ''','
      + 'true' + ', null, '
      + 'true' + ');';
  end;

  htmlres := '<script>';

  htmlres := htmlres + 'function ' + HTMLName + 'GetParentScroller() {'#13
    + '  el = document.getElementById("' + HTMLName + '");'
    + '  sel = null;'#13
    + '  if (el){'#13
    + '    sel = el.parentNode;'#13
    + '    while ((sel.tagName != "DIV") && ((sel.tagName != "BODY")))'#13
    + '      sel = sel.parentNode;'#13
    + '  }'#13
    + '  return sel;'#13
    + '}'#13;

  htmlres := htmlres + 'function ' + HTMLName + 'ScrollInit() {'#13
    + '  sel = ' + HTMLName + 'GetParentScroller();'#13
    + '  if (sel){'#13
    + '    if (sel.tagName == "BODY")'#13
    + '      window.onscroll = ' + HTMLName + 'Scroll;'#13
    + '    else'#13
    + '      sel.onscroll = ' + HTMLName + 'Scroll;'#13
    + '  }'#13
//    + '  window.onscroll = ' + HTMLName + 'Scroll;'#13
    + '}'#13;

  htmlres := htmlres + 'function ' + HTMLName + 'Scroll() {'#13
    + '  sel = ' + HTMLName + 'GetParentScroller();'#13
    + '  if (sel){'#13
//    + '    alert(sel.tagName + " " + sel.scrollTop + " " + document.body.scrollTop + "-" + document.documentElement.scrollTop + " * " + document.compatMode + " * " + document.documentElement);'#13
    + '    if (sel.tagName == "BODY"){'#13;

if (brChrome <> GetTMSBrowser(AContext)) and (brSafari <> GetTMSBrowser(AContext)) then
    htmlres := htmlres
    + '      if (document.compatMode=="CSS1Compat")'#13
    + '        FindElem("' + HTMLName + '").value = document.documentElement.scrollTop + ":" + (document.documentElement.scrollHeight - document.documentElement.clientHeight);'#13
    + '      else'#13
    + '        FindElem("' + HTMLName + '").value = document.body.scrollTop + ":" + (document.body.scrollHeight - document.body.clientHeight);'#13
else
    htmlres := htmlres
    + '      FindElem("' + HTMLName + '").value = sel.scrollTop + ":" + (sel.scrollHeight - window.innerHeight);'#13;

  htmlres := htmlres
    + '    } else {'#13
    + '      FindElem("' + HTMLName + '").value = sel.scrollTop + ":" + (sel.scrollHeight - sel.offsetHeight);'#13
    + '    }'#13
    + '  }'#13
//    + '  FindElem("' + HTMLName + '").value = document.body.scrollTop;'#13
    + ajaxcall
    + '}'#13;

  htmlres := htmlres + 'function ' + HTMLName + 'LoadItems() {'#13
    + ajaxload
    + '}'#13;

  htmlres := htmlres + 'function ' + HTMLName + 'ScrollDiv(ctrl) {'#13
//    + '  FindElem("' + HTMLName + '").value = ctrl.scrollTop;'#13
    + '    FindElem("' + HTMLName + '").value = ctrl.scrollTop + ":" + (ctrl.scrollHeight - ctrl.offsetHeight);'#13
    + ajaxcall
    + '}'#13;

  htmlres := htmlres + 'function ' + HTMLName + 'ScrollToBottom() {'#13
    + '  sel = ' + HTMLName + 'GetParentScroller();'#13
    + '  if (sel){'#13;

  if brChrome <> GetTMSBrowser(AContext) then
  begin
    htmlres := htmlres
      + '    if (sel.tagName == "BODY"){'#13
      + '      if (document.compatMode=="CSS1Compat")'#13
      + '        document.documentElement.scrollTop = document.documentElement.scrollHeight;'#13
      + '      else'#13
      + '        document.body.scrollTop = document.body.scrollHeight;'#13
      + '    } else {'#13;
  end;

  htmlres := htmlres
    + '        sel.scrollTop = sel.scrollHeight;'#13;

  if brChrome <> GetTMSBrowser(AContext) then
    htmlres := htmlres
      + '    }'#13;

  htmlres := htmlres
    + '  }'#13
//    + '  document.body.scrollTop = document.body.scrollHeight;'#13
    + '}'#13;

  htmlres := htmlres + 'function ' + HTMLName + 'ControlEventHandler(e, EventType, Index) {'#13
    + '  if (e == null)'#13
    + '    e = window.event;'#13
    + '  var elem = e.target != null ? e.target : e.srcElement;'#13
    + '  FindElem("' + HTMLName + '").value = EventType + "^" + elem.id + "^" + Index;'#13
    + ajaxcallcontrol
    + '}'#13;

  htmlres := htmlres + '</script>'#13#13;

  if (ScrollElement = seWindow) and (LoadItems = liOnScroll) then
    TIWComponent40Context(AContext).AddToInitProc(HTMLName + 'ScrollInit();');

  htmlres := htmlres
    + '<style>'

    + '#' + HTMLName + ' {'#13
    + GradientStyle(BGColor, BGColor, BGColorTo, clNone, BGGradientDirection, AContext, true)
    + ' border:' + IntToStr(BorderWidth) + 'px solid ' + ColorToRGBString(BorderColor) + ';'#13;

  if ScrollElement <> seControl then
  begin
    htmlres := htmlres
      + ' display: table;'#13
  end;

  htmlres := htmlres
    + '}'#13;

  if ScrollElement <> seControl then
  begin
    htmlres := htmlres
      + '#' + HTMLName + ' > div {'#13
      + ' display: table-cell;'#13
      + '}'#13
  end;

  htmlres := htmlres
    + '.' + HTMLName + 'ul div {'#13
//    + ' font-size: 15px;'#13
//    + ' padding: 3px;'#13
//    + ' list-style: none;'#13
    + ' outline: none;'#13
    + '}'#13

    + '.' + HTMLName + 'load {'#13;

  if LoadItemsButton.Align = aRight then
    htmlres := htmlres + ' float:right;display:inline;'#13
  else if LoadItemsButton.Align = aLeft then
    htmlres := htmlres + ' float:left;display:inline;'#13
  else if LoadItemsButton.Align = aCenter then
    htmlres := htmlres + ' width:100%;text-align:center;'#13;

  htmlres := htmlres
    + ' background-color:' + ColorToRGBString(LoadItemsButton.BGColor) + ';'#13
    + ' font:' + LoadItemsButton.Font.FontToStringStyle(AContext.WebApplication.Browser) + ';'#13
    + ' padding: 3px;'#13
    + ' cursor: pointer;'#13
    + ' outline: none;'#13
    + '}'#13;

  if (LoadItemsButton.Style = bsLabel) and (LoadItems = liOnButtonClick) then
  begin
  htmlres := htmlres
    + '.' + HTMLName + 'load:hover {'#13
    + ' background-color:' + ColorToRGBString(LoadItemsButton.BGHoverColor) + ';'#13
    + ' color:' + ColorToRGBString(LoadItemsButton.FontHoverColor) + ';'#13
    + '}'#13

    + '.' + HTMLName + 'load:active {'#13
    + ' background-color:' + ColorToRGBString(LoadItemsButton.BGDownColor) + ';'#13
    + ' color:' + ColorToRGBString(LoadItemsButton.FontDownColor) + ';'#13
    + '}'#13
  end;

  htmlres := htmlres
    + '.' + HTMLName + 'ul {'#13
    + ' outline: none;'#13
    + '}'#13
    + '</style>'#13;


  htmlres := htmlres + '<div';

  if (ScrollElement = seControl) then
    htmlres := htmlres + ' style="overflow:auto;height:' + IntToStr(Height) + 'px;width:100%;"';

  if (LoadItems = liOnScroll) then
    htmlres := htmlres + ' onScroll="' + HTMLName + 'ScrollDiv(this)"';

  htmlres := htmlres + '>';

  htmlres := htmlres
    + RenderLoadItemsButton(pTop)
    + '<div class="' + HTMLName + 'ul" id="' + HTMLName + 'list" tabindex="-1">'
    + RenderItems(0)
    + '</div>'#13
    + RenderLoadItemsButton(pBottom)
    ;

//  if ScrollElement = seControl then
    htmlres := htmlres + '</div>'#13;

  Result := TIWHTMLTag.CreateTag('DIV');

  Result.Contents.AddText(htmlres);

  with Result.Contents.AddTag('INPUT') do
  begin
    AddStringParam('TYPE','hidden');
//    AddStringParam('TYPE','text');
    AddStringParam('NAME',HTMLName+'');
    AddStringParam('ID',HTMLName+'_INPUT');
    AddStringParam('VALUE', IntToStr(FScrollTop));
  end;
end;

function TTIWContinuousScroll.HTMLReplace(s: string; ItemIndex: integer):string;
var
  BeforeTag,AfterTag,Fld,DBFld:string;
  i,j: Integer;

  function VarPos(su,s: string; var Res: Integer): Integer;
  begin
    Res := Pos(su,s);
    Result := Res;
  end;

begin
  i := 1;

  BeforeTag := '';

  while VarPos('(#',s,i) > 0 do
  begin
    BeforeTag := BeforeTag + Copy(s,1,i - 1); //part prior to the tag
    AfterTag := Copy(s,i,Length(s)); //part after the tag

    j := Pos(')',AfterTag);
    if j = 0 then
      Break;

    Fld := Copy(AfterTag,1,j - 1);

    Delete(Fld,1,2);
    Delete(s,1,i + j - 1);

    DBFld := '';

    if (DBFld = '') then
    begin
      if Assigned(OnNeedItemData) then
      begin
        OnNeedItemData(Self, ItemIndex, Fld, DBFld);
      end;
    BeforeTag := BeforeTag + DBFld;
    end;
  end;

  Result := Beforetag + s;
end;

function TTIWContinuousScroll.RenderItems(BeginIndex: integer): string;
var
  I: integer;
  ItemContent: string;
begin
  for I := BeginIndex to FItemCount - 1 do
  begin
    if I <= FMaxItemCount then
    begin
      ItemContent := HTMLReplace(ItemTemplate, I);

      if Assigned(OnNeedItem) then
        OnNeedItem(Self, I, ItemContent);

      if ItemContent = '' then
        ItemContent := 'item ' + IntToStr(I + 1);

      result := result + '<div>' + ItemContent + '</div>';
    end;
  end;
end;

function TTIWContinuousScroll.RenderLoadItemsButton(
  Position: TPosition): string;
var
  cssclass: string;
begin
  if (LoadItems = liOnButtonClick) and (Position = LoadItemsButton.Position)
    and (LoadItemsButton.Visible) then
  begin

    if LoadItemsButton.Css = '' then
      cssclass := HTMLName + 'load'
    else
      cssclass := LoadItemsButton.Css;

    if LoadItemsButton.Style = bsLabel then
      result := '<div'
    else
      result := '<button';

    result := result  + ' class="' + cssclass + '" id="' + HTMLName + 'load"'
      + ' onclick="' + HTMLName + 'LoadItems()"';

    if FItemCount >= FMaxItemCount then
      result := result + ' style="visibility:hidden;"';

    result := result
      + ' tabindex="0">'
      + LoadItemsButton.Caption;

    if LoadItemsButton.Style = bsLabel then
      result := result + '</div>'
    else
      result := result + '</button>'
  end;
end;

{ TLoadItemsButton }

constructor TLoadItemsButton.Create;
begin
  inherited;
  FAlign := aLeft;
  FBGColor := clNone;
  FBGHoverColor := clNone;
  FBGDownColor := clNone;
  FCaption := 'Load more items';
  FPosition := pBottom;
  FFontHoverColor := clNone;
  FFontDownColor := clNone;
  FFont := TIWFont.Create;
  FFont.FontName := 'Arial';
  FFont.Size := 12;
  FVisible := true;
  FCss := '';
  FStyle := bsLabel;
end;

destructor TLoadItemsButton.Destroy;
begin
  inherited;
  FFont.Free;
end;

end.
