Skip to content

Commit

Permalink
Merge branch 'release/0.5a7'
Browse files Browse the repository at this point in the history
  • Loading branch information
SergioEstevao committed Mar 27, 2017
2 parents 8b478be + 5ca6272 commit 6e006d4
Show file tree
Hide file tree
Showing 62 changed files with 4,752 additions and 2,648 deletions.
170 changes: 129 additions & 41 deletions Aztec.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

151 changes: 49 additions & 102 deletions Aztec/Classes/Converters/HTMLNodeToNSAttributedString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ class HMTLNodeToNSAttributedString: SafeConverter {
self.defaultFontDescriptor = defaultFontDescriptor
}

private lazy var defaultAttributes: [String: Any] = {
let defaultFont = UIFont(descriptor: self.defaultFontDescriptor, size: self.defaultFontDescriptor.pointSize)
return [NSFontAttributeName: defaultFont]
}()
// MARK: - Conversion

/// Main conversion method.
Expand All @@ -36,10 +40,7 @@ class HMTLNodeToNSAttributedString: SafeConverter {
/// - Returns: the converted node as an `NSAttributedString`.
///
func convert(_ node: Node) -> NSAttributedString {

let defaultFont = UIFont(descriptor: defaultFontDescriptor, size: defaultFontDescriptor.pointSize)

return convert(node, inheritingAttributes: [NSFontAttributeName: defaultFont])
return convert(node, inheritingAttributes: defaultAttributes)
}

/// Recursive conversion method. Useful for maintaining the font style of parent nodes when
Expand Down Expand Up @@ -97,6 +98,16 @@ class HMTLNodeToNSAttributedString: SafeConverter {
/// - Returns: the converted node as an `NSAttributedString`.
///
fileprivate func convertCommentNode(_ node: CommentNode, inheritingAttributes inheritedAttributes: [String:Any]) -> NSAttributedString {
let moreLabel = "more"
if node.comment.hasPrefix(moreLabel) {
var attributes = inheritedAttributes;
let moreAttachment = MoreAttachment()
let index = moreLabel.endIndex
moreAttachment.message = node.comment.substring(from: index)
moreAttachment.label = NSAttributedString(string: NSLocalizedString("MORE", comment: "Text for the center of the more divider"), attributes: defaultAttributes)
attributes[NSAttachmentAttributeName] = moreAttachment
return NSAttributedString(string:String(UnicodeScalar(NSAttachmentCharacter)!), attributes: attributes)
}
return NSAttributedString(string: node.text(), attributes: inheritedAttributes)
}

Expand Down Expand Up @@ -157,48 +168,25 @@ class HMTLNodeToNSAttributedString: SafeConverter {

var attributes = inheritedAttributes

// Since a default font is requested by this class, there's no way this attribute should
// ever be unset.
//
precondition(inheritedAttributes[NSFontAttributeName] is UIFont)
let baseFont = inheritedAttributes[NSFontAttributeName] as! UIFont
let baseFontDescriptor = baseFont.fontDescriptor
let descriptor = fontDescriptor(forNode: node, withBaseFontDescriptor: baseFontDescriptor)
var attributeValue: Any?

if descriptor != baseFontDescriptor {
attributes[NSFontAttributeName] = UIFont(descriptor: descriptor, size: descriptor.pointSize)
}

if isLink(node) {
let linkURL: String
if node.isNodeType(.a) {
var linkURL: URL?

if let attributeIndex = node.attributes.index(where: { $0.name == HTMLLinkAttribute.Href.rawValue }),
let attribute = node.attributes[attributeIndex] as? StringAttribute {

linkURL = attribute.value
linkURL = URL(string: attribute.value)
} else {
// We got a link tag without an HREF attribute
//
linkURL = ""
linkURL = URL(string: "")
}

attributes[NSLinkAttributeName] = linkURL as AnyObject?
}

if isStrikedThrough(node) {
attributes[NSStrikethroughStyleAttributeName] = NSUnderlineStyle.styleSingle.rawValue as AnyObject?
}

if isUnderlined(node) {
attributes[NSUnderlineStyleAttributeName] = NSUnderlineStyle.styleSingle.rawValue as AnyObject?
}

if isBlockquote(node) {
let formatter = BlockquoteFormatter()
attributes = formatter.apply(to: attributes)
attributeValue = linkURL
}

if isImage(node) {
if node.isNodeType(.img) {
let url: URL?

if let urlString = node.valueForStringAttribute(named: "src") {
Expand All @@ -220,79 +208,38 @@ class HMTLNodeToNSAttributedString: SafeConverter {
}
}
}
attributes[NSAttachmentAttributeName] = attachment
attributeValue = attachment
}

if node.isNodeType(.ol) {
let formatter = TextListFormatter(style: .ordered)
attributes = formatter.apply(to: attributes)
}

if node.isNodeType(.ul) {
let formatter = TextListFormatter(style: .unordered)
attributes = formatter.apply(to: attributes)
for (key, formatter) in elementToFormattersMap {
if node.isNodeType(key) {
if let standardValueFormatter = formatter as? StandardAttributeFormatter,
let value = attributeValue {
standardValueFormatter.attributeValue = value
}
attributes = formatter.apply(to: attributes);
}
}

return attributes
}

// MARK: - Font

fileprivate func fontDescriptor(forNode node: ElementNode, withBaseFontDescriptor fontDescriptor: UIFontDescriptor) -> UIFontDescriptor {
let traits = symbolicTraits(forNode: node, withBaseSymbolicTraits: fontDescriptor.symbolicTraits)

return fontDescriptor.withSymbolicTraits(traits)!
}

/// Gets a list of symbolic traits representing the specified node.
///
/// - Parameters:
/// - node: the node to get the traits from.
///
/// - Returns: the requested symbolic traits.
///
fileprivate func symbolicTraits(forNode node: ElementNode, withBaseSymbolicTraits baseTraits: UIFontDescriptorSymbolicTraits) -> UIFontDescriptorSymbolicTraits {

var traits = baseTraits

if isBold(node) {
traits.insert(.traitBold)
}

if isItalic(node) {
traits.insert(.traitItalic)
}

return traits
}

// MARK: - Node Style Checks

fileprivate func isLink(_ node: ElementNode) -> Bool {
return node.name == StandardElementType.a.rawValue
}

fileprivate func isBold(_ node: ElementNode) -> Bool {
return StandardElementType.b.equivalentNames.contains(node.name)
}

fileprivate func isItalic(_ node: ElementNode) -> Bool {
return StandardElementType.i.equivalentNames.contains(node.name)
}

fileprivate func isStrikedThrough(_ node: ElementNode) -> Bool {
return StandardElementType.s.equivalentNames.contains(node.name)
}

fileprivate func isUnderlined(_ node: ElementNode) -> Bool {
return node.name == StandardElementType.u.rawValue
}

fileprivate func isBlockquote(_ node: ElementNode) -> Bool {
return node.name == StandardElementType.blockquote.rawValue
}

fileprivate func isImage(_ node: ElementNode) -> Bool {
return node.name == StandardElementType.img.rawValue
}
public let elementToFormattersMap: [StandardElementType: AttributeFormatter] = [
.ol: TextListFormatter(style: .ordered),
.ul: TextListFormatter(style: .unordered),
.blockquote: BlockquoteFormatter(),
.strong: BoldFormatter(),
.em: ItalicFormatter(),
.u: UnderlineFormatter(),
.del: StrikethroughFormatter(),
.a: LinkFormatter(),
.img: ImageFormatter(),
.hr: HRFormatter(),
.h1: HeaderFormatter(headerLevel: .h1),
.h2: HeaderFormatter(headerLevel: .h2),
.h3: HeaderFormatter(headerLevel: .h3),
.h4: HeaderFormatter(headerLevel: .h4),
.h5: HeaderFormatter(headerLevel: .h5),
.h6: HeaderFormatter(headerLevel: .h6)
]
}
30 changes: 30 additions & 0 deletions Aztec/Classes/Extensions/NSAttributedString+Analyzers.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Foundation
import UIKit


// MARK: - NSAttributedString Analyzer Helpers
//
extension NSAttributedString {

/// Returns true if the text preceding a given location contains the NSLinkAttribute.
///
func isLocationPreceededByLink(_ location: Int) -> Bool {
let beforeRange = NSRange(location: location - 1, length: 1)
guard beforeRange.location >= 0 else {
return false
}

return attribute(NSLinkAttributeName, at: beforeRange.location, effectiveRange: nil) != nil
}

/// Returns true if the text immediately succeding a given location contains the NSLinkAttribute.
///
func isLocationSuccededByLink(_ location: Int) -> Bool {
let afterRange = NSRange(location: location, length: 1)
guard afterRange.endLocation < length else {
return false
}

return attribute(NSLinkAttributeName, at: afterRange.location, effectiveRange: nil) != nil
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Foundation

extension NSAttributedString {

typealias ProcessDifferenceClosure = (NSRange, String, Any?, Any?) -> ()

func enumerateAttributeDifferences(
in range: NSRange,
against targetAttributes: [String : Any],
do processDifference: ProcessDifferenceClosure) {

enumerateAttributes(in: range, options: [], using: { (attributes, subRange, stop) in
NSAttributedString.enumerateAttributeDifferences(
in: subRange,
sourceAttributes: attributes,
targetAttributes: targetAttributes,
do: processDifference)
})
}

static func enumerateAttributeDifferences(
in range: NSRange,
sourceAttributes: [String : Any],
targetAttributes: [String : Any],
do processDifference: ProcessDifferenceClosure) {

let sourceKeys = Set(sourceAttributes.keys)
let targetKeys = Set(targetAttributes.keys)
let joinedKeys = sourceKeys.union(targetKeys)

for key in joinedKeys {
let sourceValue = sourceAttributes.contains(where: { return $0.key == key }) ? sourceAttributes[key] : nil
let targetValue = targetAttributes.contains(where: { return $0.key == key }) ? targetAttributes[key] : nil

processDifference(range, key, sourceValue, targetValue)
}
}
}
2 changes: 1 addition & 1 deletion Aztec/Classes/Extensions/NSAttributedString+Lists.swift
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ extension NSAttributedString

/// Internal convenience helper. Returns the internal string as a NSString instance
///
fileprivate var foundationString: NSString {
var foundationString: NSString {
return string as NSString
}
}
52 changes: 42 additions & 10 deletions Aztec/Classes/Extensions/String+RangeConversion.swift
Original file line number Diff line number Diff line change
@@ -1,20 +1,52 @@
import Foundation


// MARK: - String Extensions
// MARK: - String NSRange and Location convertion Extensions
//
extension String
{
func rangeFromNSRange(_ nsRange : NSRange) -> Range<String.Index>? {

let rangeStartIndex = utf16.startIndex.advanced(by: nsRange.location)
let rangeEndIndex = rangeStartIndex.advanced(by: nsRange.length)

if let from = String.Index(rangeStartIndex, within: self),
let to = String.Index(rangeEndIndex, within: self) {
return from ..< to
let from16 = utf16.index(utf16.startIndex, offsetBy: nsRange.location)
let to16 = utf16.index(from16, offsetBy: nsRange.length)

guard
let from = from16.samePosition(in: self),
let to = to16.samePosition(in: self)
else { return nil }
return from ..< to

}

func indexFromLocation(_ location: Int) -> String.Index? {
guard
let from16 = utf16.index(utf16.startIndex, offsetBy: location, limitedBy: utf16.endIndex),
let from = from16.samePosition(in: self)
else { return nil }
return from
}

func isLastValidLocation(_ location: Int) -> Bool {
if self.isEmpty {
return false
}
return index(before: endIndex) == indexFromLocation(location)
}

func location(after: Int) -> Int? {
guard let currentIndex = indexFromLocation(after) else {
return nil
}
let afterIndex = index(after: currentIndex)
let after16 = afterIndex.samePosition(in: utf16)
return utf16.distance(from: utf16.startIndex, to: after16)
}

func location(before: Int) -> Int? {
guard let currentIndex = indexFromLocation(before) else {
return nil
}

return nil
let beforeIndex = index(before: currentIndex)
let before16 = beforeIndex.samePosition(in: utf16)
return utf16.distance(from: utf16.startIndex, to: before16)
}
}
14 changes: 14 additions & 0 deletions Aztec/Classes/Extensions/UIFont+Emoji.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Foundation
import UIKit


// MARK: - UIFont Emoji Helpers
//
extension UIFont {

/// Indicates if the current font instance matches with iOS's Internal Emoji Font, or not.
///
var isAppleEmojiFont: Bool {
return fontName == ".AppleColorEmojiUI"
}
}
13 changes: 8 additions & 5 deletions Aztec/Classes/Extensions/UIFont+Traits.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Foundation
import UIKit


// MARK:- UIFont Traits Helpers
// MARK: - UIFont Traits Helpers
//
extension UIFont {

Expand All @@ -15,18 +15,21 @@ extension UIFont {
/// - Returns: A new UIFont with the same descriptors as the current instance, but with its traits updated, as specified.
///
func modifyTraits(_ traits: UIFontDescriptorSymbolicTraits, enable: Bool) -> UIFont {
var newTraits = fontDescriptor.symbolicTraits
let descriptor = fontDescriptor
var newTraits = descriptor.symbolicTraits

if enable {
newTraits.insert(traits)
} else {
newTraits.remove(traits)
}

let descriptor = fontDescriptor.withSymbolicTraits(newTraits)
let newFont = UIFont(descriptor: descriptor!, size: pointSize)
guard let newDescriptor = descriptor.withSymbolicTraits(newTraits) else {
assertionFailure("Unable to modify Font's Traits: \(self)")
return self
}

return newFont
return UIFont(descriptor: newDescriptor, size: pointSize)
}


Expand Down
Loading

0 comments on commit 6e006d4

Please sign in to comment.