{
    FPDoc  -  Free Pascal Documentation Tool
    Copyright (C) 2000 - 2005 by
      Areca Systems GmbH / Sebastian Guenther, sg@freepascal.org

    * HTML/XHTML output generator

    See the file COPYING, included in this distribution,
    for details about the copyright.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
}
{$mode objfpc}
{$H+}

unit dw_HTML;

interface

uses Classes, contnrs, DOM, DOM_HTML, dGlobals, PasTree, dWriter, ChmWriter, ChmBase;

const
  // Subpage indices for modules
  ResstrSubindex = 1;
  ConstsSubindex = 2;
  TypesSubindex = 3;
  ClassesSubindex = 4;
  ProcsSubindex = 5;
  VarsSubindex = 6;
  // Maybe needed later for topic overview ??
  TopicsSubIndex = 7;
  IndexSubIndex = 8;

  // Subpage indices for classes
  PropertiesByInheritanceSubindex = 1;
  PropertiesByNameSubindex = 2;
  MethodsByInheritanceSubindex = 3;
  MethodsByNameSubindex = 4;
  EventsByInheritanceSubindex = 5;
  EventsByNameSubindex = 6;

type

  TFileAllocator = class
  public
    procedure AllocFilename(AElement: TPasElement; ASubindex: Integer); virtual;
    function GetFilename(AElement: TPasElement;
      ASubindex: Integer): String; virtual; abstract;
    function GetRelativePathToTop(AElement: TPasElement): String; virtual;
    function GetCSSFilename(ARelativeTo: TPasElement): DOMString; virtual;
  end;

  TShortNameFileAllocator = class(TFileAllocator)
  private
    FExtension: String;
  public
    constructor Create(const AExtension: String);
    procedure AllocFilename(AElement: TPasElement; ASubindex: Integer); override;
    property Extension: String read FExtension;
  end;

  TLongNameFileAllocator = class(TFileAllocator)
  private
    FExtension: String;
  public
    constructor Create(const AExtension: String);
    function GetFilename(AElement: TPasElement;
      ASubindex: Integer): String; override;
    function GetRelativePathToTop(AElement: TPasElement): String; override;
    property Extension: String read FExtension;
  end;


  TPageInfo = class
    Element: TPasElement;
    SubpageIndex: Integer;
  end;


  { THTMLWriter }

  THTMLWriter = class(TFPDocWriter)
  private
    FImageFileList: TStrings;

    FOnTest: TNotifyEvent;
    FPackage: TPasPackage;
    FCharSet : String;
    function GetPageCount: Integer;
    procedure SetOnTest(const AValue: TNotifyEvent);
  protected
    FCSSFile: String;
    FAllocator: TFileAllocator;
    CurDirectory: String;       // relative to curdir of process
    BaseDirectory: String;      // relative path to package base directory
    PageInfos: TObjectList;     // list of TPageInfo objects

    Doc: THTMLDocument;
    BodyElement, TitleElement: TDOMElement;

    Module: TPasModule;

    OutputNodeStack: TList;
    CurOutputNode: TDOMNode;
    InsideHeadRow, DoPasHighlighting: Boolean;
    HighlighterFlags: Byte;

    FooterFile: string;
    FIDF : Boolean;
    FDateFormat: String;
    FIndexColCount : Integer;
    FSearchPage : String;
    FBaseImageURL : String;
    FUseMenuBrackets: Boolean;

    Procedure CreateAllocator; virtual;
    function ResolveLinkID(const Name: String): DOMString;
    function ResolveLinkIDInUnit(const Name,AUnitName: String): DOMString;
    function ResolveLinkWithinPackage(AElement: TPasElement;
      ASubpageIndex: Integer): String;

    // Helper functions for creating DOM elements
    function CreateEl(Parent: TDOMNode; const AName: DOMString): THTMLElement;
    function CreatePara(Parent: TDOMNode): THTMLElement;
    function CreateH1(Parent: TDOMNode): THTMLElement;
    function CreateH2(Parent: TDOMNode): THTMLElement;
    function CreateH3(Parent: TDOMNode): THTMLElement;
    function CreateTable(Parent: TDOMNode; const AClass: DOMString = ''): THTMLElement;
    function CreateContentTable(Parent: TDOMNode): THTMLElement;
    function CreateTR(Parent: TDOMNode): THTMLElement;
    function CreateTD(Parent: TDOMNode): THTMLElement;
    function CreateTD_vtop(Parent: TDOMNode): THTMLElement;
    function CreateLink(Parent: TDOMNode; const AHRef: DOMString): THTMLElement;
    function CreateAnchor(Parent: TDOMNode; const AName: DOMString): THTMLElement;
    function CreateCode(Parent: TDOMNode): THTMLElement;
    function CreateWarning(Parent: TDOMNode): THTMLElement;

    // Description node conversion
    Procedure DescrEmitNotesHeader(AContext : TPasElement); override;
    Procedure DescrEmitNotesFooter(AContext : TPasElement); override;
    procedure PushOutputNode(ANode: TDOMNode);
    procedure PopOutputNode;
    procedure DescrWriteText(const AText: DOMString); override;
    procedure DescrBeginBold; override;
    procedure DescrEndBold; override;
    procedure DescrBeginItalic; override;
    procedure DescrEndItalic; override;
    procedure DescrBeginEmph; override;
    procedure DescrEndEmph; override;
    procedure DescrWriteImageEl(const AFileName, ACaption, ALinkName : DOMString); override;
    procedure DescrWriteFileEl(const AText: DOMString); override;
    procedure DescrWriteKeywordEl(const AText: DOMString); override;
    procedure DescrWriteVarEl(const AText: DOMString); override;
    procedure DescrBeginLink(const AId: DOMString); override;
    procedure DescrEndLink; override;
    procedure DescrBeginURL(const AURL: DOMString); override;
    procedure DescrEndURL; override;
    procedure DescrWriteLinebreak; override;
    procedure DescrBeginParagraph; override;
    procedure DescrEndParagraph; override;
    procedure DescrBeginCode(HasBorder: Boolean; const AHighlighterName: String); override;
    procedure DescrWriteCodeLine(const ALine: String); override;
    procedure DescrEndCode; override;
    procedure DescrBeginOrderedList; override;
    procedure DescrEndOrderedList; override;
    procedure DescrBeginUnorderedList; override;
    procedure DescrEndUnorderedList; override;
    procedure DescrBeginDefinitionList; override;
    procedure DescrEndDefinitionList; override;
    procedure DescrBeginListItem; override;
    procedure DescrEndListItem; override;
    procedure DescrBeginDefinitionTerm; override;
    procedure DescrEndDefinitionTerm; override;
    procedure DescrBeginDefinitionEntry; override;
    procedure DescrEndDefinitionEntry; override;
    procedure DescrBeginSectionTitle; override;
    procedure DescrBeginSectionBody; override;
    procedure DescrEndSection; override;
    procedure DescrBeginRemark; override;
    procedure DescrEndRemark; override;
    procedure DescrBeginTable(ColCount: Integer; HasBorder: Boolean); override;
    procedure DescrEndTable; override;
    procedure DescrBeginTableCaption; override;
    procedure DescrEndTableCaption; override;
    procedure DescrBeginTableHeadRow; override;
    procedure DescrEndTableHeadRow; override;
    procedure DescrBeginTableRow; override;
    procedure DescrEndTableRow; override;
    procedure DescrBeginTableCell; override;
    procedure DescrEndTableCell; override;

    procedure AppendText(Parent: TDOMNode; const AText: DOMString);
    procedure AppendNbSp(Parent: TDOMNode; ACount: Integer);
    procedure AppendSym(Parent: TDOMNode; const AText: DOMString);
    procedure AppendKw(Parent: TDOMNode; const AText: DOMString);
    function AppendPasSHFragment(Parent: TDOMNode; const AText: String;
      AShFlags: Byte): Byte;
    Procedure AppendShortDescr(AContext : TPasElement;Parent: TDOMNode; DocNode : TDocNode);
    procedure AppendShortDescr(Parent: TDOMNode; Element: TPasElement);
    procedure AppendShortDescrCell(Parent: TDOMNode; Element: TPasElement);
    procedure AppendDescr(AContext: TPasElement; Parent: TDOMNode;
      DescrNode: TDOMElement; AutoInsertBlock: Boolean);
    procedure AppendDescrSection(AContext: TPasElement; Parent: TDOMNode;
      DescrNode: TDOMElement; const ATitle: DOMString);
    function AppendHyperlink(Parent: TDOMNode; Element: TPasElement): TDOMElement;
    function AppendType(CodeEl, TableEl: TDOMElement;
      Element: TPasType; Expanded: Boolean;
      NestingLevel: Integer = 0): TDOMElement;
    function AppendProcType(CodeEl, TableEl: TDOMElement;
      Element: TPasProcedureType; Indent: Integer): TDOMElement;
    procedure AppendProcExt(CodeEl: TDOMElement; Element: TPasProcedure);
    procedure AppendProcDecl(CodeEl, TableEl: TDOMElement;
      Element: TPasProcedureBase);
    procedure AppendProcArgsSection(Parent: TDOMNode;
      Element: TPasProcedureType; SkipResult : Boolean = False);
    function AppendRecordType(CodeEl, TableEl: TDOMElement;
      Element: TPasRecordType; NestingLevel: Integer): TDOMElement;

    procedure AppendTitle(const AText: DOMString);
    procedure AppendMenuBar(ASubpageIndex: Integer);
    procedure AppendTopicMenuBar(Topic : TTopicElement);
    procedure AppendSourceRef(AElement: TPasElement);
    procedure FinishElementPage(AElement: TPasElement);
    Procedure AppendSeeAlsoSection(AElement : TPasElement;DocNode : TDocNode);
    Procedure AppendExampleSection(AElement : TPasElement;DocNode : TDocNode);
    procedure AppendFooter;
    procedure CreateIndexPage(L : TStringList);
    procedure CreateModuleIndexPage(AModule: TPasModule);
    procedure CreatePageBody(AElement: TPasElement; ASubpageIndex: Integer); virtual;
    procedure CreatePackagePageBody;
    procedure CreatePackageIndex;
    procedure AddModuleIdentifiers(AModule : TPasModule; L : TStrings);
    Procedure CreateTopicPageBody(AElement : TTopicElement);
    procedure CreateModulePageBody(AModule: TPasModule; ASubpageIndex: Integer);
    procedure CreateConstPageBody(AConst: TPasConst);
    procedure CreateTypePageBody(AType: TPasType);
    procedure CreateClassPageBody(AClass: TPasClassType; ASubpageIndex: Integer);
    procedure CreateClassMemberPageBody(AElement: TPasElement);
    procedure CreateVarPageBody(AVar: TPasVariable);
    procedure CreateProcPageBody(AProc: TPasProcedureBase);
    Procedure CreateTopicLinks(Node : TDocNode; PasElement : TPasElement);
  public
    constructor Create(APackage: TPasPackage; AEngine: TFPDocEngine); override;
    destructor Destroy; override;

    // Single-page generation
    function CreateHTMLPage(AElement: TPasElement;
      ASubpageIndex: Integer): TXMLDocument;
    function CreateXHTMLPage(AElement: TPasElement;
      ASubpageIndex: Integer): TXMLDocument;

    // For producing complete package documentation
    procedure WriteHTMLPages; virtual;
    procedure WriteXHTMLPages;

    Function InterPretOption(Const Cmd,Arg : String) : boolean; override;
    Procedure WriteDoc; override;
    Class Function FileNameExtension : String; override;
    class procedure Usage(List: TStrings); override;
    Property SearchPage: String Read FSearchPage Write FSearchPage;
    property Allocator: TFileAllocator read FAllocator;
    property Package: TPasPackage read FPackage;
    property PageCount: Integer read GetPageCount;
    Property IncludeDateInFooter : Boolean Read FIDF Write FIDF;
    Property DateFormat : String Read FDateFormat Write FDateFormat;
    property OnTest: TNotifyEvent read FOnTest write SetOnTest;
    Property CharSet : String Read FCharSet Write FCharSet;
    Property IndexColCount : Integer Read FIndexColCount write FIndexColCount;
    Property BaseImageURL : String Read FBaseImageURL Write FBaseImageURL;
    Property UseMenuBrackets : Boolean Read FUseMenuBrackets write FUseMenuBrackets;
  end;

  THTMWriter = class(THTMLWriter)
  Protected
    Procedure CreateAllocator; override;
  end;

  {$DEFINE chmInterface}
  {$I dw_htmlchm.inc}
  {$UNDEF chmInterface}

implementation

uses SysUtils, XHTML, XMLRead, XMLWrite, HTMWrite, sh_pas,chmsitemap;


Function FixHTMLpath(S : String) : STring;

