2020-12-17 04:01:44 +00:00
|
|
|
//
|
|
|
|
// GeminiHTMLRenderer.swift
|
|
|
|
// GeminiRenderer
|
|
|
|
//
|
|
|
|
// Created by Shadowfacts on 12/16/20.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
import GeminiFormat
|
|
|
|
import HTMLEntities
|
|
|
|
|
|
|
|
public class GeminiHTMLRenderer {
|
|
|
|
|
2020-12-17 23:26:32 +00:00
|
|
|
public var linkPrefix: ((URL) -> String?)?
|
2020-12-17 04:01:44 +00:00
|
|
|
|
|
|
|
public init() {
|
|
|
|
}
|
|
|
|
|
|
|
|
public func renderDocumentToHTML(_ doc: Document) -> String {
|
|
|
|
var str = ""
|
|
|
|
|
|
|
|
var inPreformatting = false
|
|
|
|
var inList = false
|
|
|
|
|
2020-12-20 18:45:22 +00:00
|
|
|
for (index, line) in doc.lines.enumerated() {
|
2020-12-17 04:01:44 +00:00
|
|
|
if inList && !line.isListItem {
|
|
|
|
str += "</ul>"
|
|
|
|
}
|
|
|
|
|
|
|
|
switch line {
|
|
|
|
case let .text(text):
|
|
|
|
str += "<p>\(text.htmlEscape())</p>"
|
|
|
|
case let .link(url, text: maybeText):
|
|
|
|
let text = maybeText ?? url.absoluteString
|
2020-12-17 23:26:32 +00:00
|
|
|
let linkPrefix = self.linkPrefix?(url) ?? ""
|
|
|
|
str += "<p class=\"link\">\(linkPrefix)<a href=\"\(url.absoluteString)\">\(text.htmlEscape())</a></p>"
|
2020-12-17 04:01:44 +00:00
|
|
|
case .preformattedToggle(alt: _):
|
|
|
|
inPreformatting = !inPreformatting
|
|
|
|
if inPreformatting {
|
|
|
|
str += "<pre>"
|
|
|
|
} else {
|
|
|
|
str += "</pre>"
|
|
|
|
}
|
|
|
|
case let .preformattedText(text):
|
|
|
|
str += text.htmlEscape()
|
|
|
|
str += "\n"
|
|
|
|
case let .heading(text, level: level):
|
|
|
|
let tag = "h\(level.rawValue)"
|
2020-12-20 18:45:22 +00:00
|
|
|
str += "<\(tag) id=\"l\(index)\">\(text.htmlEscape())</\(tag)>"
|
2020-12-17 04:01:44 +00:00
|
|
|
case let .unorderedListItem(text):
|
|
|
|
if !inList {
|
|
|
|
inList = true
|
|
|
|
str += "<ul>"
|
|
|
|
}
|
|
|
|
str += "<li>\(text.htmlEscape())</li>"
|
|
|
|
case let .quote(text):
|
|
|
|
str += "<blockquote>\(text.htmlEscape())</blockquote>"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fileprivate extension Document.Line {
|
|
|
|
var isListItem: Bool {
|
|
|
|
switch self {
|
|
|
|
case .unorderedListItem(_):
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|