// // 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 { get set } var globalEvents:Dictionary { get set } var dataAttributes:Dictionary { 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[.. = [:] public var globalEvents:Dictionary = [:] public var dataAttributes:Dictionary = [:] public init(globalAttributes: Dictionary, globalEvents: Dictionary, dataAttributes: Dictionary) { 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 = [:] public var globalEvents:Dictionary = [:] 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[.., globalEvents:Dictionary, dataAttributes:Dictionary) { 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 = [:] 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 = [:] 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[.. 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[.. NHTMLRenderable? { if let result = super.findById(id) { return result } for eachChild in children { if let result = eachChild.findById(id) { return result } } return nil } }*/