<commit_before>//
//  AlignedCollectionViewFlowLayout.swift
//
//  Created by <NAME> on 12/04/2017.
//  Copyright © 2017 <NAME>.
//
//  Licensed under the terms of the MIT license:
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

// MARK: - 🦆 Type definitions

/// An abstract protocol that defines an alignment.
protocol Alignment {}

/// Defines an alignment for UI elements.
public enum HorizontalAlignment: Alignment {
    case left
    case justified
    case right
}

/// Defines a vertical alignment for UI elements.
public enum VerticalAlignment: Alignment {
    case top
    case center
    case bottom
}

/// Describes an axis with respect to which items can be aligned.
private struct AlignmentAxis<A: Alignment> {
    
    /// Determines how items are aligned relative to the axis.
    let alignment: A
    
    /// Defines the position of the axis.
    /// * If the `Alignment` is horizontal, the alignment axis is vertical and this is the position on the `x` axis.
    /// * If the `Alignment` is vertical, the alignment axis is horizontal and this is the position on the `y` axis.
    let position: CGFloat
}


/// A `UICollectionViewFlowLayout` subclass that gives you control
/// over the horizontal and vertical alignment of the cells.
/// You can use it to align the cells like words in a left- or right-aligned text
/// and you can specify how the cells are vertically aligned in their row.
public class AlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {
    
    // MARK: - 🔶 Properties
    
    /// Determines how the cells are horizontally aligned in a row.
    /// - Note: The default is `.justified`.
    public var horizontalAlignment: HorizontalAlignment = .justified
    
    /// Determines how the cells are vertically aligned in a row.
    /// - Note: The default is `.center`.
    public var verticalAlignment: VerticalAlignment = .center
    
    /// The vertical axis with respect to which the cells are horizontally aligned.
    /// For a `justified` alignment the alignment axis is not defined and this value is `nil`.
    fileprivate var alignmentAxis: AlignmentAxis<HorizontalAlignment>? {
        switch horizontalAlignment {
        case .left:
            return AlignmentAxis(alignment: HorizontalAlignment.left, position: sectionInset.left)
        case .right:
            guard let collectionViewWidth = collectionView?.frame.size.width else {
                return nil
            }
            return AlignmentAxis(alignment: HorizontalAlignment.right, position: collectionViewWidth - sectionInset.right)
        default:
            return nil
        }
    }
    
    /// The width of the area inside the collection view that can be filled with cells.
    private var contentWidth: CGFloat? {
        guard let collectionViewWidth = collectionView?.frame.size.width else {
            return nil
        }
        return collectionViewWidth - sectionInset.left - sectionInset.right
    }
    
    
    // MARK: - 👶 Initialization
    
    /// The designated initializer.
    ///
    /// - Parameters:
    ///   - horizontalAlignment: Specifies how the cells are horizontally aligned in a row. --
    ///                          (Default: `.justified`)
    ///   - verticalAlignment:   Specified how the cells are vertically aligned in a row. --
    ///                          (Default: `.center`)
    public init(horizontalAlignment: HorizontalAlignment = .justified, verticalAlignment: VerticalAlignment = .center) {
        super.init()
        self.horizontalAlignment = horizontalAlignment
        self.verticalAlignment = verticalAlignment
    }
    
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    
    // MARK: - 🅾️ Overrides
    
    override public func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        
        // 💡 IDEA:
        // The approach for computing a cell's frame is to create a rectangle that covers the current line.
        // Then we check if the preceding cell's frame intersects with this rectangle.
        // If it does, the current item is not the first item in the line. Otherwise it is.
        // (Vice-versa for right-aligned cells.)
        //
        // +---------+----------------------------------------------------------------+---------+
        // |         |                                                                |         |
        // |         |     +------------+                                             |         |
        // |         |     |            |                                             |         |
        // | section |- - -|- - - - - - |- - - - +---------------------+ - - - - - - -| section |
        // |  inset  |     |intersection|        |                     |   line rect  |  inset  |
        // |         |- - -|- - - - - - |- - - - +---------------------+ - - - - - - -|         |
        // | (left)  |     |            |             current item                    | (right) |
        // |         |     +------------+                                             |         |
        // |         |     previous item                                              |         |
        // +---------+----------------------------------------------------------------+---------+
        //
        // ℹ️ We need this rather complicated approach because the first item in a line
        //    is not always left-aligned and the last item in a line is not always right-aligned:
        //    If there is only one item in a line UICollectionViewFlowLayout will center it.
        
