|
1 | | -using System.Xml.Linq; |
| 1 | +using System.Diagnostics; |
| 2 | +using System.Xml.Linq; |
| 3 | +using AngleSharp.Dom; |
| 4 | +using AngleSharp.Html.Dom; |
2 | 5 | using OriginLab.DocumentGeneration.Templates; |
3 | 6 |
|
4 | 7 | namespace OriginLab.DocumentGeneration; |
5 | 8 |
|
6 | | -internal sealed class DocBookTransformer : DocTransformer |
| 9 | +internal sealed class DocBookTransformer : DocToStaticPagesTransformer |
7 | 10 | { |
| 11 | + private readonly string AvailableLanguagesExpression; |
| 12 | + |
8 | 13 | private readonly string BookDirName; |
9 | 14 | private readonly (string url, string file, string titleEn, NavFiles navFiles)[] Pages; |
10 | 15 |
|
11 | | - public DocBookTransformer(DocTransformerArgs args, ProblemRecorder problems) : base(args, problems) |
| 16 | + public DocBookTransformer(DocToStaticPagesTransformerArgs args, ProblemRecorder problems) : base(args, problems) |
12 | 17 | { |
| 18 | + AvailableLanguagesExpression = String.Join(',', AvailableLanguages); |
13 | 19 | BookDirName = Path.GetFileName(Directory.EnumerateDirectories(Path.Combine(SourceFolder, "en")).Single()); |
14 | 20 |
|
15 | 21 | var bookXml = XElement.Load(Path.Combine(SourceFolder, "en", BookDirName, "book.xml")); |
@@ -61,7 +67,7 @@ public DocBookTransformer(DocTransformerArgs args, ProblemRecorder problems) : b |
61 | 67 | } |
62 | 68 | } |
63 | 69 |
|
64 | | - public override async Task TransformFilesAsync(string language) |
| 70 | + protected override async Task TransformAsync(string language) |
65 | 71 | { |
66 | 72 | var srcDir = Path.Combine(SourceFolder, language, BookDirName); |
67 | 73 | var srcEnDir = Path.Combine(SourceFolderEn, BookDirName); |
@@ -112,4 +118,179 @@ public override async Task TransformFilesAsync(string language) |
112 | 118 | } |
113 | 119 | } |
114 | 120 | } |
| 121 | + |
| 122 | + private void Transform(string srcFile, string dstFile, Nav nav, string? bannerHtml = null) |
| 123 | + { |
| 124 | + Transform(srcFile, dstFile, (document, head, body, file) => |
| 125 | + { |
| 126 | + if (bannerHtml is string banner) |
| 127 | + { |
| 128 | + var div = document.CreateElement<IHtmlDivElement>(); |
| 129 | + div.InnerHtml = banner; |
| 130 | + |
| 131 | + body.PrependNodes(div.ChildNodes.ToArray()); |
| 132 | + } |
| 133 | + |
| 134 | + var navDataDiv = CreateNavDataDiv(document, nav, Path.GetDirectoryName(file)!); |
| 135 | + body.AppendChild(navDataDiv); |
| 136 | + }); |
| 137 | + } |
| 138 | + |
| 139 | + |
| 140 | + string? CachedSiblingsParent; |
| 141 | + int CachedSiblingsCurrentIndex; |
| 142 | + IHtmlElement? CachedSiblings; |
| 143 | + |
| 144 | + private IHtmlDivElement CreateNavDataDiv(IHtmlDocument document, in Nav nav, string sourceDir) |
| 145 | + { |
| 146 | + var navDataDiv = document.CreateElement<IHtmlDivElement>(); |
| 147 | + |
| 148 | + navDataDiv.Id = "doc-nav-data"; |
| 149 | + navDataDiv.IsHidden = true; |
| 150 | + |
| 151 | + navDataDiv.SetAttribute("data-lang", Language); |
| 152 | + navDataDiv.SetAttribute("data-lang-list", AvailableLanguagesExpression); |
| 153 | + |
| 154 | + var files = nav.Files; |
| 155 | + |
| 156 | + if (!files.Parent.IsEmpty) |
| 157 | + { |
| 158 | + if (TryResolveHref(sourceDir, "../" + files.Parent, out var url, out var _)) |
| 159 | + { |
| 160 | + navDataDiv.SetAttribute("data-parent-link", url); |
| 161 | + } |
| 162 | + } |
| 163 | + else |
| 164 | + { |
| 165 | + navDataDiv.SetAttribute("data-parent-link", Language == "en" ? "/" : $"/{Language}"); |
| 166 | + } |
| 167 | + |
| 168 | + if (nav.IsBookIndex) |
| 169 | + { |
| 170 | + navDataDiv.SetAttribute("data-book-index", BookUrlName); |
| 171 | + } |
| 172 | + |
| 173 | + if (files.Siblings is not null) |
| 174 | + { |
| 175 | + IHtmlElement ul; |
| 176 | + |
| 177 | + if (nav.Files.Parent is string parent && parent == CachedSiblingsParent) |
| 178 | + { |
| 179 | + ul = CreateSiblingsULFromCache(CachedSiblings!, files.Siblings, nav.Titles); |
| 180 | + } |
| 181 | + else |
| 182 | + { |
| 183 | + CachedSiblingsParent = nav.Files.Parent; |
| 184 | + CachedSiblings = ul = CreateDataUL("doc-siblings-data", files.Siblings, nav.Titles); |
| 185 | + } |
| 186 | + |
| 187 | + navDataDiv.AppendChild(ul); |
| 188 | + } |
| 189 | + |
| 190 | + if (files.Children is not null) |
| 191 | + { |
| 192 | + var ul = CreateDataUL("doc-children-data", files.Children, nav.Titles); |
| 193 | + navDataDiv.AppendChild(ul); |
| 194 | + } |
| 195 | + |
| 196 | + return navDataDiv; |
| 197 | + |
| 198 | + IHtmlElement CreateDataUL(string id, string[] files, Dictionary<string, string> titles) |
| 199 | + { |
| 200 | + var ul = document.CreateElement<IHtmlUnorderedListElement>(); |
| 201 | + |
| 202 | + ul.Id = id; |
| 203 | + |
| 204 | + for (int i = 0; i < files.Length; i++) |
| 205 | + { |
| 206 | + string? path = files[i]; |
| 207 | + var li = document.CreateElement<IHtmlListItemElement>(); |
| 208 | + var a = document.CreateElement<IHtmlAnchorElement>(); |
| 209 | + var pathSpan = path.AsSpan(); |
| 210 | + var isCurrent = false; |
| 211 | + |
| 212 | + if (path.StartsWith('*')) |
| 213 | + { |
| 214 | + pathSpan = pathSpan[1..]; |
| 215 | + isCurrent = true; |
| 216 | + CachedSiblingsCurrentIndex = i; |
| 217 | + } |
| 218 | + |
| 219 | + if (TryResolveHref(sourceDir, $"../{pathSpan}", out var url, out var _)) |
| 220 | + { |
| 221 | + if (isCurrent) |
| 222 | + { |
| 223 | + li.ClassName = "disabled"; |
| 224 | + } |
| 225 | + else |
| 226 | + { |
| 227 | + a.SetAttribute("href", url); |
| 228 | + } |
| 229 | + |
| 230 | + a.TextContent = titles.GetAlternateLookup<ReadOnlySpan<char>>()[pathSpan]; |
| 231 | + } |
| 232 | + |
| 233 | + li.AppendChild(a); |
| 234 | + ul.AppendChild(li); |
| 235 | + } |
| 236 | + |
| 237 | + return ul; |
| 238 | + } |
| 239 | + |
| 240 | + IHtmlElement CreateSiblingsULFromCache(IHtmlElement ul, string[] files, Dictionary<string, string> titles) |
| 241 | + { |
| 242 | + document.AdoptNode(ul); |
| 243 | + |
| 244 | + var currentIdx = Array.FindIndex(files, CachedSiblingsCurrentIndex, files.Length - CachedSiblingsCurrentIndex, f => f.StartsWith('*')); |
| 245 | + var previousLi = ul.Children[CachedSiblingsCurrentIndex].SelfOrNextElementSibling(li => li.FirstElementChild!.GetAttribute("href").IsEmpty); |
| 246 | + |
| 247 | + Debug.Assert(currentIdx > 0); |
| 248 | + Debug.Assert(previousLi is not null); |
| 249 | + |
| 250 | + if (TryResolveHref(sourceDir, $"../{files[currentIdx - 1]}", out var url, out var _)) |
| 251 | + { |
| 252 | + previousLi.FirstElementChild!.SetAttribute("href", url); |
| 253 | + } |
| 254 | + |
| 255 | + previousLi.ClassName = null; |
| 256 | + |
| 257 | + var currentLi = previousLi.NextElementSibling!; |
| 258 | + currentLi.FirstElementChild!.SetAttribute("href", null); |
| 259 | + currentLi.ClassName = "disabled"; |
| 260 | + |
| 261 | + if (ul.ChildElementCount < files.Length) |
| 262 | + { |
| 263 | + var li = document.CreateElement<IHtmlListItemElement>(); |
| 264 | + var a = document.CreateElement<IHtmlAnchorElement>(); |
| 265 | + |
| 266 | + if (TryResolveHref(sourceDir, $"../{files[^1]}", out var endUrl, out var _)) |
| 267 | + { |
| 268 | + a.SetAttribute("href", endUrl); |
| 269 | + a.TextContent = titles[files[^1]]; |
| 270 | + } |
| 271 | + |
| 272 | + li.AppendChild(a); |
| 273 | + ul.AppendChild(li); |
| 274 | + } |
| 275 | + else if (currentIdx == MaxSiblingNodes / 2 && currentIdx != files.Length - 1 && currentIdx == CachedSiblingsCurrentIndex) |
| 276 | + { |
| 277 | + var li = (IHtmlElement)ul.RemoveChild(ul.FirstElementChild!); |
| 278 | + |
| 279 | + if (TryResolveHref(sourceDir, $"../{files[^1]}", out var endUrl, out var _) |
| 280 | + && endUrl != ul.LastElementChild!.FirstElementChild!.GetAttribute("href")) |
| 281 | + { |
| 282 | + var a = li.FirstElementChild!; |
| 283 | + |
| 284 | + a.SetAttribute("href", endUrl); |
| 285 | + a.TextContent = titles[files[^1]]; |
| 286 | + |
| 287 | + ul.AppendChild(li); |
| 288 | + } |
| 289 | + } |
| 290 | + |
| 291 | + CachedSiblingsCurrentIndex = currentIdx; |
| 292 | + return ul; |
| 293 | + } |
| 294 | + } |
| 295 | + |
115 | 296 | } |
0 commit comments