<commit_before>//
//  CasualHorizontalLayout.swift
//  CasualUX
//
//  Created by <NAME> on 11/2/15.
//  Copyright © 2015 <NAME>. All rights reserved.
//

import UIKit

class CasualCollectionViewFlowLayout : UICollectionViewFlowLayout {
    
    var resistance: CGFloat = 800
    var length: CGFloat = 0.0
    var damping: CGFloat = 1.0
    var frequency: CGFloat = 1.0
    
    private var dynamicAnimator: UIDynamicAnimator!
    
    private var visiblePaths: Set<NSIndexPath>!
    private var latestDelta: CGPoint = CGPointZero
    
    override init() {
        super.init()
        
        self.dynamicAnimator = UIDynamicAnimator(collectionViewLayout: self)
        self.visiblePaths = Set()
    }
    
    required init(coder aDecoder: NSCoder) {
        fatalError()
    }
    
    override func prepareLayout() {
        super.prepareLayout()
        
        let scrollView: UIScrollView = self.collectionView!
        
        let visibleRect = CGRectInset(scrollView.bounds, -100, -100)
        let itemsInVisibleRect = super.layoutAttributesForElementsInRect(visibleRect)!
        
        let indexPathsInVisibleRect = Set(itemsInVisibleRect.map({ (attribute) -> NSIndexPath in
            return attribute.indexPath
        }))
        
        
        // PART 1 - REMOVING any old behaviors as old cells go away
        
        let noLongerVisisbleBehaviors = self.dynamicAnimator.behaviors.filter { (behavior) -> Bool in
            var currentlyVisible = false
            if let attachmentBehavior = behavior as? UIAttachmentBehavior, last = attachmentBehavior.items.last as? UICollectionViewLayoutAttributes {
                currentlyVisible = indexPathsInVisibleRect.contains(last.indexPath)
            }
            return !currentlyVisible
        }
        
        for behavior in noLongerVisisbleBehaviors {
            self.dynamicAnimator.removeBehavior(behavior)
            if let attachmentBehavior = behavior as? UIAttachmentBehavior, last = attachmentBehavior.items.last as? UICollectionViewLayoutAttributes {
                visiblePaths.remove(last.indexPath)
            }
        }
        
        
        // PART 2 - ADDING any new behaviors as new cells appear
        
        let newlyVisibleItems = itemsInVisibleRect.filter { (layoutAttribute) -> Bool in
            return !visiblePaths.contains(layoutAttribute.indexPath)
        }
        
        let touchedCellCenter = self.collectionView!.touchedCellCenter()
        
        for item in newlyVisibleItems {
            var center = item.center
            
            let attachmentBehavior = UIAttachmentBehavior(item: item, attachedToAnchor: item.center)
            
            attachmentBehavior.length = self.length
            attachmentBehavior.damping = self.damping
            attachmentBehavior.frequency = self.frequency
            
            let distance = CGPointMake(
                fabs(touchedCellCenter.x - attachmentBehavior.anchorPoint.x),
                fabs(touchedCellCenter.y - attachmentBehavior.anchorPoint.y)
            )
            
            let resistance = CGPointMake(
                distance.x / self.resistance,
                distance.y / self.resistance
            )

            var shouldBeSpringy = false
            if (self.latestDelta.x > 0) {
                shouldBeSpringy = item.center.x > touchedCellCenter.x
            } else if (self.latestDelta.x < 0) {
                shouldBeSpringy = item.center.x < touchedCellCenter.x
            } else if (self.latestDelta.y > 0) {
                shouldBeSpringy = item.center.y > touchedCellCenter.y
            } else if (self.latestDelta.y < 0) {
                shouldBeSpringy = item.center.y < touchedCellCenter.y
            }
            
            if (shouldBeSpringy) {
                center.x += self.latestDelta.x * resistance.x
                center.y += self.latestDelta.y * resistance.y
            }
            
            item.center = center
            
            self.dynamicAnimator.addBehavior(attachmentBehavior)
            
            self.visiblePaths.insert(item.indexPath)
        }
    }
    
    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        return self.dynamicAnimator.itemsInRect(rect) as? [UICollectionViewLayoutAttributes]
    }
    
    override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
        return self.dynamicAnimator.layoutAttributesForCellAtIndexPath(indexPath)
    }
    
    override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
        let touchedCellCenter = self.collectionView!.touchedCellCenter()
        
        let scrollView: UIScrollView = self.collectionView!
        
        self.latestDelta = CGPointMake(
            newBounds.origin.x - scrollView.bounds.origin.x,
            newBounds.origin.y - scrollView.bounds.origin.y
        )
        
        for behavior in self.dynamicAnimator.behaviors {
            let attachmentBehavior = behavior as! UIAttachmentBehavior
        
            let distance = CGPointMake(
                fabs(touchedCellCenter.x - attachmentBehavior.anchorPoint.x),
                fabs(touchedCellCenter.y - attachmentBehavior.anchorPoint.y)
            )
            
            let resistance = CGPointMake(
                distance.x / self.resistance,
                distance.y / self.resistance
            )
            
            let item = attachmentBehavior.items.first!
            
            var center = item.center
            
            var shouldBeSpringy = false
            if (self.latestDelta.x > 0) {
                shouldBeSpringy = item.center.x > touchedCellCenter.x
            } else if (self.latestDelta.x < 0) {
                shouldBeSpringy = item.center.x < touchedCellCenter.x
            } else if (self.latestDelta.y > 0) {
                shouldBeSpringy = item.center.y > touchedCellCenter.y
            } else if (self.latestDelta.y < 0) {
                shouldBeSpringy = item.center.y < touchedCellCenter.y
            }
            
            if (shouldBeSpringy) {
                center.x += self.latestDelta.x * resistance.x
                center.y += self.latestDelta.y * resistance.y
            }
            
            item.center = center
            
            self.dynamicAnimator.updateItemUsingCurrentState(item)
        }
        
        return false
    }
    
    override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
        return self.targetContentOffsetForProposedContentOffset(proposedContentOffset)
    }
    
    override func targetContentOffsetForProposedContentOffset(proposedContentOffset: CGPoint) -> CGPoint {
        var offsets = proposedContentOffset
        
        if let view = self.collectionView {
            let collectionDelegate = view.delegate as! FeedCollectionViewDelegate
            let dx = offsets.x - collectionDelegate.leadingMargin
            
            let width = view.frame.size.height * 9.0/16.0 + self.minimumLineSpacing
            let diff = dx % width
            if (diff > (width / 2.0) - 15) {
                offsets.x = min(view.contentSize.width, dx - diff + width)
            } else {
                offsets.x = max(0, dx - diff)
            }
        }
        
        return offsets
    }
}

