         // Apply gradient mask for home location
         applyGradientMask(fadeLength, animated: false)
     }
-    
+
     /**
      Pauses the text scrolling animation, at any point during an in-progress animation.
-     
+
      - Note: This method has no effect if a scroll animation is NOT already in progress. To prevent automatic scrolling on a newly-initialized label prior to its presentation onscreen, see the `holdScrolling` property.
-     
+
      - SeeAlso: holdScrolling
      - SeeAlso: unpauseLabel
      */

import UIKit
import QuartzCore

public class MarqueeLabel: UILabel {
    
    /**
     An enum that defines the types of `MarqueeLabel` scrolling
     
     - leftRight: Scrolls left first, then back right to the original position.
     - rightLeft: Scrolls right first, then back left to the original position.
     - continuous: continuously scrolls left (with a pause at the original position if animationDelay is set).
     - continuousReverse: continuously scrolls right (with a pause at the original position if animationDelay is set).
     */
    public enum `Type` {
        case leftRight
        case rightLeft
        case continuous
        case continuousReverse
    }
    
    //
    // MARK: - Public properties
    //
    
    /**
     Defines the direction and method in which the `MarqueeLabel` instance scrolls.
     `MarqueeLabel` supports four types of scrolling: `leftRight`, `rightLeft`, `continuous`, and `continuousReverse`.
     
     Given the nature of how text direction works, the options for the `type` property require specific text alignments
     and will set the textAlignment property accordingly.
     
     - `leftRight` type is ONLY compatible with a label text alignment of `NSTextAlignmentLeft`.
     - `rightLeft` type is ONLY compatible with a label text alignment of `NSTextAlignmentRight`.
     - `continuous` does not require a text alignment (it is effectively centered).
     - `continuousReverse` does not require a text alignment (it is effectively centered).
     
     Defaults to `continuous`.
     
     - SeeAlso: textAlignment
     */
    public var type: Type = .continuous {
        didSet {
            guard type != oldValue else { return }
            updateAndScroll()
        }
    }
    
    /**
     Specifies the animation curve used in the scrolling motion of the labels.
     Allowable options:
     
     - `UIViewAnimationOptionCurveEaseInOut`
     - `UIViewAnimationOptionCurveEaseIn`
     - `UIViewAnimationOptionCurveEaseOut`
     - `UIViewAnimationOptionCurveLinear`
     
     Defaults to `UIViewAnimationOptionCurveEaseInOut`.
     */
    public var animationCurve: UIView.AnimationCurve = .linear
    
    /**
     A boolean property that sets whether the `MarqueeLabel` should behave like a normal `UILabel`.
     
     When set to `true` the `MarqueeLabel` will behave and look like a normal `UILabel`, and  will not begin any scrolling animations.
     Changes to this property take effect immediately, removing any in-flight animation as well as any edge fade. Note that `MarqueeLabel`
     will respect the current values of the `lineBreakMode` and `textAlignment`properties while labelized.
     
     To simply prevent automatic scrolling, use the `holdScrolling` property.
     
     Defaults to `false`.
     
     - SeeAlso: holdScrolling
     - SeeAlso: lineBreakMode
     @warning The label will not automatically scroll when this property is set to `true`.
     @warning The UILabel default setting for the `lineBreakMode` property is `NSLineBreakByTruncatingTail`, which truncates
     the text adds an ellipsis glyph (...). Set the `lineBreakMode` property to `NSLineBreakByClipping` in order to avoid the
     ellipsis, especially if using an edge transparency fade.
     */
    @IBInspectable public var labelize: Bool = false {
        didSet {
            guard labelize != oldValue else { return }
            updateAndScroll()
        }
    }
    
    /**
     A boolean property that sets whether the `MarqueeLabel` should hold (prevent) automatic label scrolling.
     
     When set to `true`, `MarqueeLabel` will not automatically scroll even its text is larger than the specified frame,
     although the specified edge fades will remain.
     
     To set `MarqueeLabel` to act like a normal UILabel, use the `labelize` property.
     
     Defaults to `false`.
     
     - SeeAlso: labelize
     @warning The label will not automatically scroll when this property is set to `true`.
     */
    @IBInspectable public var holdScrolling: Bool = false {
        didSet {
            guard holdScrolling != oldValue else { return }
            if oldValue == true && !(awayFromHome || labelize || tapToScroll ) && labelShouldScroll() {
                beginScroll()
            }
        }
    }
    
    /**
     A boolean property that sets whether the `MarqueeLabel` should only begin a scroll when tapped.
     
     If this property is set to `true`, the `MarqueeLabel` will only begin a scroll animation cycle when tapped. The label will
     not automatically being a scroll. This setting overrides the setting of the `holdScrolling` property.
     
     Defaults to `false`.
     
     @warning The label will not automatically scroll when this property is set to `false`.
     - SeeAlso: holdScrolling
     */
    @IBInspectable public var tapToScroll: Bool = false {
        didSet {
            guard tapToScroll != oldValue else { return }
            if tapToScroll {
                let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(MarqueeLabel.labelWasTapped(_:)))
                self.addGestureRecognizer(tapRecognizer)
                isUserInteractionEnabled = true
            } else {
                if let recognizer = self.gestureRecognizers!.first as UIGestureRecognizer? {
                    self.removeGestureRecognizer(recognizer)
                }
                isUserInteractionEnabled = false
            }
        }
    }
    
    /**
     A read-only boolean property that indicates if the label's scroll animation has been paused.
     
     - SeeAlso: pauseLabel
     - SeeAlso: unpauseLabel
     */
    public var isPaused: Bool {
        return sublabel.layer.speed == 0.0
    }
    
    /**
     A boolean property that indicates if the label is currently away from the home location.
     
     The "home" location is the traditional location of `UILabel` text. This property essentially reflects if a scroll animation is underway.
     */
    public var awayFromHome: Bool {
        guard let presentationLayer = sublabel.layer.presentation() else {
            return false
        }
        return !(presentationLayer.position.x == homeLabelFrame.origin.x)
    }
    
    /**
     The `MarqueeLabel` scrolling speed may be defined by one of two ways:
     - Rate(CGFloat): The speed is defined by a rate of motion, in units of points per second.
     - Duration(CGFloat): The speed is defined by the time to complete a scrolling animation cycle, in units of seconds.
     
     Each case takes an associated `CGFloat` value, which is the rate/duration desired.
     */
    public enum SpeedLimit {
        
        case rate(CGFloat)
        case duration(CGFloat)
        
        var value: CGFloat {
            switch self {
            case .rate(let rate):

        guard (!isPaused && awayFromHome) else {
            return
        }

        // Pause sublabel position animations
        let labelPauseTime = sublabel.layer.convertTime(CACurrentMediaTime(), from: nil)
        sublabel.layer.speed = 0.0
        sublabel.layer.timeOffset = labelPauseTime

        // Pause gradient fade animation
        let gradientPauseTime = maskLayer?.convertTime(CACurrentMediaTime(), from:nil)
        maskLayer?.speed = 0.0
        maskLayer?.timeOffset = gradientPauseTime!
    }

    /**
     Un-pauses a previously paused text scrolling animation. This method has no effect if the label was not previously paused using `pauseLabel`.

     - SeeAlso: pauseLabel
     */
    public func unpauseLabel() {
