diff --git a/components/wiki/lazwiki/wiki2xhtmlconvert.pas b/components/wiki/lazwiki/wiki2xhtmlconvert.pas index b8b8ef35f1..9905150965 100644 --- a/components/wiki/lazwiki/wiki2xhtmlconvert.pas +++ b/components/wiki/lazwiki/wiki2xhtmlconvert.pas @@ -29,17 +29,29 @@ uses laz2_XMLWrite, LazUTF8, BasicCodeTools, KeywordFuncLists, CodeToolsStructs; type + TW2XHTMLStackItem = record + Node: TDOMElement; + Token: TWPTokenType; + Txt: string; + end; + PW2XHTMLStackItem = ^TW2XHTMLStackItem; { TW2XHTMLPage } TW2XHTMLPage = class(TW2FormatPage) - private - BodyNode: TDOMElement; - CurNode: TDOMElement; // current xhtml node + protected + BodyDOMNode: TDOMElement; + CurDOMNode: TDOMElement; // current xhtml node SectionLevel: integer; + Stack: PW2XHTMLStackItem; + StackPtr: integer; + StackCapacity: integer; + procedure Push(Node: TDOMElement; Token: TWPTokenType); + procedure Pop; public XHTML: TXMLDocument; Filename: string; + constructor Create(TheConverter: TWiki2FormatConverter); override; procedure ClearConversion; override; end; @@ -172,7 +184,7 @@ var Node.SetAttribute('src', Filename); if Caption<>'' then Node.SetAttribute('alt', Caption); - Page.CurNode.AppendChild(Node); + Page.CurDOMNode.AppendChild(Node); exit(true); end; if WarnURL(LinkToken.Link) then @@ -238,7 +250,7 @@ begin Node.SetAttribute('href', URL); if Caption<>'' then Node.AppendChild(doc.CreateTextNode(Caption)); - Page.CurNode.AppendChild(Node); + Page.CurDOMNode.AppendChild(Node); end else if Caption<>'' then begin InsertText(LinkToken, Caption); Result:=true; @@ -286,23 +298,23 @@ begin CSSNode.SetAttribute('rel','stylesheet'); end; // - Page.BodyNode:=doc.CreateElement('body'); - RootNode.AppendChild(Page.BodyNode); + Page.BodyDOMNode:=doc.CreateElement('body'); + RootNode.AppendChild(Page.BodyDOMNode); // title

