<commit_before>//
//  LinkLabel.swift
//  MTLLinkLabel
//
//  Created by HiraiKokoro on 2015/10/06.
//  Copyright © 2015年 HiraiKokoro. All rights reserved.
//

import UIKit

public typealias LinkSelection = (NSURL) -> Void

public protocol LinkLabelDelegate: NSObjectProtocol {
    
    func linkAttributeForLinkLabel(linkLabel: LinkLabel, checkingType: NSTextCheckingType) -> [String: AnyObject]
    
    func linkDefaultAttributeForCustomeLink(linkLabel: LinkLabel) -> [String: AnyObject]
    
    func linkLabelExecuteLink(linkLabel: LinkLabel, text: String, result: NSTextCheckingResult) -> Void
    
    func linkLabelCheckingLinkType() -> NSTextCheckingTypes
    
}

public extension LinkLabelDelegate {
    
    func linkLabelExecuteLink(linkLabel: LinkLabel, var text: String, result: NSTextCheckingResult) -> Void {
        
        if result.resultType.contains(.Link) {
            
            let pattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]+"
            if NSPredicate(format: "SELF MATCHES '\(pattern)'").evaluateWithObject(text) {
                UIApplication.sharedApplication().openURL(NSURL(string: "mailto:" + text)!)
                return
            }
            
            if !text.hasPrefix("http://") && !text.hasPrefix("https://") {
                text = "http://" + text
            }
            guard let url = NSURL(string: text) else { return }
            UIApplication.sharedApplication().openURL(url)
            
        }
        else if result.resultType.contains(.PhoneNumber) {
            let telURLString = "tel:" + text
            UIApplication.sharedApplication().openURL(NSURL(string: telURLString)!)
        }
    }
    
    func linkAttributeForLinkLabel(linkLabel: LinkLabel, checkingType: NSTextCheckingType) -> [String: AnyObject] {
        return [
            NSForegroundColorAttributeName: linkLabel.tintColor,
            NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleSingle.rawValue
        ]
    }
    
    func linkDefaultAttributeForCustomeLink(linkLabel: LinkLabel) -> [String: AnyObject] {
        return [
            NSForegroundColorAttributeName: linkLabel.tintColor,
            NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleSingle.rawValue
        ]
    }
    
    func linkLabelCheckingLinkType() -> NSTextCheckingTypes {
        return NSTextCheckingType.Link.rawValue
            | NSTextCheckingType.PhoneNumber.rawValue
    }
}

public class LinkLabel: UILabel {
    
    public weak var delegate: LinkLabelDelegate?
    
    override public var text: String? {
        didSet {
            guard let str = text else {
                super.attributedText = nil
                return
            }
            let mAttributedString = NSMutableAttributedString(string: str)
            if let text = self.text {
                if text.characters.count > 0 {
                    mAttributedString.addAttribute(
                    NSFontAttributeName,
                        value: self.font,
                        range: NSMakeRange(0, text.characters.count)
                    )
                }
            }
            
            super.text = nil
            self.attributedText = mAttributedString
        }
    }
    
