Browse Source

* More modern HTML writer

Michaël Van Canneyt 2 weeks ago
parent
commit
129c3e666d
5 changed files with 2290 additions and 2 deletions
  1. 2280 0
      utils/fpdoc/dw_newhtml.pp
  2. 6 2
      utils/fpdoc/fpdoc.lpi
  3. 1 0
      utils/fpdoc/fpdoc.pp
  4. 2 0
      utils/fpdoc/fpdocstrs.pp
  5. 1 0
      utils/fpdoc/fpmake.pp

+ 2280 - 0
utils/fpdoc/dw_newhtml.pp

@@ -0,0 +1,2280 @@
+{
+    FPDoc  -  Free Pascal Documentation Tool
+    Copyright (C) 2000 - 2005 by
+      Areca Systems GmbH / Sebastian Guenther, [email protected]
+
+    * 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_newhtml;
+{$WARN 5024 off : Parameter "$1" not used}
+interface
+
+uses Classes, DOM, DOM_HTML, dGlobals, PasTree, dWriter, dw_basehtml, PasWrite;
+
+
+type
+  { TNewHTMLWriter }
+
+  TNewHTMLWriter = class(TBaseHTMLWriter)
+  private
+    FHeadElement: TDomElement;
+    FOnTest: TNotifyEvent;
+    FCSSFile: String;
+    FCharSet : String;
+    FHeaderHTML,
+    FNavigatorHTML,
+    FFooterHTML: TStringStream;
+    FTitleElement: TDOMElement;
+    FIncludeDateInFooter : Boolean;
+    FUseMenuBrackets: Boolean;
+    FDateFormat: String;
+    FIndexColCount : Integer;
+    FSearchPage : String;
+    function GetVarDef(aElement: TPasVariable; aPrefixParent: Boolean): string;
+    procedure SetOnTest(const AValue: TNotifyEvent);
+  protected
+    function CreateAllocator : TFileAllocator; override;
+    procedure WriteDocPage(const aFileName: String; aElement: TPasElement; aSubPageIndex: Integer);  override;
+    // General HTML creation
+    function CreateH1(Parent: TDOMNode): THTMLElement; override;
+    function CreateH2(Parent: TDOMNode): THTMLElement; override;
+    function CreateH3(Parent: TDOMNode): THTMLElement; override;
+
+    function CreateListColumn1(aParent: THTMLElement): THTMLElement;
+    function CreateListColumn2(aParent: THTMLElement): THTMLElement;
+    function CreateListColumns(aParent: THTMLElement): THTMLElement;
+    function CreateSection(aParent : THTMLElement) : THTMLElement; virtual;
+
+    function  AppendPasSHFragment(Parent: TDOMNode; const AText: String; AShFlags: Byte): Byte; override;
+    function  AppendPasSHFragment(Parent: TDOMNode; const AText: String; AShFlags: Byte; aLinkIdentifierMap : TLinkIdentifierMap): Byte; virtual;
+
+    procedure CreateCSSFile; virtual;
+
+    procedure AppendTitle(aParent: TDomElement; const AText: AnsiString; Hints : TPasMemberHints = []); virtual;
+    procedure AppendTitle(aParent: TDomElement; const AText: DOMString; Hints: TPasMemberHints=[]); virtual;
+    function AppendType(CodeEl: TDOMElement; Element: TPasType): TDOMElement; virtual;
+    function AppendProcType(CodeEl : TDOMElement;  Element: TPasProcedureType; Indent: Integer): TDOMElement; virtual;
+    procedure AppendProcExt(CodeEl: TDOMElement; Element: TPasProcedure); virtual;
+    procedure AppendProcDecl(CodeEl: TDOMElement; Element: TPasProcedureBase); virtual;
+    procedure AppendProcArgsSection(Parent: TDOMNode; Element: TPasProcedureType; SkipResult : Boolean = False); virtual;
+    procedure AppendShortDescrCell(Parent: TDOMNode; Element: TPasElement); override;
+    procedure AppendDescrSection(AContext: TPasElement; Parent: TDOMNode; DescrNode: TDOMElement; const ATitle: DOMString); override;
+    Procedure AppendSeeAlsoSection(AElement: TPasElement; aParent : TDOMElement; DocNode: TDocNode); override;
+
+    // Structural elements
+    procedure AppendMenuBar(ASubpageIndex: Integer);virtual;
+    procedure AppendTopicMenuBar(Topic : TTopicElement);virtual;
+    procedure FinishElementPage(AElement: TPasElement; aDescription: Boolean=True); virtual;
+    procedure AppendFooter;virtual;
+
+    // Class
+    procedure CreateClassMainPage(aClass: TPasClassType);virtual;
+    procedure CreateClassInheritedSubpage(AClass: TPasClassType; aType: TClassMemberType);
+    procedure CreateClassSortedSubpage(AClass: TPasClassType; aType : TClassMemberType);virtual;
+    procedure CreateClassMemberList(aParent: THTMLElement; AClass: TPasClassType; DeclaredOnly: Boolean; AFilter: TMemberFilter);
+    procedure AppendMemberListSection(aParent: THTMLELement; aClass: TPasClassType; aMemberType : TClassMemberType; aDeclaredOnly: Boolean);
+    procedure AppendInheritanceTree(aParent: THTMLELement; aClass: TPasClassType);
+
+    // Package
+    procedure CreatePageBody(AElement: TPasElement; ASubpageIndex: Integer); virtual;
+    procedure CreatePackagePageBody;virtual;
+    procedure CreatePackageIndex;
+    procedure CreatePackageClassHierarchy;
+    procedure CreateClassHierarchyPage(AddUnit : Boolean);
+    procedure CreateIndexPage(L : TStringList); virtual;
+    // Topic
+    Procedure CreateTopicPageBody(AElement : TTopicElement);
+    // Module
+    procedure CreateModuleMainPage(aModule: TPasModule);virtual;
+    procedure CreateModuleSimpleSubpage(aModule: TPasModule; ASubpageIndex: Integer; const ATitle: DOMString; AList: TFPList);virtual;
+    procedure CreateModuleResStringsPage(aModule: TPasModule);virtual;
+    procedure CreateModulePageBody(AModule: TPasModule; ASubpageIndex: Integer);
+    procedure CreateModuleIndexPage(AModule: TPasModule); virtual;
+    // Identifiers
+    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(aParent : THTMLElement; Node : TDocNode; PasElement : TPasElement);
+    // Type declarations
+    function GetElementCode(aElement: TPasElement; aSparse: boolean; aFlags: TElementFlags = []): String;
+    function AppendHighlightedCode(aParent: TDOMNode; aCode: String; const aLanguage: String=''; aMap: TLinkIdentifierMap = Nil): THTMLElement;
+    function CreateCodeLines(aLines: array of string): string;
+    procedure AppendTypeDecl(AType: TPasType);
+    procedure AppendAliasTypeDecl(aType: TPasAliasType);
+    procedure AppendClassOfTypeDecl(aType: TPasClassOfType);
+    function  AppendCodeBlock(aParent: TDOMNode; const aLanguage: String=''): THTMLElement;
+    procedure AppendEnumTypeDecl(aType: TPasEnumType);
+    procedure AppendPointerTypeDecl(aType: TPasPointerType);
+    procedure AppendProcedureTypeDecl(aType: TPasProcedureType);
+    procedure AppendRecordTypeDecl(aType: TPasRecordType);
+    procedure AppendSetTypeDecl(aType: TPasSetType);
+    procedure AppendTypeAliasTypeDecl(aType: TPasTypeAliasType);
+
+    //  Main documentation process
+    Procedure DoWriteDocumentation; override;
+
+    Property HeaderHTML : TStringStream Read FHeaderHTML;
+    Property NavigatorHTML : TStringStream read FNavigatorHTML;
+    Property FooterHTML : TStringStream read FFooterHTML;
+    Property CSSFile : String Read FCSSFile;
+    Property HeadElement : TDomElement Read FHeadElement;
+    Property TitleElement: TDOMElement Read FTitleElement;
+  public
+    // Creating all module hierarchy classes happens here !
+    constructor Create(APackage: TPasPackage; AEngine: TFPDocEngine); override;
+    // Overrides
+    Class Function FileNameExtension : String; override;
+    class procedure Usage(List: TStrings); override;
+    Class procedure SplitImport(var AFilename, ALinkPrefix: String); override;
+
+    Function InterPretOption(Const Cmd,Arg : String) : boolean; override;
+
+    // Single-page generation
+    function CreateHTMLPage(AElement: TPasElement; ASubpageIndex: Integer): TXMLDocument; virtual;
+
+    Property SearchPage: String Read FSearchPage Write FSearchPage;
+    Property IncludeDateInFooter : Boolean Read FIncludeDateInFooter Write FIncludeDateInFooter;
+    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 UseMenuBrackets : Boolean Read FUseMenuBrackets write FUseMenuBrackets;
+  end;
+
+
+implementation
+
+uses fpdocstrs, SysUtils, HTMWrite, syntax.highlighter, syntax.pascal, fpdocclasstree;
+
+{$i newcss.inc}
+
+constructor TNewHTMLWriter.Create(APackage: TPasPackage; AEngine: TFPDocEngine);
+
+begin
+  inherited Create(APackage, AEngine);
+  // should default to true since this is the old behavior
+  UseMenuBrackets:=True;
+  IndexColCount:=3;
+  Charset:='iso-8859-1';
+  FCSSFile:='fpdocs.css';
+end;
+
+function TNewHTMLWriter.CreateHTMLPage(AElement: TPasElement;
+  ASubpageIndex: Integer): TXMLDocument;
+var
+  HTMLEl: THTMLHtmlElement;
+  HeadEl: THTMLHeadElement;
+  BodyElement : THTMLElement;
+  El: TDOMElement;
+begin
+  Result := THTMLDocument.Create;
+  SetHTMLDocument(THTMLDocument(Result));
+  Doc.AppendChild(Doc.Impl.CreateDocumentType('html','',''));
+
+  HTMLEl := Doc.CreateHtmlElement;
+  Doc.AppendChild(HTMLEl);
+
+  HeadEl := Doc.CreateHeadElement;
+  FHeadElement:=HeadEl;
+  HTMLEl.AppendChild(HeadEl);
+  El := Doc.CreateElement('meta');
+  HeadEl.AppendChild(El);
+  El['http-equiv'] := 'Content-Type';
+  El['content'] := 'text/html; charset=utf-8';
+
+  El := Doc.CreateElement('meta');
+  HeadEl.AppendChild(El);
+  El['name'] := 'viewport';
+  El['content'] := 'width=device-width, initial-scale=1';
+
+
+  FTitleElement := Doc.CreateElement('title');
+  HeadEl.AppendChild(TitleElement);
+
+  BodyElement := Doc.CreateElement('body');
+  BodyElement['class']:='has-navbar-fixed-top';
+  ContentElement:=BodyElement;
+  HTMLEl.AppendChild(BodyElement);
+
+  CreatePageBody(AElement, ASubpageIndex);
+
+  AppendFooter;
+
+  El := Doc.CreateElement('link');
+  HeadEl.AppendChild(El);
+  El['rel'] := 'stylesheet';
+  El['type'] := 'text/css';
+  El['href'] := UTF8Decode(FixHtmlPath(UTF8Encode(Allocator.GetCSSFilename(AElement,'bulma'))));
+
+  El := Doc.CreateElement('link');
+  HeadEl.AppendChild(El);
+  El['rel'] := 'stylesheet';
+  El['type'] := 'text/css';
+  El['href'] := UTF8Decode(FixHtmlPath(UTF8Encode(Allocator.GetCSSFilename(AElement,'fpdocs'))));
+end;
+
+
+procedure TNewHTMLWriter.WriteDocPage(const aFileName: String; aElement: TPasElement; aSubPageIndex: Integer);
+
+Var
+  PageDoc: TXMLDocument;
+  FN : String;
+begin
+  PageDoc := CreateHTMLPage(aElement, aSubpageIndex);
+  try
+    FN:=GetFileBaseDir(Engine.Output)+aFilename;
+    //writeln('Element: ',Element.PathName, ' FileName: ', FN);
+    WriteHTMLFile(PageDoc, FN);
+  except
+    on E: Exception do
+      DoLog(SErrCouldNotCreateFile, [aFileName, e.Message]);
+  end;
+  PageDoc.Free;
+end;
+
+
+function TNewHTMLWriter.CreateH1(Parent: TDOMNode): THTMLElement;
+begin
+  Result:=inherited CreateH1(Parent);
+  Result['class']:='subtitle is-2'
+end;
+
+function TNewHTMLWriter.CreateH2(Parent: TDOMNode): THTMLElement;
+begin
+  Result:=inherited CreateH2(Parent);
+  Result['class']:='subtitle is-4'
+end;
+
+function TNewHTMLWriter.CreateH3(Parent: TDOMNode): THTMLElement;
+begin
+  Result:=inherited CreateH3(Parent);
+  Result['class']:='subtitle is-6'
+end;
+
+procedure TNewHTMLWriter.DoWriteDocumentation;
+
+
+begin
+  Inherited;
+  CreateCSSFile;
+end;
+
+procedure TNewHTMLWriter.CreateCSSFile;
+
+Var
+  TempStream: TMemoryStream;
+  Data : PByte;
+
+begin
+  TempStream := TMemoryStream.Create;
+  try
+    if (FCSSFile<>'') then
+      begin
+      if not FileExists(FCSSFile) then
+        begin
+        DoLog('Can''t find CSS file "%s"',[FCSSFILE]);
+        halt(1);
+        end;
+      TempStream.LoadFromFile(FCSSFile);
+      end
+    else
+      begin
+      DoLog('Using built-in CSS file',[]);
+      Data:=@DefaultNewCSS;
+      TempStream.WriteBuffer(Data^,SizeOf(DefaultNewCSS));
+      end;
+   TempStream.Position := 0;
+   TempStream.SaveToFile(Engine.output+'fpdocs.css');
+  finally
+    TempStream.Free;
+  end;
+end;
+
+
+{ Returns the new CodeEl, which will be the old CodeEl in most cases }
+function TNewHTMLWriter.AppendType(CodeEl: TDOMElement;  Element: TPasType): TDOMElement;
+
+Var
+  S : String;
+
+begin
+  Result := CodeEl;
+  S:=GetElementCode(Element, False);
+//  Writeln('Default code for "',Element.ClassName,'"(',Element.FullName,'):',S);
+  AppendHighlightedCode(ContentElement,S);
+end;
+
+function TNewHTMLWriter.AppendProcType(CodeEl: TDOMElement;
+  Element: TPasProcedureType; Indent: Integer): TDOMElement;
+
+var
+  i: Integer;
+  Arg: TPasArgument;
+  S : String;
+
+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]);
+      S:=AccessNames[Arg.Access];
+      if (S<>'') then
+        AppendKw(CodeEl,S);
+      AppendText(CodeEl, Arg.Name);
+      if Assigned(Arg.ArgType) then
+      begin
+        AppendSym(CodeEl, ': ');
+        CodeEl := AppendType(CodeEl, Arg.ArgType);
+      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
+      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
+        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 TNewHTMLWriter.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 standalone procedure or function. }
+
+procedure TNewHTMLWriter.AppendProcDecl(CodeEl: TDOMElement;  Element: TPasProcedureBase);
+
+  procedure WriteVariant(AProc: TPasProcedure; SkipResult : Boolean);
+  begin
+    AppendHighlightedCode(CodeEl,GetElementCode(aProc,False,[efParent]));
+  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);
+      WriteVariant(P,fc>1);
+    end
+  else
+    WriteVariant(TPasProcedure(Element),False);
+end;
+
+procedure TNewHTMLWriter.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;
+
+procedure TNewHTMLWriter.AppendTopicMenuBar(Topic : TTopicElement);
+
+  function AddLink(aParent : THTMLElement; ALinkSubpageIndex: Integer; const AName: String) : THTMLElement;
+
+  begin
+    Result:=CreateLink(aParent, ResolveLinkWithinPackage(Module, ALinkSubpageIndex));
+    Result['class']:='navbar-item';
+    AppendText(Result,aName);
+  end;
+
+  function AddPackageLink(aParent: THTMLElement; ALinkSubpageIndex: Integer; const AName: String) : THTMLElement;
+  begin
+    Result:=CreateLink(aParent, ResolveLinkWithinPackage(Package, ALinkSubpageIndex));
+    Result['class']:='navbar-item';
+    AppendText(Result,aName);
+  end;
+
+
+var
+  El,NavBrand, NavItem, NavEl, NavMenu, NavBar : THTMLElement;
+begin
+  NavEl := CreateEl(ContentElement, 'nav','navbar is-link is-fixed-top');
+  NavEl['role']:='navigation';
+  NavEl['aria-label']:='main navigation';
+  NavBrand:=CreateEl(NavEl,'div');
+  // We use the brand for the link to the overview
+  NavBrand['class']:='navbar-brand';
+  NavItem:=CreateEl(NavBrand,'a');
+  NavItem['class']:='navbar-item';
+  if Assigned(Module) then
+    begin
+    NavItem['href']:=UTF8Decode(ResolveLinkWithinPackage(Module, 0));
+    AppendText(NavItem,UTF8Decode(Module.Name));
+    end
+  else
+    begin
+    NavItem['href']:=UTF8Decode(ResolveLinkWithinPackage(Package, IndexSubIndex));
+    AppendText(NavItem,UTF8Decode(Package.Name));
+    end;
+  // Now the other items follow
+  NavMenu:=CreateEl(NavEl,'div');
+  NavMenu['class']:='navbar-menu';
+  NavBar:=CreateEl(NavMenu,'div');
+  NavBar['class']:='navbar-start';
+  if Assigned(Module) then
+    begin
+    // AddLink(NavBar,0, 'Unit '+Module.Name);
+    if Module.InterfaceSection.ResStrings.Count > 0 then
+      AddLink(NavBar,ResstrSubindex, SDocResStrings);
+    if Module.InterfaceSection.Consts.Count > 0 then
+      AddLink(NavBar,ConstsSubindex, SDocConstants);
+    if Module.InterfaceSection.Types.Count > 0 then
+      AddLink(NavBar,TypesSubindex, SDocTypes);
+    if Module.InterfaceSection.Classes.Count > 0 then
+      AddLink(NavBar,ClassesSubindex, SDocClasses);
+    if Module.InterfaceSection.Functions.Count > 0 then
+      AddLink(NavBar,ProcsSubindex, SDocProceduresAndFunctions);
+    if Module.InterfaceSection.Variables.Count > 0 then
+      AddLink(NavBar,VarsSubindex, SDocVariables);
+    AddLink(NavBar,IndexSubIndex,SDocIdentifierIndex);
+    AppendFragment(NavBar, NavigatorHTML);
+    end
+  else
+    begin
+    // Index
+    AddPackageLink(NavBar,IndexSubIndex, SDocIdentifierIndex);
+    // Class TObject tree
+    AddPackageLink(NavBar,ClassHierarchySubIndex, SDocPackageClassHierarchy);
+    AppendFragment(NavBar, NavigatorHTML)
+    end;
+  NavBar:=CreateEl(NavMenu,'div');
+  NavBar['class']:='navbar-end';
+
+  if Length(SearchPage) > 0 then
+    begin
+    El:=CreateLink(NavBar, SearchPage);
+    El['class']:='navbar-item';
+    AppendText(El, SDocSearch);
+    end;
+
+  if Assigned(Module) and Assigned(Package) then // Displays a Package page
+  begin
+    El:=AppendHyperlink(NavBar, Package) as THTMLELement;
+    El['class']:='navbar-item';
+  end;
+  AppendFragment(ContentElement,HeaderHTML);
+end;
+
+
+function TNewHTMLWriter.CreateAllocator: TFileAllocator;
+begin
+   Result:=TLongNameFileAllocator.Create('.html');
+end;
+
+procedure TNewHTMLWriter.AppendMenuBar(ASubpageIndex: Integer);
+
+  function AddLink(aParent : THTMLElement; ALinkSubpageIndex: Integer; const AName: String) : THTMLElement;
+
+  begin
+    Result:=CreateLink(aParent, ResolveLinkWithinPackage(Module, ALinkSubpageIndex));
+    Result['class']:='navbar-item';
+    AppendText(Result,aName);
+  end;
+
+  function AddPackageLink(aParent: THTMLElement; ALinkSubpageIndex: Integer; const AName: String) : THTMLElement;
+  begin
+    Result:=CreateLink(aParent, ResolveLinkWithinPackage(Package, ALinkSubpageIndex));
+    Result['class']:='navbar-item';
+    AppendText(Result,aName);
+  end;
+
+
+var
+  El,NavBrand, NavItem, NavEl, NavMenu, NavBar : THTMLElement;
+begin
+
+  NavEl := CreateEl(ContentElement, 'nav','navbar is-link is-fixed-top');
+  NavEl['role']:='navigation';
+  NavEl['aria-label']:='main navigation';
+  NavBrand:=CreateEl(NavEl,'div');
+  // We use the brand for the link to the overview
+  NavBrand['class']:='navbar-brand';
+  NavItem:=CreateEl(NavBrand,'a');
+  NavItem['class']:='navbar-item';
+  if Assigned(Module) then
+    begin
+    NavItem['href']:=UTF8Decode(ResolveLinkWithinPackage(Module, 0));
+    AppendText(NavItem,UTF8Decode(Module.Name));
+    end
+  else
+    begin
+    NavItem['href']:=UTF8Decode(ResolveLinkWithinPackage(Package, IndexSubIndex));
+    AppendText(NavItem,UTF8Decode(Package.Name));
+    end;
+  // Now the other items follow
+  NavMenu:=CreateEl(NavEl,'div');
+  NavMenu['class']:='navbar-menu';
+  NavBar:=CreateEl(NavMenu,'div');
+  NavBar['class']:='navbar-start';
+  if Assigned(Module) then
+    begin
+    // AddLink(NavBar,0, 'Unit '+Module.Name);
+    if Module.InterfaceSection.ResStrings.Count > 0 then
+      AddLink(NavBar,ResstrSubindex, SDocResStrings);
+    if Module.InterfaceSection.Consts.Count > 0 then
+      AddLink(NavBar,ConstsSubindex, SDocConstants);
+    if Module.InterfaceSection.Types.Count > 0 then
+      AddLink(NavBar,TypesSubindex, SDocTypes);
+    if Module.InterfaceSection.Classes.Count > 0 then
+      AddLink(NavBar,ClassesSubindex, SDocClasses);
+    if Module.InterfaceSection.Functions.Count > 0 then
+      AddLink(NavBar,ProcsSubindex, SDocProceduresAndFunctions);
+    if Module.InterfaceSection.Variables.Count > 0 then
+      AddLink(NavBar,VarsSubindex, SDocVariables);
+    AddLink(NavBar,IndexSubIndex,SDocIdentifierIndex);
+    AppendFragment(NavBar, NavigatorHTML);
+    end
+  else
+    begin
+    // Index
+    AddPackageLink(NavBar,IndexSubIndex, SDocIdentifierIndex);
+    // Class TObject tree
+    AddPackageLink(NavBar,ClassHierarchySubIndex, SDocPackageClassHierarchy);
+    AppendFragment(NavBar, NavigatorHTML)
+    end;
+  NavBar:=CreateEl(NavMenu,'div');
+  NavBar['class']:='navbar-end';
+
+  if Length(SearchPage) > 0 then
+    begin
+    El:=CreateLink(NavBar, SearchPage);
+    El['class']:='navbar-item';
+    AppendText(El, SDocSearch);
+    end;
+
+  if Assigned(Module) and Assigned(Package) then // Displays a Package page
+  begin
+    El:=AppendHyperlink(NavBar, Package) as THTMLELement;
+    El['class']:='navbar-item';
+  end;
+  AppendFragment(ContentElement,HeaderHTML);
+end;
+
+
+procedure TNewHTMLWriter.AppendFooter;
+
+Var
+  S : String;
+  lContent,lDateEl,lFooter : TDomElement;
+begin
+  if not (Assigned(FooterHTML) or IncludeDateInFooter) then
+    exit;
+  lFooter:=CreateEl(ContentElement, 'footer','footer');
+  lContent:=CreateEl(lFooter, 'div','has-text-centered');
+  if Assigned(FooterHTML) then
+    AppendFragment(lContent, FooterHTML)
+  else if IncludeDateInFooter then
+    begin
+    lDateEl:=CreateEl(lContent,'span','footertext');
+    If (FDateFormat='') then
+      S:=DateToStr(Date)
+    else
+      S:=FormatDateTime(FDateFormat,Date);  
+    AppendText(lDateEl,Format(SDocDateGenerated,[S]));
+    end;
+end;
+
+function TNewHTMLWriter.CreateSection(aParent: THTMLElement): THTMLElement;
+begin
+  Result:=CreateEl(aParent,'section','section');
+end;
+
+function TNewHTMLWriter.AppendPasSHFragment(Parent: TDOMNode; const AText: String; AShFlags: Byte): Byte;
+begin
+  Result:=AppendPasSHFragment(Parent, AText, AShFlags,Nil);
+end;
+
+function TNewHTMLWriter.AppendPasSHFragment(Parent: TDOMNode; const AText: String; AShFlags: Byte;
+  aLinkIdentifierMap: TLinkIdentifierMap): Byte;
+
+var
+  El: TDOMElement;
+
+  Procedure OutputToken(aToken : TSyntaxToken);
+
+  Var
+    CurParent: TDomNode;
+    lLink : String;
+
+  begin
+    lLink:='';
+    If (aToken.Text='') then
+      exit;
+    If (el<>Nil) then
+      CurParent:=El
+    else
+      begin
+      CurParent:=Parent;
+      if (aToken.Kind=shDefault) and Assigned(aLinkIdentifierMap) then
+        lLink:=aLinkIdentifierMap.GetLink(aToken.Text);
+      end;
+    if lLink<>'' then
+      CurParent:=CreateLink(CurParent,lLink);
+    AppendText(CurParent,aToken.Text);
+    El:=Nil;
+  end;
+
+  Function NewEl(Const ElType,Attr,AttrVal : DOMString) : TDomElement;
+
+  begin
+    Result:=CreateEl(Parent,ElType);
+    Result[Attr]:=AttrVal;
+  end;
+
+  Function NewSpan(Const AttrVal : DOMString) : TDomElement;
+
+  begin
+    Result:=CreateEl(Parent,'span');
+    Result['class']:=AttrVal;
+  end;
+var
+  HL : TPascalSyntaxHighlighter;
+  Tokens : TSyntaxTokenArray;
+  T : TSyntaxToken;
+begin
+  Result:=0;
+  HL:=TPascalSyntaxHighlighter.Create;
+  Try
+    Tokens:=HL.Execute(aText);
+    For T in Tokens do
+      begin
+      case T.Kind  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;
+      OutputToken(T);
+      end;
+  finally
+    HL.Free;
+  end;
+end;
+
+procedure TNewHTMLWriter.AppendDescrSection(AContext: TPasElement; Parent: TDOMNode; DescrNode: TDOMElement; const ATitle: DOMString);
+
+var
+  lSection : THTMLElement;
+begin
+  if not IsDescrNodeEmpty(DescrNode) then
+  begin
+    lSection:=CreateSection(Parent as THTMLElement);
+    If (ATitle<>'') then // Can be empty for topic.
+      AppendText(CreateH2(lSection), ATitle);
+    AppendDescr(AContext, lSection, DescrNode, True);
+  end;
+end;
+
+procedure TNewHTMLWriter.AppendSeeAlsoSection(AElement: TPasElement; aParent: TDOMElement; DocNode: TDocNode);
+
+  procedure GetSeeAlsoNodes(aList : TFPList);
+  var
+    Node : TDOMNode;
+  begin
+    // Get all nodes.
+    Node:=DocNode.SeeAlso.FirstChild;
+    While Assigned(Node) do
+      begin
+      if (Node.NodeType=ELEMENT_NODE) and (Node.NodeName='link') then
+        aList.Add(Node);
+      Node := Node.NextSibling;
+      end;
+  end;
+
+  procedure AppendSeeAlsoName(aParent : THTMLElement; El : TDOmElement);
+  var
+    NewEl : THTMLElement;
+    l,s,n : domstring;
+  begin
+    l:=El['id'];
+    // Create parent element for link text/id
+    if Assigned(Engine) and Engine.FalbackSeeAlsoLinks then
+      s:= ResolveLinkIDUnStrict(UTF8ENcode(l))
+    else
+      s:= ResolveLinkID(UTF8ENcode(l));
+    if Length(s)=0 then
+      begin
+      if assigned(module) then
+        s:=UTF8Decode(module.name)
+      else
+        s:='?';
+      if l='' then l:='<empty>';
+      if Assigned(AElement) then
+        N:=UTF8Decode(AElement.PathName)
+      else
+        N:='?';
+      DoLog(SErrUnknownLinkID, [s,N,l]);
+      LinkUnresolvedInc();
+      NewEl := CreateEl(aParent,'b')
+      end
+    else
+      NewEl := CreateLink(aParent,s);
+    // Append link
+    if Not IsDescrNodeEmpty(El) then
+      begin
+      PushOutputNode(NewEl);
+      Try
+        ConvertBaseShortList(AElement, El, True)
+      Finally
+        PopOutputNode;
+      end;
+      end
+    else
+      AppendText(NewEl,El['id']);
+  end;
+
+  Procedure AppendLinkShortDescr(aParent : THTMLElement; aDocEl: TDomElement);
+  var
+     l : domstring;
+     DescrEl : TDomElement;
+  begin
+     l:=aDocEl['id'];
+     DescrEl := Engine.FindShortDescr(AElement.GetModule,UTF8Encode(L));
+     if Not Assigned(DescrEl) then
+       exit;
+     aParent['class'] := aParent['class']+' cmt';
+     PushOutputNode(aParent);
+     try
+       ConvertShort(aElement, DescrEl);
+     finally
+       PopOutputNode;
+     end;
+  end;
+
+var
+  DocEl : TDOMElement;
+  lSection, lColumns, lColumn: THTMLElement;
+  i : integer;
+  List : TFPList;
+
+begin
+  if Not (Assigned(DocNode) and Assigned(DocNode.SeeAlso)) then
+    Exit;
+
+  List:=TFPList.Create;
+  try
+    GetSeeAlsoNodes(List);
+    if List.Count=0 then
+      exit;
+    lSection:=CreateSection(aParent as THTMLElement);
+    AppendText(CreateH2(lSection), SDocSeeAlso);
+    For I:=0 to List.Count-1 do
+      begin
+      DocEl:=TDOMElement(List[i]);
+      lColumns:=CreateListColumns(lSection);
+      // Name
+      lColumn:=CreateListColumn1(lColumns);
+      AppendSeeAlsoName(lColumn,DocEl);
+      lColumn:=CreateListColumn2(lColumns);
+      AppendLinkShortDescr(lColumn,DocEl);
+      end;
+  finally
+    List.Free;
+  end;
+end;
+
+procedure TNewHTMLWriter.FinishElementPage(AElement: TPasElement; aDescription : Boolean = True);
+
+var
+  DocNode: TDocNode;
+
+begin
+  DocNode := Engine.FindDocNode(AElement);
+  If Not Assigned(DocNode) then
+    exit;
+
+  // Description
+  if Assigned(DocNode.Descr) then
+    AppendDescrSection(AElement, ContentElement, DocNode.Descr, UTF8Decode(SDocDescription));
+
+  // Append "Errors" section
+  if Assigned(DocNode.ErrorsDoc) then
+    AppendDescrSection(AElement, ContentElement, DocNode.ErrorsDoc, UTF8Decode(SDocErrors));
+
+  // Append Version info
+  if Assigned(DocNode.Version) then
+    AppendDescrSection(AElement, ContentElement, DocNode.Version, UTF8Decode(SDocVersion));
+
+  // Append "See also" section
+  AppendSeeAlsoSection(AElement,ContentElement,DocNode);
+
+  // Append examples, if present
+  AppendExampleSection(AElement,DocNode);
+  // Append notes, if present
+  ConvertNotes(AElement,DocNode.Notes);
+end;
+
+procedure TNewHTMLWriter.CreateTopicPageBody(AElement: TTopicElement);
+
+var
+  DocNode: TDocNode;
+  lSection : THTMLElement;
+
+begin
+  AppendTopicMenuBar(AElement);
+  DocNode:=AElement.TopicNode;
+  if Assigned(DocNode) then  // should always be true, but we're being careful.
+    begin
+    lSection:=CreateSection(ContentElement);
+    AppendShortDescr(AElement,CreateH2(lSection), DocNode);
+    if Assigned(DocNode.Descr) then
+       AppendDescrSection(AElement, lSection, DocNode.Descr, '');
+    AppendSeeAlsoSection(AElement,ContentElement,DocNode);
+    CreateTopicLinks(ContentElement,DocNode,AElement);
+    AppendExampleSection(AElement,DocNode);
+    ConvertNotes(AElement,DocNode.Notes);
+    end;
+end;
+
+procedure TNewHTMLWriter.CreateClassHierarchyPage(AddUnit : Boolean);
+
+type
+  TypeEN = (NPackage, NModule, NName);
+
+  function PushClassElement(aParent : THTMLElement; IsParent : Boolean) : THTMLElement;
+  Var
+    H : THTMLElement;
+  begin
+    H:=CreateEl(aParent, 'li');
+    if IsParent then
+      begin
+      H['class']:='parent expanded';
+      H['onclick']:='expandorcollapse(event)';
+      end;
+    Result:=H;
+  end;
+
+  Function PushClassList(aParent : THTMLELement) : THTMLElement;
+
+  begin
+    Result:=CreateEl(aParent, 'ul');
+    Result['class']:='treeview';
+  end;
+
+  function ExtractName(APathName: String; Tp: TypeEN):String;
+  var
+  l:TStringList;
+  begin
+    Result:= Trim(APathName);
+    if Result = '' then exit;
+    l:=TStringList.Create;
+    try
+      l.AddDelimitedText(Result, '.', True);
+      if l.Count=3 then
+        Result:= l.Strings[Integer(Tp)]
+      else
+        Result:='';
+    finally
+      l.free;
+    end;
+  end;
+
+  Procedure AppendClass(aParent : THTMLElement; EN : TPasElementNode);
+
+  Var
+    PE,PM : TPasElement;
+    I : Integer;
+    El, SubParent : THTMLELement;
+
+  begin
+    if not Assigned(EN) then exit;
+    PE:=EN.Element;
+    SubParent:=PushClassElement(aParent,EN.ChildCount>0);
+    if (PE<>Nil) then
+      begin
+      El:=CreateEl(SubParent, 'span');
+      AppendHyperLink(El,PE);
+      PM:=PE.GetModule();
+      if (PM<>Nil) then
+        begin
+        AppendText(El,' (');
+        AppendHyperLink(El,PM);
+        AppendText(el,')');
+        end
+      end
+    else
+      AppendText(El,EN.Element.Name);
+    if EN.ChildCount>0 then
+      begin
+      El:=PushClassList(SubParent);
+      For I:=0 to EN.ChildCount-1 do
+        AppendClass(El,EN.Children[i] as TPasElementNode);
+      end;
+  end;
+
+begin
+  AppendClass(PushClassList(ContentElement),TreeClass.RootNode);
+end;
+
+procedure TNewHTMLWriter.CreatePackageClassHierarchy;
+
+Const
+  SFunc = 'function expandorcollapse (event) { '+sLineBreak+
+          '  var el = event.target;'+sLineBreak+
+          '  if (el) { '+sLineBreak+
+          '    el.classList.toggle("expanded");'+sLineBreak+
+          '    event.stopPropagation();'+sLineBreak+
+          '  }'+sLineBreak+
+          '}';
+
+Var
+  S : String;
+  SE : THTMLElement;
+
+begin
+  SE := Doc.CreateElement('script');
+  AppendText(SE,SFunc);
+  HeadElement.AppendChild(SE);
+  AppendMenuBar(ClassHierarchySubIndex);
+  S:=Package.Name;
+  If Length(S)>0 then
+    Delete(S,1,1);
+  AppendTitle(ContentElement,UTF8Decode(Format(SDocPackageClassHierarchy, [S])));
+  CreateClassHierarchyPage(True);
+end;
+
+procedure TNewHTMLWriter.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  
+    else if ASubPageIndex=ClassHierarchySubIndex then
+      CreatePackageClassHierarchy
+    end
+  else
+    begin
+    Element := AElement;
+    while (Element<>Nil) and (not (Element.ClassType.inheritsfrom(TPasModule))) do
+      Element := Element.Parent;
+    Module := TPasModule(Element);
+
+    if AElement.ClassType.inheritsfrom(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(TPasProcedureBase(AElement))
+    else if AElement.ClassType = TTopicELement then
+      CreateTopicPageBody(TTopicElement(AElement))
+    else if AElement.ClassType = TPasProperty then
+      CreateClassMemberPageBody(TPasProperty(AElement))
+    else
+      writeln('Unknown classtype: ',AElement.classtype.classname);
+  end;
+end;
+
+procedure TNewHTMLWriter.CreateIndexPage(L : TStringList);
+Var
+  Lists  : Array['A'..'Z'] of 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 := CreateEl(ContentElement,'div','columns');
+  for C:='A' to 'Z' do
+    If (Lists[C]<>Nil) then
+      begin
+      El:=CreateEl(TableEl,'div','column is-1');
+      EL:=CreateLink(El,UTF8Decode('#SECTION'+C));
+      EL['class']:='button is-link';
+      AppendText(EL,UTF8Decode(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
+      AppendText(CreateH2(ContentElement),UTF8Decode(C));
+      CreateAnchor(El,UTF8Decode('SECTION'+C));
+      TableEl := CreateTable(ContentElement);
+      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 TNewHTMLWriter.CreatePackageIndex;
+
+Var
+  L : TStringList;
+  I : Integer;
+  M : TPasModule;
+  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(ContentElement,UTF8Decode(Format(SDocPackageIndex, [S])));
+    CreateIndexPage(L);
+  Finally
+    L.Free;
+  end;
+end;
+
+procedure TNewHTMLWriter.CreatePackagePageBody;
+
+var
+  DocNode: TDocNode;
+  lSection : THTMLElement;
+  TableEl, TREl: TDOMElement;
+  i: Integer;
+  ThisModule: TPasModule;
+  L : TStringList;
+
+begin
+  AppendMenuBar(0);
+  lSection:=CreateSection(ContentElement);
+  AppendTitle(lSection,Format(SDocPackageTitle, [Copy(Package.Name, 2, 256)]));
+  AppendShortDescr(CreatePara(lSection), Package);
+  AppendText(CreateH2(lSection), UTF8Encode(SDocUnits));
+  TableEl := CreateTable(lSection);
+  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, ContentElement, DocNode.Descr, UTF8Decode(SDocDescription));
+    CreateTopicLinks(ContentElement, DocNode,Package);
+    end;
+end;
+
+function TNewHTMLWriter.CreateListColumns(aParent : THTMLElement) : THTMLElement;
+begin
+  Result:=CreateEl(aParent,'div','columns list');
+end;
+
+function TNewHTMLWriter.CreateListColumn1(aParent : THTMLElement) : THTMLElement;
+begin
+  Result:=CreateEl(aParent,'div','column is-2 list');
+  Result['style']:='overflow:hidden; text-overflow: ellipsis;';
+end;
+
+function TNewHTMLWriter.CreateListColumn2(aParent : THTMLElement) : THTMLElement;
+begin
+  Result:=CreateEl(aParent,'div','column is-10 list');
+end;
+
+
+procedure TNewHTMLWriter.CreateTopicLinks (aParent : THTMLElement; Node: TDocNode; PasElement: TPasElement) ;
+
+var
+  DocNode: TDocNode;
+  lSection,lColumns,lColumn: THTMLElement;
+  HaveTopics : Boolean;
+  ThisTopic: TPasElement;
+
+begin
+  DocNode:=Node.FirstChild;
+  HaveTopics:=False;
+  While Assigned(DocNode) and not HaveTopics do
+    begin
+    HaveTopics:=DocNode.TopicNode;
+    DocNode:=DocNode.NextSibling;
+    end;
+  if not HaveTopics then
+    exit;
+  lSection:=CreateSection(aParent);
+  AppendText(CreateH2(lSection), UTF8Decode(SDocRelatedTopics));
+  DocNode:=Node.FirstChild;
+  While Assigned(DocNode) do
+    begin
+    if DocNode.TopicNode then
+      begin
+      lColumns:=CreateListColumns(lSection);
+      lColumn:=CreateListColumn1(lColumns);
+      ThisTopic:=FindTopicElement(DocNode);
+      if Assigned(ThisTopic) then
+        AppendHyperlink(lColumn, ThisTopic);
+      lColumn:=CreateListColumn2(lColumns);
+      if Assigned(ThisTopic) then
+        AppendShortDescrCell(lColumn, ThisTopic);
+      end;
+    DocNode:=DocNode.NextSibling;
+    end;
+end;
+
+function TNewHTMLWriter.GetElementCode(aElement: TPasElement; aSparse : boolean; aFlags : TElementFlags =  []): String;
+var
+  W : TPasWriter;
+  S : TStringStream;
+  Vis : TPasMemberVisibilities;
+begin
+  Vis:=[];
+  if Engine.HidePrivate then
+    Vis:=Vis+[visPrivate,visStrictPrivate];
+  if Engine.HideProtected then
+    Vis:=Vis+[visProtected,visStrictProtected];
+  W:=nil;
+  S:=TStringStream.Create('');
+  try
+    W:=TPasWriter.Create(S);
+    if aSparse then
+      W.Options:=W.Options+[woSparse];
+    W.SkipVisibilities:=Vis;
+    W.WriteElement(aElement,[efSkipSection]+aFlags);
+    Result:=S.DataString;
+  finally
+    S.Free;
+    W.Free;
+  end;
+
+end;
+
+procedure TNewHTMLWriter.CreateModuleIndexPage(AModule: TPasModule);
+
+Var
+  L : TStringList;
+  lSection: THTMLElement;
+begin
+  L:=TStringList.Create;
+  try
+    AddModuleIdentifiers(AModule,L);
+    AppendMenuBar(IndexSubIndex);
+    lSection:=CreateSection(ContentElement);
+    AppendTitle(lSection,Format(SDocModuleIndex, [AModule.Name]));
+    PushContentElement(lSection);
+    CreateIndexPage(L);
+    PopContentElement;
+  Finally
+    L.Free;
+  end;  
+end;
+
+procedure TNewHTMLWriter.CreateModuleMainPage(aModule : TPasModule);
+
+var
+  lSection,TableEl, TREl, TDEl, CodeEl: TDOMElement;
+  i: Integer;
+  UnitRef: TPasType;
+  DocNode: TDocNode;
+
+begin
+  AppendMenuBar(0);
+  lSection:=CreateSection(ContentElement);
+  AppendTitle(lSection,Format(SDocUnitTitle, [AModule.Name]),AModule.Hints);
+  AppendShortDescr(CreatePara(lSection), AModule);
+
+  if AModule.InterfaceSection.UsesList.Count > 0 then
+  begin
+    TableEl := CreateTable(lSection);
+    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, ContentElement, DocNode.Descr, UTF8Decode(SDocOverview));
+    ConvertNotes(AModule,DocNode.Notes);
+    CreateTopicLinks(ContentElement, DocNode,AModule);
+    end;
+end;
+
+
+procedure TNewHTMLWriter.CreateModuleSimpleSubpage(aModule: TPasModule; ASubpageIndex: Integer; const ATitle: DOMString; AList: TFPList);
+
+var
+  lSection, ColumnsEl, ColumnEl, CodeEl: TDOMElement;
+  i, j: Integer;
+  Decl: TPasElement;
+  SortedList: TFPList;
+  DocNode: TDocNode;
+  S : String;
+
+begin
+  AppendMenuBar(ASubpageIndex);
+  S:=UTF8Encode(ATitle);
+  lSection:=CreateSection(ContentElement);
+  AppendTitle(lSection,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;
+
+    for i := 0 to SortedList.Count - 1 do
+    begin
+      ColumnsEl := CreateEl(lSection,'div','columns');
+      Decl := TPasElement(SortedList[i]);
+      ColumnEl := CreateEl(ColumnsEl,'div','column is-one-fifth list');
+      ColumnEl['style']:='overflow:hidden; text-overflow: ellipsis;';
+      CodeEl := CreateCode(ColumnEl);
+      AppendHyperlink(CodeEl, Decl);
+      ColumnEl := CreateEl(ColumnsEl,'div','column is-four-fifths list');
+      AppendShortDescrCell(ColumnEl, Decl);
+    end;
+  finally
+    SortedList.Free;
+  end;
+end;
+
+procedure TNewHTMLWriter.CreateModuleResStringsPage(aModule : TPasModule);
+var
+  lsection,lColumns,lColumn: THTMLElement;
+  i: Integer;
+  Decl: TPasResString;
+
+begin
+  AppendMenuBar(ResstrSubindex);
+  lSection:=CreateSection(ContentElement);
+  AppendTitle(lSection,Format(SDocUnitTitle + ': %s', [AModule.Name, SDocResStrings]));
+
+  for i := 0 to AModule.InterfaceSection.ResStrings.Count - 1 do
+  begin
+    lColumns:=CreateListColumns(lSection);
+    Decl := TPasResString(AModule.InterfaceSection.ResStrings[i]);
+    lColumn:=CreateListColumn1(lColumns);
+    CreateEl(lColumn, 'a')['name'] := UTF8Decode(LowerCase(Decl.Name));
+    AppendText(lColumn, UTF8Decode(Decl.Name));
+    lColumn := CreateListColumn2(lColumns);
+    AppendText(lColumn, UTF8Decode(Decl.Expr.getDeclaration(true)));
+  end;
+end;
+
+
+procedure TNewHTMLWriter.CreateModulePageBody(AModule: TPasModule;
+  ASubpageIndex: Integer);
+
+begin
+  case ASubpageIndex of
+    0:
+      CreateModuleMainPage(aModule);
+    ResstrSubindex:
+      CreateModuleResStringsPage(aModule);
+    ConstsSubindex:
+      CreateModuleSimpleSubpage(aModule, ConstsSubindex,UTF8Decode(SDocConstants), AModule.InterfaceSection.Consts);
+    TypesSubindex:
+      CreateModuleSimpleSubpage(aModule, TypesSubindex,UTF8Decode(SDocTypes), AModule.InterfaceSection.Types);
+    ClassesSubindex:
+      CreateModuleSimpleSubpage(aModule, ClassesSubindex,UTF8Decode(SDocClasses), AModule.InterfaceSection.Classes);
+    ProcsSubindex:
+      CreateModuleSimpleSubpage(aModule, ProcsSubindex, UTF8Decode(SDocProceduresAndFunctions), AModule.InterfaceSection.Functions);
+    VarsSubindex:
+      CreateModuleSimpleSubpage(aModule, VarsSubindex,UTF8Decode(SDocVariables), AModule.InterfaceSection.Variables);
+    IndexSubIndex: 
+      CreateModuleIndexPage(AModule);
+  end;
+end;
+
+procedure TNewHTMLWriter.CreateConstPageBody(AConst: TPasConst);
+var
+  Section,CodeEl: THTMLElement;
+
+begin
+  AppendMenuBar(-1);
+  Section:=CreateSection(ContentElement);
+  AppendTitle(Section,AConst.Name,AConst.Hints);
+  AppendShortDescr(CreatePara(Section), AConst);
+  Section:=CreateSection(ContentElement);
+  AppendText(CreateH2(Section), UTF8Decode(SDocDeclaration));
+  AppendSourceRef(Section,AConst);
+  CodeEl := AppendCodeBlock(Section);
+  AppendPasSHFragment(CodeEl, GetElementCode(aConst,False),0);
+  FinishElementPage(AConst);
+end;
+
+procedure TNewHTMLWriter.AppendShortDescrCell(Parent: TDOMNode;  Element: TPasElement);
+
+var
+  ParaEl: TDOMElement absolute Parent;
+
+begin
+  if Assigned(Engine.FindShortDescr(Element)) then
+  begin
+    if Parent is TDOMElement then
+      ParaEl['class'] := ParaEl['class'] +' cmt';
+    AppendShortDescr(Parent, Element);
+  end;
+
+end;
+
+function TNewHTMLWriter.AppendCodeBlock(aParent : TDOMNode; const aLanguage : String = ''): THTMLElement;
+var
+  lLanguage : String;
+
+begin
+  Result:=CreateEl(aParent,'pre','code');
+  lLanguage:=aLanguage;
+  if lLanguage='' then
+    lLanguage:='pascal';
+  lLanguage:='code-'+lLanguage;
+  Result:=CreateEl(Result,UTF8Decode('code'),UTF8Decode(lLanguage));
+end;
+
+function TNewHTMLWriter.AppendHighlightedCode(aParent: TDOMNode; aCode: String; const aLanguage: String; aMap: TLinkIdentifierMap
+  ): THTMLElement;
+begin
+//  Writeln('Code:',aCode);
+  Result:=AppendCodeBlock(aParent,aLanguage);
+  AppendPasSHFragment(Result, aCode, 0, aMap);
+end;
+
+function TNewHTMLWriter.CreateCodeLines(aLines: array of string): string;
+var
+  i : integer;
+begin
+  Result:='';
+  if Length(aLines)=0 then
+    exit;
+  Result:=aLines[0];
+  For I:=1 to Length(aLines)-1 do
+    Result:=Result+sLineBreak+aLines[i];
+end;
+
+procedure TNewHTMLWriter.AppendAliasTypeDecl(aType: TPasAliasType);
+begin
+  AppendHighlightedCode(ContentElement,CreateCodeLines(['type','  '+aType.GetDeclaration(True)+';']));
+end;
+
+procedure TNewHTMLWriter.AppendTypeAliasTypeDecl(aType: TPasTypeAliasType);
+
+var
+  CodeEl : THTMLElement;
+begin
+  CodeEl:=AppendHighlightedCode(ContentElement,CreateCodeLines(['type','  '+aType.GetDeclaration(True)+';']));
+  AppendHyperlink(CodeEl, TPasTypeAliasType(AType).DestType);
+end;
+
+
+procedure TNewHTMLWriter.AppendClassOfTypeDecl(aType: TPasClassOfType);
+var
+  CodeEl : THTMLElement;
+
+begin
+  CodeEl:=AppendHighlightedCode(ContentElement,CreateCodeLines(['type','  '+aType.GetDeclaration(True)+';']));
+  if Assigned(TPasClassOfType(AType).DestType) then
+    AppendHyperlink(CodeEl, TPasClassOfType(AType).DestType);
+end;
+
+procedure TNewHTMLWriter.AppendEnumTypeDecl(aType: TPasEnumType);
+var
+  S : String;
+  i : integer;
+  lColumns,lColumn : THTMLElement;
+  EnumValue: TPasEnumValue;
+begin
+  S:=aType.GetDeclaration(true);
+  AppendHighlightedCode(ContentElement,CreateCodeLines(['type','  '+S]));
+  AppendText(CreateH3(ContentElement),'Values');
+  for i := 0 to AType.Values.Count - 1 do
+  begin
+    lColumns := CreateListColumns(ContentElement);
+    EnumValue := TPasEnumValue(AType.Values[i]);
+    lColumn := CreateListColumn1(lColumns);
+    AppendText(lColumn,EnumValue.Name);
+    lColumn:= CreateListColumn2(lColumns);
+    AppendShortDescrCell(lColumn, EnumValue);
+  end;
+end;
+
+procedure TNewHTMLWriter.AppendPointerTypeDecl(aType: TPasPointerType);
+var
+  CodeEl : THTMLElement;
+begin
+  CodeEl:=AppendHighlightedCode(ContentElement,CreateCodeLines(['type','  '+aType.GetDeclaration(True)+';']));
+  if Assigned(AType.DestType) then
+    AppendHyperlink(CodeEl, AType.DestType)
+end;
+
+procedure TNewHTMLWriter.AppendProcedureTypeDecl(aType: TPasProcedureType);
+
+begin
+  AppendHighlightedCode(ContentElement,CreateCodeLines(['type','  '+aType.GetDeclaration(True)+';']));
+//  AppendProcArgsSection(ContentElement, AType);
+end;
+
+
+procedure TNewHTMLWriter.AppendRecordTypeDecl(aType: TPasRecordType);
+var
+  lMap: TLinkIdentifierMap;
+  I : Integer;
+
+begin
+  lMap:=TLinkIdentifierMap.Create(Self);
+  try
+    for I:=0 to aType.Members.Count-1 do
+      lMap.AddLink(TPasElement(aType.Members[i]));
+    AppendHighlightedCode(ContentElement,GetElementCode(aType,True),'',lMap);
+  finally
+    lMap.Free;
+  end;
+end;
+
+
+procedure TNewHTMLWriter.AppendSetTypeDecl(aType: TPasSetType);
+var
+  dlEl,ddEl,dtEl,CodeEl : THTMLElement;
+  EnumType : TPasEnumType;
+  EnumValue : TPasEnumValue;
+  I : Integer;
+
+begin
+  CodeEl:=AppendHighlightedCode(ContentElement,CreateCodeLines(['type','  '+aType.GetDeclaration(True)+';']));
+  if AType.EnumType.ClassType = TPasEnumType then
+    begin
+    EnumType:=TPasEnumType(AType.EnumType);
+    AppendText(CreateH3(ContentElement),'Values');
+    dlEl := CreateEl(ContentElement,'div','columns list');
+    for i := 0 to EnumType.Values.Count - 1 do
+    begin
+      EnumValue := TPasEnumValue(EnumType.Values[i]);
+      dtEl := CreateEl(dlEl,'div','column is-2 list');
+      AppendText(dtEl,EnumValue.Name);
+      ddEl := CreateEl(dlEl,'div','column is-10 list');
+      AppendShortDescrCell(ddEl, EnumValue);
+    end;
+    end
+  else
+    begin
+    AppendHyperlink(CodeEl, TPasSetType(AType).EnumType);
+    AppendSym(CodeEl, ';');
+    end;
+end;
+
+procedure TNewHTMLWriter.AppendTypeDecl(AType: TPasType);
+
+begin
+  // Alias
+  if AType.ClassType = TPasAliasType then
+    AppendAliasTypeDecl(TPasAliasType(aType))
+  else if AType.ClassType = TPasClassOfType then
+    AppendClassOfTypeDecl(TPasClassOfType(AType))
+  else if AType.ClassType = TPasEnumType then
+    AppendEnumTypeDecl(TPasEnumType(AType))
+  else if AType.ClassType = TPasPointerType then
+    AppendPointerTypeDecl(TPasPointerType(aType))
+  else if AType.InheritsFrom(TPasProcedureType) then
+    AppendProcedureTypeDecl(TPasProcedureType(aType))
+  else if AType.ClassType = TPasRecordType then
+    AppendRecordTypeDecl(TPasRecordType(aType))
+ else if AType.ClassType = TPasSetType then
+   AppendSetTypeDecl(TPasSetType(aType))
+ else if AType.ClassType = TPasTypeAliasType then
+    AppendTypeAliasTypeDecl(TPasTypeAliasType(aType))
+ else
+  // Probably one of the simple types, which allowed in other places as wel...
+    AppendType(ContentElement, TPasType(AType));
+end;
+
+procedure TNewHTMLWriter.CreateTypePageBody(AType: TPasType);
+var
+  Section,CodeEl: THTMLElement;
+  DocNode: TDocNode;
+begin
+  AppendMenuBar(-1);
+  Section:=CreateSection(ContentElement);
+  AppendTitle(Section,AType.Name,AType.Hints);
+  AppendShortDescr(CreatePara(section), AType);
+  Section:=CreateSection(ContentElement);
+  AppendText(CreateH2(section), UTF8Decode(SDocDeclaration));
+  AppendSourceRef(Section,AType);
+  DocNode := Engine.FindDocNode(AType);
+  If Assigned(DocNode) and
+     Assigned(DocNode.Node) and
+     (Docnode.Node['opaque']='1') then
+    begin
+    CodeEl := AppendCodeBlock(Section);
+    AppendKw(CodeEl, 'type ');
+    AppendText(CodeEl, UTF8Decode(AType.Name));
+    AppendSym(CodeEl, ' = ');
+    AppendText(CodeEl,UTF8Decode(SDocOpaque))
+    end
+  else
+    begin
+    PushContentElement(Section);
+    try
+      AppendTypeDecl(AType);
+    finally
+      PopContentElement;
+    end;
+    end;
+  FinishElementPage(AType);
+end;
+
+
+procedure TNewHTMLWriter.AppendTitle(aParent: TDomElement; const AText: AnsiString; Hints: TPasMemberHints);
+
+begin
+  AppendTitle(AParent,UTF8Decode(aText),Hints);
+end;
+
+procedure TNewHTMLWriter.AppendTitle(aParent : TDomElement; const AText: DOMString; Hints : TPasMemberHints = []);
+
+Var
+  T : UnicodeString;
+
+begin
+  T:=AText;
+  if (Hints<>[]) then
+    T:=T+' ('+UTF8Decode(Engine.HintsToStr(Hints))+')';
+  AppendText(TitleElement, AText);
+  AppendText(CreateH1(aParent), T)
+end;
+
+procedure TNewHTMLWriter.AppendMemberListSection(aParent: THTMLELement; aClass: TPasClassType; aMemberType: TClassMemberType;
+  aDeclaredOnly: Boolean);
+var
+   LinkEl,lSection: THTMLELement;
+   LinkAll : Boolean;
+begin
+  if not HasMembersToShow(aClass,True,GetMemberFilter(aMemberType)) then
+    exit;
+  linkAll:=aDeclaredOnly and Assigned(aClass.AncestorType) and Not aClass.AncestorType.InheritsFrom(TPasUnresolvedTypeRef);
+  lSection:=CreateSection(ContentElement);
+  AppendText(CreateH2(lSection),UTF8Decode(GetMemberDocName(aMemberType)));
+  CreateAnchor(lsection,UTF8Decode(GetAnchorName(aMemberType)));
+  CreateClassMemberList(lSection,AClass,True,GetMemberFilter(aMemberType));
+  if LinkAll then
+    begin
+    LinkEl:=CreateLink(lSection,FixHtmlPath(ResolveLinkWithinPackage(AClass,GetMemberSubIndex(aMemberType))));
+    AppendText(LinkEl,SSeeAll+' '+GetMemberDocName(aMemberType));
+    end;
+end;
+
+procedure TNewHTMLWriter.AppendInheritanceTree(aParent : THTMLELement; aClass : TPasClassType);
+
+  procedure AppendInterfaceInfo(ACodeEl : TDomElement ; AThisClass: TPasClassType);
+  var
+    i:Integer;
+    ThisInterface:TPasClassType;
+  begin
+  if Assigned(AThisClass) and (AThisClass.Interfaces.count>0) then
+    begin
+      for i:=0 to AThisClass.interfaces.count-1 do
+        begin
+          ThisInterface:=TPasClassType(AThisClass.Interfaces[i]);
+          AppendText(ACodeEl,',');
+          AppendHyperlink(ACodeEl, ThisInterface);
+        end;
+    end;
+  end;
+
+var
+  TableEl, TREl, TDEl, CodeEl: TDOMElement;
+  ThisClass, PrevClass: TPasType;
+  ThisTreeNode: TPasElementNode;
+begin
+  TableEl := CreateTable(aParent);
+  // Process tree class information
+  // First tree class link is to This class
+  PrevClass:= nil;
+  ThisClass:=aClass;
+  ThisClass := AClass; ThisTreeNode := Nil;
+  if AClass.ObjKind = okInterface then
+    ThisTreeNode := TreeInterface.GetPasElNode(AClass)
+  else
+    ThisTreeNode := TreeClass.GetPasElNode(AClass);
+  Repeat
+    TREl := CreateTR(TableEl);
+    TDEl := CreateTD_vtop(TREl);
+    TDEl['align'] := 'center';
+    CodeEl := CreateCode(CreatePara(TDEl));
+
+
+    // Show class item
+    AppendHyperlink(CodeEl, ThisClass);
+    if Assigned(PrevClass) and (PrevClass Is TPasClassType)  then // Interfaces from prevClass
+      AppendInterfaceInfo(CodeEl, TPasClassType(PrevClass));
+    AppendShortDescrCell(TREl, ThisClass);
+
+    if Assigned(ThisTreeNode) then
+      if Assigned(ThisTreeNode.ParentNode) then
+        begin
+        TDEl := CreateTD(CreateTR(TableEl));
+        TDEl['align'] := 'center';
+        AppendText(TDEl, '|');
+        PrevClass:= ThisClass;
+        ThisClass := ThisTreeNode.ParentNode.Element;
+        ThisTreeNode := ThisTreeNode.ParentNode;
+        end
+      else
+        begin
+        ThisClass := nil;
+        ThisTreeNode:= nil;
+        PrevClass:= nil;
+        end
+   Until (ThisTreeNode=Nil);
+end;
+
+procedure TNewHTMLWriter.CreateClassMainPage(aClass : TPasClassType);
+
+var
+  lSection: THTMLElement;
+  ParaEl: TDOMElement;
+  lMap: TLinkIdentifierMap;
+  I : Integer;
+  DocNode: TDocNode;
+begin
+  // Menu bar
+  AppendMenuBar(-1);
+  // Title, short description & navs
+  lSection:=CreateSection(ContentElement);
+  AppendTitle(lSection,AClass.Name,AClass.Hints);
+  ParaEl := CreatePara(lSection);
+  AppendShortDescr(CreatePara(lSection), AClass);
+  ParaEl:=CreateEl(lSection,'div','tabs');;
+  ParaEl:=CreateEl(ParaEl,'ul');
+  if HasMembersToShow(aClass,True,@PropertyFilter) then
+    AppendText(CreateLink(ParaEl,'#properties'),UTF8Decode(SDocProperties));
+  if HasMembersToShow(aClass,True,@MethodFilter) then
+    AppendText(CreateLink(ParaEl,'#methods'),UTF8Decode(SDocMethods));
+  if HasMembersToShow(aClass,True,@EventFilter) then
+    AppendText(CreateLink(ParaEl,'#events'),UTF8Decode(SDocEvents));
+  // Declaration
+  lSection:=CreateSection(ContentElement);
+  AppendText(CreateH2(lSection), UTF8Decode(SDocDeclaration));
+  AppendSourceRef(lSection,AClass);
+  lMap:=TLinkIdentifierMap.Create(Self);
+  try
+    if assigned(aClass.AncestorType) then
+      lMap.AddLink(aClass.AncestorType);
+    if assigned(aClass.Interfaces) then
+      for I:=0 to AClass.Interfaces.Count-1 do
+        lMap.AddLink(TPasElement(aClass.Interfaces[i]));
+    for I:=0 to AClass.Members.Count-1 do
+      lMap.AddLink(TPasElement(AClass.Members[i]));
+    AppendHighlightedCode(lSection,GetElementCode(aClass,True),'',lMap);
+  finally
+    lMap.Free;
+  end;
+  // Inheritance
+  lSection:=CreateSection(ContentElement);
+  AppendText(CreateH2(lSection), UTF8Decode(SDocInheritance));
+  AppendInheritanceTree(lSection,aClass);
+  // Description
+  DocNode := Engine.FindDocNode(aClass);
+  If Assigned(DocNode) and Assigned(DocNode.Descr) then
+    begin
+    lSection:=CreateSection(ContentElement);
+    AppendDescrSection(aClass,lSection,DocNode.Descr,UTF8Decode(SDocDescription));
+    end;
+  // Properties, methods and events
+  AppendMemberListSection(ContentElement,aClass,cmtProperty,True);
+  AppendMemberListSection(ContentElement,aClass,cmtMethod,True);
+  AppendMemberListSection(ContentElement,aClass,cmtEvent,True);
+  AppendMemberListSection(ContentElement,aClass,cmtField,True);
+  // The rest
+  FinishElementPage(aClass,False);
+end;
+
+
+procedure TNewHTMLWriter.CreateClassMemberList(aParent : THTMLElement; AClass: TPasClassType; DeclaredOnly : Boolean; AFilter: TMemberFilter);
+
+  Function GetMemberHints(aMember : TPasElement) : String;
+  var
+    S : String;
+
+  begin
+    S:='';
+    case aMember.Visibility of
+      visPrivate,
+      visStrictPrivate:
+        S:='pv';
+      visProtected,
+      visStrictProtected:
+        S:='pt';
+      visPublished:
+        S:='pl';
+    else
+      //
+    end;
+    if (aMember.ClassType = TPasProperty) and
+       ((TPasProperty(aMember).WriteAccessorName) = '') then
+      begin
+      if S<>'' then
+        S:=S+',';
+      S:=S+'ro';
+      end;
+    Result:=S;
+  end;
+
+var
+  List: TFPList;
+  ThisClass: TPasClassType;
+  i, j: Integer;
+  Member: TPasElement;
+  DlEl,DtEl,DdEl: TDOMElement;
+  S : String;
+
+begin
+  List := TFPList.Create;
+  try
+    ThisClass := AClass;
+    while Assigned(ThisClass) do
+    begin
+      for i := 0 to ThisClass.Members.Count - 1 do
+      begin
+        Member := TPasElement(ThisClass.Members[i]);
+        if Engine.ShowElement(Member) 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 DeclaredOnly or (Assigned(ThisClass.AncestorType) and not (ThisClass.AncestorType.inheritsfrom(TPasClassType))) then
+        ThisClass:=Nil
+      else
+        ThisClass := TPasClassType(ThisClass.AncestorType);
+    end;
+
+    for i := 0 to List.Count - 1 do
+    begin
+      dlEl := CreateEl(aParent,'div','columns list');
+      Member := TPasElement(List[i]);
+      dTEl:= CreateEl(dlEl,'div','column list is-2');
+      AppendHyperlink(dtEl, Member);
+      ddEl:= CreateEl(dlEl,'div','column is-10 list');
+      S:=GetMemberHints(Member);
+      if S<>'' then
+        begin
+        S:='{'+S+'}';
+        AppendNbSp(dtEl,1);
+        AppendText(CreateEl(dtEl,'span','cmt'),S);
+        end;
+      AppendShortDescr(DdEl,Member);
+    end;
+  finally
+    List.Free;
+  end;
+end;
+
+procedure TNewHTMLWriter.CreateClassSortedSubpage(AClass: TPasClassType; aType: TClassMemberType);
+
+var
+  lSection,TitleEl, linkEl: THTMLElement;
+
+begin
+  AppendMenuBar(-1);
+  lSection:=CreateSection(ContentElement);
+  TitleEl:=CreateH1(lSection);
+  AppendText(TitleEl, GetMemberOverviewTitle(aType));
+  linkEl:=CreateLink(TitleEl,ResolveLinkWithinPackage(aClass,0));
+  AppendText(LinkEl, aClass.Name);
+  AppendMemberListSection(lSection,aClass,aType,False);
+end;
+
+procedure TNewHTMLWriter.CreateClassInheritedSubpage(AClass: TPasClassType; aType: TClassMemberType);
+
+var
+  lSection,TitleEl, linkEl: THTMLElement;
+begin
+  AppendMenuBar(-1);
+  lSection:=CreateSection(ContentElement);
+  TitleEl:=CreateH1(lSection);
+  AppendText(TitleEl, GetMemberOverviewTitle(aType));
+  linkEl:=CreateLink(TitleEl,ResolveLinkWithinPackage(aClass,0));
+  AppendText(LinkEl, aClass.Name);
+  AppendMemberListSection(lSection,aClass,aType,False);
+end;
+
+
+procedure TNewHTMLWriter.CreateClassPageBody(AClass: TPasClassType; ASubpageIndex: Integer);
+
+begin
+  case ASubpageIndex of
+    0:
+      CreateClassMainPage(aClass);
+    PropertiesByInheritanceSubindex:
+      CreateClassInheritedSubpage(aClass,cmtProperty);
+    PropertiesByNameSubindex:
+      CreateClassSortedSubpage(aClass,cmtProperty);
+    MethodsByInheritanceSubindex:
+      CreateClassInheritedSubpage(aClass,cmtMethod);
+    MethodsByNameSubindex:
+      CreateClassSortedSubpage(aClass,cmtMethod);
+    EventsByInheritanceSubindex:
+      CreateClassInheritedSubpage(aClass,cmtEvent);
+    EventsByNameSubindex:
+      CreateClassSortedSubpage(aClass,cmtEvent);
+    FieldsByNameSubindex:
+      CreateClassSortedSubpage(aClass,cmtField);
+  end;
+end;
+
+
+function TNewHTMLWriter.GetVarDef(aElement : TPasVariable; aPrefixParent : Boolean) : string;
+
+begin
+  Result:=GetElementCode(aElement,False);
+end;
+
+procedure TNewHTMLWriter.CreateClassMemberPageBody(AElement: TPasElement);
+var
+  CodeBlock : TDOMElement;
+
+  procedure CreateVarPage(Element: TPasVariable);
+
+  var
+    S : String;
+
+  begin
+    S:=GetElementCode(Element,False);
+    AppendCodeBlock(ContentElement,S);
+  end;
+
+  procedure CreateTypePage(Element: TPasType);
+  var
+    S : String;
+
+  begin
+    S:=GetElementCode(Element,False);
+    AppendCodeBlock(ContentElement,S);
+  end;
+
+  procedure CreateConstPage(Element: TPasConst);
+  var
+    S : String;
+
+  begin
+    S:=GetElementCode(Element,False);
+    AppendCodeBlock(ContentElement,S);
+  end;
+
+  procedure CreatePropertyPage(Element: TPasProperty);
+  var
+    S : String;
+  begin
+    S:=GetElementCode(Element,True);
+    AppendCodeBlock(ContentElement,S);
+  end;
+
+var
+  s: String;
+
+begin
+  AppendMenuBar(-1);
+  AppendTitle(ContentElement,AElement.FullName,AElement.Hints);
+  AppendShortDescr(CreatePara(ContentElement), AElement);
+
+  AppendText(CreateH2(ContentElement), SDocDeclaration);
+  AppendSourceRef(ContentElement,AElement);
+
+  CodeBlock := AppendCodeBlock(ContentElement);
+  if (Assigned(aElement.Parent) and aElement.Parent.InheritsFrom(TPasType)) and (AElement.Visibility<>visDefault) then
+    begin
+    s:=VisibilityNames[AElement.Visibility];
+    AppendKw(CodeBlock, s);
+    AppendText(CodeBlock, ' ');
+    end;
+  if AElement is TPasProperty then
+    CreatePropertyPage(TPasProperty(AElement))
+  else if AElement is TPasConst then
+    CreateConstPage(TPasConst(AElement))
+  else if (AElement is TPasVariable) then
+    CreateVarPage(TPasVariable(AElement))
+  else if AElement is TPasProcedureBase then
+    AppendProcDecl(CodeBlock,TPasProcedureBase(AElement))
+  else if AElement is TPasType then
+    CreateTypePage(TPasType(AElement))
+  else
+    AppendText(CreateWarning(ContentElement), '<' + AElement.ClassName + '>');
+
+  FinishElementPage(AElement);
+end;
+
+procedure TNewHTMLWriter.CreateVarPageBody(AVar: TPasVariable);
+var
+  CodeEl: TDOMElement;
+begin
+  AppendMenuBar(-1);
+  AppendTitle(ContentElement,AVar.FullName,AVar.Hints);
+  AppendShortDescr(CreatePara(ContentElement), AVar);
+  AppendText(CreateH2(ContentElement), SDocDeclaration);
+  AppendSourceRef(ContentElement,AVar);
+  CodeEl := AppendCodeBlock(ContentElement);
+  AppendPasSHFragment(CodeEl, GetElementCode(aVar,False),0);
+  FinishElementPage(AVar);
+end;
+
+procedure TNewHTMLWriter.CreateProcPageBody(AProc: TPasProcedureBase);
+
+var
+  lSection,CodeEl: TDOMElement;
+
+begin
+  AppendMenuBar(-1);
+  lSection:=CreateSection(ContentElement);
+  AppendTitle(lSection,AProc.Name,AProc.Hints);
+  AppendShortDescr(CreatePara(lSection), AProc);
+  lSection:=CreateSection(ContentElement);
+  AppendText(CreateH2(lSection), SDocDeclaration);
+  AppendSourceRef(lSection,AProc);
+  CodeEl := CreateCode(lSection);
+  AppendProcDecl(CodeEl, AProc);
+  FinishElementPage(AProc);
+end;
+
+function TNewHTMLWriter.InterPretOption ( const Cmd, Arg: String ) : boolean;
+
+  Function ReadFile(aFileName : string) : TstringStream;
+
+  begin
+    aFileName:= SetDirSeparators(aFileName);
+    try
+      if copy(aFileName,1,1)<>'@' then
+        Result:=TStringStream.Create(aFileName)
+      else
+        begin
+        Delete(aFileName,1,1);
+        Result:=TStringStream.Create('');
+        Result.LoadFromFile(aFileName);
+        Result.Position:=0;
+        end;
+    except
+      Result.Free;
+      Raise;
+    end;
+  end;
+
+begin
+  Result:=True;
+  if Cmd = '--html-search' then
+    SearchPage := Arg
+  else if Cmd = '--footer' then
+    FFooterHTML := ReadFile(Arg)
+  else if Cmd = '--header' then
+    FHeaderHTML := ReadFile(Arg)
+  else if Cmd = '--navigator' then
+    FNavigatorHTML := ReadFile(Arg)
+  else if Cmd = '--charset' then
+    CharSet := Arg
+  else if Cmd = '--index-colcount' then
+    IndexColCount := StrToIntDef(Arg,IndexColCount)
+  else if Cmd = '--image-url' then
+    BaseImageURL  := Arg
+  else if Cmd = '--css-file' then
+    FCSSFile := arg
+  else if Cmd = '--footer-date' then
+    begin
+    FIncludeDateInFooter:=True;
+    FDateFormat:=Arg;
+    end
+  else if Cmd = '--disable-menu-brackets' then
+    FUseMenuBrackets:=False
+  else
+    Result:=inherited InterPretOption(Cmd, Arg);
+end;
+
+
+class procedure TNewHTMLWriter.Usage(List: TStrings);
+begin
+  List.add('--header=file');
+  List.Add(SHTMLUsageHeader);
+  List.add('--footer=file');
+  List.Add(SHTMLUsageFooter);
+  List.add('--navigator=file');
+  List.Add(SHTMLUsageNavigator);
+  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);
+  inherited Usage(List);
+end;
+
+class procedure TNewHTMLWriter.SplitImport(var AFilename, ALinkPrefix: String);
+var
+  i: integer;
+begin
+  i := Pos(',', AFilename);
+  if i > 0 then
+    begin  //split into filename and prefix
+    ALinkPrefix := Copy(AFilename,i+1,Length(AFilename));
+    SetLength(AFilename, i-1);
+    end
+  else if ALinkPrefix = '' then
+    begin  //synthesize outdir\pgk.xct, ..\pkg
+    ALinkPrefix := '../' + ChangeFileExt(ExtractFileName(AFilename), '');
+    AFilename := ChangeFileExt(AFilename, '.xct');
+    end;
+end;
+
+class function TNewHTMLWriter.FileNameExtension: String;
+begin
+  result:='';
+end;
+
+// private methods
+
+
+procedure TNewHTMLWriter.SetOnTest(const AValue: TNotifyEvent);
+begin
+  if FOnTest=AValue then exit;
+    FOnTest:=AValue;
+end;
+
+
+initialization
+  // Do not localize.
+  RegisterWriter(TNewHTMLWriter,'newhtml','HTML output using fpdocs.css stylesheet.');
+
+finalization
+  UnRegisterWriter('newhtml');
+
+end.

+ 6 - 2
utils/fpdoc/fpdoc.lpi

@@ -40,7 +40,7 @@
         <PackageName Value="FCL"/>
       </Item1>
     </RequiredPackages>
-    <Units Count="21">
+    <Units Count="22">
       <Unit0>
         <Filename Value="fpdoc.pp"/>
         <IsPartOfProject Value="True"/>
@@ -132,6 +132,10 @@
         <Filename Value="fpdocstrs.pp"/>
         <IsPartOfProject Value="True"/>
       </Unit20>
+      <Unit21>
+        <Filename Value="dw_newhtml.pp"/>
+        <IsPartOfProject Value="True"/>
+      </Unit21>
     </Units>
   </ProjectOptions>
   <CompilerOptions>
@@ -141,7 +145,7 @@
     </Target>
     <SearchPaths>
       <IncludeFiles Value="$(ProjOutDir)"/>
-      <OtherUnitFiles Value="../../packages/fcl-passrc/src"/>
+      <OtherUnitFiles Value=".;../../packages/fcl-passrc/src"/>
       <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
     <Linking>

+ 1 - 0
utils/fpdoc/fpdoc.pp

@@ -32,6 +32,7 @@ uses
   dw_XML,    // XML writer
   dw_dxml,   // Delphi XML doc.
   dw_HTML,   // HTML writer
+  dw_newhtml,   // HTML writer
   dw_chm,    // CHM Writer
   dw_markdown, // Markdown writer
   dw_ipflin, // IPF writer (new linear output)

+ 2 - 0
utils/fpdoc/fpdocstrs.pp

@@ -62,6 +62,7 @@ resourcestring
   SDocProperties             = 'Properties';
   SDocMethods                = 'Methods';
   SDocEvents                 = 'Events';
+  SDocFields                 = 'Fieldss';
   SDocByName                 = 'by Name';
   SDocByInheritance          = 'By inheritance';
   SDocValue                  = 'Value';
@@ -73,6 +74,7 @@ resourcestring
   SDocVisibility             = 'Visibility';
   SDocOpaque                 = 'Opaque type';
   SDocDateGenerated          = 'Documentation generated on: %s';
+  SSeeAll                    = 'See all';
   // The next line requires leading/trailing space due to XML comment layout:
   SDocGeneratedByComment     = ' Generated using FPDoc - (c) 2000-2021 FPC contributors and Sebastian Guenther, [email protected] ';
   SDocNotes                  = 'Notes';

+ 1 - 0
utils/fpdoc/fpmake.pp

@@ -109,6 +109,7 @@ begin
     if Bin2Obj <> '' then
       begin
       P.Commands.AddCommand(Bin2Obj,'-o $(DEST) -c DefaultCSS $(SOURCE)','css.inc','fpdoc.css');
+      P.Commands.AddCommand(Bin2Obj,'-o $(DEST) -c DefaultNewCSS $(SOURCE)','newcss.inc','fpdocs.css');
       P.Commands.AddCommand(Bin2Obj,'-o $(DEST) -c PlusImageData $(SOURCE)','plusimage.inc','images/plus.png');
       P.Commands.AddCommand(Bin2Obj,'-o $(DEST) -c MinusImageData $(SOURCE)','minusimage.inc','images/minus.png');
       end;