diff --git a/CHDataManagement.xcodeproj/project.pbxproj b/CHDataManagement.xcodeproj/project.pbxproj index d3a6ad1..221b930 100644 --- a/CHDataManagement.xcodeproj/project.pbxproj +++ b/CHDataManagement.xcodeproj/project.pbxproj @@ -189,6 +189,7 @@ E2B85F432C4294F60047CD0C /* FeedEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2B85F422C4294F60047CD0C /* FeedEntry.swift */; }; E2B85F452C429ED60047CD0C /* ImageGallery.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2B85F442C429ED60047CD0C /* ImageGallery.swift */; }; E2B85F572C4BD0BB0047CD0C /* Binding+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2B85F562C4BD0BB0047CD0C /* Binding+Extension.swift */; }; + E2BF1BC62D6B16FF003089F1 /* HeadlineLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2BF1BC52D6B16FA003089F1 /* HeadlineLink.swift */; }; E2DD04742C276F31003BFF1F /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2DD04732C276F31003BFF1F /* MainView.swift */; }; E2DD047A2C276F32003BFF1F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2DD04792C276F32003BFF1F /* Assets.xcassets */; }; E2DD047E2C276F32003BFF1F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2DD047D2C276F32003BFF1F /* Preview Assets.xcassets */; }; @@ -457,6 +458,7 @@ E2B85F422C4294F60047CD0C /* FeedEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedEntry.swift; sourceTree = ""; }; E2B85F442C429ED60047CD0C /* ImageGallery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageGallery.swift; sourceTree = ""; }; E2B85F562C4BD0BB0047CD0C /* Binding+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Extension.swift"; sourceTree = ""; }; + E2BF1BC52D6B16FA003089F1 /* HeadlineLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineLink.swift; sourceTree = ""; }; E2DD04702C276F31003BFF1F /* CHDataManagement.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CHDataManagement.app; sourceTree = BUILT_PRODUCTS_DIR; }; E2DD04732C276F31003BFF1F /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; E2DD04792C276F32003BFF1F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -694,6 +696,7 @@ E29D311E2D0320D90051B7F4 /* ContentElements */ = { isa = PBXGroup; children = ( + E2BF1BC52D6B16FA003089F1 /* HeadlineLink.swift */, E2B4821F2D67074C005C309D /* WallpaperSlider.swift */, E29D31C12D0DBED70051B7F4 /* AudioPlayer */, E29D318A2D0B07E60051B7F4 /* ContentBox.swift */, @@ -1285,6 +1288,7 @@ E229904C2D10BE5D009F8D77 /* InitialSetupView.swift in Sources */, E218502B2CF790B30090B18B /* PostContentView.swift in Sources */, E29D317D2D086AB00051B7F4 /* Int+Random.swift in Sources */, + E2BF1BC62D6B16FF003089F1 /* HeadlineLink.swift in Sources */, E25DA56F2D00F9A100AEF16D /* PostFeedSettingsView.swift in Sources */, E2521E042D51796000C56662 /* StorageErrorView.swift in Sources */, E29D313B2D04464A0051B7F4 /* LocalizedTagDetailView.swift in Sources */, diff --git a/CHDataManagement/Generator/Blocks/PhoneScreensBlock.swift b/CHDataManagement/Generator/Blocks/PhoneScreensBlock.swift index 4ee68d5..0a7408c 100644 --- a/CHDataManagement/Generator/Blocks/PhoneScreensBlock.swift +++ b/CHDataManagement/Generator/Blocks/PhoneScreensBlock.swift @@ -23,7 +23,6 @@ struct PhoneScreensBlock: OrderedKeyBlockProcessor { } func process(_ arguments: [(key: Key, value: String)], markdown: Substring) -> String { - print("Processing Phone Screens Block") guard let frameId = arguments.first(where: {$0.key == .frame })?.value else { invalid(markdown) return "" diff --git a/CHDataManagement/Generator/Markdown/MarkdownHeadlineProcessor.swift b/CHDataManagement/Generator/Markdown/MarkdownHeadlineProcessor.swift index b66ed60..5f196c9 100644 --- a/CHDataManagement/Generator/Markdown/MarkdownHeadlineProcessor.swift +++ b/CHDataManagement/Generator/Markdown/MarkdownHeadlineProcessor.swift @@ -19,20 +19,20 @@ struct MarkdownHeadlineProcessor: MarkdownProcessor { /** Modify headlines by extracting an id from the headline and adding it into the html element - Format: ###<id> - The id is created by lowercasing the string, removing all special characters, and replacing spaces with scores */ func process(html: String, markdown: Substring) -> String { - let id = markdown - .last(after: "#") - .trimmed - .filter { $0.isNumber || $0.isLetter || $0 == " " } - .lowercased() - .components(separatedBy: " ") - .filter { $0 != "" } - .joined(separator: "-") - let parts = html.components(separatedBy: ">") - return parts[0] + " id=\"\(id)\">" + parts.dropFirst().joined(separator: ">") + let parts2 = markdown.components(separatedBy: "#") + // Determine the type of heading, e.g. 2 for <h2> + let headlineParts = parts2.drop { $0.isEmpty } + let headlineType = parts2.count - headlineParts.count + + guard headlineType > 1 && headlineType < 4 else { + return html + } + + let title = headlineParts.joined(separator: "#") + let headline = HeadlineLink(level: headlineType, title: title) + return headline.content } } diff --git a/CHDataManagement/Page Elements/ContentElements/HeadlineLink.swift b/CHDataManagement/Page Elements/ContentElements/HeadlineLink.swift new file mode 100644 index 0000000..9c476ce --- /dev/null +++ b/CHDataManagement/Page Elements/ContentElements/HeadlineLink.swift @@ -0,0 +1,42 @@ + +struct HeadlineLink: HtmlProducer { + + /// The type of headline, e.g. `2` for `<h2>` + let level: Int + + let title: String + + var id: String { + title.validHtmlId() + } + + func populate(_ result: inout String) { + result += "<h\(level) id='\(id)'>" + result += "<a href='#\(id)'>\(title)</a>" + result += "</h\(level)>" + } +} + +private extension String { + + static let characterReplacements: [Character: String] = [ + "ä": "ae", "ö": "oe", "ü": "ue", "ß": "ss", + "Ä": "Ae", "Ö": "Oe", "Ü": "Ue", + " ": "-", "/": "-", "&": "and" + ] + + func validHtmlId() -> String { + var result = self.lowercased() + + for (char, replacement) in String.characterReplacements { + result = result.replacingOccurrences(of: String(char), with: replacement) + } + + result = result.filter { $0.isLetter || $0.isNumber || $0 == "-" } + + return result + .components(separatedBy: "-") + .filter { $0 != "" } + .joined(separator: "-") + } +}