        // We may not change the original layout attributes or UICollectionViewFlowLayout might complain.
        guard let layoutAttributes = super.layoutAttributesForItem(at: indexPath)?.copy() as? UICollectionViewLayoutAttributes else {
            return nil
        }
        
        // For a justified layout there's nothing to do here
        // as UICollectionViewFlowLayout justifies the items in a line by default.
        if horizontalAlignment != .justified {
            layoutAttributes.alignHorizontally(collectionViewLayout: self)
        }
        
        // For a vertically centered layout there's nothing to do here
        // as UICollectionViewFlowLayout center-aligns the items in a line by default.
        if verticalAlignment != .center {
            layoutAttributes.alignVertically(collectionViewLayout: self)
        }
        
        return layoutAttributes
    }
    
    override public func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        // We may not change the original layout attributes or UICollectionViewFlowLayout might complain.
        let layoutAttributesObjects = copy(super.layoutAttributesForElements(in: rect))
        layoutAttributesObjects?.forEach({ (layoutAttributes) in
            setFrame(forLayoutAttributes: layoutAttributes)
        })
        return layoutAttributesObjects
    }
    
    
    // MARK: - 👷 Private layout helpers
    
    /// Sets the frame for the passed layout attributes object by calling the `layoutAttributesForItem(at:)` function.
    private func setFrame(forLayoutAttributes layoutAttributes: UICollectionViewLayoutAttributes) {
        if layoutAttributes.representedElementCategory == .cell { // Do not modify header views etc.
            let indexPath = layoutAttributes.indexPath
            if let newFrame = layoutAttributesForItem(at: indexPath)?.frame {
                layoutAttributes.frame = newFrame
            }
        }
    }
    
    /// A function to access the `super` implementation of `layoutAttributesForItem(at:)` externally.
    ///
    /// - Parameter indexPath: The index path of the item for which to return the layout attributes.
    /// - Returns: The unmodified layout attributes for the item at the specified index path
    ///            as computed by `UICollectionViewFlowLayout`.
    fileprivate func originalLayoutAttribute(forItemAt indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {<commit_msg>Allow inheritance for Swift 3.0+<commit_after>//
//  AlignedCollectionViewFlowLayout.swift
//
//  Created by <NAME> on 12/04/2017.
//  Copyright © 2017 <NAME>.
//
//  Licensed under the terms of the MIT license:
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

// MARK: - 🦆 Type definitions

/// An abstract protocol that defines an alignment.
protocol Alignment {}

/// Defines an alignment for UI elements.
public enum HorizontalAlignment: Alignment {
    case left
    case justified
    case right
}

/// Defines a vertical alignment for UI elements.
public enum VerticalAlignment: Alignment {
    case top
    case center
    case bottom
}

/// Describes an axis with respect to which items can be aligned.
private struct AlignmentAxis<A: Alignment> {
    
    /// Determines how items are aligned relative to the axis.
    let alignment: A
    
    /// Defines the position of the axis.
    /// * If the `Alignment` is horizontal, the alignment axis is vertical and this is the position on the `x` axis.
    /// * If the `Alignment` is vertical, the alignment axis is horizontal and this is the position on the `y` axis.
    let position: CGFloat
}


/// A `UICollectionViewFlowLayout` subclass that gives you control
/// over the horizontal and vertical alignment of the cells.
/// You can use it to align the cells like words in a left- or right-aligned text
/// and you can specify how the cells are vertically aligned in their row.
open class AlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {
    
    // MARK: - 🔶 Properties
    
    /// Determines how the cells are horizontally aligned in a row.
    /// - Note: The default is `.justified`.
    public var horizontalAlignment: HorizontalAlignment = .justified
    
    /// Determines how the cells are vertically aligned in a row.
    /// - Note: The default is `.center`.
    public var verticalAlignment: VerticalAlignment = .center
    
    /// The vertical axis with respect to which the cells are horizontally aligned.
    /// For a `justified` alignment the alignment axis is not defined and this value is `nil`.
    fileprivate var alignmentAxis: AlignmentAxis<HorizontalAlignment>? {
        switch horizontalAlignment {
        case .left:
            return AlignmentAxis(alignment: HorizontalAlignment.left, position: sectionInset.left)
        case .right:
            guard let collectionViewWidth = collectionView?.frame.size.width else {
                return nil
            }
            return AlignmentAxis(alignment: HorizontalAlignment.right, position: collectionViewWidth - sectionInset.right)
        default:
            return nil
        }
    }
    
    /// The width of the area inside the collection view that can be filled with cells.
    private var contentWidth: CGFloat? {
        guard let collectionViewWidth = collectionView?.frame.size.width else {
            return nil
        }
        return collectionViewWidth - sectionInset.left - sectionInset.right
    }
    
    
    // MARK: - 👶 Initialization
    
    /// The designated initializer.
    ///
    /// - Parameters:
    ///   - horizontalAlignment: Specifies how the cells are horizontally aligned in a row. --
    ///                          (Default: `.justified`)
    ///   - verticalAlignment:   Specified how the cells are vertically aligned in a row. --
    ///                          (Default: `.center`)
    public init(horizontalAlignment: HorizontalAlignment = .justified, verticalAlignment: VerticalAlignment = .center) {
        super.init()
        self.horizontalAlignment = horizontalAlignment
        self.verticalAlignment = verticalAlignment
    }
    
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    
    // MARK: - 🅾️ Overrides
    
    override open func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        
        // 💡 IDEA:
        // The approach for computing a cell's frame is to create a rectangle that covers the current line.
        // Then we check if the preceding cell's frame intersects with this rectangle.
        // If it does, the current item is not the first item in the line. Otherwise it is.
        // (Vice-versa for right-aligned cells.)
        //
        // +---------+----------------------------------------------------------------+---------+
        // |         |                                                                |         |
        // |         |     +------------+                                             |         |
        // |         |     |            |                                             |         |
        // | section |- - -|- - - - - - |- - - - +---------------------+ - - - - - - -| section |
        // |  inset  |     |intersection|        |                     |   line rect  |  inset  |
        // |         |- - -|- - - - - - |- - - - +---------------------+ - - - - - - -|         |
        // | (left)  |     |            |             current item                    | (right) |
        // |         |     +------------+                                             |         |
        // |         |     previous item                                              |         |
        // +---------+----------------------------------------------------------------+---------+
        //
        // ℹ️ We need this rather complicated approach because the first item in a line
        //    is not always left-aligned and the last item in a line is not always right-aligned:
        //    If there is only one item in a line UICollectionViewFlowLayout will center it.
        
        // We may not change the original layout attributes or UICollectionViewFlowLayout might complain.
        guard let layoutAttributes = super.layoutAttributesForItem(at: indexPath)?.copy() as? UICollectionViewLayoutAttributes else {
            return nil
        }
        
        // For a justified layout there's nothing to do here
        // as UICollectionViewFlowLayout justifies the items in a line by default.
        if horizontalAlignment != .justified {
            layoutAttributes.alignHorizontally(collectionViewLayout: self)
        }
        
        // For a vertically centered layout there's nothing to do here
        // as UICollectionViewFlowLayout center-aligns the items in a line by default.
        if verticalAlignment != .center {
            layoutAttributes.alignVertically(collectionViewLayout: self)
        }
        
        return layoutAttributes
    }
    
    override open func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        // We may not change the original layout attributes or UICollectionViewFlowLayout might complain.
        let layoutAttributesObjects = copy(super.layoutAttributesForElements(in: rect))
        layoutAttributesObjects?.forEach({ (layoutAttributes) in
            setFrame(forLayoutAttributes: layoutAttributes)
        })
        return layoutAttributesObjects
    }
    
    
    // MARK: - 👷 Private layout helpers
    
    /// Sets the frame for the passed layout attributes object by calling the `layoutAttributesForItem(at:)` function.
    private func setFrame(forLayoutAttributes layoutAttributes: UICollectionViewLayoutAttributes) {
        if layoutAttributes.representedElementCategory == .cell { // Do not modify header views etc.
            let indexPath = layoutAttributes.indexPath
            if let newFrame = layoutAttributesForItem(at: indexPath)?.frame {
                layoutAttributes.frame = newFrame
            }
        }
    }
    
    /// A function to access the `super` implementation of `layoutAttributesForItem(at:)` externally.
    ///
    /// - Parameter indexPath: The index path of the item for which to return the layout attributes.
    /// - Returns: The unmodified layout attributes for the item at the specified index path
    ///            as computed by `UICollectionViewFlowLayout`.
    fileprivate func originalLayoutAttribute(forItemAt indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
{'fraction_non_alphanumeric': 0.10208208098803213, 'fraction_numerical': 0.0014208426689983058, 'mean_word_length': 1.863849765258216, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1893380', 'n_tokens_mistral': 4379, 'n_tokens_neox': 4263, 'n_words': 1692}