begin
  Result:=StringReplace(S,'\','/',[rfReplaceAll]);
end;

{$I dw_htmlchm.inc}

procedure TFileAllocator.AllocFilename(AElement: TPasElement;
  ASubindex: Integer);
begin
end;

function TFileAllocator.GetRelativePathToTop(AElement: TPasElement): String;
begin
  SetLength(Result, 0);
end;

function TFileAllocator.GetCSSFilename(ARelativeTo: TPasElement): DOMString;
begin
  Result := GetRelativePathToTop(ARelativeTo) + 'fpdoc.css';
end;


constructor TShortNameFileAllocator.Create(const AExtension: String);
begin
  inherited Create;
  FExtension := AExtension;
end;

procedure TShortNameFileAllocator.AllocFilename(AElement: TPasElement;
  ASubindex: Integer);
begin
  // !!!: Add element to file list
end;


constructor TLongNameFileAllocator.Create(const AExtension: String);
begin
  inherited Create;
  FExtension := AExtension;
end;

function TLongNameFileAllocator.GetFilename(AElement: TPasElement;
  ASubindex: Integer): String;
var
  s: String;
  i: Integer;
begin
  if AElement.ClassType = TPasPackage then
    Result := 'index'
  else if AElement.ClassType = TPasModule then
    Result := LowerCase(AElement.Name) + PathDelim + 'index'
  else
  begin
    if AElement is TPasOperator then
    begin
      Result := LowerCase(AElement.Parent.PathName) + '.op-';
      s := Copy(AElement.Name, Pos(' ', AElement.Name) + 1, Length(AElement.Name));
      s := Copy(s, 1, Pos('(', s) - 1);
      if s = ':=' then
        s := 'assign'
      else if s = '+' then
        s := 'add'
      else if s = '-' then
        s := 'sub'
      else if s = '*' then
        s := 'mul'
      else if s = '/' then
        s := 'div'
      else if s = '**' then
        s := 'power'
      else if s = '=' then
        s := 'equal'
      else if s = '<>' then
        s := 'unequal'
      else if s = '<' then
        s := 'less'
      else if s = '<=' then
        s := 'lessequal'
      else if s = '>' then
        s := 'greater'
      else if s = '>=' then
        s := 'greaterthan'
      else if s = '><' then
        s := 'symmetricdifference';
      Result := Result + s + '-';
      s := '';
      i := 1;
      while AElement.Name[i] <> '(' do
        Inc(i);
      Inc(i);
      while AElement.Name[i] <> ')' do
      begin
        if AElement.Name[i] = ',' then
	begin
	  s := s + '-';
	  Inc(i);
	end else
	  s := s + AElement.Name[i];
        Inc(i);
      end;
      Result := Result + LowerCase(s) + '-' + LowerCase(Copy(AElement.Name,
        Pos('):', AElement.Name) + 3, Length(AElement.Name)));
    end else
      Result := LowerCase(AElement.PathName);
    i := 1;
    if (Length(Result) > 0) and (Result[1] = '#') then
    begin
      while Result[i] <> '.' do
        Inc(i);
      Result := Copy(Result, i + 1, Length(Result));
    end;
    i := 1;
    while (i <= Length(Result)) and (Result[i] <> '.') do
      Inc(i);
    if (i <= Length(Result)) and (i > 0) then
      Result[i] := PathDelim;
  end;

  if ASubindex > 0 then
    Result := Result + '-' + IntToStr(ASubindex);
  Result := Result + Extension;
end;

function TLongNameFileAllocator.GetRelativePathToTop(AElement: TPasElement): String;
begin
  if (AElement.ClassType=TPasPackage) then
    Result := ''
  else if (AElement.ClassType=TTopicElement) then
    begin
    If (AElement.Parent.ClassType=TTopicElement) then
      Result:='../'+GetRelativePathToTop(AElement.Parent)
    else if (AElement.Parent.ClassType=TPasPackage) then
      Result:=''
    else if (AElement.Parent.ClassType=TPasModule) then
      Result:='../';
    end
  else
    Result := '../';
end;

Type

  { TLinkData }

  TLinkData = Class(TObject)
    FPathName,
    FLink,
    FModuleName : String;

    Constructor Create(Const APathName,ALink,AModuleName : string);
  end;

{ TLinkData }

constructor TLinkData.Create(Const APathName, ALink, AModuleName: string);
begin
  FPathName:=APathName;
  FLink:=ALink;
  FModuleName:=AModuleName;
end;

constructor THTMLWriter.Create(APackage: TPasPackage; AEngine: TFPDocEngine);

  procedure AddPage(AElement: TPasElement; ASubpageIndex: Integer);
  var
    PageInfo: TPageInfo;
  begin
    PageInfo := TPageInfo.Create;
    PageInfo.Element := AElement;
    PageInfo.SubpageIndex := ASubpageIndex;
    PageInfos.Add(PageInfo);
    Allocator.AllocFilename(AElement, ASubpageIndex);
    if ASubpageIndex = 0 then
      Engine.AddLink(AElement.PathName,
        Allocator.GetFilename(AElement, ASubpageIndex));
  end;

  procedure AddTopicPages(AElement: TPasElement);

  var
    PreviousTopic,
    TopicElement : TTopicElement;
    PageInfo : TPageInfo;
    DocNode,
    TopicNode : TDocNode;

  begin
    DocNode:=Engine.FindDocNode(AElement);
    If not Assigned(DocNode) then
      exit;
    TopicNode:=DocNode.FirstChild;
    PreviousTopic:=Nil;
    While Assigned(TopicNode) do
      begin
      If TopicNode.TopicNode then
        begin
        TopicElement:=TTopicElement.Create(TopicNode.Name,AElement);
        Topics.Add(TopicElement);
        TopicElement.TopicNode:=TopicNode;
        TopicElement.Previous:=PreviousTopic;
        If Assigned(PreviousTopic) then
          PreviousTopic.Next:=TopicElement;
        PreviousTopic:=TopicElement;
        if AElement is TTopicElement then
          TTopicElement(AElement).SubTopics.Add(TopicElement);
        PageInfo := TPageInfo.Create;
        PageInfo.Element := TopicElement;
        PageInfo.SubpageIndex := 0;
        PageInfos.Add(PageInfo);
        Allocator.AllocFilename(TopicElement,0);
        Engine.AddLink(TopicElement.PathName, Allocator.GetFilename(TopicElement,0));
        if AElement is TTopicElement then
          TTopicElement(AElement).SubTopics.Add(TopicElement)
        else // Only one level of recursion.
          AddTopicPages(TopicElement);
        end;
      TopicNode:=TopicNode.NextSibling;
      end;
  end;

  procedure AddPages(AElement: TPasElement; ASubpageIndex: Integer;
    AList: TFPList);
  var
    i: Integer;
  begin
    if AList.Count > 0 then
    begin
      AddPage(AElement, ASubpageIndex);
      for i := 0 to AList.Count - 1 do
        AddPage(TPasElement(AList[i]), 0);
    end;
  end;

  procedure ScanModule(AModule: TPasModule; LinkList : TObjectList);
  var
    i, j, k: Integer;
    s: String;
    ClassEl: TPasClassType;
    FPEl, AncestorMemberEl: TPasElement;
    DocNode: TDocNode;
    ALink : DOMString;
    DidAutolink: Boolean;
  begin
    AddPage(AModule, 0);
    AddPage(AModule,IndexSubIndex);
    AddTopicPages(AModule);
    with AModule do
      begin
      if InterfaceSection.ResStrings.Count > 0 then
        begin
        AddPage(AModule, ResstrSubindex);
        s := Allocator.GetFilename(AModule, ResstrSubindex);
        for i := 0 to InterfaceSection.ResStrings.Count - 1 do
          with TPasResString(InterfaceSection.ResStrings[i]) do
            Engine.AddLink(PathName, s + '#' + LowerCase(Name));
        end;
      AddPages(AModule, ConstsSubindex, InterfaceSection.Consts);
      AddPages(AModule, TypesSubindex, InterfaceSection.Types);
      if InterfaceSection.Classes.Count > 0 then
        begin
        AddPage(AModule, ClassesSubindex);
        for i := 0 to InterfaceSection.Classes.Count - 1 do
          begin
          ClassEl := TPasClassType(InterfaceSection.Classes[i]);
          AddPage(ClassEl, 0);
          // !!!: Only add when there are items
          AddPage(ClassEl, PropertiesByInheritanceSubindex);
          AddPage(ClassEl, PropertiesByNameSubindex);
          AddPage(ClassEl, MethodsByInheritanceSubindex);
          AddPage(ClassEl, MethodsByNameSubindex);
          AddPage(ClassEl, EventsByInheritanceSubindex);
          AddPage(ClassEl, EventsByNameSubindex);

          for j := 0 to ClassEl.Members.Count - 1 do
            begin
            FPEl := TPasElement(ClassEl.Members[j]);
            if ((FPEl.Visibility = visPrivate) and Engine.HidePrivate) or
              ((FPEl.Visibility = visProtected) and Engine.HideProtected) then
              continue;

            DocNode := Engine.FindDocNode(FPEl);
            if Assigned(DocNode) then
              begin
              if Assigned(DocNode.Node) then
                ALink:=DocNode.Node['link']
              else
                ALink:='';
              If (ALink<>'') then
                LinkList.Add(TLinkData.Create(FPEl.PathName,ALink,AModule.name))
              else
                AddPage(FPEl, 0);
              end
            else
              begin
              DidAutolink := False;
              if Assigned(ClassEl.AncestorType) and
                (ClassEl.AncestorType.ClassType = TPasClassType) then
                begin
                for k := 0 to TPasClassType(ClassEl.AncestorType).Members.Count - 1 do
                  begin
                  AncestorMemberEl :=
                    TPasElement(TPasClassType(ClassEl.AncestorType).Members[k]);
                  if AncestorMemberEl.Name = FPEl.Name then
                    begin
                    DocNode := Engine.FindDocNode(AncestorMemberEl);
                    if Assigned(DocNode) then
                      begin
                      DidAutolink := True;
                      Engine.AddLink(FPEl.PathName,
                        Engine.FindAbsoluteLink(AncestorMemberEl.PathName));
                      break;
                      end;
                    end;
                  end;
                end;
              if not DidAutolink then
                AddPage(FPEl, 0);
              end;
            end;
          end;
        end;
      AddPages(AModule, ProcsSubindex, InterfaceSection.Functions);
      AddPages(AModule, VarsSubindex, InterfaceSection.Variables);
      end;
  end;

var
  i: Integer;
  L : TObjectList;

begin
  inherited ;

  // should default to true since this is the old behavior
  UseMenuBrackets:=True;

  IndexColCount:=3;
  Charset:='iso-8859-1';
  CreateAllocator;
  FPackage := APackage;
  OutputNodeStack := TList.Create;

  PageInfos := TObjectList.Create;
  FImageFileList := TStringList.Create;

  // Allocate page for the package itself, if a name is given (i.e. <> '#')
  if Length(Package.Name) > 1 then
    begin
    AddPage(Package, 0);
    AddPage(Package,IndexSubIndex);
    AddTopicPages(Package);
    end;
  L:=TObjectList.Create;
  try
    for i := 0 to Package.Modules.Count - 1 do
      ScanModule(TPasModule(Package.Modules[i]),L);
    // Resolve links
    For I:=0 to L.Count-1 do
      With TLinkData(L[i]) do
        Engine.AddLink(FPathName,ResolveLinkIDInUnit(FLink,FModuleName));
  finally
    L.Free;
  end;
end;

destructor THTMLWriter.Destroy;
begin
  PageInfos.Free;
  OutputNodeStack.Free;
  FAllocator.Free;
  FImageFileList.Free;
  inherited Destroy;
end;

function THTMLWriter.CreateHTMLPage(AElement: TPasElement;
  ASubpageIndex: Integer): TXMLDocument;
var
  HTMLEl: THTMLHtmlElement;
  HeadEl: THTMLHeadElement;
  El: TDOMElement;
begin
  Doc := THTMLDocument.Create;
  Result := Doc;
  Doc.AppendChild(Doc.Impl.CreateDocumentType(
    'HTML', '-//W3C//DTD HTML 4.01 Transitional//EN',
    'http://www.w3.org/TR/html4/loose.dtd'));

  HTMLEl := Doc.CreateHtmlElement;
  Doc.AppendChild(HTMLEl);

  HeadEl := Doc.CreateHeadElement;
  HTMLEl.AppendChild(HeadEl);
  El := Doc.CreateElement('meta');
  HeadEl.AppendChild(El);
  El['http-equiv'] := 'Content-Type';
  
  El['content'] := 'text/html; charset=utf-8';
  TitleElement := Doc.CreateElement('title');
  HeadEl.AppendChild(TitleElement);
  El := Doc.CreateElement('link');

  BodyElement := Doc.CreateElement('body');
  HTMLEl.AppendChild(BodyElement);

  CreatePageBody(AElement, ASubpageIndex);
  AppendFooter;

  HeadEl.AppendChild(El);
  El['rel'] := 'stylesheet';
  El['type'] := 'text/css';
  El['href'] := FixHtmlPath(Allocator.GetCSSFilename(AElement));
end;

function THTMLWriter.CreateXHTMLPage(AElement: TPasElement;
  ASubpageIndex: Integer): TXMLDocument;
begin
  Result := nil;
end;

procedure CreatePath(const AFilename: String);
var
  EndIndex: Integer;
  Path: String;
begin
  EndIndex := Length(AFilename);
  if EndIndex = 0 then
    exit;
  while not (AFilename[EndIndex] in AllowDirectorySeparators) do
  begin
    Dec(EndIndex);
    if EndIndex = 0 then
      exit;
  end;

  Path := Copy(AFilename, 1, EndIndex - 1);
  if not DirectoryExists(Path) then
  begin
    CreatePath(Path);
    MkDir(Path);
  end;
end;

procedure THTMLWriter.WriteHTMLPages;
var
  i: Integer;
  PageDoc: TXMLDocument;
  Filename: String;
  TempStream: TMemoryStream;

begin
  if Engine.Output <> '' then
    Engine.Output := IncludeTrailingBackSlash(Engine.Output);
  for i := 0 to PageInfos.Count - 1 do
    with TPageInfo(PageInfos[i]) do
    begin
      PageDoc := CreateHTMLPage(Element, SubpageIndex);
      try
        Filename := Engine.Output + Allocator.GetFilename(Element, SubpageIndex);
        try
          CreatePath(Filename);
          WriteHTMLFile(PageDoc, Filename);
        except
	  on E: Exception do
            DoLog(SErrCouldNotCreateFile, [FileName, e.Message]);
        end;
      finally
        PageDoc.Free;
      end;
    end;

  if FCSSFile <> '' then
  begin
    if not FileExists(FCSSFile) Then
      begin
        DoLog('Can''t find CSS file "%s"',[FCSSFILE]);
        halt(1);
      end;
    TempStream := TMemoryStream.Create;
    TempStream.LoadFromFile(FCSSFile);
    TempStream.Position := 0;
    TempStream.SaveToFile(Engine.output+ExtractFileName(FCSSFile));
    TempStream.Free;
  end;
end;

procedure THTMLWriter.WriteXHTMLPages;
begin
end;

{procedure THTMLWriter.CreateDoc(const ATitle: DOMString;
  AElement: TPasElement; const AFilename: String);
var
  El: TDOMElement;
  DocInfo: TDocInfo;
  CSSName: String;

begin
  Doc := TXHTMLDocument.Create;
  with TXHTMLDocument(Doc) do
  begin
    Encoding := 'ISO8859-1';
    CSSName := 'fpdoc.css';
    if Assigned(Module) then
      CSSName := '../' + CSSName;
$IFNDEF ver1_0
    StylesheetType := 'text/css';
    StylesheetHRef := CSSName;
$ENDIF
    CreateRoot(xhtmlStrict);
    with RequestHeadElement do
    begin
      AppendText(RequestTitleElement, ATitle);
      El := CreateElement('link');
      AppendChild(El);
      El['rel'] := 'stylesheet';
      El['type'] := 'text/css';
      El['href'] := FixHtmlPath(CSSName);
    end;
    Self.BodyElement := RequestBodyElement('en');
  end;

  if Length(AFilename) > 0 then
  begin
    DocInfo := TDocInfo.Create;
    DocInfos.Add(DocInfo);
    DocInfo.Element := AElement;
    DocInfo.Filename := AFilename;
  end;
end;
}


{ Used for:
  - <link> elements in descriptions
  - "see also" entries
  - AppendHyperlink (for unresolved parse tree element links)
}

function THTMLWriter.ResolveLinkIDInUnit(const Name,AUnitName: String): DOMString;

begin
  Result:=ResolveLinkID(Name);
  If (Result='') and (AUnitName<>'')  then
    Result:=ResolveLinkID(AUnitName+'.'+Name);
end;

function THTMLWriter.ResolveLinkID(const Name: String): DOMString;
var
  i: Integer;
  ThisPackage: TLinkNode;
begin
  if Length(Name) = 0 then
  begin
    SetLength(Result, 0);
    exit;
  end;

  if Name[1] = '#' then
    Result := Engine.FindAbsoluteLink(Name)
  else
  begin
    SetLength(Result, 0);
    { Try all packages }
    ThisPackage := Engine.RootLinkNode.FirstChild;
    while Assigned(ThisPackage) do
    begin
      Result := Engine.FindAbsoluteLink(ThisPackage.Name + '.' + Name);
      if Length(Result) = 0 then
      begin
        if Assigned(Module) then
          begin
          Result := Engine.FindAbsoluteLink(Module.PathName + '.' + Name);
//          WriteLn('Searching for ', Module.PathName + '.' + Name, ' => ', Result);
          end;
        if Length(Result) = 0 then
          for i := Length(Name) downto 1 do
            if Name[i] = '.' then
            begin
              Result := ResolveLinkID(Copy(Name, 1, i - 1));
              exit;
            end;
      end;
      ThisPackage := ThisPackage.NextSibling;
    end;
  end;

  if Length(Result) > 0 then
    if Copy(Result, 1, Length(CurDirectory) + 1) = CurDirectory + '/' then
      Result := Copy(Result, Length(CurDirectory) + 2, Length(Result))
    else if not IsLinkAbsolute(Result) then
      Result := BaseDirectory + Result;
end;

function THTMLWriter.ResolveLinkWithinPackage(AElement: TPasElement;
  ASubpageIndex: Integer): String;
var
  ParentEl: TPasElement;
begin
  ParentEl := AElement;
  while Assigned(ParentEl) and not (ParentEl.ClassType = TPasPackage) do
    ParentEl := ParentEl.Parent;
  if Assigned(ParentEl) and (TPasPackage(ParentEl) = Engine.Package) then
  begin
    Result := Allocator.GetFilename(AElement, ASubpageIndex);
    if Copy(Result, 1, Length(CurDirectory) + 1) = CurDirectory + '/' then
      Result := Copy(Result, Length(CurDirectory) + 2, Length(Result))
    else
      Result := BaseDirectory + Result;
  end else
    SetLength(Result, 0);
end;

function THTMLWriter.CreateEl(Parent: TDOMNode;
  const AName: DOMString): THTMLElement;
begin
  Result := Doc.CreateElement(AName);
  Parent.AppendChild(Result);
end;

function THTMLWriter.CreatePara(Parent: TDOMNode): THTMLElement;
begin
  Result := CreateEl(Parent, 'p');
end;

function THTMLWriter.CreateH1(Parent: TDOMNode): THTMLElement;
begin
  Result := CreateEl(Parent, 'h1');
end;

function THTMLWriter.CreateH2(Parent: TDOMNode): THTMLElement;
begin
  Result := CreateEl(Parent, 'h2');
end;

function THTMLWriter.CreateH3(Parent: TDOMNode): THTMLElement;
begin
  Result := CreateEl(Parent, 'h3');
end;

function THTMLWriter.CreateTable(Parent: TDOMNode; const AClass: DOMString = ''): THTMLElement;
begin
  Result := CreateEl(Parent, 'table');
  Result['cellspacing'] := '0';
  Result['cellpadding'] := '0';
  if AClass <> '' then
    Result['class'] := AClass;
end;

function THTMLWriter.CreateContentTable(Parent: TDOMNode): THTMLElement;
begin
  Result := CreateEl(Parent, 'table');
end;

function THTMLWriter.CreateTR(Parent: TDOMNode): THTMLElement;
begin
  Result := CreateEl(Parent, 'tr');
end;

function THTMLWriter.CreateTD(Parent: TDOMNode): THTMLElement;
begin
  Result := CreateEl(Parent, 'td');
end;

function THTMLWriter.CreateTD_vtop(Parent: TDOMNode): THTMLElement;
begin
  Result := CreateEl(Parent, 'td');
  Result['valign'] := 'top';
end;

function THTMLWriter.CreateLink(Parent: TDOMNode;
  const AHRef: DOMString): THTMLElement;
begin
  Result := CreateEl(Parent, 'a');
  Result['href'] := FixHtmlPath(AHRef);
end;

function THTMLWriter.CreateAnchor(Parent: TDOMNode;
  const AName: DOMString): THTMLElement;
begin
  Result := CreateEl(Parent, 'a');
  Result['name'] := AName;
end;

function THTMLWriter.CreateCode(Parent: TDOMNode): THTMLElement;
begin
  Result := CreateEl(CreateEl(Parent, 'tt'), 'span');
  Result['class'] := 'code';
end;

function THTMLWriter.CreateWarning(Parent: TDOMNode): THTMLElement;
begin
  Result := CreateEl(Parent, 'span');
  Result['class'] := 'warning';
end;

procedure THTMLWriter.DescrEmitNotesHeader(AContext: TPasElement);
begin
  AppendText(CreateH2(BodyElement), SDocNotes);
  PushOutputNode(BodyElement);
end;

procedure THTMLWriter.DescrEmitNotesFooter(AContext: TPasElement);
begin
  PopOutPutNode;
end;

procedure THTMLWriter.PushOutputNode(ANode: TDOMNode);
begin
  OutputNodeStack.Add(CurOutputNode);
  CurOutputNode := ANode;
end;

procedure THTMLWriter.PopOutputNode;
begin
  CurOutputNode := TDOMNode(OutputNodeStack[OutputNodeStack.Count - 1]);
  OutputNodeStack.Delete(OutputNodeStack.Count - 1);
end;

procedure THTMLWriter.DescrWriteText(const AText: DOMString);
begin
  AppendText(CurOutputNode, AText);
end;

procedure THTMLWriter.DescrBeginBold;
begin
  PushOutputNode(CreateEl(CurOutputNode, 'b'));
end;

procedure THTMLWriter.DescrEndBold;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrBeginItalic;
begin
  PushOutputNode(CreateEl(CurOutputNode, 'i'));
end;

procedure THTMLWriter.DescrEndItalic;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrBeginEmph;
begin
  PushOutputNode(CreateEl(CurOutputNode, 'em'));
end;

procedure THTMLWriter.DescrEndEmph;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrWriteImageEl(const AFileName, ACaption, ALinkName : DOMString);

Var
  Pel,Cel,Lel : TDOMNode;
  El :TDomElement;
  D : String;
  L : Integer;
   
begin
  // Determine parent node.
  If (ACaption='') then
    Pel:=CurOutputNode
  else
    begin
    Cel:=CreateTable(CurOutputNode, 'imagetable');
    Pel:=CreateTD(CreateTR(Cel));
    Cel:=CreateTD(CreateTR(Cel));
    El := CreateEl(Cel, 'span');
    El['class'] := 'imagecaption';
    Cel := El;
    If (ALinkName<>'') then
      Cel:=CreateAnchor(Cel,ALinkName);
    AppendText(Cel,ACaption);
    end;

  // Determine URL for image.  
  If (Module=Nil) then
    D:=Allocator.GetRelativePathToTop(Package)
  else
    D:=Allocator.GetRelativePathToTop(Module);
  L:=Length(D);
  If (L>0) and (D[L]<>'/') then
    D:=D+'/';

  // Create image node.  
  El:=CreateEl(Pel,'img');
  EL['src']:=D + BaseImageURL + AFileName;
  El['alt']:=ACaption;

  //cache image filename, so it can be used later (CHM)
  FImageFileList.Add(BaseImageURL + AFileName);
end;

procedure THTMLWriter.DescrWriteFileEl(const AText: DOMString);
var
  NewEl: TDOMElement;
begin
  NewEl := CreateEl(CurOutputNode, 'span');
  NewEl['class'] := 'file';
  AppendText(NewEl, AText);
end;

procedure THTMLWriter.DescrWriteKeywordEl(const AText: DOMString);
var
  NewEl: TDOMElement;
begin
  NewEl := CreateEl(CurOutputNode, 'span');
  NewEl['class'] := 'kw';
  AppendText(NewEl, AText);
end;

procedure THTMLWriter.DescrWriteVarEl(const AText: DOMString);
begin
  AppendText(CreateEl(CurOutputNode, 'var'), AText);
end;

procedure THTMLWriter.DescrBeginLink(const AId: DOMString);
var
  a,s: String;
begin
  a:=AId;
  s := ResolveLinkID(a);
  if Length(s) = 0 then
  begin
    if assigned(module) then
      s:=module.name
    else
      s:='?';
    if a='' then a:='<empty>';
    DoLog(SErrUnknownLinkID, [s,a]);
    PushOutputNode(CreateEl(CurOutputNode, 'b'));
  end else
    PushOutputNode(CreateLink(CurOutputNode, s));
end;

procedure THTMLWriter.DescrEndLink;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrBeginURL(const AURL: DOMString);
begin
  PushOutputNode(CreateLink(CurOutputNode, AURL));
end;

procedure THTMLWriter.DescrEndURL;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrWriteLinebreak;
begin
  CreateEl(CurOutputNode, 'br');
end;

procedure THTMLWriter.DescrBeginParagraph;
begin
  PushOutputNode(CreatePara(CurOutputNode));
end;

procedure THTMLWriter.DescrEndParagraph;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrBeginCode(HasBorder: Boolean; const AHighlighterName: String);
begin
  DoPasHighlighting := (AHighlighterName = '') or (AHighlighterName = 'Pascal');
  HighlighterFlags := 0;
  PushOutputNode(CreateEl(CurOutputNode, 'pre'));
end;

procedure THTMLWriter.DescrWriteCodeLine(const ALine: String);
begin
  if DoPasHighlighting then
  begin
    HighlighterFlags := AppendPasSHFragment(CurOutputNode, ALine,
      HighlighterFlags);
    AppendText(CurOutputNode, #10);
  end else
    AppendText(CurOutputNode, ALine + #10);
end;

procedure THTMLWriter.DescrEndCode;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrBeginOrderedList;
begin
  PushOutputNode(CreateEl(CurOutputNode, 'ol'));
end;

procedure THTMLWriter.DescrEndOrderedList;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrBeginUnorderedList;
begin
  PushOutputNode(CreateEl(CurOutputNode, 'ul'));
end;

procedure THTMLWriter.DescrEndUnorderedList;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrBeginDefinitionList;
begin
  PushOutputNode(CreateEl(CurOutputNode, 'dl'));
end;

procedure THTMLWriter.DescrEndDefinitionList;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrBeginListItem;
begin
  PushOutputNode(CreateEl(CurOutputNode, 'li'));
end;

procedure THTMLWriter.DescrEndListItem;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrBeginDefinitionTerm;
begin
  PushOutputNode(CreateEl(CurOutputNode, 'dt'));
end;

procedure THTMLWriter.DescrEndDefinitionTerm;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrBeginDefinitionEntry;
begin
  PushOutputNode(CreateEl(CurOutputNode, 'dd'));
end;

procedure THTMLWriter.DescrEndDefinitionEntry;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrBeginSectionTitle;
begin
  PushOutputNode(CreateEl(CurOutputNode, 'h3'));
end;

procedure THTMLWriter.DescrBeginSectionBody;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrEndSection;
begin
end;

procedure THTMLWriter.DescrBeginRemark;
var
  NewEl, TDEl: TDOMElement;
begin
  NewEl := CreateEl(CurOutputNode, 'table');
  NewEl['width'] := '100%';
  NewEl['border'] := '0';
  NewEl['CellSpacing'] := '0';
  NewEl['class'] := 'remark';
  NewEl := CreateTR(NewEl);
  TDEl := CreateTD(NewEl);
  TDEl['valign'] := 'top';
  TDEl['class'] := 'pre';
  AppendText(CreateEl(TDEl, 'b'), SDocRemark);
  PushOutputNode(CreateTD(NewEl));
end;

procedure THTMLWriter.DescrEndRemark;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrBeginTable(ColCount: Integer; HasBorder: Boolean);
var
  Table: TDOMElement;
begin
  Table := CreateEl(CurOutputNode, 'table');
  Table['border'] := IntToStr(Ord(HasBorder));
  PushOutputNode(Table);
end;

procedure THTMLWriter.DescrEndTable;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrBeginTableCaption;
begin
  PushOutputNode(CreateEl(CurOutputNode, 'caption'));
end;

procedure THTMLWriter.DescrEndTableCaption;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrBeginTableHeadRow;
begin
  PushOutputNode(CreateTr(CurOutputNode));
  InsideHeadRow := True;
end;

procedure THTMLWriter.DescrEndTableHeadRow;
begin
  InsideHeadRow := False;
  PopOutputNode;
end;

procedure THTMLWriter.DescrBeginTableRow;
begin
  PushOutputNode(CreateTR(CurOutputNode));
end;

procedure THTMLWriter.DescrEndTableRow;
begin
  PopOutputNode;
end;

procedure THTMLWriter.DescrBeginTableCell;
begin
  if InsideHeadRow then
    PushOutputNode(CreateEl(CurOutputNode, 'th'))
  else
    PushOutputNode(CreateTD(CurOutputNode));
end;

procedure THTMLWriter.DescrEndTableCell;
begin
  PopOutputNode;
end;




procedure THTMLWriter.AppendText(Parent: TDOMNode; const AText: DOMString);
begin
  Parent.AppendChild(Doc.CreateTextNode(AText));
end;

procedure THTMLWriter.AppendNbSp(Parent: TDOMNode; ACount: Integer);
begin
  while ACount > 0 do
  begin
    Parent.AppendChild(Doc.CreateEntityReference('nbsp'));
    Dec(ACount);
  end;
end;

procedure THTMLWriter.AppendSym(Parent: TDOMNode; const AText: DOMString);
var
  El: TDOMElement;
begin
  El := CreateEl(Parent, 'span');
  El['class'] := 'sym';
  AppendText(El, AText);
end;

procedure THTMLWriter.AppendKw(Parent: TDOMNode; const AText: DOMString);
var
  El: TDOMElement;
begin
  El := CreateEl(Parent, 'span');
  El['class'] := 'kw';
  AppendText(El, AText);
end;

function THTMLWriter.AppendPasSHFragment(Parent: TDOMNode;
  const AText: String; AShFlags: Byte): Byte;


var
  Line, Last, p: PChar;
  IsInSpecial: Boolean;
  lastwasasm : boolean;
  El: TDOMElement;

  Procedure MaybeOutput;

  Var
    CurParent: TDomNode;

  begin
    If (Last<>Nil) then
      begin
      If (el<>Nil) then
        CurParent:=El
      else
        CurParent:=Parent;
      AppendText(CurParent,Last);
      El:=Nil;
      Last:=Nil;
      end;
  end;

  Function NewEl(Const ElType,Attr,AttrVal : String) : TDomElement;

  begin
    Result:=CreateEl(Parent,ElType);
    Result[Attr]:=AttrVal;
  end;

  Function NewSpan(Const AttrVal : String) : TDomElement;

  begin
    Result:=CreateEl(Parent,'span');
    Result['class']:=AttrVal;
  end;

begin
  GetMem(Line, Length(AText) * 3 + 4);
  Try
  DoPascalHighlighting(AShFlags, PChar(AText), Line);
  Result := AShFlags;
  IsInSpecial := False;
  Last := Nil;
  p := Line;
  el:=nil;
  while p[0] <> #0 do
  begin
    if p[0] = LF_ESCAPE then
      begin
      p[0] := #0;
      MaybeOutput;
      case Ord(p[1]) of
        shDefault:    El:=Nil;
        shInvalid:    El:=newel('font','color','red');
        shSymbol :    El:=newspan('sym');
        shKeyword:    El:=newspan('kw');
        shComment:    El:=newspan('cmt');
        shDirective:  El:=newspan('dir');
        shNumbers:    El:=newspan('num');
        shCharacters: El:=newspan('chr');
        shStrings:    El:=newspan('str');
        shAssembler:  El:=newspan('asm');
      end;
      Inc(P);
      end
    else If (Last=Nil) then
      Last:=P;
    Inc(p);
  end;
  MaybeOutput;
  Finally
    FreeMem(Line);
  end;
end;

Procedure THTMLWriter.AppendShortDescr(AContext: TPasElement; Parent: TDOMNode; DocNode : TDocNode);

Var
  N : TDocNode;

begin
  if Assigned(DocNode) then
    begin
    If (DocNode.Link<>'') then
      begin
      N:=Engine.FindLinkedNode(DocNode);
      If (N<>Nil) then
        DocNode:=N;
      end;
    If Assigned(DocNode.ShortDescr) then
      begin
      PushOutputNode(Parent);
      try
        if not ConvertShort(AContext,TDomElement(DocNode.ShortDescr)) then
          Warning(AContext, SErrInvalidShortDescr)
      finally
        PopOutputNode;
      end;
      end;
    end;
end;

procedure THTMLWriter.AppendShortDescr(Parent: TDOMNode; Element: TPasElement);

begin
  AppendShortDescr(Element,Parent,Engine.FindDocNode(Element));
end;

procedure THTMLWriter.AppendShortDescrCell(Parent: TDOMNode;
  Element: TPasElement);

var
  ParaEl: TDOMElement;

begin
  if Assigned(Engine.FindShortDescr(Element)) then
  begin
    AppendNbSp(CreatePara(CreateTD(Parent)), 2);
    ParaEl := CreatePara(CreateTD(Parent));
    ParaEl['class'] := 'cmt';
    AppendShortDescr(ParaEl, Element);
  end;
end;

procedure THTMLWriter.AppendDescr(AContext: TPasElement; Parent: TDOMNode;
  DescrNode: TDOMElement; AutoInsertBlock: Boolean);
begin
  if Assigned(DescrNode) then
  begin
    PushOutputNode(Parent);
    try
      ConvertDescr(AContext, DescrNode, AutoInsertBlock);
    finally
      PopOutputNode;
    end;
  end;
end;

procedure THTMLWriter.AppendDescrSection(AContext: TPasElement;
  Parent: TDOMNode; DescrNode: TDOMElement; const ATitle: DOMString);
begin
  if not IsDescrNodeEmpty(DescrNode) then
  begin
    If (ATitle<>'') then // Can be empty for topic.
      AppendText(CreateH2(Parent), ATitle);
    AppendDescr(AContext, Parent, DescrNode, True);
  end;
end;



function THTMLWriter.AppendHyperlink(Parent: TDOMNode;
  Element: TPasElement): TDOMElement;
var
  s: String;
  UnitList: TFPList;
  i: Integer;
  ThisPackage: TLinkNode;
begin
  if Assigned(Element) then
  begin
    if Element.InheritsFrom(TPasUnresolvedTypeRef) then
    begin
      s := ResolveLinkID(Element.Name);
      if Length(s) = 0 then
      begin
        { Try all packages }
        ThisPackage := Engine.RootLinkNode.FirstChild;
        while Assigned(ThisPackage) do
        begin
          s := ResolveLinkID(ThisPackage.Name + '.' + Element.Name);
          if Length(s) > 0 then
            break;
          ThisPackage := ThisPackage.NextSibling;
        end;
        if Length(s) = 0 then
        begin
          { Okay, then we have to try all imported units of the current module }
          UnitList := Module.InterfaceSection.UsesList;
          for i := UnitList.Count - 1 downto 0 do
          begin
            { Try all packages }
            ThisPackage := Engine.RootLinkNode.FirstChild;
            while Assigned(ThisPackage) do
            begin
              s := ResolveLinkID(ThisPackage.Name + '.' +
                TPasType(UnitList[i]).Name + '.' + Element.Name);
              if Length(s) > 0 then
                break;
              ThisPackage := ThisPackage.NextSibling;
            end;
            if length(s)=0 then
              s := ResolveLinkID('#rtl.System.' + Element.Name);
            if Length(s) > 0 then
              break;
          end;
        end;
      end;
    end else if Element is TPasEnumValue then
      s := ResolveLinkID(Element.Parent.PathName)
    else  
      s := ResolveLinkID(Element.PathName);

    if Length(s) > 0 then
    begin
      Result := CreateLink(Parent, s);
      AppendText(Result, Element.Name);
    end else
    begin
      Result := nil;
      AppendText(Parent, Element.Name);
    end;
  end else
  begin
    Result := nil;
    AppendText(CreateWarning(Parent), '<NIL>');
  end;
end;


{ Returns the new CodeEl, which will be the old CodeEl in most cases }
function THTMLWriter.AppendType(CodeEl, TableEl: TDOMElement;
  Element: TPasType; Expanded: Boolean; NestingLevel: Integer): TDOMElement;

Var
  S : String;

begin
  Result := CodeEl;

  if not Assigned(Element) then
    AppendText(CreateWarning(CodeEl), '<NIL>')
  else if (not Expanded) and (Length(Element.Name) > 0) then
    AppendHyperlink(CodeEl, Element)
  else
  // Array
  if Element.ClassType = TPasArrayType then
  begin
    S:='array ';
    If (TPasArrayType(Element).IndexRange<>'') then
      S:=S+'[' + TPasArrayType(Element).IndexRange + '] ';
    S:=S+'of ';
    If (TPasArrayType(Element).ElType=Nil) then
      S:=S+'Const';
    AppendPasSHFragment(CodeEl,S,0);
    If (TPasArrayType(Element).ElType<>Nil) then
      Result := AppendType(CodeEl, TableEl, TPasArrayType(Element).ElType, False);
  end else
  // Procedure or funtion type
  if Element.InheritsFrom(TPasProcedureType) then
  begin
    AppendKw(CodeEl, TPasProcedureType(Element).TypeName);
    Result := AppendProcType(CodeEl, TableEl, TPasProcedureType(Element), 0)
  end else
  // Range type
  if Element.InheritsFrom(TPasRangeType) then
    AppendPasSHFragment(CodeEl, TPasRangeType(Element).RangeStart + '..' +
      TPasRangeType(Element).RangeEnd, 0)
  // Record type
  else if Element.ClassType = TPasRecordType then
    Result := AppendRecordType(CodeEl, TableEl, TPasRecordType(Element), NestingLevel)
  else if (Element.ClassType = TPasFileType) and (TPasFileType(Element).elType=Nil) then
    AppendPasSHFragment(CodeEl,'file',0)
  else
  // Other types
    AppendHyperlink(CodeEl, Element);
end;

function THTMLWriter.AppendProcType(CodeEl, TableEl: TDOMElement;
  Element: TPasProcedureType; Indent: Integer): TDOMElement;

  function CreateIndentedCodeEl(Indent: Integer): TDOMElement;
  begin
    Result := CreateCode(CreatePara(CreateTD(CreateTR(TableEl))));
    AppendNbSp(Result, Indent);
  end;

var
  i: Integer;
  Arg: TPasArgument;
begin
  if Element.Args.Count > 0 then
  begin
    AppendSym(CodeEl, '(');

    for i := 0 to Element.Args.Count - 1 do
    begin
      Arg := TPasArgument(Element.Args[i]);
      CodeEl := CreateIndentedCodeEl(Indent + 2);

      case Arg.Access of
        argConst: AppendKw(CodeEl, 'const ');
        argVar: AppendKw(CodeEl, 'var ');
        argOut: AppendKw(CodeEl, 'out ');
      end;
      AppendText(CodeEl, Arg.Name);
      if Assigned(Arg.ArgType) then
      begin
        AppendSym(CodeEl, ': ');
        CodeEl := AppendType(CodeEl, TableEl, Arg.ArgType, False);
      end;
      if Length(Arg.Value) > 0 then
        AppendPasSHFragment(CodeEl, ' = ' + Arg.Value, 0);
      if i < Element.Args.Count - 1 then
        AppendSym(CodeEl, ';');
    end;

    if Element.InheritsFrom(TPasFunctionType) or Element.IsOfObject then
    begin
      CodeEl := CreateIndentedCodeEl(Indent);
      if Element.InheritsFrom(TPasFunctionType) then
      begin
        AppendSym(CodeEl, '):');
        AppendHyperlink(CodeEl, TPasFunctionType(Element).ResultEl.ResultType);
      end else
        AppendSym(CodeEl, ')');
      if Element.IsOfObject then
      begin
        AppendText(CodeEl, ' ');        // Don't remove
        AppendKw(CodeEl, 'of object');
      end;
    end else
      if Indent > 0 then
        AppendSym(CodeEl, ')')
      else
      begin
        CodeEl := CreateCode(CreatePara(CreateTD(CreateTR(TableEl))));
        AppendSym(CodeEl, ')');
      end;
  end else
  begin
    { Procedure or function without arguments }
    if Element.InheritsFrom(TPasFunctionType) then
    begin
      AppendSym(CodeEl, ': ');
      AppendHyperlink(CodeEl, TPasFunctionType(Element).ResultEl.ResultType);
    end;
    if Element.IsOfObject then
      AppendKw(CodeEl, ' of object');
  end;
  Result := CodeEl;
end;

procedure THTMLWriter.AppendProcExt(CodeEl: TDOMElement;
  Element: TPasProcedure);

  procedure AppendExt(const Ext: String);
  begin
    AppendKw(CodeEl, ' ' + Ext);
    AppendSym(CodeEl, ';');
  end;

begin
  if Element.IsVirtual then
    AppendExt('virtual');
  if Element.IsDynamic then
    AppendExt('dynamic');
  if Element.IsAbstract then
    AppendExt('abstract');
  if Element.IsOverride then
    AppendExt('override');
  if Element.IsOverload then
    AppendExt('overload');
  if Element.IsMessage then
    AppendExt('message');
end;


{ Used in two places:
  - Page for the method of a class
  - Page for a tandalone procedure or function. }

procedure THTMLWriter.AppendProcDecl(CodeEl, TableEl: TDOMElement;
  Element: TPasProcedureBase);

  procedure WriteVariant(AProc: TPasProcedure; SkipResult : Boolean);
  begin
    AppendProcArgsSection(TableEl.ParentNode, AProc.ProcType, SkipResult);

    AppendKw(CodeEl, AProc.TypeName);
    if Element.Parent.ClassType = TPasClassType then
    begin
      AppendText(CodeEl, ' ');
      AppendHyperlink(CodeEl, Element.Parent);
      AppendSym(CodeEl, '.');
      AppendText(CodeEl, AProc.Name);
    end else
      AppendText(CodeEl, ' ' + AProc.FullName);
    CodeEl := AppendProcType(CodeEl, TableEl, AProc.ProcType, 0);
    AppendSym(CodeEl, ';');
    AppendProcExt(CodeEl, AProc);
  end;

var
  i,fc: Integer;
  P : TPasProcedure;
begin
  fc:=0;
  if Element.ClassType = TPasOverloadedProc then
    for i := 0 to TPasOverloadedProc(Element).Overloads.Count - 1 do
    begin
      P:=TPasProcedure(TPasOverloadedProc(Element).Overloads[i]);
      if (P.ProcType is TPasFunctionType) then
        Inc(fc);
      if i > 0 then
      begin
        CreateEl(CodeEl, 'br');
        CreateEl(CodeEl, 'br');
      end;
      WriteVariant(P,fc>1);
    end
  else
    WriteVariant(TPasProcedure(Element),False);
end;

procedure THTMLWriter.AppendProcArgsSection(Parent: TDOMNode;
  Element: TPasProcedureType; SkipResult : Boolean = False);
var
  HasFullDescr, IsFirst: Boolean;
  ResultEl: TPasResultElement;
  ArgTableEl, TREl: TDOMElement;
  DocNode: TDocNode;
  i: Integer;
  Arg: TPasArgument;
begin
  IsFirst := True;
  for i := 0 to Element.Args.Count - 1 do
  begin
    Arg := TPasArgument(Element.Args[i]);
    if IsDescrNodeEmpty(Engine.FindShortDescr(Arg)) then
      continue;
    if IsFirst then
    begin
      IsFirst := False;
      AppendText(CreateH2(Parent), SDocArguments);
      ArgTableEl := CreateTable(Parent);
    end;
    TREl := CreateTR(ArgTableEl);
    AppendText(CreateCode(CreatePara(CreateTD_vtop(TREl))), Arg.Name);
    AppendShortDescrCell(TREl, Arg);
  end;

  if (Element.ClassType = TPasFunctionType) and not SkipResult then
  begin
    ResultEl := TPasFunctionType(Element).ResultEl;
    DocNode := Engine.FindDocNode(ResultEl);
    HasFullDescr := Assigned(DocNode) and not IsDescrNodeEmpty(DocNode.Descr);
    if HasFullDescr or
      (Assigned(DocNode) and not IsDescrNodeEmpty(DocNode.ShortDescr)) then
    begin
      AppendText(CreateH2(Parent), SDocFunctionResult);
      if HasFullDescr then
        AppendDescr(ResultEl, Parent, DocNode.Descr, True)
      else
        AppendDescr(ResultEl, CreatePara(Parent), DocNode.ShortDescr, False);
    end;
  end;
end;

function THTMLWriter.AppendRecordType(CodeEl, TableEl: TDOMElement;
  Element: TPasRecordType; NestingLevel: Integer): TDOMElement;
var
  i, j: Integer;
  Variable: TPasVariable;
  TREl, TDEl: TDOMElement;
  CurVariant: TPasVariant;
begin
  if not (Element.Parent is TPasVariant) then
    if Element.IsPacked then
      If Element.IsBitPacked then
        AppendKw(CodeEl, 'bitpacked record')
      else
        AppendKW(CodeEl, 'packed record')
    else
      AppendKw(CodeEl, 'record');

  for i := 0 to Element.Members.Count - 1 do
  begin
    Variable := TPasVariable(Element.Members[i]);
    TREl := CreateTR(TableEl);
    CodeEl := CreateCode(CreatePara(CreateTD_vtop(TREl)));
    AppendShortDescrCell(TREl, Variable);
    AppendNbSp(CodeEl, NestingLevel * 2 + 2);
    AppendText(CodeEl, Variable.Name);
    AppendSym(CodeEl, ': ');
    CodeEl := AppendType(CodeEl, TableEl, Variable.VarType, False, NestingLevel + 1);
    AppendSym(CodeEl, ';');
  end;

  if Assigned(Element.VariantType) then
  begin
    TREl := CreateTR(TableEl);
    CodeEl := CreateCode(CreatePara(CreateTD_vtop(TREl)));
    AppendNbSp(CodeEl, NestingLevel * 2 + 2);
    AppendKw(CodeEl, 'case ');
    if TPasRecordType(Element).VariantName <> '' then
    begin
      AppendText(CodeEl, TPasRecordType(Element).VariantName);
      AppendSym(CodeEl, ': ');
    end;
    CodeEl := AppendType(CodeEl, TableEl, TPasRecordType(Element).VariantType, True);
    AppendKw(CodeEl, ' of');
    for i := 0 to TPasRecordType(Element).Variants.Count - 1 do
    begin
      CurVariant := TPasVariant(Element.Variants[i]);
      TREl := CreateTR(TableEl);
      CodeEl := CreateCode(CreatePara(CreateTD_vtop(TREl)));
      AppendNbSp(CodeEl, NestingLevel * 2 + 4);
      for j := 0 to CurVariant.Values.Count - 1 do
      begin
	if j > 0 then
	  AppendSym(CodeEl, ', ');
	AppendPasSHFragment(CodeEl, CurVariant.Values[j], 0);
      end;
      AppendSym(CodeEl, ': (');
      AppendType(CodeEl, TableEl, CurVariant.Members, True, NestingLevel + 3);
      CodeEl := CreateCode(CreatePara(CreateTD_vtop(CreateTR(TableEl))));
      AppendNbSp(CodeEl, NestingLevel * 2 + 6);
      AppendSym(CodeEl, ');');
    end;
  end;

  if not (Element.Parent is TPasVariant) then
  begin
    CodeEl := CreateCode(CreatePara(CreateTD(CreateTR(TableEl))));
    AppendText(CodeEl, ' '); // !!!: Dirty trick, necessary for current XML writer
    AppendNbSp(CodeEl, NestingLevel * 2);
    AppendKw(CodeEl, 'end');
  end;
  Result := CodeEl;
end;

procedure THTMLWriter.AppendTitle(const AText: DOMString);
begin
  AppendText(TitleElement, AText);
  AppendText(CreateH1(BodyElement), AText);
end;

procedure THTMLWriter.AppendTopicMenuBar(Topic : TTopicElement);

var
  TableEl, TREl, ParaEl, TitleEl: TDOMElement;

  procedure AddLink(El : TPasElement; const AName: String);
  begin
    if FUseMenuBrackets then
      AppendText(ParaEl, '[');
    AppendText(CreateLink(ParaEl, ResolveLinkWithinPackage(El,0)),AName);
    if FUseMenuBrackets then
      AppendText(ParaEl, ']');
  end;

begin
  TableEl := CreateEl(BodyElement, 'table');
  TableEl['cellpadding'] := '4';
  TableEl['cellspacing'] := '0';
  TableEl['border'] := '0';
  TableEl['width'] := '100%';
  TableEl['class'] := 'bar';
  TREl := CreateTR(TableEl);
  ParaEl := CreateEl(CreateTD(TREl), 'b');
  If Assigned(Topic.Previous) then
    AddLink(Topic.Previous,SDocPrevious);
  If Assigned(Topic.Parent) then
    AddLink(Topic.Parent,SDocUp);
  if Assigned(Topic.Next) then
    AddLink(Topic.Next,SDocNext);
  if Length(SearchPage) > 0 then
    begin
    if FUseMenuBrackets then
      AppendText(ParaEl, '[');
    AppendText(CreateLink(ParaEl, SearchPage), SDocSearch);
    if FUseMenuBrackets then
      AppendText(ParaEl, ']');
    end;
  ParaEl := CreateTD(TREl);
  ParaEl['align'] := 'right';
  TitleEl := CreateEl(ParaEl, 'span');
  TitleEl['class'] := 'bartitle';
  if Assigned(Module) then
    AppendText(TitleEl, Format(SDocUnitTitle, [Module.Name]));
  if Assigned(Package) then
  begin
    AppendText(TitleEl, ' (');
    AppendHyperlink(TitleEl, Package);
    AppendText(TitleEl, ')');
  end;
end;

procedure THTMLWriter.AppendMenuBar(ASubpageIndex: Integer);

var
  TableEl, TREl, ParaEl, TitleEl: TDOMElement;

  procedure AddLink(ALinkSubpageIndex: Integer; const AName: String);
  begin
    if FUseMenuBrackets then
      AppendText(ParaEl, '[');
    if ALinkSubpageIndex = ASubpageIndex then
      AppendText(ParaEl, AName)
    else
      AppendText(
        CreateLink(ParaEl, ResolveLinkWithinPackage(Module, ALinkSubpageIndex)),
        AName);
    if FUseMenuBrackets then
      AppendText(ParaEl, ']');
  end;

begin
  TableEl := CreateEl(BodyElement, 'table');
  TableEl['cellpadding'] := '4';
  TableEl['cellspacing'] := '0';
  TableEl['border'] := '0';
  TableEl['width'] := '100%';
  TableEl['class'] := 'bar';
  TREl := CreateTR(TableEl);
  ParaEl := CreateEl(CreateTD(TREl), 'b');

  if Assigned(Module) then
    begin
    AddLink(0, SDocOverview);
    if Module.InterfaceSection.ResStrings.Count > 0 then
      AddLink(ResstrSubindex, SDocResStrings);
    if Module.InterfaceSection.Consts.Count > 0 then
      AddLink(ConstsSubindex, SDocConstants);
    if Module.InterfaceSection.Types.Count > 0 then
      AddLink(TypesSubindex, SDocTypes);
    if Module.InterfaceSection.Classes.Count > 0 then
      AddLink(ClassesSubindex, SDocClasses);
    if Module.InterfaceSection.Functions.Count > 0 then
      AddLink(ProcsSubindex, SDocProceduresAndFunctions);
    if Module.InterfaceSection.Variables.Count > 0 then
      AddLink(VarsSubindex, SDocVariables);
    AddLink(IndexSubIndex,SDocIdentifierIndex);  
    end
  else
    begin
    // Manually add link for package page
    if FUseMenuBrackets then
      AppendText(ParaEl, '[');
    if (IndexSubIndex = ASubpageIndex) then
      AppendText(ParaEl, SDocIdentifierIndex)
    else
      AppendText(
        CreateLink(ParaEl, ResolveLinkWithinPackage(Package, IndexSubIndex)),
        SDocIdentifierIndex);
    if FUseMenuBrackets then
      AppendText(ParaEl, ']');
    end;

  if Length(SearchPage) > 0 then
  begin
    if FUseMenuBrackets then
      AppendText(ParaEl, '[');
    AppendText(CreateLink(ParaEl, SearchPage), SDocSearch);
    if FUseMenuBrackets then
      AppendText(ParaEl, ']');
  end;
  ParaEl := CreateTD(TREl);
  ParaEl['align'] := 'right';
  TitleEl := CreateEl(ParaEl, 'span');
  TitleEl['class'] := 'bartitle';
  if Assigned(Module) then
    AppendText(TitleEl, Format(SDocUnitTitle, [Module.Name]));
  if Assigned(Package) then
  begin
    AppendText(TitleEl, ' (');
    AppendHyperlink(TitleEl, Package);
    AppendText(TitleEl, ')');
  end;
end;

procedure THTMLWriter.AppendSourceRef(AElement: TPasElement);
begin
  AppendText(CreatePara(BodyElement), Format(SDocSourcePosition,
    [ExtractFileName(AElement.SourceFilename), AElement.SourceLinenumber]));
end;

Procedure THTMLWriter.AppendSeeAlsoSection(AElement : TPasElement;DocNode : TDocNode);

var
  Node: TDOMNode;
  TableEl, El, TREl, TDEl, ParaEl, NewEl, DescrEl: TDOMElement;
  l,s: String;
  f: Text;
  IsFirstSeeAlso : Boolean;

begin
  if Not (Assigned(DocNode) and Assigned(DocNode.SeeAlso)) then
    Exit;
  IsFirstSeeAlso := True;
  Node:=DocNode.SeeAlso.FirstChild;
  While Assigned(Node) do
    begin
    if (Node.NodeType=ELEMENT_NODE) and (Node.NodeName='link') then
      begin
       if IsFirstSeeAlso then
         begin
         IsFirstSeeAlso := False;
         AppendText(CreateH2(BodyElement), SDocSeeAlso);
         TableEl := CreateTable(BodyElement);
         end;
       El:=TDOMElement(Node);
       TREl:=CreateTR(TableEl);
       ParaEl:=CreatePara(CreateTD_vtop(TREl));
       l:=El['id'];
       s:= ResolveLinkID(l);
       if Length(s)=0 then
         begin
         if assigned(module) then
           s:=module.name
         else
           s:='?';
         if l='' then l:='<empty>';
         DoLog(SErrUnknownLinkID, [s,l]);
         NewEl := CreateEl(ParaEl,'b')
         end
       else
         NewEl := CreateLink(ParaEl,s);
        if Not IsDescrNodeEmpty(El) then
          begin
          PushOutputNode(NewEl);
          Try
            ConvertBaseShortList(AElement, El, True)
          Finally
            PopOutputNode;
          end;
          end
        else
          AppendText(NewEl,El['id']);
       l:=El['id'];
       DescrEl := Engine.FindShortDescr(AElement.GetModule,L);
       if Assigned(DescrEl) then
         begin
         AppendNbSp(CreatePara(CreateTD(TREl)), 2);
         ParaEl := CreatePara(CreateTD(TREl));
         ParaEl['class'] := 'cmt';
         PushOutputNode(ParaEl);
         try
           ConvertShort(AElement, DescrEl);
         finally
           PopOutputNode;
         end;
         end;
       end; // Link node
     Node := Node.NextSibling;
     end; // While
end;

Procedure THTMLWriter.AppendExampleSection(AElement : TPasElement;DocNode : TDocNode);

var
  Node: TDOMNode;
//  TableEl, El, TREl, TDEl, ParaEl, NewEl, DescrEl: TDOMElement;
  fn,s: String;
  f: Text;

begin
  if not (Assigned(DocNode) and Assigned(DocNode.FirstExample)) then
    Exit;
  Node := DocNode.FirstExample;
  while Assigned(Node) do
    begin
    if (Node.NodeType = ELEMENT_NODE) and (Node.NodeName = 'example') then
      begin
      fn:=Engine.GetExampleFilename(TDOMElement(Node));
      If (fn<>'') then
        begin
        AppendText(CreateH2(BodyElement), SDocExample);
        try
          Assign(f, FN);
          Reset(f);
          try
            PushOutputNode(BodyElement);
            DescrBeginCode(False, TDOMElement(Node)['highlighter']);
            while not EOF(f) do
              begin
              ReadLn(f, s);
              DescrWriteCodeLine(s);
              end;
            DescrEndCode;
            PopOutputNode;
          finally
            Close(f);
          end;
        except
          on e: Exception do
            begin
            e.Message := '[example] ' + e.Message;
            raise;
            end;
        end;
        end;
      end;
    Node := Node.NextSibling;
    end;
end;

procedure THTMLWriter.AppendFooter;

Var
  S : String;
  F : TDomElement;
begin
  if FooterFile<>'' then
    ReadXMLFragment(BodyElement, FooterFile)
  else if IncludeDateInFooter then
    begin
    CreateEl(BodyElement, 'hr');
    F:=CreateEl(BodyElement,'span');
    F['class']:='footer';
    If (FDateFormat='') then
      S:=DateToStr(Date)
    else
      S:=FormatDateTime(FDateFormat,Date);  
    AppendText(F,Format(SDocDateGenerated,[S]));
    end;
end;

procedure THTMLWriter.FinishElementPage(AElement: TPasElement);

var
  DocNode: TDocNode;

begin
  DocNode := Engine.FindDocNode(AElement);
  If Assigned(DocNode) then
    begin
    // Description
    if Assigned(DocNode.Descr) then
      AppendDescrSection(AElement, BodyElement, DocNode.Descr, SDocDescription);

    // Append "Errors" section
    if Assigned(DocNode.ErrorsDoc) then
      AppendDescrSection(AElement, BodyElement, DocNode.ErrorsDoc, SDocErrors);

    // Append Version info
    if Assigned(DocNode.Version) then
      AppendDescrSection(AElement, BodyElement, DocNode.Version, SDocVersion);

    // Append "See also" section
    AppendSeeAlsoSection(AElement,DocNode);

    // Append examples, if present
    AppendExampleSection(AElement,DocNode);
    // Append notes, if present
    ConvertNotes(AElement,DocNode.Notes);
    end;
end;

Procedure THTMLWriter.CreateTopicPageBody(AElement : TTopicElement);

var
  DocNode: TDocNode;
  TableEl, TREl: TDOMElement;
  I : Integer;
  S : String;

begin
  AppendTopicMenuBar(AElement);
  DocNode:=AElement.TopicNode;
  if Assigned(DocNode) then  // should always be true, but we're being careful.
    begin
    AppendShortDescr(AElement,TitleElement, DocNode);
    AppendShortDescr(AElement,CreateH2(BodyElement), DocNode);
    if Assigned(DocNode.Descr) then
       AppendDescrSection(AElement, BodyElement, DocNode.Descr, '');
    AppendSeeAlsoSection(AElement,DocNode);
    CreateTopicLinks(DocNode,AElement);
    AppendExampleSection(AElement,DocNode);
    ConvertNotes(AElement,DocNode.Notes);
    end;
end;

procedure THTMLWriter.CreatePageBody(AElement: TPasElement;
  ASubpageIndex: Integer);
var
  i: Integer;
  Element: TPasElement;
begin
  CurDirectory := Allocator.GetFilename(AElement, ASubpageIndex);
  i := Length(CurDirectory);
  while (i > 0) and not (CurDirectory[i] in AllowDirectorySeparators) do
    Dec(i);
  CurDirectory := Copy(CurDirectory, 1, i);
  BaseDirectory := Allocator.GetRelativePathToTop(AElement);

  if AElement.ClassType = TPasPackage then
    begin
    Module:=Nil;
    If (ASubPageIndex=0) then
      CreatePackagePageBody
    else if ASubPageIndex=IndexSubIndex then
      CreatePackageIndex  
    end
  else
    begin
    Element := AElement;
    while (Element<>Nil) and (Element.ClassType<>TPasModule) do
      Element := Element.Parent;
    Module := TPasModule(Element);

    if AElement.ClassType = TPasModule then
      CreateModulePageBody(TPasModule(AElement), ASubpageIndex)
    else if AElement.Parent.InheritsFrom(TPasClassType) then
      CreateClassMemberPageBody(AElement)
    else if AElement.ClassType = TPasConst then
      CreateConstPageBody(TPasConst(AElement))
    else if AElement.InheritsFrom(TPasClassType) then
      CreateClassPageBody(TPasClassType(AElement), ASubpageIndex)
    else if AElement.InheritsFrom(TPasType) then
      CreateTypePageBody(TPasType(AElement))
    else if AElement.ClassType = TPasVariable then
      CreateVarPageBody(TPasVariable(AElement))
    else if AElement.InheritsFrom(TPasProcedureBase) then
      CreateProcPageBody(TPasProcedure(AElement))
    else if AElement.ClassType = TTopicELement then
      CreateTopicPageBody(TTopicElement(AElement))
  end;
end;

	procedure THTMLWriter.CreateIndexPage(L : TStringList);

Var
  Lists  : Array['A'..'Z'] of TStringList;
  LOther : TStringList;
  
  CL : TStringList;
  TableEl, TREl, EL: TDOMElement;
  E : TPasElement;
  I,Rows,J,Index : Integer;
  S : String;
  C : Char;

begin
  For C:='A' to 'Z' do
    Lists[C]:=Nil;
  L.Sort;
  Cl:=Nil;
  // Divide over alphabet
  For I:=0 to L.Count-1 do
    begin
    S:=L[i];
    E:=TPasElement(L.Objects[i]);
    If not (E is TPasUnresolvedTypeRef) then
      begin
      If (S<>'') then 
        begin
        C:=Upcase(S[1]);
        If C='_' then
          C:='A';
        If (C in ['A'..'Z']) and (Lists[C]=Nil) then
          begin
          CL:=TStringList.Create;
          Lists[C]:=CL;
          end;
        end;
      if assigned(cl) then  
        CL.AddObject(S,E);
      end;  
    end;  
  Try  
  // Create a quick jump table to all available letters.    
  TableEl := CreateTable(BodyElement);
  TableEl['border']:='1';
  TableEl['width']:='50%';
  TREl := CreateTR(TableEl);
  for C:='A' to 'Z' do
    If (Lists[C]<>Nil) then
      begin
      El:=CreateTD_vtop(TREl);
      AppendText(CreateLink(El,'#SECTION'+C),C);
      If C<>'Z' then
       AppendNBsp(El,1);
      end;
  // Now emit all identifiers.    
  TableEl:=Nil;
  For C:='A' to 'Z' do
    begin
    CL:=Lists[C];
    If CL<>Nil then
      begin
      El:=CreateH2(BodyElement);
      AppendText(El,C);
      CreateAnchor(El,'SECTION'+C);
      TableEl := CreateTable(BodyElement);
      TableEl['Width']:='80%';
      // Determine number of rows needed
      Rows:=(CL.Count div IndexColCount);
      If ((CL.Count Mod IndexColCount)<>0) then
        Inc(Rows);
      // Fill rows  
      For I:=0 to Rows-1 do
        begin
        TREl := CreateTR(TableEl);
        For J:=0 to IndexColCount-1 do 
          begin
          El:=CreateTD_vtop(TREl);
          Index:=(J*Rows)+I;
          If (Index<CL.Count) then
            begin
            S:=CL[Index];
            E:=TPasElement(CL.Objects[Index]);
            AppendHyperlink(El,E);
            end;
          end;  
        end;  
      end; // have List
    end;  // For C:=
  Finally
    for C:='A' to 'Z' do
      FreeAndNil(Lists[C]);
  end;  
end;

procedure THTMLWriter.AddModuleIdentifiers(AModule : TPasModule; L : TStrings);

  Procedure AddElementsFromList(L : TStrings; List : TFPList);
  
  Var
    I : Integer;
    El : TPasElement;
    
  begin
    For I:=0 to List.Count-1 do
      begin
      El:=TPasElement(List[I]);
      L.AddObject(El.Name,El);
      If el is TPasEnumType then
        AddElementsFromList(L,TPasEnumType(el).Values);
      end;
  end;
  
begin
  AddElementsFromList(L,AModule.InterfaceSection.Consts);
  AddElementsFromList(L,AModule.InterfaceSection.Types);
  AddElementsFromList(L,AModule.InterfaceSection.Functions);
  AddElementsFromList(L,AModule.InterfaceSection.Classes);
  AddElementsFromList(L,AModule.InterfaceSection.Variables);
  AddElementsFromList(L,AModule.InterfaceSection.ResStrings);
end;


procedure THTMLWriter.CreatePackageIndex;

Var
  L : TStringList;
  I : Integer;
  M : TPasModule;
  E : TPasElement;
  S : String;    
  
begin
  L:=TStringList.Create;
  try
    L.Capacity:=PageInfos.Count; // Too much, but that doesn't hurt.
    For I:=0 to Package.Modules.Count-1 do
      begin
      M:=TPasModule(Package.Modules[i]);
      L.AddObject(M.Name,M);
      AddModuleIdentifiers(M,L);
      end;
    AppendMenuBar(IndexSubIndex);
    S:=Package.Name;
    If Length(S)>0 then
      Delete(S,1,1);
    AppendTitle(Format(SDocPackageIndex, [S]));
    CreateIndexPage(L);
  Finally
    L.Free;
  end;
end;

procedure THTMLWriter.CreatePackagePageBody;
var
  DocNode: TDocNode;
  TableEl, TREl: TDOMElement;
  i: Integer;
  ThisModule: TPasModule;
  L : TStringList;

begin
  AppendMenuBar(0);
  AppendTitle(Format(SDocPackageTitle, [Copy(Package.Name, 2, 256)]));
  AppendShortDescr(CreatePara(BodyElement), Package);

  AppendText(CreateH2(BodyElement), SDocUnits);
  TableEl := CreateTable(BodyElement);
  L:=TStringList.Create;
  Try
    L.Sorted:=True;
    // Sort modules.
    For I:=0 to Package.Modules.Count-1 do
      L.AddObject(TPasModule(Package.Modules[i]).Name,TPasModule(Package.Modules[i]));
    // Now create table.
    for i:=0 to L.Count - 1 do
      begin
      ThisModule := TPasModule(L.Objects[i]);
      TREl := CreateTR(TableEl);
      AppendHyperlink(CreateCode(CreatePara(CreateTD_vtop(TREl))), ThisModule);
      AppendShortDescrCell(TREl, ThisModule);
      end;
  Finally
    L.Free;
  end;

  DocNode := Engine.FindDocNode(Package);
  if Assigned(DocNode) then
    begin
    if Assigned(DocNode.Descr) then
       AppendDescrSection(nil, BodyElement, DocNode.Descr, SDocDescription);
    CreateTopicLinks(DocNode,Package);
    end;
end;

Procedure THTMLWriter.CreateTopicLinks(Node : TDocNode; PasElement : TPasElement);

var
  DocNode: TDocNode;
  TableEl, TREl: TDOMElement;
  First : Boolean;
  ThisTopic: TPasElement;

begin
  DocNode:=Node.FirstChild;
  First:=True;
  While Assigned(DocNode) do
    begin
    If DocNode.TopicNode then
      begin
      if first then
        begin
        First:=False;
        AppendText(CreateH2(BodyElement), SDocRelatedTopics);
        TableEl := CreateTable(BodyElement);
        end;
      TREl := CreateTR(TableEl);
      ThisTopic:=FindTopicElement(DocNode);
      if Assigned(ThisTopic) then
        AppendHyperlink(CreateCode(CreatePara(CreateTD_vtop(TREl))), ThisTopic);
      AppendShortDescrCell(TREl, ThisTopic);
      end;
    DocNode:=DocNode.NextSibling;
    end;
end;

procedure THTMLWriter.CreateModuleIndexPage(AModule: TPasModule);

Var
  L : TStringList;

begin
  L:=TStringList.Create;
  try
    AddModuleIdentifiers(AModule,L);
    AppendMenuBar(IndexSubIndex);
    AppendTitle(Format(SDocModuleIndex, [AModule.Name]));
    CreateIndexPage(L);
  Finally
    L.Free;
  end;  
end;

procedure THTMLWriter.CreateModulePageBody(AModule: TPasModule;
  ASubpageIndex: Integer);

  procedure CreateMainPage;
  var
    TableEl, TREl, TDEl, CodeEl: TDOMElement;
    i: Integer;
    UnitRef: TPasType;
    DocNode: TDocNode;
  begin
    AppendMenuBar(0);
    AppendTitle(Format(SDocUnitTitle, [AModule.Name]));
    AppendShortDescr(CreatePara(BodyElement), AModule);

    if AModule.InterfaceSection.UsesList.Count > 0 then
    begin
      TableEl := CreateTable(BodyElement);
      AppendKw(CreateCode(CreatePara(CreateTD(CreateTR(TableEl)))), 'uses');
      for i := 0 to AModule.InterfaceSection.UsesList.Count - 1 do
      begin
        UnitRef := TPasType(AModule.InterfaceSection.UsesList[i]);
        DocNode := Engine.FindDocNode(UnitRef);
        if Assigned(DocNode) and DocNode.IsSkipped then
          continue;
        TREl := CreateTR(TableEl);
        TDEl := CreateTD_vtop(TREl);
        CodeEl := CreateCode(CreatePara(TDEl));
        AppendNbSp(CodeEl, 2);
        AppendHyperlink(CodeEl, UnitRef);
        if i < AModule.InterfaceSection.UsesList.Count - 1 then
          AppendSym(CodeEl, ',')
        else
          AppendSym(CodeEl, ';');
        AppendText(CodeEl, '  ');               // Space for descriptions
        AppendShortDescrCell(TREl, UnitRef);
      end;
    end;

    DocNode := Engine.FindDocNode(AModule);
    if Assigned(DocNode) then
      begin
      if Assigned(DocNode.Descr) then
        AppendDescrSection(AModule, BodyElement, DocNode.Descr, SDocOverview);
      ConvertNotes(AModule,DocNode.Notes);
      CreateTopicLinks(DocNode,AModule);
      end;
  end;

  procedure CreateSimpleSubpage(const ATitle: DOMString; AList: TFPList);
  var
    TableEl, TREl, TDEl, CodeEl: TDOMElement;
    i, j: Integer;
    Decl: TPasElement;
    SortedList: TFPList;
    DocNode: TDocNode;
    S : String;

  begin
    AppendMenuBar(ASubpageIndex);
    S:=ATitle;
    AppendTitle(Format(SDocUnitTitle + ': %s', [AModule.Name, S]));
    SortedList := TFPList.Create;
    try
      for i := 0 to AList.Count - 1 do
      begin
        Decl := TPasElement(AList[i]);
        DocNode := Engine.FindDocNode(Decl);
        if (not Assigned(DocNode)) or (not DocNode.IsSkipped) then
        begin
          j := 0;
          while (j < SortedList.Count) and (CompareText(
            TPasElement(SortedList[j]).PathName, Decl.PathName) < 0) do
            Inc(j);
          SortedList.Insert(j, Decl);
        end;
      end;

      TableEl := CreateTable(BodyElement);
      for i := 0 to SortedList.Count - 1 do
      begin
        Decl := TPasElement(SortedList[i]);
        TREl := CreateTR(TableEl);
        CodeEl := CreateCode(CreatePara(CreateTD_vtop(TREl)));
        AppendHyperlink(CodeEl, Decl);
        AppendShortDescrCell(TREl, Decl);
      end;
    finally
      SortedList.Free;
    end;
  end;

  procedure CreateResStringsPage;
  var
    ParaEl: TDOMElement;
    i, j: Integer;
    Decl: TPasResString;
    DocNode: TDocNode;
  begin
    AppendMenuBar(ResstrSubindex);
    AppendTitle(Format(SDocUnitTitle + ': %s', [AModule.Name, SDocResStrings]));
    for i := 0 to AModule.InterfaceSection.ResStrings.Count - 1 do
    begin
      Decl := TPasResString(AModule.InterfaceSection.ResStrings[i]);
      CreateEl(BodyElement, 'a')['name'] := LowerCase(Decl.Name);
      ParaEl := CreatePara(BodyElement);
      AppendText(CreateCode(ParaEl), Decl.Name);
      CreateEl(ParaEl, 'br');
      AppendText(ParaEl, Decl.Expr.getDeclaration(true));
    end;
  end;
  

begin
  case ASubpageIndex of
    0:
      CreateMainPage;
    ResstrSubindex:
      CreateResStringsPage;
    ConstsSubindex:
      CreateSimpleSubpage(SDocConstants, AModule.InterfaceSection.Consts);
    TypesSubindex:
      CreateSimpleSubpage(SDocTypes, AModule.InterfaceSection.Types);
    ClassesSubindex:
      CreateSimpleSubpage(SDocClasses, AModule.InterfaceSection.Classes);
    ProcsSubindex:
      CreateSimpleSubpage(SDocProceduresAndFunctions, AModule.InterfaceSection.Functions);
    VarsSubindex:
      CreateSimpleSubpage(SDocVariables, AModule.InterfaceSection.Variables);
    IndexSubIndex: 
      CreateModuleIndexPage(AModule);
  end;
end;

procedure THTMLWriter.CreateConstPageBody(AConst: TPasConst);
var
  TableEl, CodeEl: TDOMElement;
begin
  AppendMenuBar(-1);
  AppendTitle(AConst.Name);
  AppendShortDescr(CreatePara(BodyElement), AConst);
  AppendText(CreateH2(BodyElement), SDocDeclaration);
  AppendSourceRef(AConst);

  TableEl := CreateTable(BodyElement);
  CodeEl := CreateCode(CreatePara(CreateTD(CreateTR(TableEl))));

  AppendKw(CodeEl, 'const');
  AppendText(CodeEl, ' ' + AConst.Name);
  if Assigned(AConst.VarType) then
  begin
    AppendSym(CodeEl, ': ');
    AppendType(CodeEl, TableEl, AConst.VarType, False);
  end;
  AppendPasSHFragment(CodeEl, ' = ' + AConst.Expr.GetDeclaration(True) + ';', 0);

  FinishElementPage(AConst);
end;

procedure THTMLWriter.CreateTypePageBody(AType: TPasType);
var
  TableEl, TREl, TDEl, CodeEl: TDOMElement;
  DocNode: TDocNode;
  i: Integer;
  s: String;
  EnumType: TPasEnumType;
  EnumValue: TPasEnumValue;
  Variable: TPasVariable;
begin
  AppendMenuBar(-1);
  AppendTitle(AType.Name);
  AppendShortDescr(CreatePara(BodyElement), AType);
  AppendText(CreateH2(BodyElement), SDocDeclaration);
  AppendSourceRef(AType);

  TableEl := CreateTable(BodyElement);
  TREl := CreateTR(TableEl);
  TDEl := CreateTD(TREl);
  CodeEl := CreateCode(CreatePara(TDEl));

  DocNode := Engine.FindDocNode(AType);
  AppendKw(CodeEl, 'type ');
  AppendText(CodeEl, AType.Name);
  AppendSym(CodeEl, ' = ');

  If Assigned(DocNode) and
     Assigned(DocNode.Node) and
     (Docnode.Node['opaque']='1') then
    AppendText(CodeEl,SDocOpaque)
  else
    begin
    // Alias
    if AType.ClassType = TPasAliasType then
      begin
      if Assigned(TPasAliasType(AType).DestType) then
        AppendHyperlink(CodeEl, TPasAliasType(AType).DestType)
      else
        AppendText(CreateWarning(CodeEl), '<Destination type is NIL>');
      AppendSym(CodeEl, ';');
    end else
    // Class of
    if AType.ClassType = TPasClassOfType then
    begin
      AppendKw(CodeEl, 'class of ');
      AppendHyperlink(CodeEl, TPasClassOfType(AType).DestType);
      AppendSym(CodeEl, ';');
    end else
    // Enumeration
    if AType.ClassType = TPasEnumType then
    begin
      AppendSym(CodeEl, '(');
      for i := 0 to TPasEnumType(AType).Values.Count - 1 do
      begin
        EnumValue := TPasEnumValue(TPasEnumType(AType).Values[i]);
        TREl := CreateTR(TableEl);
        CodeEl := CreateCode(CreatePara(CreateTD_vtop(TREl)));
        AppendShortDescrCell(TREl, EnumValue);
        AppendNbSp(CodeEl, 2);
        s := EnumValue.Name;
        if EnumValue.AssignedValue<>'' then
          s := s + ' = ' + EnumValue.AssignedValue;
        if i < TPasEnumType(AType).Values.Count - 1 then
          s := s + ',';
        AppendPasSHFragment(CodeEl, s, 0);
      end;
      AppendSym(CreateCode(CreatePara(CreateTD(CreateTR(TableEl)))), ');');
    end else
    // Pointer type
    if AType.ClassType = TPasPointerType then
    begin
      AppendSym(CodeEl, '^');
      if Assigned(TPasPointerType(AType).DestType) then
        AppendHyperlink(CodeEl, TPasPointerType(AType).DestType)
      else
        AppendText(CreateWarning(CodeEl), '<Destination type is NIL>');
      AppendSym(CodeEl, ';');
    end else
    if AType.InheritsFrom(TPasProcedureType) then
    begin
      AppendSym(AppendType(CodeEl, TableEl, TPasType(AType), True), ';');
      AppendProcArgsSection(BodyElement, TPasProcedureType(AType));
    end else
    // Record
    if AType.ClassType = TPasRecordType then
    begin
      CodeEl := AppendRecordType(CodeEl, TableEl, TPasRecordType(AType), 0);
      AppendSym(CodeEl, ';');
    end else
    // Set
    if AType.ClassType = TPasSetType then
    begin
      AppendKw(CodeEl, 'set of ');
      if TPasSetType(AType).EnumType.ClassType = TPasEnumType then
      begin
        AppendSym(CodeEl, '(');
        EnumType := TPasEnumType(TPasSetType(AType).EnumType);
        for i := 0 to EnumType.Values.Count - 1 do
        begin
          EnumValue := TPasEnumValue(EnumType.Values[i]);
          TREl := CreateTR(TableEl);
          CodeEl := CreateCode(CreatePara(CreateTD_vtop(TREl)));
          AppendShortDescrCell(TREl, EnumValue);
          AppendNbSp(CodeEl, 2);
          s := EnumValue.Name;
          if (EnumValue.AssignedValue<>'') then
            s := s + ' = ' + EnumValue.AssignedValue;
          if i < EnumType.Values.Count - 1 then
            s := s + ',';
          AppendPasSHFragment(CodeEl, s, 0);
        end;
        AppendSym(CreateCode(CreatePara(CreateTD(CreateTR(TableEl)))), ');');
      end else
      begin
        AppendHyperlink(CodeEl, TPasSetType(AType).EnumType);
        AppendSym(CodeEl, ';');
      end;
    end else
    // Type alias
    if AType.ClassType = TPasTypeAliasType then
    begin
      AppendKw(CodeEl, 'type ');
      AppendHyperlink(CodeEl, TPasTypeAliasType(AType).DestType);
      AppendSym(CodeEl, ';');
    end else
    // Probably one of the simple types, which allowed in other places as wel...
      AppendSym(AppendType(CodeEl, TableEl, TPasType(AType), True), ';');
    end;
  FinishElementPage(AType);
end;


function PropertyFilter(AMember: TPasElement): Boolean;
begin
  Result := (AMember.ClassType = TPasProperty) and
    (Copy(AMember.Name, 1, 2) <> 'On');
end;

function MethodFilter(AMember: TPasElement): Boolean;
begin
  Result := AMember.InheritsFrom(TPasProcedureBase);
end;

function EventFilter(AMember: TPasElement): Boolean;
begin
  Result := (AMember.ClassType = TPasProperty) and
    (Copy(AMember.Name, 1, 2) = 'On');
end;

procedure THTMLWriter.CreateClassPageBody(AClass: TPasClassType;
  ASubpageIndex: Integer);
type
  TMemberFilter = function(AMember: TPasElement): Boolean;
var
  ParaEl: TDOMElement;

  procedure AppendMemberListLink(AListSubpageIndex: Integer;
    const AText: DOMString);
  var
    LinkEl: TDOMElement;
  begin
    if FUseMenuBrackets then
      AppendText(ParaEl, '[');
    LinkEl := CreateEl(ParaEl, 'a');
    LinkEl['href'] :=
      FixHtmlPath(ResolveLinkWithinPackage(AClass, AListSubpageIndex));
    LinkEl['onClick'] := 'window.open(''' + LinkEl['href'] + ''', ''list'', ' +
     '''dependent=yes,resizable=yes,scrollbars=yes,height=400,width=300''); return false;';
    AppendText(LinkEl, AText);
    AppendText(ParaEl, ' (');
    LinkEl := CreateEl(ParaEl, 'a');
    LinkEl['href'] :=
      FixHtmlPath(ResolveLinkWithinPackage(AClass, AListSubpageIndex + 1));
    LinkEl['onClick'] := 'window.open(''' + LinkEl['href'] + ''', ''list'', ' +
     '''dependent=yes,resizable=yes,scrollbars=yes,height=400,width=300''); return false;';
    AppendText(LinkEl, SDocByName);
    AppendText(ParaEl, ')');
    if FUseMenuBrackets then
      AppendText(ParaEl, '] ')
    else
      AppendText(ParaEl, ' ');
  end;

  procedure AppendGenericTypes(CodeEl : TDomElement; AList : TFPList; isSpecialize : Boolean);

  Var
    I : integer;
  begin
    for I:=0 to AList.Count-1 do
      begin
      if I=0 then
        AppendSym(CodeEl, '<')
      else
        AppendSym(CodeEl, ',');
      AppendText(CodeEl,TPasGenericTemplateType(AList[i]).Name);
      end;
    AppendSym(CodeEl, '>');
  end;

  procedure CreateMainPage;
  var
    TableEl, TREl, TDEl, CodeEl: TDOMElement;
    DocNode: TDocNode;
    Member: TPasElement;
    CurVisibility: TPasMemberVisibility;
    i: Integer;
    s: String;
    ThisInterface,
    ThisClass: TPasClassType;
    HaveSeenTObject: Boolean;
  begin
    AppendMenuBar(-1);
    AppendTitle(AClass.Name);

    ParaEl := CreatePara(BodyElement);
    AppendMemberListLink(PropertiesByInheritanceSubindex, SDocProperties);
    AppendMemberListLink(MethodsByInheritanceSubindex, SDocMethods);
    AppendMemberListLink(EventsByInheritanceSubindex, SDocEvents);

    AppendShortDescr(CreatePara(BodyElement), AClass);
    AppendText(CreateH2(BodyElement), SDocDeclaration);
    AppendSourceRef(AClass);

    TableEl := CreateTable(BodyElement);

    TREl := CreateTR(TableEl);
    TDEl := CreateTD(TREl);
    CodeEl := CreateCode(CreatePara(TDEl));
    AppendKw(CodeEl, 'type');
    if AClass.ObjKind=okGeneric then
      AppendKw(CodeEl, ' generic ');
    AppendText(CodeEl, ' ' + AClass.Name + ' ');
    if AClass.ObjKind=okGeneric then
      AppendGenericTypes(CodeEl,AClass.GenericTemplateTypes,false);
    AppendSym(CodeEl, '=');
    AppendText(CodeEl, ' ');
    if AClass.ObjKind<>okSpecialize then
      AppendKw(CodeEl, ObjKindNames[AClass.ObjKind])
    else
      AppendKw(CodeEl, ' specialize ');

    if Assigned(AClass.AncestorType) then
    begin
      if AClass.ObjKind=okSpecialize then
        begin
        AppendHyperlink(CodeEl, AClass.AncestorType);
        AppendGenericTypes(CodeEl,AClass.GenericTemplateTypes,true)
        end
      else
        begin
        AppendSym(CodeEl, '(');
        AppendHyperlink(CodeEl, AClass.AncestorType);
        if AClass.Interfaces.count>0 Then
          begin
            for i:=0 to AClass.interfaces.count-1 do
             begin
               AppendSym(CodeEl, ', ');
               AppendHyperlink(CodeEl,TPasClassType(AClass.Interfaces[i]));
             end;
          end;
        AppendSym(CodeEl, ')');
        end;
    end;

    if AClass.Members.Count > 0 then
    begin
      CurVisibility := visDefault;
      for i := 0 to AClass.Members.Count - 1 do
      begin
        Member := TPasElement(AClass.Members[i]);
        if CurVisibility <> Member.Visibility then
        begin
          CurVisibility := Member.Visibility;
          if ((CurVisibility = visPrivate) and Engine.HidePrivate) or
            ((CurVisibility = visProtected) and Engine.HideProtected) then
            continue;
          case CurVisibility of
            visPrivate: s := 'private';
            visProtected: s := 'protected';
            visPublic: s := 'public';
            visPublished: s := 'published';
            visAutomated: s := 'automated';
          end;
          AppendKw(CreateCode(CreatePara(CreateTD(CreateTR(TableEl)))), s);
        end else
          if ((CurVisibility = visPrivate) and Engine.HidePrivate) or
            ((CurVisibility = visProtected) and Engine.HideProtected) then
            continue;

        TREl := CreateTR(TableEl);
        CodeEl := CreateCode(CreatePara(CreateTD_vtop(TREl)));
        AppendNbSp(CodeEl, 2);
        AppendShortDescrCell(TREl, Member);

        if Member.InheritsFrom(TPasProcedureBase) then
        begin
          AppendKw(CodeEl, TPasProcedureBase(Member).TypeName + ' ');
          AppendHyperlink(CodeEl, Member);
          if (Member.ClassType = TPasOverloadedProc) or
            (TPasProcedure(Member).ProcType.Args.Count > 0) then
            AppendSym(CodeEl, '();')
          else
            AppendSym(CodeEl, ';');
          if Member.ClassType <> TPasOverloadedProc then
            AppendProcExt(CodeEl, TPasProcedure(Member));
        end else
        if Member.ClassType = TPasVariable then
        begin
          AppendHyperlink(CodeEl, Member);
          AppendSym(CodeEl, ': ');
          AppendHyperlink(CodeEl, TPasVariable(Member).VarType);
          AppendSym(CodeEl, ';');
        end else
        if Member.ClassType = TPasProperty then
        begin
          AppendKw(CodeEl, 'property ');
          AppendHyperlink(CodeEl, Member);
          if Assigned(TPasProperty(Member).VarType) then
          begin
            AppendSym(CodeEl, ': ');
            AppendHyperlink(CodeEl, TPasProperty(Member).VarType);
          end;
          AppendSym(CodeEl, ';');
          if TPasProperty(Member).IsDefault then
          begin
            AppendKw(CodeEl, ' default');
            AppendSym(CodeEl, ';');
          end;
          if (TPasProperty(Member).ImplementsName<>'') then
          begin
            AppendKw(CodeEl, ' implements');
            AppendText(CodeEl, ' '+TPasProperty(Member).ImplementsName);
            AppendSym(CodeEl, ';');
          end;
          SetLength(s, 0);
          if Length(TPasProperty(Member).ReadAccessorName) > 0 then
            s := s + 'r';
          if Length(TPasProperty(Member).WriteAccessorName) > 0 then
            s := s + 'w';
          if Length(TPasProperty(Member).StoredAccessorName) > 0 then
            s := s + 's';
          if Length(s) > 0 then
            AppendText(CodeEl, '  [' + s + ']');
        end else
          AppendText(CreateWarning(CodeEl), '<' + Member.ClassName + '>');
      end;

      CodeEl := CreateCode(CreatePara(CreateTD(CreateTR(TableEl))));
    end;

    AppendText(CodeEl, ' '); // !!!: Dirty trick, necessary for current XML writer
    if not AClass.IsShortDefinition then
      AppendKw(CodeEl, 'end');
    AppendSym(CodeEl, ';');


    AppendText(CreateH2(BodyElement), SDocInheritance);
    TableEl := CreateTable(BodyElement);
    HaveSeenTObject := AClass.ObjKind <> okClass;
    ThisClass := AClass;
    while True do
    begin
      TREl := CreateTR(TableEl);
      TDEl := CreateTD_vtop(TREl);
      TDEl['align'] := 'center';
      CodeEl := CreateCode(CreatePara(TDEl));
      AppendHyperlink(CodeEl, ThisClass);
      if ThisClass.Interfaces.count>0 then
        begin
          for i:=0 to ThisClass.interfaces.count-1 do
            begin
              ThisInterface:=TPasClassType(ThisClass.Interfaces[i]);
              AppendText(CodeEl,',');
              AppendHyperlink(CodeEl, ThisInterface);
            end;
        end;
      AppendShortDescrCell(TREl, ThisClass);
      if HaveSeenTObject or (CompareText(ThisClass.Name, 'TObject') = 0) then
        HaveSeenTObject := True
      else
      begin
        TDEl := CreateTD(CreateTR(TableEl));
        TDEl['align'] := 'center';
        AppendText(TDEl, '|');
      end;

      if Assigned(ThisClass.AncestorType) then
      begin
        if ThisClass.AncestorType.InheritsFrom(TPasClassType) then
          ThisClass := TPasClassType(ThisClass.AncestorType)
        else
        begin
          TDEl := CreateTD(CreateTR(TableEl));
          TDEl['align'] := 'center';
          AppendText(CreateCode(CreatePara(TDEl)), ThisClass.AncestorType.Name);
          if CompareText(ThisClass.AncestorType.Name, 'TObject') = 0 then
            HaveSeenTObject := True
          else
          begin
            TDEl := CreateTD(CreateTR(TableEl));
            TDEl['align'] := 'center';
            AppendText(TDEl, '?');
          end;
          break;
        end
      end else
        break;
    end;

    if not HaveSeenTObject then
    begin
      TDEl := CreateTD(CreateTR(TableEl));
      TDEl['align'] := 'center';
      AppendText(CreateCode(CreatePara(TDEl)), 'TObject');
    end;

    FinishElementPage(AClass);
  end;

  procedure CreateInheritanceSubpage(AFilter: TMemberFilter);
  var
    ThisClass: TPasClassType;
    i: Integer;
    Member: TPasElement;
    TableEl, TREl, TDEl, ParaEl, LinkEl: TDOMElement;
  begin
    TableEl := CreateTable(BodyElement);
    ThisClass := AClass;
    while True do
    begin
      TREl := CreateTR(TableEl);
      TDEl := CreateTD(TREl);
      TDEl['colspan'] := '3';
      CreateTD(TREl);
      LinkEl := AppendHyperlink(CreateEl(CreateCode(CreatePara(TDEl)), 'b'), ThisClass);
      if Assigned(LinkEl) then
        LinkEl['onClick'] := 'opener.location.href = ''' + LinkEl['href'] +
          '''; return false;';
      for i := 0 to ThisClass.Members.Count - 1 do
      begin
        Member := TPasElement(ThisClass.Members[i]);
        if ((Member.Visibility = visPrivate) and Engine.HidePrivate) or
          ((Member.Visibility = visProtected) and Engine.HideProtected) or
          not AFilter(Member) then
          continue;
        TREl := CreateTR(TableEl);
        ParaEl := CreatePara(CreateTD(TREl));
        case Member.Visibility of
          visPrivate:
            AppendText(ParaEl, 'pv');
          visProtected:
            AppendText(ParaEl, 'pt');
          visPublished:
            AppendText(ParaEl, 'pl');
        end;
        AppendNbSp(ParaEl, 1);

        ParaEl := CreateTD(TREl);
        if (Member.ClassType = TPasProperty) and
          (Length(TPasProperty(Member).WriteAccessorName) = 0) then
        begin
          AppendText(ParaEl, 'ro');
          AppendNbSp(ParaEl, 1);
        end;

        LinkEl := AppendHyperlink(CreatePara(CreateTD(TREl)), Member);
        if Assigned(LinkEl) then
          LinkEl['onClick'] := 'opener.location.href = ''' + LinkEl['href'] +
            '''; return false;';
      end;
      if (not Assigned(ThisClass.AncestorType)) or
        (not (ThisClass.AncestorType.ClassType = TPasClassType)) then
        break;
      ThisClass := TPasClassType(ThisClass.AncestorType);
      AppendNbSp(CreatePara(CreateTD(CreateTR(TableEl))), 1);
    end;
  end;

  procedure CreateSortedSubpage(AFilter: TMemberFilter);
  var
    List: TFPList;
    ThisClass: TPasClassType;
    i, j: Integer;
    Member: TPasElement;
    TableEl, TREl, TDEl, ParaEl, LinkEl: TDOMElement;
  begin
    List := TFPList.Create;
    try
      ThisClass := AClass;
      while True do
      begin
        for i := 0 to ThisClass.Members.Count - 1 do
        begin
          Member := TPasElement(ThisClass.Members[i]);
          if (not (((Member.Visibility = visPrivate) and Engine.HidePrivate) or
            ((Member.Visibility = visProtected) and Engine.HideProtected))) and
            AFilter(Member) then
          begin
            j := 0;
            while (j < List.Count) and
              (CompareText(TPasElement(List[j]).Name, Member.Name) < 0) do
              Inc(j);
            List.Insert(j, Member);
          end;
        end;
        if (not Assigned(ThisClass.AncestorType)) or
          (not (ThisClass.AncestorType.ClassType = TPasClassType)) then
          break;
        ThisClass := TPasClassType(ThisClass.AncestorType);
      end;

      TableEl := CreateTable(BodyElement);
      for i := 0 to List.Count - 1 do
      begin
        Member := TPasElement(List[i]);
        TREl := CreateTR(TableEl);
        ParaEl := CreatePara(CreateTD(TREl));
        case Member.Visibility of
          visPrivate:
            AppendText(ParaEl, 'pv');
          visProtected:
            AppendText(ParaEl, 'pt');
          visPublished:
            AppendText(ParaEl, 'pl');
        end;
        AppendNbSp(ParaEl, 1);

        ParaEl := CreatePara(CreateTD(TREl));
        if (Member.ClassType = TPasProperty) and
          (Length(TPasProperty(Member).WriteAccessorName) = 0) then
        begin
          AppendText(ParaEl, 'ro');
          AppendNbSp(ParaEl, 1);
        end;

        TDEl := CreateTD(TREl);
        TDEl['nowrap'] := 'nowrap';
        ParaEl := CreatePara(TDEl);
        LinkEl := AppendHyperlink(ParaEl, Member);
        if Assigned(LinkEl) then
          LinkEl['onClick'] := 'opener.location.href = ''' + LinkEl['href'] +
            '''; return false;';
        AppendText(ParaEl, ' (');
        LinkEl := AppendHyperlink(ParaEl, Member.Parent);
        if Assigned(LinkEl) then
          LinkEl['onClick'] := 'opener.location.href = ''' + LinkEl['href'] +
            '''; return false;';
        AppendText(ParaEl, ')');
      end;
    finally
      List.Free;
    end;
  end;

begin
  case ASubpageIndex of
    0:
      CreateMainPage;
    PropertiesByInheritanceSubindex:
      CreateInheritanceSubpage(@PropertyFilter);
    PropertiesByNameSubindex:
      CreateSortedSubpage(@PropertyFilter);
    MethodsByInheritanceSubindex:
      CreateInheritanceSubpage(@MethodFilter);
    MethodsByNameSubindex:
      CreateSortedSubpage(@MethodFilter);
    EventsByInheritanceSubindex:
      CreateInheritanceSubpage(@EventFilter);
    EventsByNameSubindex:
      CreateSortedSubpage(@EventFilter);
  end;
end;

procedure THTMLWriter.CreateClassMemberPageBody(AElement: TPasElement);
var
  TableEl, TREl, CodeEl: TDOMElement;

  procedure CreateVarPage(Element: TPasVariable);
  begin
    AppendHyperlink(CodeEl, Element.Parent);
    AppendSym(CodeEl, '.');
    AppendText(CodeEl, Element.Name);
    if Assigned(Element.VarType) then
    begin
      AppendSym(CodeEl, ': ');
      AppendSym(AppendType(CodeEl, TableEl, Element.VarType, False), ';');
    end;
  end;

  procedure CreatePropertyPage(Element: TPasProperty);
  var
    NeedBreak: Boolean;
  begin
    AppendKw(CodeEl, 'property ');
    AppendHyperlink(CodeEl, Element.Parent);
    AppendSym(CodeEl, '.');
    AppendText(CodeEl, Element.Name);
    if Assigned(Element.VarType) then
    begin
      AppendSym(CodeEl, ': ');
      AppendType(CodeEl, TableEl, Element.VarType, False);
    end;

    NeedBreak := False;
    if Length(TPasProperty(Element).IndexValue) <> 0 then
    begin
      CreateEl(CodeEl, 'br');
      AppendNbsp(CodeEl, 2);
      AppendKw(CodeEl, 'index ');
      AppendPasSHFragment(CodeEl, TPasProperty(Element).IndexValue, 0);
      NeedBreak := True;
    end;
    if Length(TPasProperty(Element).ReadAccessorName) <> 0 then
    begin
      CreateEl(CodeEl, 'br');
      AppendNbsp(CodeEl, 2);
      AppendKw(CodeEl, 'read ');
      AppendText(CodeEl, TPasProperty(Element).ReadAccessorName);
      NeedBreak := True;
    end;
    if Length(TPasProperty(Element).WriteAccessorName) <> 0 then
    begin
      CreateEl(CodeEl, 'br');
      AppendNbsp(CodeEl, 2);
      AppendKw(CodeEl, 'write ');
      AppendText(CodeEl, TPasProperty(Element).WriteAccessorName);
      NeedBreak := True;
    end;
    if Length(TPasProperty(Element).StoredAccessorName) <> 0 then
    begin
      CreateEl(CodeEl, 'br');
      AppendNbsp(CodeEl, 2);
      AppendKw(CodeEl, 'stored ');
      AppendText(CodeEl, TPasProperty(Element).StoredAccessorName);
      NeedBreak := True;
    end;
    if Length(TPasProperty(Element).DefaultValue) <> 0 then
    begin
      CreateEl(CodeEl, 'br');
      AppendNbsp(CodeEl, 2);
      AppendKw(CodeEl, 'default ');
      AppendPasSHFragment(CodeEl, TPasProperty(Element).DefaultValue, 0);
      NeedBreak := True;
    end;

    AppendSym(CodeEl, ';');

    if TPasProperty(Element).IsDefault or TPasProperty(Element).IsNodefault then
    begin
      if NeedBreak then
      begin
        CreateEl(CodeEl, 'br');
        AppendNbsp(CodeEl, 2);
      end;
      if TPasProperty(Element).IsDefault then
        AppendKw(CodeEl, 'default')
      else
        AppendKw(CodeEl, 'nodefault');
      AppendSym(CodeEl, ';');
    end;
  end;

var
  s: String;
  DocNode: TDocNode;
begin
  AppendMenuBar(-1);
  AppendTitle(AElement.FullName);
  AppendShortDescr(CreatePara(BodyElement), AElement);
  AppendText(CreateH2(BodyElement), SDocDeclaration);
  AppendSourceRef(AElement);

  TableEl := CreateTable(BodyElement);
  TREl := CreateTR(TableEl);
  CodeEl := CreateCode(CreatePara(CreateTD(TREl)));
  AppendText(CodeEl, ' ');      // !!!: Workaround for current HTML writer

  case AElement.Visibility of
    visPrivate: s := 'private';
    visProtected: s := 'protected';
    visPublic: s := 'public';
    visPublished: s := 'published';
    visAutomated: s := 'automated';
    else s := '';
  end;
  if Length(s) > 0 then
    AppendKw(CodeEl, s);
  AppendText(CodeEl, ' ');

  if AElement.ClassType = TPasVariable then
    CreateVarPage(TPasVariable(AElement))
  else if AElement.InheritsFrom(TPasProcedureBase) then
    AppendProcDecl(CodeEl, TableEl, TPasProcedureBase(AElement))
  else if AElement.ClassType = TPasProperty then
    CreatePropertyPage(TPasProperty(AElement))
  else
    AppendText(CreateWarning(BodyElement), '<' + AElement.ClassName + '>');

  FinishElementPage(AElement);
end;

procedure THTMLWriter.CreateVarPageBody(AVar: TPasVariable);
var
  TableEl, TREl, TDEl, CodeEl, El: TDOMElement;
  DocNode: TDocNode;
begin
  AppendMenuBar(-1);
  AppendTitle(AVar.FullName);
  AppendShortDescr(CreatePara(BodyElement), AVar);
  AppendText(CreateH2(BodyElement), SDocDeclaration);
  AppendSourceRef(AVar);

  TableEl := CreateTable(BodyElement);
  TREl := CreateTR(TableEl);
  TDEl := CreateTD(TREl);
  CodeEl := CreateCode(CreatePara(TDEl));

  AppendKw(CodeEl, 'var');
  AppendText(CodeEl, ' ' + AVar.Name);
  if Assigned(AVar.VarType) then
  begin
    AppendSym(CodeEl, ': ');
    El := AppendType(CodeEl, TableEl, AVar.VarType, False);
  end else
    El := CodeEl;
  if Length(AVar.Value) > 0 then
    AppendPasSHFragment(El, ' = ' + AVar.Value + ';', 0)
  else
    AppendSym(El, ';');

  FinishElementPage(AVar);
end;

procedure THTMLWriter.CreateProcPageBody(AProc: TPasProcedureBase);
var
  TableEl, TREl, TDEl, CodeEl: TDOMElement;
begin
  AppendMenuBar(-1);
  AppendTitle(AProc.Name);
  AppendShortDescr(CreatePara(BodyElement), AProc);
  AppendText(CreateH2(BodyElement), SDocDeclaration);
  AppendSourceRef(AProc);

  TableEl := CreateTable(BodyElement);
  TREl := CreateTR(TableEl);
  TDEl := CreateTD(TREl);
  CodeEl := CreateCode(CreatePara(TDEl));

  AppendProcDecl(CodeEl, TableEl, AProc);

  FinishElementPage(AProc);
end;

Function THTMLWriter.InterPretOption(Const Cmd,Arg : String) : boolean;

begin
  Result:=True;

  if Cmd = '--html-search' then
    SearchPage := Arg
  else if Cmd = '--footer' then
    FooterFile := Arg
  else if Cmd = '--charset' then
    CharSet := Arg
  else if Cmd = '--index-colcount' then
    IndexColCount := StrToIntDef(Arg,IndexColCount)
  else if Cmd = '--image-url' then
    FBaseImageURL  := Arg
  else if Cmd = '--css-file' then
    FCSSFile := arg
  else if Cmd = '--footer-date' then
    begin
    FIDF:=True;
    FDateFormat:=Arg;
    end
  else if Cmd = '--disable-menu-brackets' then
    FUseMenuBrackets:=False
  else
    Result:=False;
end;

procedure THTMLWriter.WriteDoc;
begin
   DoLog(SWritingPages, [PageCount]);
   WriteHTMLPages;
end;

class procedure THTMLWriter.Usage(List: TStrings);
begin
  List.add('--footer');
  List.Add(SHTMLUsageFooter);
  List.Add('--footer-date[=Fmt]');
  List.Add(SHTMLUsageFooterDate);
  List.Add('--charset=set');
  List.Add(SHTMLUsageCharset);
  List.Add('--html-search=pagename');
  List.Add(SHTMLHtmlSearch);
  List.Add('--index-colcount=N');
  List.Add(SHTMLIndexColcount);
  List.Add('--image-url=url');
  List.Add(SHTMLImageUrl);
  List.Add('--disable-menu-brackets');
  List.Add(SHTMLDisableMenuBrackets);
end;

Class Function THTMLWriter.FileNameExtension : String; 
begin
  result:='';
end;

// private methods

function THTMLWriter.GetPageCount: Integer;
begin
  Result := PageInfos.Count;
end;

procedure THTMLWriter.SetOnTest(const AValue: TNotifyEvent);
begin
  if FOnTest=AValue then exit;
    FOnTest:=AValue;
end;

procedure THTMLWriter.CreateAllocator;
begin
  FAllocator:=TLongNameFileAllocator.Create('.html');
end;


procedure THTMWriter.CreateAllocator;
begin
  FAllocator:=TShortNameFileAllocator.Create('.htm');
end;

initialization
  // Do not localize.
  RegisterWriter(THTMLWriter,'html','HTML output using fpdoc.css stylesheet.');
  RegisterWriter(THTMWriter,'htm','HTM (8.3 filenames) output using fpdoc.css stylesheet.');
  RegisterWriter(TCHMHTMLWriter,'chm','Compressed HTML file output using fpdoc.css stylesheet.');

finalization
  UnRegisterWriter('html');
  UnRegisterWriter('htm');
  UnRegisterWriter('chm');
end.
