Initial Commit
This commit is contained in:
358
Sources/HRW/BaseComponents.swift
Normal file
358
Sources/HRW/BaseComponents.swift
Normal file
@@ -0,0 +1,358 @@
|
||||
//
|
||||
// BaseComponents.swift
|
||||
// HRW
|
||||
//
|
||||
// Created by Isaac Paul on 10/15/24.
|
||||
// Non-commercial license, see LICENSE.MD in the project root for details
|
||||
//
|
||||
|
||||
public protocol IFlowContent : HTMLNode {}
|
||||
|
||||
public protocol IGlobalContainer {
|
||||
var globalAttributes:Dictionary<GlobalAttributeKey, String> { get set }
|
||||
var globalEvents:Dictionary<GlobalEventKey, String> { get set }
|
||||
var dataAttributes:Dictionary<String, String> { get set }
|
||||
}
|
||||
|
||||
extension IGlobalContainer {
|
||||
mutating func trySetGlobalAttribute(_ key:String, _ value:String) -> Bool {
|
||||
if let attr = GlobalAttributeKey(rawValue: key) {
|
||||
globalAttributes.updateValue(value, forKey: attr)
|
||||
return true
|
||||
}
|
||||
if let attr = GlobalEventKey(rawValue: key.asSubstring()) {
|
||||
globalEvents[attr] = value
|
||||
return true
|
||||
}
|
||||
if key[..<key.index(key.startIndex, offsetBy: 5)] == "data-" {
|
||||
dataAttributes[key] = value
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public class NParent : NRenderable {
|
||||
public var children:[NRenderable] = []
|
||||
|
||||
public override init() {
|
||||
super.init()
|
||||
children = []
|
||||
}
|
||||
}
|
||||
|
||||
public class NRenderable {
|
||||
public var parent:NParent? = nil
|
||||
}
|
||||
|
||||
public class NNone: NRenderable { }
|
||||
|
||||
public struct GlobalAttributesBuilder : IGlobalContainer{
|
||||
public var globalAttributes:Dictionary<GlobalAttributeKey, String> = [:]
|
||||
public var globalEvents:Dictionary<GlobalEventKey, String> = [:]
|
||||
public var dataAttributes:Dictionary<String, String> = [:]
|
||||
|
||||
public init(globalAttributes: Dictionary<GlobalAttributeKey, String>, globalEvents: Dictionary<GlobalEventKey, String>, dataAttributes: Dictionary<String, String>) {
|
||||
self.globalAttributes = globalAttributes
|
||||
self.globalEvents = globalEvents
|
||||
self.dataAttributes = dataAttributes
|
||||
}
|
||||
|
||||
public init() {
|
||||
self.globalAttributes = [:]
|
||||
self.globalEvents = [:]
|
||||
self.dataAttributes = [:]
|
||||
}
|
||||
|
||||
public init(_ attributes: [String: String]) throws {
|
||||
self.globalAttributes = [:]
|
||||
self.globalEvents = [:]
|
||||
self.dataAttributes = [:]
|
||||
for (key, value) in attributes {
|
||||
if self.trySetGlobalAttribute(key, value) {
|
||||
continue
|
||||
}
|
||||
throw AppError("Unexpected attribute: \(key)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The problem is pausing and resuming so this is a n-squared search, but is it faster than make allocations? do we even need this?
|
||||
/*
|
||||
public struct HtmlIterator : IteratorProtocol {
|
||||
public init(src: NHTMLRenderable) {
|
||||
self._src = src
|
||||
}
|
||||
|
||||
public typealias Element = NHTMLRenderable
|
||||
|
||||
private let _src:NHTMLRenderable
|
||||
private var _index:Int = 0
|
||||
|
||||
public mutating func next() -> NHTMLRenderable? {
|
||||
let (index, result) = _src.renderableAtIndex(_index)
|
||||
_index += 1
|
||||
return result
|
||||
}
|
||||
}*/
|
||||
|
||||
public class HTMLNode : XMLNode, IGlobalContainer {
|
||||
|
||||
public var globalAttributes:Dictionary<GlobalAttributeKey, String> = [:]
|
||||
public var globalEvents:Dictionary<GlobalEventKey, String> = [:]
|
||||
|
||||
public var children:[HTMLNode] = []
|
||||
|
||||
public init(expectedAttributes:[String:String]) throws {
|
||||
super.init()
|
||||
for (key, value) in expectedAttributes {
|
||||
if let attr = GlobalAttributeKey(rawValue: key) {
|
||||
globalAttributes.updateValue(value, forKey: attr)
|
||||
continue
|
||||
}
|
||||
if let attr = GlobalEventKey(rawValue: key.asSubstring()) {
|
||||
globalEvents[attr] = value
|
||||
continue
|
||||
}
|
||||
if key[..<key.index(key.startIndex, offsetBy: 5)] == "data-" {
|
||||
dataAttributes[key] = value
|
||||
continue
|
||||
}
|
||||
throw AppError("Unexpected attribute: \(key)")
|
||||
}
|
||||
}
|
||||
|
||||
public init(globalAttributes:Dictionary<GlobalAttributeKey, String>, globalEvents:Dictionary<GlobalEventKey, String>, dataAttributes:Dictionary<String, String>) {
|
||||
self.globalAttributes = globalAttributes
|
||||
self.globalEvents = globalEvents
|
||||
super.init(dataAttributes: dataAttributes)
|
||||
}
|
||||
|
||||
public init(_ builder:GlobalAttributesBuilder, _ children:[HTMLNode] = []) {
|
||||
self.globalAttributes = builder.globalAttributes
|
||||
self.globalEvents = builder.globalEvents
|
||||
self.children = children
|
||||
super.init(dataAttributes: builder.dataAttributes)
|
||||
}
|
||||
|
||||
public func findById(_ id:String) -> HTMLNode? {
|
||||
if (globalAttributes[.id] == id) {
|
||||
return self
|
||||
}
|
||||
for eachChild in children {
|
||||
if let result = eachChild.findById(id) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public override func renderAttributes() -> String {
|
||||
var result = super.renderAttributes()
|
||||
var first = result.count == 0
|
||||
for eachAttr in globalAttributes {
|
||||
if (!first) {
|
||||
result += " "
|
||||
}
|
||||
first = false
|
||||
if (eachAttr.value.count > 0) {
|
||||
result += "\(eachAttr.key)='\(eachAttr.value)'"
|
||||
} else {
|
||||
result += "\(eachAttr.key)"
|
||||
}
|
||||
}
|
||||
|
||||
for eachAttr in globalEvents {
|
||||
if (!first) {
|
||||
result += " "
|
||||
}
|
||||
first = false
|
||||
if (eachAttr.value.count > 0) {
|
||||
result += "\(eachAttr.key) = \(eachAttr.value)"
|
||||
} else {
|
||||
result += "\(eachAttr.key)"
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
override var nodeName: String {
|
||||
return "HTML"
|
||||
}
|
||||
|
||||
override var isVoidElement: Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
override public func toString(_ depth:Int = 0, spacingStrat:SpacingStrat = .tabs) -> (Int, String) {
|
||||
var newDepth = depth
|
||||
var result = renderTag()
|
||||
if (!isVoidElement) {
|
||||
for eachChild in children {
|
||||
let (nextDepth, renderedChild) = eachChild.toString(newDepth, spacingStrat: spacingStrat)
|
||||
newDepth = nextDepth
|
||||
result += renderedChild
|
||||
}
|
||||
result += "<\(nodeName)/>"
|
||||
}
|
||||
return (newDepth, result)
|
||||
}
|
||||
}
|
||||
|
||||
public enum SpacingStrat {
|
||||
case tabs
|
||||
case spaces(num:Int)
|
||||
}
|
||||
|
||||
public class GenericXMLNode : XMLNode {
|
||||
|
||||
public var attributes:Dictionary<String, String> = [:]
|
||||
public var children:[XMLNode] = []
|
||||
public var name:String
|
||||
|
||||
public init(_ name:String, _ attributes:[String:String], _ children:[XMLNode] = []) {
|
||||
self.attributes = attributes
|
||||
self.children = children
|
||||
self.name = name
|
||||
super.init(attributes)
|
||||
}
|
||||
|
||||
public init(_ name:String, _ attributes:[String:String], _ parser:XMLParser? = nil) throws {
|
||||
self.attributes = attributes
|
||||
self.name = name
|
||||
super.init(attributes)
|
||||
|
||||
|
||||
var allItems:[XMLNode] = []
|
||||
while let obj = try parser?.readObjectXml(endTag: "a") {
|
||||
allItems.append(obj)
|
||||
}
|
||||
children = allItems
|
||||
}
|
||||
}
|
||||
|
||||
public class XMLNode { //Not sure if should be a parent class or just protocol
|
||||
|
||||
public var dataAttributes:Dictionary<String, String> = [:]
|
||||
public var parent:XMLNode? = nil
|
||||
var nodeName: String {
|
||||
return ""
|
||||
}
|
||||
var isVoidElement: Bool {
|
||||
return true
|
||||
}
|
||||
/*
|
||||
public func it() -> HtmlIterator {
|
||||
return HtmlIterator(src: self)
|
||||
}*/
|
||||
|
||||
public init() {
|
||||
|
||||
}
|
||||
|
||||
public init(_ attributes:[String:String]) {
|
||||
for (key, value) in attributes {
|
||||
if key[..<key.index(key.startIndex, offsetBy: 5)] == "data-" {
|
||||
dataAttributes[key] = value
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public init(dataAttributes:[String:String]) {
|
||||
self.dataAttributes = dataAttributes
|
||||
}
|
||||
|
||||
public func renderAttributes() -> String {
|
||||
var result = ""
|
||||
var first = true
|
||||
for eachAttr in dataAttributes {
|
||||
if (!first) {
|
||||
result += " "
|
||||
}
|
||||
first = false
|
||||
if (eachAttr.value.count > 0) {
|
||||
result += "\(eachAttr.key) = \(eachAttr.value)"
|
||||
} else {
|
||||
result += "\(eachAttr.key)"
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/*
|
||||
public func renderableAtIndex(_ index:Int) -> (Int, NHTMLRenderable?) {
|
||||
if (index == 0) {
|
||||
return (-1, self)
|
||||
}
|
||||
var nextIndex = index - 1
|
||||
for eachChild in children {
|
||||
let (index, result) = eachChild.renderableAtIndex(nextIndex)
|
||||
if let result = result {
|
||||
return (-1, result)
|
||||
}
|
||||
nextIndex = index
|
||||
}
|
||||
return (nextIndex, nil)
|
||||
}*/
|
||||
|
||||
public func toString(_ depth:Int = 0, spacingStrat:SpacingStrat = .tabs) -> (Int, String) {
|
||||
var newDepth = depth
|
||||
var result = "<\(nodeName) \(renderAttributes())"
|
||||
if (isVoidElement) {
|
||||
result += "/>"
|
||||
} else {
|
||||
result += ">"
|
||||
}
|
||||
return (newDepth, result)
|
||||
}
|
||||
|
||||
public func renderTag() -> String {
|
||||
let closing = isVoidElement ? "/" : ""
|
||||
let attributes = renderAttributes()
|
||||
if (attributes.isEmpty) {
|
||||
let result = "<\(nodeName)\(closing)>"
|
||||
return result
|
||||
} else {
|
||||
let result = "<\(nodeName) \(renderAttributes()) \(closing)>"
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func isGlobalHTMLAttribute(_ key:String) -> Bool {
|
||||
if let _ = GlobalAttributeKey(rawValue: key) {
|
||||
return true
|
||||
}
|
||||
if let _ = GlobalEventKey(rawValue: key.asSubstring()) {
|
||||
return true
|
||||
}
|
||||
if key[..<key.index(key.startIndex, offsetBy: 5)] == "data-" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
public protocol IHTMLParent : HTMLNode {
|
||||
var childrenAny:[HTMLNode] { get set }
|
||||
}
|
||||
|
||||
/*
|
||||
public class NHTMLParent : NHTMLRenderable {
|
||||
public var children:[NHTMLRenderable] { get {} set {} }
|
||||
|
||||
override open func findById(_ id:String) -> NHTMLRenderable? {
|
||||
if let result = super.findById(id) {
|
||||
return result
|
||||
}
|
||||
for eachChild in children {
|
||||
if let result = eachChild.findById(id) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}*/
|
||||
|
||||
Reference in New Issue
Block a user