wiki: anchors for headers

git-svn-id: trunk@35876 -
This commit is contained in:
mattias 2012-03-10 21:37:43 +00:00
parent a8926e9b1a
commit d9155823c1
3 changed files with 150 additions and 73 deletions

View File

@ -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;
// <body>
Page.BodyNode:=doc.CreateElement('body');
RootNode.AppendChild(Page.BodyNode);
Page.BodyDOMNode:=doc.CreateElement('body');
RootNode.AppendChild(Page.BodyDOMNode);
// title <h1 class="firstHeading">
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 <a href="BaseURL+WikiDocumentName">LinkToBaseDocument</a><br>
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;

View File

@ -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)+'</a><br>'+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;

View File

@ -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]);