    override public var attributedText: NSAttributedString? {
        didSet {
            self.reloadAttributedString()
        }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.delegate = self.dummyDelegate
    }

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.delegate = self.dummyDelegate
    }
    
    // MARK: - Add custome link
    
    public func addLink(url: NSURL, range: NSRange, linkAttribute: [String: AnyObject]? = nil, selection: LinkSelection?) -> LinkLabel {
        self.customLinks.append(
            CustomLink(
                url: url,
                range: range,
                linkAttribute: linkAttribute ?? self.delegate?.linkDefaultAttributeForCustomeLink(self) ?? [String: AnyObject](),
                selection: selection
            )
        )
        self.reloadAttributedString()
        return self
    }
    
    public func removeLink(url: NSURL, range: NSRange) -> LinkLabel {
        self.customLinks = self.customLinks.filter{!($0.url.path == url.path && $0.range.location == range.location && $0.range.length == range.length)}
        self.reloadAttributedString()
        return self
    }
    
    // MARK: - touch
    
    override public func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        guard let location = touches.first?.locationInView(self) else { return }
        guard let textContainer = self.textView?.textContainer else { return }
        let index = layoutManager.glyphIndexForPoint(location, inTextContainer: textContainer)
        
        self.searchCustomeLink(index, inCustomeLinks: self.customLinks) { (linkOrNil) -> Void in
            
            if let link = linkOrNil {
                let mAttributedString = NSMutableAttributedString(attributedString: self.attributedText!)
                mAttributedString.addAttribute(NSBackgroundColorAttributeName, value: UIColor(white: 0.0, alpha: 0.1), range: link.range)
                self.attributedText = mAttributedString
                return
            }
            
            self.searchResult(index, inResults: self.lastCheckingResults) { (resultOrNil) -> Void in
                
                guard let result = resultOrNil else { return }
                
                let mAttributedString = NSMutableAttributedString(attributedString: self.attributedText!)
                mAttributedString.addAttribute(NSBackgroundColorAttributeName, value: UIColor(white: 0.0, alpha: 0.1), range: result.range)
                self.attributedText = mAttributedString
            }
        }
    }
    
    override public func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        guard let location = touches.first?.locationInView(self) else { return }
        guard let textContainer = self.textView?.textContainer else { return }
        let index = layoutManager.glyphIndexForPoint(location, inTextContainer: textContainer)
        
        self.searchCustomeLink(index, inCustomeLinks: self.customLinks) { (linkOrNil) -> Void in
            if let link = linkOrNil {
                link.selection?(link.url)
                return
            }
            
            self.searchResult(index, inResults: self.lastCheckingResults) { (resultOrNil) -> Void in
                
                guard let result = resultOrNil else {
                    return
                }
                
                self.delegate?.linkLabelExecuteLink(
                    self,
                    text: (self.attributedText!.string as NSString).substringWithRange(result.range),
                    result: result
                )
            }
        }
        
        // NSAttributedStrings range length is NSStrings lenhgth. I can't use "Swift.String.charactors.count".
        let count = ((self.attributedText?.string ?? "") as NSString).length
<commit_msg>reset custom links<commit_after>//
//  LinkLabel.swift
//  MTLLinkLabel
//
//  Created by HiraiKokoro on 2015/10/06.
//  Copyright © 2015年 HiraiKokoro. All rights reserved.
//

import UIKit

public typealias LinkSelection = (NSURL) -> Void

public protocol LinkLabelDelegate: NSObjectProtocol {
    
    func linkAttributeForLinkLabel(linkLabel: LinkLabel, checkingType: NSTextCheckingType) -> [String: AnyObject]
    
    func linkDefaultAttributeForCustomeLink(linkLabel: LinkLabel) -> [String: AnyObject]
    
    func linkLabelExecuteLink(linkLabel: LinkLabel, text: String, result: NSTextCheckingResult) -> Void
    
    func linkLabelCheckingLinkType() -> NSTextCheckingTypes
    
}

public extension LinkLabelDelegate {
    
    func linkLabelExecuteLink(linkLabel: LinkLabel, var text: String, result: NSTextCheckingResult) -> Void {
        
        if result.resultType.contains(.Link) {
            
            let pattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]+"
            if NSPredicate(format: "SELF MATCHES '\(pattern)'").evaluateWithObject(text) {
                UIApplication.sharedApplication().openURL(NSURL(string: "mailto:" + text)!)
                return
            }
            
            if !text.hasPrefix("http://") && !text.hasPrefix("https://") {
                text = "http://" + text
            }
            guard let url = NSURL(string: text) else { return }
            UIApplication.sharedApplication().openURL(url)
            
        }
        else if result.resultType.contains(.PhoneNumber) {
            let telURLString = "tel:" + text
            UIApplication.sharedApplication().openURL(NSURL(string: telURLString)!)
        }
    }
    
    func linkAttributeForLinkLabel(linkLabel: LinkLabel, checkingType: NSTextCheckingType) -> [String: AnyObject] {
        return [
            NSForegroundColorAttributeName: linkLabel.tintColor,
            NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleSingle.rawValue
        ]
    }
    
    func linkDefaultAttributeForCustomeLink(linkLabel: LinkLabel) -> [String: AnyObject] {
        return [
            NSForegroundColorAttributeName: linkLabel.tintColor,
            NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleSingle.rawValue
        ]
    }
    
    func linkLabelCheckingLinkType() -> NSTextCheckingTypes {
        return NSTextCheckingType.Link.rawValue
            | NSTextCheckingType.PhoneNumber.rawValue
    }
}

