From bd6b30e87ce78f3db2d51aa22f629699076419e7 Mon Sep 17 00:00:00 2001 From: Jorge Leandro Perez Date: Wed, 29 Nov 2017 16:36:14 -0300 Subject: [PATCH 1/7] ElementNode: Implements `onlyChild` helper --- .../Libxml2/DOM/Data/ElementNode.swift | 18 +++++++++++ AztecTests/HTML/Nodes/ElementNodeTests.swift | 31 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift b/Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift index 04b9ee766..c9c2710bb 100644 --- a/Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift +++ b/Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift @@ -234,6 +234,24 @@ public class ElementNode: Node { } + /// Returns the ElementNode instance, whenever there's a *single* children, of the specified nodeType (or nil otherwise). + /// + /// - Parameter type: Type of the 'single children' node to be retrieved. + /// + /// - Returns: the requested children (if it's the only children in the collection, and if the type matches), or nil otherwise. + /// + func onlyChild(ofType type: StandardElementType) -> ElementNode? { + guard children.count == 1, + let onlyChild = children.first as? ElementNode, + onlyChild.isNodeType(type) + else { + return nil + } + + return onlyChild + } + + /// Indicates whether the children of the specified node can be merged in, or not. /// /// - Parameters: diff --git a/AztecTests/HTML/Nodes/ElementNodeTests.swift b/AztecTests/HTML/Nodes/ElementNodeTests.swift index 3c2538069..6850c4bab 100644 --- a/AztecTests/HTML/Nodes/ElementNodeTests.swift +++ b/AztecTests/HTML/Nodes/ElementNodeTests.swift @@ -22,6 +22,7 @@ class ElementNodeTests: XCTestCase { XCTAssert(style1 == style1) } + /// Verifies that two different ElementNode(s) instances, with the same name and the exact same Children array /// return true when equality is checked. /// @@ -37,4 +38,34 @@ class ElementNodeTests: XCTestCase { XCTAssert(style1 == style2) XCTAssert(style1 !== style2) } + + + /// Verifies that `onlyChildr` returns the receiver's only child, if it's type matches with the specified one. + /// + func testOnlyChildReturnsSingleChildrenIfItRepresentsAnImage() { + let image = ElementNode(type: .img) + let parent = ElementNode(type: .a, attributes: [], children: [image]) + + XCTAssertEqual(parent.onlyChild(ofType: .img), image) + } + + + /// Verifies that `onlyChild` returns nil, if there is more than one children, no matter if their type match with the specified one. + /// + func testOnlyChildReturnsNilIfThereIsMoreThanOneChild() { + let image = ElementNode(type: .img) + let parent = ElementNode(type: .a, attributes: [], children: [image, image]) + + XCTAssertNil(parent.onlyChild(ofType: .img)) + } + + + /// Verifies that `onlyChild` returns nil, if there is at least one child, but with different type. + /// + func testOnlyChildReturnsNilIfThereIsNoMatchingChild() { + let image = ElementNode(type: .img) + let parent = ElementNode(type: .a, attributes: [], children: [image]) + + XCTAssertNil(parent.onlyChild(ofType: .b)) + } } From abfe81e840156e9010e7d53442c64a72d4c7daf2 Mon Sep 17 00:00:00 2001 From: Jorge Leandro Perez Date: Wed, 29 Nov 2017 16:36:41 -0300 Subject: [PATCH 2/7] NSAttributedString+Attachments: Updating `textAttachment` string mechanism --- .../NSAttributedString+Attachments.swift | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Aztec/Classes/Extensions/NSAttributedString+Attachments.swift b/Aztec/Classes/Extensions/NSAttributedString+Attachments.swift index bcf8c5960..9dfa8ce4b 100644 --- a/Aztec/Classes/Extensions/NSAttributedString+Attachments.swift +++ b/Aztec/Classes/Extensions/NSAttributedString+Attachments.swift @@ -2,6 +2,15 @@ import Foundation import UIKit +// MARK: - Constants +// +extension String { + /// String containing the NSTextAttachment Character + /// + static let textAttachment = String(UnicodeScalar(NSAttachmentCharacter)!) +} + + // MARK: - NSAttributedString Extension for Attachments // extension NSAttributedString @@ -11,11 +20,6 @@ extension NSAttributedString static let lengthOfTextAttachment = NSAttributedString(attachment: NSTextAttachment()).length - /// String containing the NSTextAttachment Character - /// - static let textAttachmentString = String(UnicodeScalar(NSAttachmentCharacter)!) - - /// Helper Initializer: returns an Attributed String, with the specified attachment, styled with a given /// collection of attributes. @@ -24,7 +28,7 @@ extension NSAttributedString var attributesWithAttachment = attributes attributesWithAttachment[.attachment] = attachment - self.init(string: NSAttributedString.textAttachmentString, attributes: attributesWithAttachment) + self.init(string: .textAttachment, attributes: attributesWithAttachment) } /// Loads any NSTextAttachment's lazy file reference, into a UIImage instance, in memory. From f132c438251306bf9b3a3090f6c38e3914cc332e Mon Sep 17 00:00:00 2001 From: Jorge Leandro Perez Date: Wed, 29 Nov 2017 16:39:15 -0300 Subject: [PATCH 3/7] AttributedStringSerializer: Wiring .textAttachment constant --- .../Conversions/AttributedStringSerializer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift b/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift index 6c7ed61da..fae5c1ff9 100644 --- a/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift +++ b/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift @@ -406,7 +406,7 @@ private extension AttributedStringSerializer { switch elementType { case .hr, .img, .video: - return NSAttributedString(string: String(UnicodeScalar(NSAttachmentCharacter)!), attributes: attributes) + return NSAttributedString(string: .textAttachment, attributes: attributes) case .br: return NSAttributedString(.lineSeparator, attributes: attributes) default: From 561b80fe0823258dc703da573e59ee2ca6789760 Mon Sep 17 00:00:00 2001 From: Jorge Leandro Perez Date: Wed, 29 Nov 2017 16:43:47 -0300 Subject: [PATCH 4/7] AttributedStringSerializer: Splitting linkedImage mechanism --- .../AttributedStringSerializer.swift | 61 +++++++++++-------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift b/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift index fae5c1ff9..3829c814c 100644 --- a/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift +++ b/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift @@ -358,9 +358,10 @@ extension AttributedStringSerializer { } } -private extension AttributedStringSerializer { - // MARK: - Implicit Representations +// MARK: - Implicit Representations +// +private extension AttributedStringSerializer { /// Some elements have an implicit representation that doesn't really follow the standard /// conversion logic. This method takes care of such specialized conversions. @@ -376,23 +377,15 @@ private extension AttributedStringSerializer { guard let elementType = element.standardName else { return nil } - - if let imgElement = linkedImageElement(for: element) { - var attributesWithoutLink = attributes - attributesWithoutLink[.link] = nil - attributesWithoutLink[.linkHtmlRepresentation] = nil - - let imgAttributes = self.attributes(for: imgElement, inheriting: attributesWithoutLink) - let attachment = imgAttributes[.attachment] as! ImageAttachment - let linkText = element.stringValueForAttribute(named: HTMLLinkAttribute.Href.rawValue) ?? "" - attachment.linkURL = URL(string: linkText) - - return implicitRepresentation(for: imgElement, inheriting: imgAttributes)! + + if let linkedImageAttributes = linkedImageAttributes(for: element, inheriting: attributes) { + return implicitRepresentation(for: .img, inheriting: linkedImageAttributes) } return implicitRepresentation(for: elementType, inheriting: attributes) } + /// Some element types have an implicit representation that doesn't really follow the standard /// conversion logic. This method takes care of such specialized conversions. /// @@ -413,26 +406,40 @@ private extension AttributedStringSerializer { return nil } } - - /// Checks if the specified node is of type '.a' with one child '.img' node. - /// If true, returns the '.img' node. + + + /// Whenever the `element`'s nodeType is `a` (link!), and there's a single children of the `.img` type, this method will return the + /// NSAttributedString attributes representing the 'Linked Image' Element. /// /// - Parameters: - /// - element: the node to get the information from + /// - element: The container element. + /// - attributes: NSAttributedString attributes, to be inherited. /// - /// - Returns: the child '.img' node if there is one + /// - Returns: The collection of 'Inherited Attributes' with it's internal ImageAttachment modified, so that it carries the 'linkElement' + /// target URL. /// - private func linkedImageElement(for element: ElementNode) -> ElementNode? { - guard element.isNodeType(.a) && element.children.count == 1, - let imgElement = element.children.first as? ElementNode, - imgElement.isNodeType(.img) else { - return nil + private func linkedImageAttributes(for element: ElementNode, inheriting attributes: [AttributedStringKey: Any]) -> [AttributedStringKey: Any]? { + guard element.isNodeType(.a), + let imgElement = element.onlyChild(ofType: .img) + else { + return nil } - - return imgElement + + var attributesWithoutLink = attributes + attributesWithoutLink[.link] = nil + attributesWithoutLink[.linkHtmlRepresentation] = nil + + let imgAttributes = self.attributes(for: imgElement, inheriting: attributesWithoutLink) + let linkText = element.stringValueForAttribute(named: HTMLLinkAttribute.Href.rawValue) ?? "" + + let attachment = imgAttributes[.attachment] as! ImageAttachment + attachment.linkURL = URL(string: linkText) + + return imgAttributes } } - + + // MARK: - Text Sanitization for Rendering private extension AttributedStringSerializer { From 878c1c3ce9c07d734e252cee01a4275769202234 Mon Sep 17 00:00:00 2001 From: Jorge Leandro Perez Date: Thu, 30 Nov 2017 15:27:24 -0300 Subject: [PATCH 5/7] Fixing multiple documentation typos --- Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift | 4 ++-- .../Conversions/AttributedStringSerializer.swift | 2 +- AztecTests/HTML/Nodes/ElementNodeTests.swift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift b/Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift index c9c2710bb..012c4babb 100644 --- a/Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift +++ b/Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift @@ -234,9 +234,9 @@ public class ElementNode: Node { } - /// Returns the ElementNode instance, whenever there's a *single* children, of the specified nodeType (or nil otherwise). + /// Returns the ElementNode instance, whenever there's a *single* child, of the specified nodeType (or nil otherwise). /// - /// - Parameter type: Type of the 'single children' node to be retrieved. + /// - Parameter type: Type of the 'single child' node to be retrieved. /// /// - Returns: the requested children (if it's the only children in the collection, and if the type matches), or nil otherwise. /// diff --git a/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift b/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift index 3829c814c..8b7a5cc0c 100644 --- a/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift +++ b/Aztec/Classes/NSAttributedString/Conversions/AttributedStringSerializer.swift @@ -408,7 +408,7 @@ private extension AttributedStringSerializer { } - /// Whenever the `element`'s nodeType is `a` (link!), and there's a single children of the `.img` type, this method will return the + /// Whenever the `element`'s nodeType is `a` (link!), and there's a single child of the `.img` type, this method will return the /// NSAttributedString attributes representing the 'Linked Image' Element. /// /// - Parameters: diff --git a/AztecTests/HTML/Nodes/ElementNodeTests.swift b/AztecTests/HTML/Nodes/ElementNodeTests.swift index 6850c4bab..62e0f3109 100644 --- a/AztecTests/HTML/Nodes/ElementNodeTests.swift +++ b/AztecTests/HTML/Nodes/ElementNodeTests.swift @@ -40,7 +40,7 @@ class ElementNodeTests: XCTestCase { } - /// Verifies that `onlyChildr` returns the receiver's only child, if it's type matches with the specified one. + /// Verifies that `onlyChild` returns the receiver's only child, if it's type matches with the specified one. /// func testOnlyChildReturnsSingleChildrenIfItRepresentsAnImage() { let image = ElementNode(type: .img) From a6e2ead401c223ec8300ab68ff27727758a1e3aa Mon Sep 17 00:00:00 2001 From: Jorge Leandro Perez Date: Thu, 30 Nov 2017 16:04:41 -0300 Subject: [PATCH 6/7] ElementNode: Addressing feedback --- .../Libxml2/DOM/Data/ElementNode.swift | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift b/Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift index 012c4babb..305492593 100644 --- a/Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift +++ b/Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift @@ -234,21 +234,29 @@ public class ElementNode: Node { } - /// Returns the ElementNode instance, whenever there's a *single* child, of the specified nodeType (or nil otherwise). + /// If there's exactly just one child node, this method will return it's instance. Otherwise, nil will be returned + /// + func onlyChild() -> ElementNode? { + guard children.count == 1 else { + return nil + } + + return children.first as? ElementNode + } + + + /// Returns the child ElementNode of the specified nodeType -whenever there's a *single* child-, or nil otherwise. /// /// - Parameter type: Type of the 'single child' node to be retrieved. /// /// - Returns: the requested children (if it's the only children in the collection, and if the type matches), or nil otherwise. /// func onlyChild(ofType type: StandardElementType) -> ElementNode? { - guard children.count == 1, - let onlyChild = children.first as? ElementNode, - onlyChild.isNodeType(type) - else { + guard let child = onlyChild(), child.isNodeType(type) else { return nil } - return onlyChild + return child } From 707a52674e5aea9da4d8bca76e4d193f8926da63 Mon Sep 17 00:00:00 2001 From: Jorge Leandro Perez Date: Thu, 30 Nov 2017 21:43:12 -0300 Subject: [PATCH 7/7] ElementNode: Fixing comment --- Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift b/Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift index 305492593..41f1f1a69 100644 --- a/Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift +++ b/Aztec/Classes/Libxml2/DOM/Data/ElementNode.swift @@ -249,7 +249,7 @@ public class ElementNode: Node { /// /// - Parameter type: Type of the 'single child' node to be retrieved. /// - /// - Returns: the requested children (if it's the only children in the collection, and if the type matches), or nil otherwise. + /// - Returns: the requested child (if it's the only children in the collection, and if the type matches), or nil otherwise. /// func onlyChild(ofType type: StandardElementType) -> ElementNode? { guard let child = onlyChild(), child.isNodeType(type) else {