Node:=doc.CreateElement('h1'); - Page.BodyNode.AppendChild(Node); + Page.BodyDOMNode.AppendChild(Node); Node.SetAttribute('class','firstHeading'); Node.AppendChild(doc.CreateTextNode(Page.WikiPage.Title)); try Page.SectionLevel:=0; - Page.CurNode:=Page.BodyNode; + Page.CurDOMNode:=Page.BodyDOMNode; Page.WikiPage.Parse(@OnWikiToken,Page); if LinkToBaseDocument<>'' then begin // add LinkToBaseDocument
Node:=doc.CreateElement('a'); - Page.BodyNode.AppendChild(Node); + Page.BodyDOMNode.AppendChild(Node); Link:=Page.WikiPage.BaseURL; if (Link<>'') and (Link[length(Link)]<>'/') then Link+='/'; @@ -310,11 +322,11 @@ begin Node.SetAttribute('href',Link); Node.AppendChild(doc.CreateTextNode(LinkToBaseDocument)); Node:=doc.CreateElement('br'); - Page.BodyNode.AppendChild(Node); + Page.BodyDOMNode.AppendChild(Node); end; finally - Page.BodyNode:=nil; - Page.CurNode:=nil; + Page.BodyDOMNode:=nil; + Page.CurDOMNode:=nil; end; end; @@ -326,7 +338,7 @@ var procedure NodeNotOpen; begin raise Exception.Create('TWiki2XHTMLConverter.OnWikiToken can not close:' - +' Token='+dbgs(Token.Token)+' '+DbgSName(Token)+' CurNode='+Page.CurNode.TagName); + +' Token='+dbgs(Token.Token)+' '+DbgSName(Token)+' CurNode='+Page.CurDOMNode.TagName); end; procedure MissingNodeName; @@ -344,6 +356,7 @@ var NameValueToken: TWPNameValueToken; CurName: String; CurValue: String; + HeaderTxt: DOMString; begin Page:=TW2XHTMLPage(Token.UserData); W:=Page.WikiPage; @@ -360,16 +373,16 @@ begin wptLineBreak: begin // only append a br if there is something in front - if Page.CurNode.FirstChild<>nil then - Page.CurNode.AppendChild(doc.CreateElement('br')); + if Page.CurDOMNode.FirstChild<>nil then + Page.CurDOMNode.AppendChild(doc.CreateElement('br')); exit; end; wptHorizontalRow: begin // only append a hr if there is something in front - if Page.CurNode.FirstChild<>nil then - Page.CurNode.AppendChild(doc.CreateElement('hr')); + if Page.CurDOMNode.FirstChild<>nil then + Page.CurDOMNode.AppendChild(doc.CreateElement('hr')); exit; end; @@ -378,7 +391,7 @@ begin wptPre, wptCenter, wptBulletList, wptNumberedList, wptDefinitionList, wptTable, wptTableRow, wptTableCell, wptTableHeadCell, - wptSection, wptHeader: + wptSection: begin // simple range NodeClass:=''; @@ -421,51 +434,78 @@ begin else if Token.Range=wprClose then dec(Page.SectionLevel); end; - wptHeader: - begin - if Page.SectionLevel<=1 then - NodeName:='h1' - else if Page.SectionLevel<=MaxH then - NodeName:='h'+IntToStr(Page.SectionLevel) - else if Page.SectionLevel>MaxH then begin - NodeName:='h'+IntToStr(MaxH); - NodeClass:='subTitle'; - end; - end; end; if NodeName='' then MissingNodeName; if Token.Range=wprOpen then begin Node:=doc.CreateElement(NodeName); - Page.CurNode.AppendChild(Node); + Page.CurDOMNode.AppendChild(Node); if NodeClass<>'' then Node.SetAttribute('class',NodeClass); - Page.CurNode:=Node; + Page.CurDOMNode:=Node; exit; end else if Token.Range=wprClose then begin - if Page.CurNode.TagName<>NodeName then + if Page.CurDOMNode.TagName<>NodeName then NodeNotOpen; - Page.CurNode:=Page.CurNode.ParentNode as TDOMElement; + Page.CurDOMNode:=Page.CurDOMNode.ParentNode as TDOMElement; + exit; + end; + end; + + wptHeader: + begin + if Page.SectionLevel<=1 then + NodeName:='h1' + else if Page.SectionLevel<=MaxH then + NodeName:='h'+IntToStr(Page.SectionLevel) + else if Page.SectionLevel>MaxH then begin + NodeName:='h'+IntToStr(MaxH); + NodeClass:='subTitle'; + end; + if Token.Range=wprOpen then begin + // open header + Node:=doc.CreateElement(NodeName); + Page.CurDOMNode.AppendChild(Node); + if NodeClass<>'' then + Node.SetAttribute('class',NodeClass); + Page.CurDOMNode:=Node; + Page.Push(Node,wptHeader); + exit; + end else if Token.Range=wprClose then begin + // close header + if Page.CurDOMNode.TagName<>NodeName then + NodeNotOpen; + HeaderTxt:=''; + if Page.CurDOMNode.FirstChild is TDOMText then + HeaderTxt:=TDOMText(Page.CurDOMNode.FirstChild).Data; + if HeaderTxt<>'' then begin + // add anchor + Node:=doc.CreateElement('a'); + Node.SetAttribute('name',WikiHeaderToLink(HeaderTxt)); + Page.CurDOMNode.ParentNode.InsertBefore(Node,Page.CurDOMNode); + end; + Page.CurDOMNode:=Page.CurDOMNode.ParentNode as TDOMElement; + Page.Pop; exit; end; end; wptListItem: if Token.Range=wprOpen then begin - if Page.CurNode.TagName='dl' then + if Page.CurDOMNode.TagName='dl' then NodeName:='dd' else NodeName:='li'; Node:=doc.CreateElement(NodeName); - Page.CurNode.AppendChild(Node); - Page.CurNode:=Node; + Page.CurDOMNode.AppendChild(Node); + Page.CurDOMNode:=Node; exit; end else if Token.Range=wprClose then begin - if (Page.CurNode.TagName<>'dd') - and (Page.CurNode.TagName<>'li') then + if (Page.CurDOMNode.TagName<>'dd') + and (Page.CurDOMNode.TagName<>'li') then NodeNotOpen; - Page.CurNode:=Page.CurNode.ParentNode as TDOMElement; + Page.CurDOMNode:=Page.CurDOMNode.ParentNode as TDOMElement; exit; end; @@ -474,7 +514,7 @@ begin NameValueToken:=TWPNameValueToken(Token); CurName:=copy(W.Src,NameValueToken.NameStartPos,NameValueToken.NameEndPos-NameValueToken.NameStartPos); CurValue:=copy(W.Src,NameValueToken.ValueStartPos,NameValueToken.ValueEndPos-NameValueToken.ValueStartPos); - Page.CurNode.SetAttribute(CurName,CurValue); + Page.CurDOMNode.SetAttribute(CurName,CurValue); exit; end; @@ -486,7 +526,7 @@ begin Node:=doc.CreateElement('span'); if CurName<>'' then Node.SetAttribute('class',CurName); - Page.CurNode.AppendChild(Node); + Page.CurDOMNode.AppendChild(Node); if CurValue<>'' then Node.AppendChild(doc.CreateTextNode(CurValue)); exit; @@ -541,22 +581,23 @@ procedure TWiki2XHTMLConverter.InsertText(Token: TWPToken; Txt: string); var Page: TW2XHTMLPage; doc: TXMLDocument; + CurNode: TDOMElement; begin Page:=TW2XHTMLPage(Token.UserData); doc:=Page.XHTML; //Log(['TWiki2XHTMLConverter.InsertText Txt="'+dbgstr(Txt)+'"']); if Txt='' then exit; - if Page.CurNode.TagName<>'pre' then begin + CurNode:=Page.CurDOMNode; + if CurNode.TagName<>'pre' then begin if UTF8Trim(Txt)='' then begin // skip empty text exit; end; - if Page.CurNode.FirstChild=nil then + if CurNode.FirstChild=nil then Txt:=UTF8Trim(Txt,[u8tKeepEnd]); end; - //Log('TWiki2XHTMLConverter.InsertText Node="'+Page.CurNode.TagName+'" Text="'+Txt+'"'); - Txt:=EncodeLesserAndGreaterThan(Txt); - Page.CurNode.AppendChild(doc.CreateTextNode(Txt)); + //Log('TWiki2XHTMLConverter.InsertText Node="'+Page.CurDOMNode.TagName+'" Text="'+Txt+'"'); + CurNode.AppendChild(doc.CreateTextNode(Txt)); end; procedure TWiki2XHTMLConverter.InsertCode(Token: TWPNameValueToken); @@ -635,7 +676,7 @@ begin CurName:='pascal'; if CurName<>'' then CodeNode.SetAttribute('class',CurName); - Page.CurNode.AppendChild(CodeNode); + Page.CurDOMNode.AppendChild(CodeNode); if CurValue<>'' then begin if (CurName='pascal') then begin p:=PChar(CurValue); @@ -792,11 +833,44 @@ end; { TW2XHTMLPage } +procedure TW2XHTMLPage.Push(Node: TDOMElement; Token: TWPTokenType); +var + s: PW2XHTMLStackItem; + OldCapacity: Integer; +begin + inc(StackPtr); + if StackPtr=StackCapacity then begin + OldCapacity:=StackCapacity; + StackCapacity:=StackCapacity*2+8; + ReAllocMem(Stack,SizeOf(TW2XHTMLStackItem)*StackCapacity); + FillByte(Stack[StackPtr],SizeOf(TW2XHTMLStackItem)*(StackCapacity-OldCapacity),0); + end; + s:=@Stack[StackPtr]; + s^.Node:=Node; + s^.Token:=Token; +end; + +procedure TW2XHTMLPage.Pop; +begin + if StackPtr<0 then + raise Exception.Create('bug'); // push and pop are not balanced + dec(StackPtr); +end; + +constructor TW2XHTMLPage.Create(TheConverter: TWiki2FormatConverter); +begin + inherited Create(TheConverter); + StackPtr:=-1; +end; + procedure TW2XHTMLPage.ClearConversion; begin - BodyNode:=nil; - CurNode:=nil; + BodyDOMNode:=nil; + CurDOMNode:=nil; SectionLevel:=0; + ReAllocMem(Stack,0); + StackPtr:=-1; + StackCapacity:=0; FreeAndNil(XHTML); end; diff --git a/components/wiki/test/wikihelpmanager.pas b/components/wiki/test/wikihelpmanager.pas index b971adb414..fb9b7a88a5 100644 --- a/components/wiki/test/wikihelpmanager.pas +++ b/components/wiki/test/wikihelpmanager.pas @@ -126,8 +126,8 @@ type TW2HelpPage = class(TW2HTMLPage) public - TextRoot: TWHTextNode; - CurNode: TWHTextNode; + WHRoot: TWHTextNode; + CurWHNode: TWHTextNode; Score: single; destructor Destroy; override; function GetScore(Query: TWikiHelpQuery): TWHScore; @@ -655,7 +655,7 @@ end; destructor TW2HelpPage.Destroy; begin - FreeAndNil(TextRoot); + FreeAndNil(WHRoot); inherited Destroy; end; @@ -733,7 +733,7 @@ procedure TW2HelpPage.GetFit(Query: TWikiHelpQuery; Fit: PWHPhrasePageFit); begin CheckTxt(WikiPage.Title,whfcPageTitle); - Traverse(TextRoot); + Traverse(WHRoot); end; function TW2HelpPage.GetNodeHighestScore(Query: TWikiHelpQuery): TWHTextNode; @@ -789,7 +789,7 @@ var begin Result:=nil; NodeScore:=0; - Traverse(TextRoot,Result,NodeScore); + Traverse(WHRoot,Result,NodeScore); end; { TWHTextNode } @@ -992,8 +992,8 @@ var begin Page:=TW2HelpPage(Token.UserData); W:=Page.WikiPage; - CurNode:=Page.CurNode; - if CurNode=nil then CurNode:=Page.TextRoot; + CurNode:=Page.CurWHNode; + if CurNode=nil then CurNode:=Page.WHRoot; case Token.Token of wptText: if Token is TWPTextToken then begin @@ -1015,10 +1015,10 @@ begin NodeType:=whnHeader else NodeType:=whnTxt; - Page.CurNode:=TWHTextNode.Create(NodeType,CurNode); + Page.CurWHNode:=TWHTextNode.Create(NodeType,CurNode); exit; end else if Token.Range=wprClose then begin - Page.CurNode:=CurNode.Parent; + Page.CurWHNode:=CurNode.Parent; exit; end; @@ -1111,14 +1111,14 @@ end; procedure TWiki2HelpConverter.ExtractPageText(Page: TW2HelpPage); begin - FreeAndNil(Page.TextRoot); - Page.TextRoot:=TWHTextNode.Create(whnTxt,nil); + FreeAndNil(Page.WHRoot); + Page.WHRoot:=TWHTextNode.Create(whnTxt,nil); try - Page.CurNode:=Page.TextRoot; + Page.CurWHNode:=Page.WHRoot; if Page.WikiPage<>nil then Page.WikiPage.Parse(@ExtractTextToken,Page); finally - Page.CurNode:=nil; + Page.CurWHNode:=nil; end; end; @@ -1460,7 +1460,7 @@ begin +TextToHTMLSnipped(aPage.WikiPage.Title,aQuery.LoPhrases,200)+'
'+LineEnding; if aNode=nil then begin // get the first node with some text - aNode:=aPage.TextRoot; + aNode:=aPage.WHRoot; while (aNode<>nil) and (UTF8Trim(aNode.Txt)='') do aNode:=aNode.Next; end; diff --git a/components/wiki/test/wikisearchmain.pas b/components/wiki/test/wikisearchmain.pas index fb37eb429b..1bd2674db9 100644 --- a/components/wiki/test/wikisearchmain.pas +++ b/components/wiki/test/wikisearchmain.pas @@ -132,8 +132,8 @@ type procedure UpdateHistoryButtons; procedure UpdateProgress; procedure LoadWikiPage(Documentname, Anchor: string; AddToHistory: boolean); - procedure LoadHTML(Target: TIpHtmlPanel; HTML: string); overload; - procedure LoadHTML(Target: TIpHtmlPanel; aStream: TStream); overload; + procedure LoadHTML(Target: TIpHtmlPanel; HTML: string; Anchor: string = ''); overload; + procedure LoadHTML(Target: TIpHtmlPanel; aStream: TStream; Anchor: string = ''); overload; procedure ViewSource(aTitle, aSource: string; aHighlighter: TSynCustomHighlighter); procedure WikiSearchOptsWndOptionsChanged(Sender: TObject); procedure WikiHelpScanned(Sender: TObject); @@ -429,8 +429,8 @@ begin DocumentName:=HRef; p:=Pos('#',DocumentName); if p>0 then begin - DocumentName:=LeftStr(DocumentName,p-1); AnchorName:=copy(DocumentName,p+1,length(DocumentName)); + DocumentName:=LeftStr(DocumentName,p-1); end; LoadWikiPage(DocumentName,AnchorName,true); end; @@ -583,8 +583,7 @@ begin if Src<>'' then ms.Read(Src[1],length(Src)); ms.Position:=0; - // ToDo: anchor - LoadHTML(PageIpHtmlPanel,ms); + LoadHTML(PageIpHtmlPanel,ms,Anchor); FPageDocumentName:=DocumentName; FPageAnchor:=Anchor; FPageSource:=Src; @@ -611,7 +610,8 @@ begin end; end; -procedure TWikiSearchDemoForm.LoadHTML(Target: TIpHtmlPanel; HTML: string); +procedure TWikiSearchDemoForm.LoadHTML(Target: TIpHtmlPanel; HTML: string; + Anchor: string); var ms: TMemoryStream; begin @@ -622,7 +622,7 @@ begin try ms.Write(HTML[1],length(HTML)); ms.Position:=0; - LoadHTML(Target,ms); + LoadHTML(Target,ms,Anchor); except on E: Exception do begin debugln(['TWikiSearchDemoForm.LoadHTML ',E.Message]); @@ -633,15 +633,18 @@ begin end; end; -procedure TWikiSearchDemoForm.LoadHTML(Target: TIpHtmlPanel; aStream: TStream); +procedure TWikiSearchDemoForm.LoadHTML(Target: TIpHtmlPanel; aStream: TStream; + Anchor: string); var NewHTML: TIpHtml; begin try NewHTML:=TIpHtml.Create; // Beware: Will be freed automatically by IpHtmlPanel - //NewHTML.OnGetImageX:=@HTMLGetImageX; Target.SetHtml(NewHTML); NewHTML.LoadFromStream(aStream); + // ToDo: fix TIpHtmlNodeA.MakeVisible + if Anchor<>'' then + Target.MakeAnchorVisible(Anchor+'/'); // ipHTML store anchor names with / at end except on E: Exception do begin debugln(['TWikiSearchDemoForm.LoadHTML ',E.Message]);