public class LinkLabel: UILabel {
    
    public weak var delegate: LinkLabelDelegate?
    
    override public var text: String? {
        didSet {
            self.customLinks.removeAll()
            guard let str = text else {
                super.attributedText = nil
                return
            }
            let mAttributedString = NSMutableAttributedString(string: str)
            if let text = self.text {
                if text.characters.count > 0 {
                    mAttributedString.addAttribute(
                    NSFontAttributeName,
                        value: self.font,
                        range: NSMakeRange(0, text.characters.count)
                    )
                }
            }
            
            super.text = nil
            self.attributedText = mAttributedString
        }
    }
    
    override public var attributedText: NSAttributedString? {
        didSet {
            self.customLinks.removeAll()
            self.reloadAttributedString()
        }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.delegate = self.dummyDelegate
    }

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.delegate = self.dummyDelegate
    }
    
    // MARK: - Add custome link
    
    public func addLink(url: NSURL, range: NSRange, linkAttribute: [String: AnyObject]? = nil, selection: LinkSelection?) -> LinkLabel {
        self.customLinks.append(
            CustomLink(
                url: url,
                range: range,
                linkAttribute: linkAttribute ?? self.delegate?.linkDefaultAttributeForCustomeLink(self) ?? [String: AnyObject](),
                selection: selection
            )
        )
        self.reloadAttributedString()
        return self
    }
    
    public func removeLink(url: NSURL, range: NSRange) -> LinkLabel {
        self.customLinks = self.customLinks.filter{!($0.url.path == url.path && $0.range.location == range.location && $0.range.length == range.length)}
        self.reloadAttributedString()
        return self
    }
    
    // MARK: - touch
    
    override public func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        guard let location = touches.first?.locationInView(self) else { return }
        guard let textContainer = self.textView?.textContainer else { return }
        let index = layoutManager.glyphIndexForPoint(location, inTextContainer: textContainer)
        
        self.searchCustomeLink(index, inCustomeLinks: self.customLinks) { (linkOrNil) -> Void in
            
            if let link = linkOrNil {
                let mAttributedString = NSMutableAttributedString(attributedString: self.attributedText!)
                mAttributedString.addAttribute(NSBackgroundColorAttributeName, value: UIColor(white: 0.0, alpha: 0.1), range: link.range)
                self.attributedText = mAttributedString
                return
            }
            
            self.searchResult(index, inResults: self.lastCheckingResults) { (resultOrNil) -> Void in
                
                guard let result = resultOrNil else { return }
                
                let mAttributedString = NSMutableAttributedString(attributedString: self.attributedText!)
                mAttributedString.addAttribute(NSBackgroundColorAttributeName, value: UIColor(white: 0.0, alpha: 0.1), range: result.range)
                self.attributedText = mAttributedString
            }
        }
    }
    
    override public func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        guard let location = touches.first?.locationInView(self) else { return }
        guard let textContainer = self.textView?.textContainer else { return }
        let index = layoutManager.glyphIndexForPoint(location, inTextContainer: textContainer)

        self.searchCustomeLink(index, inCustomeLinks: self.customLinks) { (linkOrNil) -> Void in
            if let link = linkOrNil {
                link.selection?(link.url)
                return
            }
            
            self.searchResult(index, inResults: self.lastCheckingResults) { (resultOrNil) -> Void in
                
                guard let result = resultOrNil else {
                    return
                }
                
                self.delegate?.linkLabelExecuteLink(
                    self,
                    text: (self.attributedText!.string as NSString).substringWithRange(result.range),
                    result: result
                )
            }
        }
        
        // NSAttributedStrings range length is NSStrings lenhgth. I can't use "Swift.String.charactors.count".
        let count = ((self.attributedText?.string ?? "") as NSString).length

{'fraction_non_alphanumeric': 0.08985647923455592, 'fraction_numerical': 0.00402135478055883, 'mean_word_length': 1.9943948515673655, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 39, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3598974', 'n_tokens_mistral': 4014, 'n_tokens_neox': 3780, 'n_words': 792}