chmsitemap.pas 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. { Copyright (C) <2005> <Andrew Haines> chmsitemap.pas
  2. This library is free software; you can redistribute it and/or modify it
  3. under the terms of the GNU Library General Public License as published by
  4. the Free Software Foundation; either version 2 of the License, or (at your
  5. option) any later version.
  6. This program is distributed in the hope that it will be useful, but WITHOUT
  7. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  8. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  9. for more details.
  10. You should have received a copy of the GNU Library General Public License
  11. along with this library; if not, write to the Free Software Foundation,
  12. Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  13. }
  14. {
  15. See the file COPYING.FPC, included in this distribution,
  16. for details about the copyright.
  17. }
  18. unit chmsitemap;
  19. {$mode objfpc}{$H+}
  20. interface
  21. uses
  22. Classes, SysUtils, fasthtmlparser;
  23. type
  24. TChmSiteMapItems = class; // forward
  25. TChmSiteMap = class;
  26. { TChmSiteMapItem }
  27. TChmSiteMapItem = class(TPersistent)
  28. private
  29. FChildren: TChmSiteMapItems;
  30. FComment: String;
  31. FImageNumber: Integer;
  32. FIncreaseImageIndex: Boolean;
  33. FKeyWord: String;
  34. FLocal: String;
  35. FOwner: TChmSiteMapItems;
  36. FSeeAlso: String;
  37. FText: String;
  38. FURL: String;
  39. procedure SetChildren(const AValue: TChmSiteMapItems);
  40. public
  41. constructor Create(AOwner: TChmSiteMapItems);
  42. destructor Destroy; override;
  43. published
  44. property Children: TChmSiteMapItems read FChildren write SetChildren;
  45. property Text: String read FText write FText; // Name for TOC; KeyWord for index
  46. property KeyWord: String read FKeyWord write FKeyWord;
  47. property Local: String read FLocal write FLocal;
  48. property URL: String read FURL write FURL;
  49. property SeeAlso: String read FSeeAlso write FSeeAlso;
  50. property ImageNumber: Integer read FImageNumber write FImageNumber default -1;
  51. property IncreaseImageIndex: Boolean read FIncreaseImageIndex write FIncreaseImageIndex;
  52. property Comment: String read FComment write FComment;
  53. property Owner: TChmSiteMapItems read FOwner;
  54. //property FrameName: String read FFrameName write FFrameName;
  55. //property WindowName: String read FWindowName write FWindowName;
  56. //property Type_: Integer read FType_ write FType_; either Local or URL
  57. //property Merge: Boolean read FMerge write FMerge;
  58. end;
  59. { TChmSiteMapItems }
  60. TChmSiteMapItems = class(TPersistent)
  61. private
  62. FInternalData: Dword;
  63. FList: TList;
  64. FOwner: TChmSiteMap;
  65. FParentItem: TChmSiteMapItem;
  66. function GetCount: Integer;
  67. function GetItem(AIndex: Integer): TChmSiteMapItem;
  68. procedure SetItem(AIndex: Integer; const AValue: TChmSiteMapItem);
  69. public
  70. constructor Create(AOwner: TChmSiteMap; AParentItem: TChmSiteMapItem);
  71. destructor Destroy; override;
  72. procedure Delete(AIndex: Integer);
  73. function Add(AItem: TChmSiteMapItem): Integer;
  74. function NewItem: TChmSiteMapItem;
  75. function Insert(AItem: TChmSiteMapItem; AIndex: Integer): Integer;
  76. procedure Clear;
  77. procedure Sort(Compare: TListSortCompare);
  78. property Item[AIndex: Integer]: TChmSiteMapItem read GetItem write SetItem;
  79. property Count: Integer read GetCount;
  80. property ParentItem: TChmSiteMapItem read FParentItem;
  81. property Owner: TChmSiteMap read FOwner;
  82. property InternalData: Dword read FInternalData write FInternalData;
  83. end;
  84. { TChmSiteMapTree }
  85. TSiteMapType = (stTOC, stIndex);
  86. TSiteMapTag = (smtUnknown, smtNone, smtHTML, smtHEAD, smtBODY);
  87. TSiteMapTags = set of TSiteMapTag;
  88. TSiteMapBodyTag = (smbtUnknown, smbtNone, smbtUL, smbtLI, smbtOBJECT, smbtPARAM);
  89. TSiteMapBodyTags = set of TSiteMapBodyTag;
  90. TLIObjectParamType = (ptName, ptLocal, ptKeyword);
  91. TChmSiteMap = class
  92. private
  93. FAutoGenerated: Boolean;
  94. FBackgroundColor: LongInt;
  95. FCurrentItems: TChmSiteMapItems;
  96. FExWindowStyles: LongInt;
  97. FFont: String;
  98. FForegroundColor: LongInt;
  99. FFrameName: String;
  100. FImageList: String;
  101. FImageWidth: Integer;
  102. FSiteMapTags: TSiteMapTags;
  103. FSiteMapBodyTags: TSiteMapBodyTags;
  104. FHTMLParser: THTMLParser;
  105. FItems: TChmSiteMapItems;
  106. FSiteMapType: TSiteMapType;
  107. FUseFolderImages: Boolean;
  108. FWindowName: String;
  109. FLevel: Integer;
  110. FLevelForced: Boolean;
  111. FWindowStyles: LongInt;
  112. procedure SetItems(const AValue: TChmSiteMapItems);
  113. protected
  114. procedure FoundTag (ACaseInsensitiveTag, AActualTag: string);
  115. procedure FoundText(AText: string);
  116. public
  117. constructor Create(AType: TSiteMapType);
  118. destructor Destroy; override;
  119. procedure LoadFromFile(AFileName: String);
  120. procedure LoadFromStream(AStream: TStream);
  121. procedure SaveToFile(AFileName:String);
  122. procedure SaveToStream(AStream: TStream);
  123. property Items: TChmSiteMapItems read FItems write SetItems;
  124. property SiteMapType: TSiteMapType read FSiteMapType;
  125. // SiteMap properties. most of these are invalid for the index
  126. property FrameName: String read FFrameName write FFrameName;
  127. property WindowName: String read FWindowName write FWindowName;
  128. property ImageList: String read FImageList write FImageList;
  129. property ImageWidth: Integer read FImageWidth write FImageWidth;
  130. property BackgroundColor: LongInt read FBackgroundColor write FBackgroundColor;
  131. property ForegroundColor: LongInt read FForegroundColor write FForegroundColor;
  132. property ExWindowStyles: LongInt read FExWindowStyles write FExWindowStyles;
  133. property WindowStyles: LongInt read FWindowStyles write FWindowStyles;
  134. property UseFolderImages: Boolean read FUseFolderImages write FUseFolderImages;
  135. property Font: String read FFont write FFont;
  136. property AutoGenerated: Boolean read FAutoGenerated write FAutoGenerated;
  137. end;
  138. implementation
  139. uses HTMLUtil;
  140. { TChmSiteMapTree }
  141. procedure TChmSiteMap.SetItems(const AValue: TChmSiteMapItems);
  142. begin
  143. if FItems=AValue then exit;
  144. FItems:=AValue;
  145. end;
  146. procedure TChmSiteMap.FoundTag(ACaseInsensitiveTag, AActualTag: string);
  147. function ActiveItem: TChmSiteMapItem;
  148. begin
  149. Result := FCurrentItems.Item[FCurrentItems.Count-1]
  150. end;
  151. procedure IncreaseULevel;
  152. begin
  153. if FCurrentItems = nil then FCurrentItems := Items
  154. else begin
  155. //WriteLn('NewLevel. Count = ', FCurrentItems.Count, ' Index = ',Items.Count-1);
  156. FCurrentItems := ActiveItem.Children;
  157. end;
  158. Inc(FLevel);
  159. end;
  160. procedure DecreaseULevel;
  161. begin
  162. if Assigned(FCurrentItems) and Assigned(FCurrentItems.ParentItem) then
  163. FCurrentItems := FCurrentItems.ParentItem.Owner
  164. else FCurrentItems := nil;
  165. Dec(FLevel);
  166. end;
  167. procedure NewSiteMapItem;
  168. begin
  169. FCurrentItems.Add(TChmSiteMapItem.Create(FCurrentItems));
  170. end;
  171. var
  172. TagName,
  173. //TagAttribute,
  174. TagAttributeName,
  175. TagAttributeValue: String;
  176. begin
  177. //WriteLn('TAG:', AActualTag);
  178. TagName := GetTagName(ACaseInsensitiveTag);
  179. { if not (smtHTML in FSiteMapTags) then begin
  180. if (TagName = 'HTML') or (TagName = '/HTML') then Include(FSiteMapTags, smtHTML);
  181. end
  182. else begin // looking for /HTML
  183. if TagName = '/HTML' then Exclude(FSiteMapTags, smtHTML);
  184. end;}
  185. //if (smtHTML in FSiteMapTags) then begin
  186. if not (smtBODY in FSiteMapTags) then begin
  187. if TagName = 'BODY' then Include(FSiteMapTags, smtBODY);
  188. end
  189. else begin
  190. if TagName = '/BODY' then Exclude(FSiteMapTags, smtBODY);
  191. end;
  192. if (smtBODY in FSiteMapTags) then begin
  193. //WriteLn('GOT TAG: ', AActualTag);
  194. if TagName = 'UL' then begin
  195. //WriteLN('Inc Level');
  196. IncreaseULevel;
  197. end
  198. else if TagName = '/UL' then begin
  199. //WriteLN('Dec Level');
  200. DecreaseULevel;
  201. end
  202. else if (TagName = 'LI') and (FLevel = 0) then
  203. FLevelForced := True
  204. else if TagName = 'OBJECT' then begin
  205. Include(FSiteMapBodyTags, smbtOBJECT);
  206. if FLevelForced then
  207. IncreaseULevel;
  208. If FLevel > 0 then // if it is zero it is the site properties
  209. NewSiteMapItem;
  210. end
  211. else if TagName = '/OBJECT' then begin
  212. Exclude(FSiteMapBodyTags, smbtOBJECT);
  213. if FLevelForced then
  214. begin
  215. DecreaseULevel;
  216. FLevelForced := False;
  217. end;
  218. end
  219. else begin // we are the properties of the object tag
  220. if (FLevel > 0 ) and (smbtOBJECT in FSiteMapBodyTags) then begin
  221. if LowerCase(GetTagName(AActualTag)) = 'param' then begin
  222. TagAttributeName := GetVal(AActualTag, 'name');
  223. TagAttributeValue := GetVal(AActualTag, 'value');
  224. if TagAttributeName <> '' then begin
  225. if CompareText(TagAttributeName, 'keyword') = 0 then begin
  226. ActiveItem.Text := TagAttributeValue;
  227. end
  228. else if CompareText(TagAttributeName, 'name') = 0 then begin
  229. if ActiveItem.Text = '' then ActiveItem.Text := TagAttributeValue;
  230. end
  231. else if CompareText(TagAttributeName, 'local') = 0 then begin
  232. ActiveItem.Local := TagAttributeValue;
  233. end
  234. else if CompareText(TagAttributeName, 'URL') = 0 then begin
  235. ActiveItem.URL := TagAttributeValue;
  236. end
  237. else if CompareText(TagAttributeName, 'ImageNumber') = 0 then begin
  238. ActiveItem.ImageNumber := StrToInt(TagAttributeValue);
  239. end
  240. else if CompareText(TagAttributeName, 'New') = 0 then begin
  241. ActiveItem.IncreaseImageIndex := (LowerCase(TagAttributeValue) = 'yes');
  242. end
  243. else if CompareText(TagAttributeName, 'Comment') = 0 then begin
  244. ActiveItem.Comment := TagAttributeValue;
  245. end;
  246. //else if CompareText(TagAttributeName, '') = 0 then begin
  247. //end;
  248. end;
  249. end;
  250. end;
  251. end;
  252. end;
  253. //end
  254. end;
  255. procedure TChmSiteMap.FoundText(AText: string);
  256. begin
  257. //WriteLn('TEXT:', AText);
  258. end;
  259. constructor TChmSiteMap.Create(AType: TSiteMapType);
  260. begin
  261. Inherited Create;
  262. FSiteMapType := AType;
  263. FSiteMapTags := [smtNone];
  264. FSiteMapBodyTags := [smbtNone];
  265. FHTMLParser:=nil;
  266. FItems := TChmSiteMapItems.Create(Self, nil); ;
  267. end;
  268. destructor TChmSiteMap.Destroy;
  269. begin
  270. if Assigned(FHTMLParser) then FHTMLParser.Free;
  271. FItems.Free;
  272. Inherited Destroy;
  273. end;
  274. procedure TChmSiteMap.LoadFromFile(AFileName: String);
  275. var
  276. Buffer: String;
  277. TmpStream: TMemoryStream;
  278. begin
  279. if Assigned(FHTMLParser) then FHTMLParser.Free;
  280. TmpStream := TMemoryStream.Create;
  281. TmpStream.LoadFromFile(AFileName);
  282. SetLength(Buffer, TmpStream.Size);
  283. TmpStream.Position := 0;
  284. TmpStream.Read(Buffer[1], TmpStream.Size);
  285. FHTMLParser := THTMLParser.Create(Buffer);
  286. FHTMLParser.OnFoundTag := @FoundTag;
  287. FHTMLParser.OnFoundText := @FoundText;
  288. FHTMLParser.Exec;
  289. FreeAndNil(FHTMLParser);
  290. end;
  291. procedure TChmSiteMap.LoadFromStream(AStream: TStream);
  292. var
  293. Buffer: String;
  294. begin
  295. if Assigned(FHTMLParser) then FHTMLParser.Free;
  296. SetLength(Buffer, AStream.Size-AStream.Position);
  297. if AStream.Read(Buffer[1], AStream.Size-AStream.Position) > 0 then begin;
  298. FHTMLParser := THTMLParser.Create(Buffer);
  299. FHTMLParser.OnFoundTag := @FoundTag;
  300. FHTMLParser.OnFoundText := @FoundText;
  301. FHTMLParser.Exec;
  302. FreeAndNil(FHTMLParser);
  303. end;
  304. end;
  305. procedure TChmSiteMap.SaveToFile(AFileName:String);
  306. var
  307. fs : TFileStream;
  308. begin
  309. fs:=TFileStream.Create(AFileName,fmcreate);
  310. try
  311. SaveToStream(fs);
  312. finally
  313. fs.free;
  314. end;
  315. end;
  316. procedure TChmSiteMap.SaveToStream(AStream: TStream);
  317. var
  318. Indent: Integer;
  319. procedure WriteString(AString: String);
  320. var
  321. I: Integer;
  322. begin
  323. for I := 0 to Indent-1 do AStream.WriteByte(Byte(' '));
  324. AStream.Write(AString[1], Length(AString));
  325. AStream.WriteByte(10);
  326. end;
  327. procedure WriteParam(AName: String; AValue: String);
  328. begin
  329. WriteString('<param name="'+AName+'" value="'+AValue+'">');
  330. end;
  331. procedure WriteEntries(AItems: TChmSiteMapItems);
  332. var
  333. I : Integer;
  334. Item: TChmSiteMapItem;
  335. begin
  336. for I := 0 to AItems.Count-1 do begin
  337. Item := AItems.Item[I];
  338. WriteString('<LI> <OBJECT type="text/sitemap">');
  339. Inc(Indent, 8);
  340. if (SiteMapType = stIndex) and (Item.Children.Count > 0) then
  341. WriteParam('Keyword', Item.Text);
  342. //if Item.KeyWord <> '' then WriteParam('Keyword', Item.KeyWord);
  343. if Item.Text <> '' then WriteParam('Name', Item.Text);
  344. if (Item.Local <> '') or (SiteMapType = stIndex) then WriteParam('Local', StringReplace(Item.Local, '\', '/', [rfReplaceAll]));
  345. if Item.URL <> '' then WriteParam('URL', StringReplace(Item.URL, '\', '/', [rfReplaceAll]));
  346. if (SiteMapType = stIndex) and (Item.SeeAlso <> '') then WriteParam('See Also', Item.SeeAlso);
  347. //if Item.FrameName <> '' then WriteParam('FrameName', Item.FrameName);
  348. //if Item.WindowName <> '' then WriteParam('WindowName', Item.WindowName);
  349. if Item.Comment <> '' then WriteParam('Comment', Item.Comment);
  350. if (SiteMapType = stTOC) and (Item.IncreaseImageIndex) then WriteParam('New', 'yes'); // is this a correct value?
  351. if (SiteMapType = stTOC) and (Item.ImageNumber <> -1) then WriteParam('ImageNumber', IntToStr(Item.ImageNumber));
  352. Dec(Indent, 3);
  353. WriteString('</OBJECT>');
  354. Dec(Indent, 5);
  355. // Now Sub Entries
  356. if Item.Children.Count > 0 then begin
  357. WriteString('<UL>');
  358. Inc(Indent, 8);
  359. WriteEntries(Item.Children);
  360. Dec(Indent, 8);
  361. WriteString('</UL>');
  362. end;
  363. end;
  364. end;
  365. begin
  366. Indent := 0;
  367. WriteString('<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">');
  368. WriteString('<HTML>');
  369. WriteString('<HEAD>');
  370. WriteString('<meta name="GENERATOR" content="Microsoft&reg; HTML Help Workshop 4.1">'); // Should we change this?
  371. WriteString('<!-- Sitemap 1.0 -->');
  372. WriteString('</HEAD><BODY>');
  373. // Site Properties
  374. WriteString('<OBJECT type="text/site properties">');
  375. Inc(Indent, 8);
  376. if SiteMapType = stTOC then begin
  377. if FrameName <> '' then WriteParam('FrameName', FrameName);
  378. if WindowName <> '' then WriteParam('WindowName', WindowName);
  379. if ImageList <> '' then WriteParam('ImageList', ImageList);
  380. if ImageWidth > 0 then WriteParam('Image Width', IntToStr(ImageWidth));
  381. if BackgroundColor <> 0 then WriteParam('Background', hexStr(BackgroundColor, 4));
  382. if ForegroundColor <> 0 then WriteParam('Foreground', hexStr(ForegroundColor, 4));
  383. if ExWindowStyles <> 0 then WriteParam('ExWindow Styles', hexStr(ExWindowStyles, 4));
  384. if WindowStyles <> 0 then WriteParam('Window Styles', hexStr(WindowStyles, 4));
  385. if UseFolderImages then WriteParam('ImageType', 'Folder');
  386. end;
  387. // both TOC and Index have font
  388. if Font <> '' then
  389. WriteParam('Font', Font);
  390. Dec(Indent, 8);
  391. WriteString('</OBJECT>');
  392. // And now the items
  393. if Items.Count > 0 then begin
  394. WriteString('<UL>');
  395. Inc(Indent, 8);
  396. // WriteEntries
  397. WriteEntries(Items);
  398. Dec(Indent, 8);
  399. WriteString('</UL>');
  400. end;
  401. WriteString('</BODY></HTML>');
  402. AStream.Size := AStream.Position;
  403. end;
  404. { TChmSiteMapItem }
  405. procedure TChmSiteMapItem.SetChildren(const AValue: TChmSiteMapItems);
  406. begin
  407. if FChildren = AValue then exit;
  408. FChildren := AValue;
  409. end;
  410. constructor TChmSiteMapItem.Create(AOwner: TChmSiteMapItems);
  411. begin
  412. Inherited Create;
  413. FOwner := AOwner;
  414. FChildren := TChmSiteMapItems.Create(Owner.Owner, Self);
  415. end;
  416. destructor TChmSiteMapItem.Destroy;
  417. begin
  418. FChildren.Free;
  419. Inherited Destroy;
  420. end;
  421. { TChmSiteMapItems }
  422. function TChmSiteMapItems.GetItem(AIndex: Integer): TChmSiteMapItem;
  423. begin
  424. Result := TChmSiteMapItem(FList.Items[AIndex]);
  425. end;
  426. function TChmSiteMapItems.GetCount: Integer;
  427. begin
  428. Result := FList.Count;
  429. end;
  430. procedure TChmSiteMapItems.SetItem(AIndex: Integer; const AValue: TChmSiteMapItem);
  431. begin
  432. FList.Items[AIndex] := AValue;
  433. end;
  434. constructor TChmSiteMapItems.Create(AOwner: TChmSiteMap; AParentItem: TChmSiteMapItem);
  435. begin
  436. FList := TList.Create;
  437. FParentItem := AParentItem;
  438. FOwner := AOwner;
  439. FInternalData := maxLongint;
  440. end;
  441. destructor TChmSiteMapItems.Destroy;
  442. begin
  443. Clear;
  444. FList.Free;
  445. inherited Destroy;
  446. end;
  447. procedure TChmSiteMapItems.Delete(AIndex: Integer);
  448. begin
  449. Item[AIndex].Free;
  450. FList.Delete(AIndex);
  451. end;
  452. function TChmSiteMapItems.Add(AItem: TChmSiteMapItem): Integer;
  453. begin
  454. Result := FList.Add(AItem);
  455. end;
  456. function TChmSiteMapItems.NewItem: TChmSiteMapItem;
  457. begin
  458. Result := TChmSiteMapItem.Create(Self);
  459. Add(Result);
  460. end;
  461. function TChmSiteMapItems.Insert(AItem: TChmSiteMapItem; AIndex: Integer): Integer;
  462. begin
  463. Result := AIndex;
  464. FList.Insert(AIndex, AItem);
  465. end;
  466. procedure TChmSiteMapItems.Clear;
  467. var
  468. I: LongInt;
  469. begin
  470. for I := Count-1 downto 0 do Delete(I);
  471. end;
  472. procedure TChmSiteMapItems.Sort(Compare: TListSortCompare);
  473. begin
  474. FList.Sort(Compare);
  475. end;
  476. end.