private extension UICollectionView {
    func touchedCellCenter() -> CGPoint {
        let panGesture = self.panGestureRecognizer
        
        if (panGesture.numberOfTouches() < 1) {
            return CGPointZero
        }
        <commit_msg>Takes out some project-specific code<commit_after>//
//  CasualHorizontalLayout.swift
//  CasualUX
//
//  Created by <NAME> on 11/2/15.
//  Copyright © 2015 <NAME>. All rights reserved.
//

import UIKit

class CasualCollectionViewFlowLayout : UICollectionViewFlowLayout {
    
    var resistance: CGFloat = 800
    var length: CGFloat = 0.0
    var damping: CGFloat = 1.0
    var frequency: CGFloat = 1.0
    
    private var dynamicAnimator: UIDynamicAnimator!
    
    private var visiblePaths: Set<NSIndexPath>!
    private var latestDelta: CGPoint = CGPointZero
    
    override init() {
        super.init()
        
        self.dynamicAnimator = UIDynamicAnimator(collectionViewLayout: self)
        self.visiblePaths = Set()
    }
    
    required init(coder aDecoder: NSCoder) {
        fatalError()
    }
    
    override func prepareLayout() {
        super.prepareLayout()
        
        let scrollView: UIScrollView = self.collectionView!
        
        let visibleRect = CGRectInset(scrollView.bounds, -100, -100)
        let itemsInVisibleRect = super.layoutAttributesForElementsInRect(visibleRect)!
        
        let indexPathsInVisibleRect = Set(itemsInVisibleRect.map({ (attribute) -> NSIndexPath in
            return attribute.indexPath
        }))
        
        
        // PART 1 - REMOVING any old behaviors as old cells go away
        
        let noLongerVisisbleBehaviors = self.dynamicAnimator.behaviors.filter { (behavior) -> Bool in
            var currentlyVisible = false
            if let attachmentBehavior = behavior as? UIAttachmentBehavior, last = attachmentBehavior.items.last as? UICollectionViewLayoutAttributes {
                currentlyVisible = indexPathsInVisibleRect.contains(last.indexPath)
            }
            return !currentlyVisible
        }
        
        for behavior in noLongerVisisbleBehaviors {
            self.dynamicAnimator.removeBehavior(behavior)
            if let attachmentBehavior = behavior as? UIAttachmentBehavior, last = attachmentBehavior.items.last as? UICollectionViewLayoutAttributes {
                visiblePaths.remove(last.indexPath)
            }
        }
        
        
        // PART 2 - ADDING any new behaviors as new cells appear
        
        let newlyVisibleItems = itemsInVisibleRect.filter { (layoutAttribute) -> Bool in
            return !visiblePaths.contains(layoutAttribute.indexPath)
        }
        
        let touchedCellCenter = self.collectionView!.touchedCellCenter()
        
        for item in newlyVisibleItems {
            var center = item.center
            
            let attachmentBehavior = UIAttachmentBehavior(item: item, attachedToAnchor: item.center)
            
            attachmentBehavior.length = self.length
            attachmentBehavior.damping = self.damping
            attachmentBehavior.frequency = self.frequency
            
            let distance = CGPointMake(
                fabs(touchedCellCenter.x - attachmentBehavior.anchorPoint.x),
                fabs(touchedCellCenter.y - attachmentBehavior.anchorPoint.y)
            )
            
            let resistance = CGPointMake(
                distance.x / self.resistance,
                distance.y / self.resistance
            )

            var shouldBeSpringy = false
            if (self.latestDelta.x > 0) {
                shouldBeSpringy = item.center.x > touchedCellCenter.x
            } else if (self.latestDelta.x < 0) {
                shouldBeSpringy = item.center.x < touchedCellCenter.x
            } else if (self.latestDelta.y > 0) {
                shouldBeSpringy = item.center.y > touchedCellCenter.y
            } else if (self.latestDelta.y < 0) {
                shouldBeSpringy = item.center.y < touchedCellCenter.y
            }
            
            if (shouldBeSpringy) {
                center.x += self.latestDelta.x * resistance.x
                center.y += self.latestDelta.y * resistance.y
            }
            
            item.center = center
            
            self.dynamicAnimator.addBehavior(attachmentBehavior)
            
            self.visiblePaths.insert(item.indexPath)
        }
    }
    
    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        return self.dynamicAnimator.itemsInRect(rect) as? [UICollectionViewLayoutAttributes]
    }
    
    override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes? {
        return self.dynamicAnimator.layoutAttributesForCellAtIndexPath(indexPath)
    }
    
    override func shouldInvalidateLayoutForBoundsChange(newBounds: CGRect) -> Bool {
        let touchedCellCenter = self.collectionView!.touchedCellCenter()
        
        let scrollView: UIScrollView = self.collectionView!
        
        self.latestDelta = CGPointMake(
            newBounds.origin.x - scrollView.bounds.origin.x,
            newBounds.origin.y - scrollView.bounds.origin.y
        )
        
        for behavior in self.dynamicAnimator.behaviors {
            let attachmentBehavior = behavior as! UIAttachmentBehavior
        
            let distance = CGPointMake(
                fabs(touchedCellCenter.x - attachmentBehavior.anchorPoint.x),
                fabs(touchedCellCenter.y - attachmentBehavior.anchorPoint.y)
            )
            
            let resistance = CGPointMake(
                distance.x / self.resistance,
                distance.y / self.resistance
            )
            
            let item = attachmentBehavior.items.first!
            
            var center = item.center
            
            var shouldBeSpringy = false
            if (self.latestDelta.x > 0) {
                shouldBeSpringy = item.center.x > touchedCellCenter.x
            } else if (self.latestDelta.x < 0) {
                shouldBeSpringy = item.center.x < touchedCellCenter.x
            } else if (self.latestDelta.y > 0) {
                shouldBeSpringy = item.center.y > touchedCellCenter.y
            } else if (self.latestDelta.y < 0) {
                shouldBeSpringy = item.center.y < touchedCellCenter.y
            }
            
            if (shouldBeSpringy) {
                center.x += self.latestDelta.x * resistance.x
                center.y += self.latestDelta.y * resistance.y
            }
            
            item.center = center
            
            self.dynamicAnimator.updateItemUsingCurrentState(item)
        }
        
        return false
    }
}

private extension UICollectionView {
    func touchedCellCenter() -> CGPoint {
        let panGesture = self.panGestureRecognizer
        
        if (panGesture.numberOfTouches() < 1) {
            return CGPointZero
        }
        
{'fraction_non_alphanumeric': 0.07299421009098428, 'fraction_numerical': 0.005514199062586159, 'mean_word_length': 1.8118217054263566, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 42, 'https://': 0, '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': '2370276', 'n_tokens_mistral': 3856, 'n_tokens_neox': 3689, 'n_words': 765}