diff --git a/Aztec/Classes/Converters/HTMLNodeToNSAttributedString.swift b/Aztec/Classes/Converters/HTMLNodeToNSAttributedString.swift index 95b14580b..0e5968162 100644 --- a/Aztec/Classes/Converters/HTMLNodeToNSAttributedString.swift +++ b/Aztec/Classes/Converters/HTMLNodeToNSAttributedString.swift @@ -216,17 +216,17 @@ private extension HTMLNodeToNSAttributedString { return attributes } - let representation = HTMLRepresentation(for: .element(HTMLElementRepresentation(element))) + let elementRepresentation = HTMLElementRepresentation(element) + let representation = HTMLRepresentation(for: .element(elementRepresentation)) var finalAttributes = attributes if let elementFormatter = formatter(for: element) { finalAttributes = elementFormatter.apply(to: finalAttributes, andStore: representation) - } else if element.name == StandardElementType.li.rawValue { + } else if element.name == StandardElementType.li.rawValue { // ^ Since LI is handled by the OL and UL formatters, we can safely ignore it here. - finalAttributes = attributes } else { - finalAttributes = self.attributes(storing: element, in: finalAttributes) + finalAttributes = self.attributes(storing: elementRepresentation, in: finalAttributes) } for attribute in element.attributes { @@ -270,12 +270,17 @@ private extension HTMLNodeToNSAttributedString { /// /// - Returns: A collection of NSAttributedString Attributes, including the specified HTMLElementRepresentation. /// - private func attributes(storing element: ElementNode, in attributes: [String: Any]) -> [String: Any] { - let unsupportedHTML = attributes[UnsupportedHTMLAttributeName] as? UnsupportedHTML ?? UnsupportedHTML() - unsupportedHTML.append(element: element) - + private func attributes(storing representation: HTMLElementRepresentation, in attributes: [String: Any]) -> [String: Any] { + let unsupportedHTML = attributes[UnsupportedHTMLAttributeName] as? UnsupportedHTML + var representations = unsupportedHTML?.representations ?? [] + representations.append(representation) + + // Note: + // We'll *ALWAYS* store a copy of the UnsupportedHTML instance. Reason is: reusing the old instance + // would mean affecting a range that may fall beyond what we expected! + // var updated = attributes - updated[UnsupportedHTMLAttributeName] = unsupportedHTML + updated[UnsupportedHTMLAttributeName] = UnsupportedHTML(representations: representations) return updated } diff --git a/Aztec/Classes/Converters/NSAttributedStringToNodes.swift b/Aztec/Classes/Converters/NSAttributedStringToNodes.swift index ab99b7b91..9f59ed768 100644 --- a/Aztec/Classes/Converters/NSAttributedStringToNodes.swift +++ b/Aztec/Classes/Converters/NSAttributedStringToNodes.swift @@ -685,11 +685,13 @@ private extension NSAttributedStringToNodes { /// Extracts all of the Unsupported HTML Snippets contained within a collection of Attributes. /// private func processUnsupportedHTML(in attributes: [String: Any]) -> [ElementNode] { - guard let unsupported = attributes[UnsupportedHTMLAttributeName] as? UnsupportedHTML else { + guard let unsupportedHTML = attributes[UnsupportedHTMLAttributeName] as? UnsupportedHTML else { return [] } - return unsupported.elements + return unsupportedHTML.representations.map { representation in + return representation.toElementNode() + } } } diff --git a/Aztec/Classes/NSAttributedString/Styles/UnsupportedHTML.swift b/Aztec/Classes/NSAttributedString/Styles/UnsupportedHTML.swift index 8468aa36c..8cf59fd76 100644 --- a/Aztec/Classes/NSAttributedString/Styles/UnsupportedHTML.swift +++ b/Aztec/Classes/NSAttributedString/Styles/UnsupportedHTML.swift @@ -10,34 +10,24 @@ let UnsupportedHTMLAttributeName = "UnsupportedHTMLAttributeName" // class UnsupportedHTML: NSObject { - /// HTML Snippets not (natively) supported by the Editor (which will be re-serialized!!) + /// ElementRepresentation for Unsupported HTML /// - private(set) var snippets = [String]() + let representations: [HTMLElementRepresentation] - /// HTML Snippets not supported, converted back to their ElementNode representations + /// Default Initializer /// - var elements: [ElementNode] { - let converter = InHTMLConverter() - - return snippets.flatMap { snippet in - // Strip the Root Node(s): Always return the first child element - let root = converter.convert(snippet) - return root.children.first as? ElementNode - } + init(representations: [HTMLElementRepresentation]) { + self.representations = representations } /// Required Initializers /// - public required convenience init?(coder aDecoder: NSCoder) { - self.init() - self.snippets = aDecoder.decodeObject(forKey: Keys.elements) as? [String] ?? [] - } + public required init?(coder aDecoder: NSCoder) { + guard let representations = aDecoder.decodeObject(forKey: Keys.representations) as? [HTMLElementRepresentation] else { + return nil + } - /// Appends the specified Element Representation - /// - func append(element: ElementNode) { - let snippet = OutHTMLConverter().convert(element) - snippets.append(snippet) + self.representations = representations } } @@ -47,10 +37,10 @@ class UnsupportedHTML: NSObject { extension UnsupportedHTML: NSCoding { struct Keys { - static let elements = "elements" + static let representations = "representations" } open func encode(with aCoder: NSCoder) { - aCoder.encode(snippets, forKey: Keys.elements) + aCoder.encode(representations, forKey: Keys.representations) } } diff --git a/AztecTests/Converters/HTMLNodeToNSAttributedStringTests.swift b/AztecTests/Converters/HTMLNodeToNSAttributedStringTests.swift index 7804bd1d1..8b1bbe1e0 100644 --- a/AztecTests/Converters/HTMLNodeToNSAttributedStringTests.swift +++ b/AztecTests/Converters/HTMLNodeToNSAttributedStringTests.swift @@ -33,17 +33,18 @@ class HTMLNodeToNSAttributedStringTests: XCTestCase { return } + let representations = unsupportedHTML.representations XCTAssert(range.length == textNode.length()) - XCTAssert(unsupportedHTML.elements.count == 2) + XCTAssert(representations.count == 2) - let restoredSpanElement2 = unsupportedHTML.elements.last + let restoredSpanElement2 = representations.last XCTAssertEqual(restoredSpanElement2?.name, "span") let restoredSpanAttribute2 = restoredSpanElement2?.attributes.first XCTAssertEqual(restoredSpanAttribute2?.name, "class") XCTAssertEqual(restoredSpanAttribute2?.value.toString(), "aztec") - let restoredSpanElement1 = unsupportedHTML.elements.first + let restoredSpanElement1 = representations.first XCTAssertEqual(restoredSpanElement1?.name, "span") let restoredSpanAttribute1 = restoredSpanElement1?.attributes.first @@ -51,6 +52,7 @@ class HTMLNodeToNSAttributedStringTests: XCTestCase { XCTAssertEqual(restoredSpanAttribute1?.value.toString(), "first") } + /// Verifies that the DivFormatter effectively appends the DIV Element Representation, to the properties collection. /// func testHtmlDivFormatterEffectivelyAppendsNewDivProperty() { @@ -95,6 +97,41 @@ class HTMLNodeToNSAttributedStringTests: XCTestCase { XCTAssert(restoredDiv3.name == divNode3.name) XCTAssert(restoredDiv3.attributes == [divAttr3]) } + + + /// Verifies that BR elements contained within div tags do not cause any side effect. + /// Ref. #658 + /// + func testLineBreakTagWithinHTMLDivGetsProperlyEncodedAndDecoded() { + let inHtml = "