text:
typedef NS_ENUM(NSInteger, JHChainableAnimatorContinuationMode) {
    JHChainableAnimatorContinuationModeContinue,
    JHChainableAnimatorContinuationModePause,
    JHChainableAnimatorContinuationModeStop,
};

@interface JHChainableAnimator ()

@property (nonatomic, weak) UIView *view;
@property (strong, nonatomic) NSMutableArray<JHAnimationChainLink *> *animationChainLinks;
@property (strong, nonatomic) NSMapTable<JHAnimationChainLink *, NSNumber *> *animationDurationMapping;
@property (atomic, assign, getter=isAnimating) BOOL animating;
@property (atomic, assign, getter=isPaused) BOOL paused;
@property (nonatomic, assign) JHChainableAnimatorContinuationMode continuationMode;
@property (nonatomic, readonly) JHAnimationChainLink *currentAnimationLink;

@end

#define JHDegreesToRadians( degrees )( ( degrees )/ 180.0 * M_PI )
#define JHRadiansToDegrees( radians )( ( radians )* ( 180.0 / M_PI ))

@implementation JHChainableAnimator

- (instancetype)init
{
    NSAssert(NO, @"JHChainableAnimator: Must use `- (instancetype)initWithView:(UIView *)view` method.");
    [self doesNotRecognizeSelector:_cmd];
    return [self initWithView:nil];
}

- (instancetype)initWithView:(UIView *)view
{
    self = [super init];
    if (self) {
        NSParameterAssert(view != nil);
        _view = view;
        [self clear];
    }
    return self;
}

- (void)clear
{
    self.completionBlock = nil;
    self.animationDurationMapping = [NSMapTable weakToStrongObjectsMapTable];
    JHAnimationChainLink *chainLink = [[JHAnimationChainLink alloc] initWithView:self.view animator:self];
    self.animationChainLinks = [NSMutableArray arrayWithObject:chainLink];
    self.continuationMode = JHChainableAnimatorContinuationModeContinue;
}

- (void)pause
{
    self.continuationMode = JHChainableAnimatorContinuationModePause;
}

- (void)resume
{
    if (self.continuationMode == JHChainableAnimatorContinuationModePause && self.isAnimating) {
        self.continuationMode = JHChainableAnimatorContinuationModeContinue;
        [self animateChain];
    }
}

- (void)stop
{
    self.continuationMode = JHChainableAnimatorContinuationModeStop;
}

- (JHAnimationChainLink *)currentAnimationLink
{
    return self.animationChainLinks.lastObject;
}

- (void)animateChain
{
    switch (self.continuationMode) {
        case JHChainableAnimatorContinuationModeContinue:
        {
            self.animating = YES;
            self.paused = NO;
            JHAnimationChainLink *chainLink = self.animationChainLinks.firstObject;
            if (chainLink != nil) {
                NSTimeInterval duration = [self.animationDurationMapping objectForKey:chainLink].doubleValue;
                [chainLink animateWithDuration:duration completion:^{
                    [self.animationChainLinks removeObjectAtIndex:0];
                    [self animateChain];
                }];
            }
            else {
                void(^completionBlock)(void) = self.completionBlock ?: ^{};
                [self clear];
                completionBlock();
                self.animating = NO;
            }
            break;
        }
        case JHChainableAnimatorContinuationModePause:
        {
            self.paused = YES;
            self.animating = YES;
            break;
        }
        case JHChainableAnimatorContinuationModeStop:
        {
            self.paused = NO;
            self.animating = NO;
            [self clear];
            break;
        }
    }
}

- (JHKeyframeAnimation *)basicAnimationForKeyPath:(NSString *)keypath
{
    JHKeyframeAnimation * animation = [JHKeyframeAnimation animationWithKeyPath:keypath];
    animation.repeatCount = 0;
    animation.autoreverses = NO;
    return animation;
}

- (void)addAnimationFromCalculationBlock:(JHKeyframeAnimation *)animation
{
    [self.animationChainLinks.firstObject addAnimationFromCalculationBlock:animation];
}

- (void)addAnimationCalculationAction:(JHAnimationCalculationAction)action
{
    [self.currentAnimationLink addAnimationCalculationAction:[action copy]];
}

- (void)addAnimationCompletionAction:(JHAnimationCompletionAction)action
{
    [self.currentAnimationLink addAnimationCompletionAction:[action copy]];
}

- (CGPoint)newPositionFromNewFrame:(CGRect)newRect
{
    CGPoint anchor = self.view.layer.anchorPoint;
    CGSize size = newRect.size;
    CGPoint newPosition;
    newPosition.x = newRect.origin.x + anchor.x*size.width;
    newPosition.y = newRect.origin.y + anchor.y*size.height;
    return newPosition;
}

- (CGPoint)newPositionFromNewOrigin:(CGPoint)newOrigin
{
    CGPoint anchor = self.view.layer.anchorPoint;
    CGSize size = self.view.bounds.size;
    CGPoint newPosition;
    newPosition.x = newOrigin.x + anchor.x*size.width;
    newPosition.y = newOrigin.y + anchor.y*size.height;
    return newPosition;
}

- (CGPoint)newPositionFromNewCenter:(CGPoint)newCenter
{
    CGPoint anchor = self.view.layer.anchorPoint;
    CGSize size = self.view.bounds.size;
    CGPoint newPosition;
    newPosition.x = newCenter.x + (anchor.x-0.5)*size.width;
    newPosition.y = newCenter.y + (anchor.y-0.5)*size.height;
    return newPosition;
}

- (void)makeAnchorFromX:(CGFloat) x Y:(CGFloat)y
{
    JHAnimationCalculationAction action = ^void(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
        CGPoint anchorPoint = CGPointMake(x, y);
        if (CGPointEqualToPoint(anchorPoint, view.layer.anchorPoint)) {
            return;
        }
        CGPoint newPoint = CGPointMake(view.bounds.size.width * anchorPoint.x,
                                       view.bounds.size.height * anchorPoint.y);
        CGPoint oldPoint = CGPointMake(view.bounds.size.width * view.layer.anchorPoint.x,
                                       view.bounds.size.height * view.layer.anchorPoint.y);

        newPoint = CGPointApplyAffineTransform(newPoint, view.transform);
        oldPoint = CGPointApplyAffineTransform(oldPoint, view.transform);

        CGPoint position = view.layer.position;

        position.x -= oldPoint.x;
        position.x += newPoint.x;

        position.y -= oldPoint.y;
        position.y += newPoint.y;

        view.layer.position = position;
        view.layer.anchorPoint = anchorPoint;
    };
    self.currentAnimationLink.anchorCalculationAction = [action copy];
}

- (JHChainableRepeatAnimation)repeat
{
    JHChainableRepeatAnimation chainable = JHChainableRepeatAnimation(t, count) {

        @synchronized (self) {
            for (NSInteger i = 0; i < count; ++i) {
                JHAnimationChainLink *previousChainLink = self.animationChainLinks.lastObject;
                NSParameterAssert(previousChainLink != nil);
                if (previousChainLink != nil) {
                    [self.animationDurationMapping setObject:@(t) forKey:previousChainLink];
                }

                JHAnimationChainLink *newChainLink;
                BOOL isRepeatedChainLink = (i + 1 < count);
                if (isRepeatedChainLink) {
                    newChainLink = [previousChainLink copy];
                }
                else {
                    newChainLink = [[JHAnimationChainLink alloc] initWithView:self.view animator:self];
                }
                [self.animationChainLinks addObject:newChainLink];
            }

            return self;
        }
    };

    return chainable;
}

- (JHChainableTimeInterval)thenAfter
{

    JHChainableTimeInterval chainable = JHChainableTimeInterval(t) {

        return self.repeat(t, 1);
    };

    return chainable;
}

- (JHChainableAnimation)animate
{
    JHChainableAnimation chainable = JHChainableAnimation(t) {

        return self.animateWithCompletion(t, self.completionBlock);
    };

    return chainable;
}

- (JHChainableRepeatAnimation)animateWithRepeat
{
    JHChainableRepeatAnimation chainable = JHChainableRepeatAnimation(t, count) {
        @synchronized (self) {
            for (NSInteger i = 0; i < count; ++i) {
                JHAnimationChainLink *previousChainLink = self.animationChainLinks.lastObject;
                NSParameterAssert(previousChainLink != nil);
                if (previousChainLink != nil) {
                    [self.animationDurationMapping setObject:@(t) forKey:previousChainLink];
                }

                BOOL isRepeatedChainLink = (i + 1 < count);
                if (isRepeatedChainLink) {
                    JHAnimationChainLink *newChainLink = [previousChainLink copy];
                    [self.animationChainLinks addObject:newChainLink];
                }
                else {
                    [self animateChain];
                }
            }

            return self;
        }
    };
    return chainable;
}

- (JHChainableAnimationWithCompletion)animateWithCompletion
{
    JHChainableAnimationWithCompletion chainable = JHChainableAnimationWithCompletion(t, completion) {

        @synchronized (self) {
            self.completionBlock = completion;
            JHAnimationChainLink *previousChainLink = self.animationChainLinks.lastObject;
            if (previousChainLink != nil) {
                [self.animationDurationMapping setObject:@(t) forKey:previousChainLink];
            }
            [self animateChain];

            return self;
        }
    };

    return chainable;
}

#pragma mark - Chainable Properties
- (JHChainableRect)makeFrame
{
    JHChainableRect chainable = JHChainableRect(rect) {

        return self.makeOrigin(rect.origin.x, rect.origin.y).makeBounds(rect);
    };

    return chainable;
}

- (JHChainableRect)makeBounds
{
    JHChainableRect chainable = JHChainableRect(rect) {

        return self.makeSize(rect.size.width, rect.size.height);
    };
    return chainable;
}

- (JHChainableSize)makeSize
{
    JHChainableSize chainable = JHChainableSize(width, height) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *sizeAnimation = [weakSelf basicAnimationForKeyPath:@"bounds.size"];
            sizeAnimation.fromValue = [NSValue valueWithCGSize:view.layer.bounds.size];
            sizeAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(width, height)];
            [weakSelf addAnimationFromCalculationBlock:sizeAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGRect bounds = CGRectMake(0, 0, width, height);
            view.layer.bounds = bounds;
            view.bounds = bounds;
        }];

        return self;
    };
    return chainable;
}

- (JHChainablePoint)makeOrigin
{
    JHChainablePoint chainable = JHChainablePoint(x, y) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *positionAnimation = [weakSelf basicAnimationForKeyPath:@"position"];
            CGPoint newPosition = [weakSelf newPositionFromNewOrigin:CGPointMake(x, y)];
            positionAnimation.fromValue = [NSValue valueWithCGPoint:view.layer.position];
            positionAnimation.toValue = [NSValue valueWithCGPoint:newPosition];
            [weakSelf addAnimationFromCalculationBlock:positionAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGPoint newPosition = [weakSelf newPositionFromNewOrigin:CGPointMake(x, y)];
            view.layer.position = newPosition;
        }];

        return self;
    };
    return chainable;
}

- (JHChainablePoint)makeCenter
{
    JHChainablePoint chainable = JHChainablePoint(x, y) {
        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *positionAnimation = [weakSelf basicAnimationForKeyPath:@"position"];
            CGPoint newPosition = [weakSelf newPositionFromNewCenter:CGPointMake(x, y)];
            positionAnimation.fromValue = [NSValue valueWithCGPoint:view.layer.position];
            positionAnimation.toValue = [NSValue valueWithCGPoint:newPosition];
            [weakSelf addAnimationFromCalculationBlock:positionAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            view.center = CGPointMake(x, y);
        }];

        return self;
    };
    return chainable;
}

- (JHChainableFloat)makeX
{
    JHChainableFloat chainable = JHChainableFloat(f) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *positionAnimation = [weakSelf basicAnimationForKeyPath:@"position.x"];
            CGPoint newPosition = [weakSelf newPositionFromNewOrigin:CGPointMake(f, view.layer.frame.origin.y)];
            positionAnimation.fromValue = @(view.layer.position.x);
            positionAnimation.toValue = @(newPosition.x);
            [weakSelf addAnimationFromCalculationBlock:positionAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGPoint newPosition = [weakSelf newPositionFromNewOrigin:CGPointMake(f, view.layer.frame.origin.y)];
            view.layer.position = newPosition;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableFloat)makeY
{
    JHChainableFloat chainable = JHChainableFloat(f) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *positionAnimation = [weakSelf basicAnimationForKeyPath:@"position.y"];
            CGPoint newPosition = [weakSelf newPositionFromNewOrigin:CGPointMake(view.layer.frame.origin.x, f)];
            positionAnimation.fromValue = @(view.layer.position.y);
            positionAnimation.toValue = @(newPosition.y);
            [weakSelf addAnimationFromCalculationBlock:positionAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGPoint newPosition = [weakSelf newPositionFromNewOrigin:CGPointMake(view.layer.frame.origin.x, f)];
            view.layer.position = newPosition;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableFloat)makeWidth
{
    JHChainableFloat chainable = JHChainableFloat(f) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *sizeAnimation = [weakSelf basicAnimationForKeyPath:@"bounds.size"];
            sizeAnimation.fromValue = [NSValue valueWithCGSize:view.layer.bounds.size];
            sizeAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(f, view.frame.size.height)];
            [weakSelf addAnimationFromCalculationBlock:sizeAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGRect bounds = CGRectMake(0, 0, f, view.frame.size.height);
            view.layer.bounds = bounds;
            view.bounds = bounds;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableFloat)makeHeight
{
    JHChainableFloat chainable = JHChainableFloat(f) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *sizeAnimation = [weakSelf basicAnimationForKeyPath:@"bounds.size"];
            sizeAnimation.fromValue = [NSValue valueWithCGSize:view.layer.bounds.size];
            sizeAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(view.frame.size.width, f)];
            [weakSelf addAnimationFromCalculationBlock:sizeAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGRect bounds = CGRectMake(0, 0, view.frame.size.width, f);
            view.layer.bounds = bounds;
            view.bounds = bounds;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableFloat)makeOpacity
{
    JHChainableFloat chainable = JHChainableFloat(f) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *opacityAnimation = [weakSelf basicAnimationForKeyPath:@"opacity"];
            opacityAnimation.fromValue = @(view.alpha);
            opacityAnimation.toValue = @(f);
            [weakSelf addAnimationFromCalculationBlock:opacityAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            view.alpha = f;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableColor)makeBackground
{
    JHChainableColor chainable = JHChainableColor(color) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *colorAnimation = [weakSelf basicAnimationForKeyPath:@"backgroundColor"];
            colorAnimation.fromValue = view.backgroundColor;
            colorAnimation.toValue = color;
            [weakSelf addAnimationFromCalculationBlock:colorAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            view.layer.backgroundColor = color.CGColor;
            view.backgroundColor = color;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableColor)makeBorderColor
{
    JHChainableColor chainable = JHChainableColor(color) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *colorAnimation = [weakSelf basicAnimationForKeyPath:@"borderColor"];
            UIColor *borderColor = (__bridge UIColor*)(view.layer.borderColor);
            colorAnimation.fromValue = borderColor;
            colorAnimation.toValue = color;
            [weakSelf addAnimationFromCalculationBlock:colorAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            view.layer.borderColor = color.CGColor;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableFloat)makeBorderWidth
{
    JHChainableFloat chainable = JHChainableFloat(f) {

        f = MAX(0, f);
        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *borderAnimation = [weakSelf basicAnimationForKeyPath:@"borderWidth"];
            borderAnimation.fromValue = @(view.layer.borderWidth);
            borderAnimation.toValue = @(f);
            [weakSelf addAnimationFromCalculationBlock:borderAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            view.layer.borderWidth = f;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableFloat)makeCornerRadius
{
    JHChainableFloat chainable = JHChainableFloat(f) {

        f = MAX(0, f);
        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *cornerAnimation = [weakSelf basicAnimationForKeyPath:@"cornerRadius"];
            cornerAnimation.fromValue = @(view.layer.cornerRadius);
            cornerAnimation.toValue = @(f);
            [weakSelf addAnimationFromCalculationBlock:cornerAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            view.layer.cornerRadius = f;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableFloat)makeScale
{
    JHChainableFloat chainable = JHChainableFloat(f) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *boundsAnimation = [weakSelf basicAnimationForKeyPath:@"bounds"];
            CGRect rect = CGRectMake(0, 0, MAX(view.bounds.size.width*f, 0), MAX(view.bounds.size.height*f, 0));
            boundsAnimation.fromValue = [NSValue valueWithCGRect:view.layer.bounds];
            boundsAnimation.toValue = [NSValue valueWithCGRect:rect];
            [weakSelf addAnimationFromCalculationBlock:boundsAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGRect rect = CGRectMake(0, 0, MAX(view.bounds.size.width*f, 0), MAX(view.bounds.size.height*f, 0));
            view.layer.bounds = rect;
            view.bounds = rect;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableFloat)makeScaleX
{
    JHChainableFloat chainable = JHChainableFloat(f) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *boundsAnimation = [weakSelf basicAnimationForKeyPath:@"bounds"];
            CGRect rect = CGRectMake(0, 0, MAX(view.bounds.size.width*f, 0), view.bounds.size.height);
            boundsAnimation.fromValue = [NSValue valueWithCGRect:view.layer.bounds];
            boundsAnimation.toValue = [NSValue valueWithCGRect:rect];
            [weakSelf addAnimationFromCalculationBlock:boundsAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGRect rect = CGRectMake(0, 0, MAX(view.bounds.size.width*f, 0), view.bounds.size.height);
            view.layer.bounds = rect;
            view.bounds = rect;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableFloat)makeScaleY
{
    JHChainableFloat chainable = JHChainableFloat(f) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *boundsAnimation = [weakSelf basicAnimationForKeyPath:@"bounds"];
            CGRect rect = CGRectMake(0, 0, view.bounds.size.width, MAX(view.bounds.size.height*f, 0));
            boundsAnimation.fromValue = [NSValue valueWithCGRect:view.layer.bounds];
            boundsAnimation.toValue = [NSValue valueWithCGRect:rect];
            [weakSelf addAnimationFromCalculationBlock:boundsAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGRect rect = CGRectMake(0, 0, view.bounds.size.width, MAX(view.bounds.size.height*f, 0));
            view.layer.bounds = rect;
            view.bounds = rect;
        }];

        return self;
    };
    return chainable;
}

- (JHChainablePoint)makeAnchor
{
    JHChainablePoint chainable = JHChainablePoint(x, y) {

        [self makeAnchorFromX:x Y:y];

        return self;
    };
    return chainable;
}

- (JHChainableFloat)moveX
{
    JHChainableFloat chainable = JHChainableFloat(f) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *positionAnimation = [weakSelf basicAnimationForKeyPath:@"position.x"];
            positionAnimation.fromValue = @(view.layer.position.x);
            positionAnimation.toValue = @(view.layer.position.x+f);
            [weakSelf addAnimationFromCalculationBlock:positionAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGPoint position = view.layer.position;
            position.x += f;
            view.layer.position = position;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableFloat)moveY
{
    JHChainableFloat chainable = JHChainableFloat(f) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *positionAnimation = [weakSelf basicAnimationForKeyPath:@"position.y"];
            positionAnimation.fromValue = @(view.layer.position.y);
            positionAnimation.toValue = @(view.layer.position.y+f);
            [weakSelf addAnimationFromCalculationBlock:positionAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGPoint position = view.layer.position;
            position.y += f;
            view.layer.position = position;
        }];

        return self;
    };
    return chainable;
}

- (JHChainablePoint)moveXY
{
    JHChainablePoint chainable = JHChainablePoint(x, y) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *positionAnimation = [weakSelf basicAnimationForKeyPath:@"position"];
            CGPoint oldOrigin = view.layer.frame.origin;
            CGPoint newPosition = [weakSelf newPositionFromNewOrigin:CGPointMake(oldOrigin.x+x, oldOrigin.y+y)];
            positionAnimation.fromValue = [NSValue valueWithCGPoint:view.layer.position];
            positionAnimation.toValue = [NSValue valueWithCGPoint:newPosition];
            [weakSelf addAnimationFromCalculationBlock:positionAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGPoint position = view.layer.position;
            position.x +=x; position.y += y;
            view.layer.position = position;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableFloat)moveHeight
{
    JHChainableFloat chainable = JHChainableFloat(f) {
        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *sizeAnimation = [weakSelf basicAnimationForKeyPath:@"bounds.size"];
            sizeAnimation.fromValue = [NSValue valueWithCGSize:view.layer.bounds.size];
            sizeAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(view.bounds.size.width, MAX(view.bounds.size.height+f, 0))];
            [weakSelf addAnimationFromCalculationBlock:sizeAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGRect bounds = CGRectMake(0, 0, view.bounds.size.width, MAX(view.bounds.size.height+f, 0));
            view.layer.bounds = bounds;
            view.bounds = bounds;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableFloat)moveWidth
{
    JHChainableFloat chainable = JHChainableFloat(f) {
        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *sizeAnimation = [weakSelf basicAnimationForKeyPath:@"bounds.size"];
            sizeAnimation.fromValue = [NSValue valueWithCGSize:view.layer.bounds.size];
            sizeAnimation.toValue = [NSValue valueWithCGSize:CGSizeMake(MAX(view.bounds.size.width+f, 0), view.bounds.size.height)];
            [weakSelf addAnimationFromCalculationBlock:sizeAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGRect bounds = CGRectMake(0, 0, MAX(view.bounds.size.width+f, 0), view.bounds.size.height);
            view.layer.bounds = bounds;
            view.bounds = bounds;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableDegrees)rotate
{
    return [self rotateZ];
}

- (JHChainableDegrees)rotateX
{
    JHChainableDegrees chainable = JHChainableDegrees(angle) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *rotationAnimation = [weakSelf basicAnimationForKeyPath:@"transform.rotation.x"];
            CATransform3D transform = view.layer.transform;
            CGFloat originalRotation = atan2(transform.m23, transform.m22);
            rotationAnimation.fromValue = @(originalRotation);
            rotationAnimation.toValue = @(originalRotation+JHDegreesToRadians(angle));
            [weakSelf addAnimationFromCalculationBlock:rotationAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CATransform3D transform = view.layer.transform;
            CGFloat originalRotation = atan2(transform.m23, transform.m22);
            CATransform3D xRotation = CATransform3DMakeRotation(JHDegreesToRadians(angle)+originalRotation, 1.0, 0, 0);
            view.layer.transform = xRotation;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableDegrees)rotateY
{
    JHChainableDegrees chainable = JHChainableDegrees(angle) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *rotationAnimation = [weakSelf basicAnimationForKeyPath:@"transform.rotation.y"];
            CATransform3D transform = view.layer.transform;
            CGFloat originalRotation = atan2(transform.m31, transform.m33);
            rotationAnimation.fromValue = @(originalRotation);
            rotationAnimation.toValue = @(originalRotation+JHDegreesToRadians(angle));
            [weakSelf addAnimationFromCalculationBlock:rotationAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CATransform3D transform = view.layer.transform;
            CGFloat originalRotation = atan2(transform.m31, transform.m33);
            CATransform3D yRotation = CATransform3DMakeRotation(JHDegreesToRadians(angle)+originalRotation, 0, 1.0, 0);
            view.layer.transform = yRotation;
        }];

        return self;
    };
    return chainable;
}

- (JHChainableDegrees)rotateZ
{
    JHChainableDegrees chainable = JHChainableDegrees(angle) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *rotationAnimation = [weakSelf basicAnimationForKeyPath:@"transform.rotation.z"];
            CATransform3D transform = view.layer.transform;
            CGFloat originalRotation = atan2(transform.m12, transform.m11);
            rotationAnimation.fromValue = @(originalRotation);
            rotationAnimation.toValue = @(originalRotation+JHDegreesToRadians(angle));
            [weakSelf addAnimationFromCalculationBlock:rotationAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CATransform3D transform = view.layer.transform;
            CGFloat originalRotation = atan2(transform.m12, transform.m11);
            CATransform3D zRotation = CATransform3DMakeRotation(JHDegreesToRadians(angle)+originalRotation, 0, 0, 1.0);
            view.layer.transform = zRotation;
        }];

        return self;
    };
    return chainable;
}

- (JHChainablePolarCoordinate)movePolar
{
    JHChainablePolarCoordinate chainable = JHChainablePolarCoordinate(radius, angle) {
        CGFloat x = radius*cosf(JHDegreesToRadians(angle));
        CGFloat y = -radius*sinf(JHDegreesToRadians(angle));
        return self.moveXY(x, y);
    };
    return chainable;
}

// Transforms

- (JHChainableAnimator *)transformIdentity
{
    [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
        JHKeyframeAnimation *transformAnimation = [weakSelf basicAnimationForKeyPath:@"transform"];
        CATransform3D transform = CATransform3DIdentity;
        transformAnimation.fromValue = [NSValue valueWithCATransform3D:view.layer.transform];
        transformAnimation.toValue = [NSValue valueWithCATransform3D:transform];
        [weakSelf addAnimationFromCalculationBlock:transformAnimation];
    }];
    [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
        CATransform3D transform = CATransform3DIdentity;
        view.layer.transform = transform;
    }];
    return self;
}

- (JHChainableFloat)transformX
{
    JHChainableFloat chainable = JHChainableFloat(f) {
        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *transformAnimation = [weakSelf basicAnimationForKeyPath:@"transform"];
            CATransform3D transform = view.layer.transform;
            transform = CATransform3DTranslate(transform, f, 0, 0);
            transformAnimation.fromValue = [NSValue valueWithCATransform3D:view.layer.transform];
            transformAnimation.toValue = [NSValue valueWithCATransform3D:transform];
            [weakSelf addAnimationFromCalculationBlock:transformAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CATransform3D transform = view.layer.transform;
            transform = CATransform3DTranslate(transform, f, 0, 0);
            view.layer.transform = transform;
        }];
        return self;
    };
    return chainable;
}

- (JHChainableFloat)transformY
{
    JHChainableFloat chainable = JHChainableFloat(f) {
        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *transformAnimation = [weakSelf basicAnimationForKeyPath:@"transform"];
            CATransform3D transform = view.layer.transform;
            transform = CATransform3DTranslate(transform, 0, f, 0);
            transformAnimation.fromValue = [NSValue valueWithCATransform3D:view.layer.transform];
            transformAnimation.toValue = [NSValue valueWithCATransform3D:transform];
            [weakSelf addAnimationFromCalculationBlock:transformAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CATransform3D transform = view.layer.transform;
            transform = CATransform3DTranslate(transform, 0, f, 0);
            view.layer.transform = transform;
        }];
        return self;
    };
    return chainable;
}

- (JHChainableFloat)transformZ
{
    JHChainableFloat chainable = JHChainableFloat(f) {
        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *transformAnimation = [weakSelf basicAnimationForKeyPath:@"transform"];
            CATransform3D transform = view.layer.transform;
            transform = CATransform3DTranslate(transform, 0, 0, f);
            transformAnimation.fromValue = [NSValue valueWithCATransform3D:view.layer.transform];
            transformAnimation.toValue = [NSValue valueWithCATransform3D:transform];
            [weakSelf addAnimationFromCalculationBlock:transformAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CATransform3D transform = view.layer.transform;
            transform = CATransform3DTranslate(transform, 0, 0, f);
            view.layer.transform = transform;
        }];
        return self;
    };
    return chainable;
}

- (JHChainablePoint)transformXY
{
    JHChainablePoint chainable = JHChainablePoint(x, y) {
        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *transformAnimation = [weakSelf basicAnimationForKeyPath:@"transform"];
            CATransform3D transform = view.layer.transform;
            transform = CATransform3DTranslate(transform, x, y, 0);
            transformAnimation.fromValue = [NSValue valueWithCATransform3D:view.layer.transform];
            transformAnimation.toValue = [NSValue valueWithCATransform3D:transform];
            [weakSelf addAnimationFromCalculationBlock:transformAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CATransform3D transform = view.layer.transform;
            transform = CATransform3DTranslate(transform, x, y, 0);
            view.layer.transform = transform;
        }];
        return self;
    };
    return chainable;
}

- (JHChainableFloat)transformScale
{
    JHChainableFloat chainable = JHChainableFloat(f) {
        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *transformAnimation = [weakSelf basicAnimationForKeyPath:@"transform"];
            CATransform3D transform = view.layer.transform;
            transform = CATransform3DScale(transform, f, f, 1);
            transformAnimation.fromValue = [NSValue valueWithCATransform3D:view.layer.transform];
            transformAnimation.toValue = [NSValue valueWithCATransform3D:transform];
            [weakSelf addAnimationFromCalculationBlock:transformAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CATransform3D transform = view.layer.transform;
            transform = CATransform3DScale(transform, f, f, 1);
            view.layer.transform = transform;
        }];
        return self;
    };
    return chainable;
}

- (JHChainableFloat)transformScaleX
{
    JHChainableFloat chainable = JHChainableFloat(f) {
        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *transformAnimation = [weakSelf basicAnimationForKeyPath:@"transform"];
            CATransform3D transform = view.layer.transform;
            transform = CATransform3DScale(transform, f, 1, 1);
            transformAnimation.fromValue = [NSValue valueWithCATransform3D:view.layer.transform];
            transformAnimation.toValue = [NSValue valueWithCATransform3D:transform];
            [weakSelf addAnimationFromCalculationBlock:transformAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CATransform3D transform = view.layer.transform;
            transform = CATransform3DScale(transform, f, 1, 1);
            view.layer.transform = transform;
        }];
        return self;
    };
    return chainable;
}

- (JHChainableFloat)transformScaleY
{
    JHChainableFloat chainable = JHChainableFloat(f) {
        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *transformAnimation = [weakSelf basicAnimationForKeyPath:@"transform"];
            CATransform3D transform = view.layer.transform;
            transform = CATransform3DScale(transform, 1, f, 1);
            transformAnimation.fromValue = [NSValue valueWithCATransform3D:view.layer.transform];
            transformAnimation.toValue = [NSValue valueWithCATransform3D:transform];
            [weakSelf addAnimationFromCalculationBlock:transformAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CATransform3D transform = view.layer.transform;
            transform = CATransform3DScale(transform, 1, f, 1);
            view.layer.transform = transform;
        }];
        return self;
    };
    return chainable;
}

- (JHChainableBlock)block
{
    JHChainableBlock chainable = JHChainableBlock(block) {
        [self addAnimationCalculationAction:^(UIView *__weak view, JHChainableAnimator *__weak weakSelf) {
            if (block != nil) {
                block();
            }
        }];
        return self;
    };
    return chainable;
}

- (JHChainableBlock)preAnimationBlock
{
    JHChainableBlock chainable = JHChainableBlock(block) {
        [self.currentAnimationLink addPreAnimationBlock:block];
        return self;
    };
    return chainable;
}

- (JHChainableBlock)postAnimationBlock
{
    JHChainableBlock chainable = JHChainableBlock(block) {
        [self addAnimationCompletionAction:^(UIView *__weak view, JHChainableAnimator *__weak weakSelf) {
            if (block != nil) {
                block();
            }
        }];
        return self;
    };
    return chainable;
}

- (JHChainableBezierPath)moveOnPath
{
    JHChainableBezierPath chainable = JHChainableBezierPath(path) {
        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *pathAnimation = [weakSelf basicAnimationForKeyPath:@"position"];
            pathAnimation.path = path.CGPath;
            [weakSelf addAnimationFromCalculationBlock:pathAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGPoint endPoint = path.currentPoint;
            view.layer.position = endPoint;
        }];
        return self;
    };
    return chainable;
}

- (JHChainableBezierPath)moveAndRotateOnPath
{
    JHChainableBezierPath chainable = JHChainableBezierPath(path) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *pathAnimation = [weakSelf basicAnimationForKeyPath:@"position"];
            pathAnimation.path = path.CGPath;
            pathAnimation.rotationMode = kCAAnimationRotateAuto;
            [weakSelf addAnimationFromCalculationBlock:pathAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGPoint endPoint = path.currentPoint;
            view.layer.position = endPoint;
        }];
        return self;
    };
    return chainable;
}

- (JHChainableBezierPath)moveAndReverseRotateOnPath
{
    JHChainableBezierPath chainable = JHChainableBezierPath(path) {

        [self addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            JHKeyframeAnimation *pathAnimation = [weakSelf basicAnimationForKeyPath:@"position"];
            pathAnimation.path = path.CGPath;
            pathAnimation.rotationMode = kCAAnimationRotateAutoReverse;
            [weakSelf addAnimationFromCalculationBlock:pathAnimation];
        }];
        [self addAnimationCompletionAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
            CGPoint endPoint = path.currentPoint;
            view.layer.position = endPoint;
        }];
        return self;
    };
    return chainable;
}

- (JHChainableAnimator *)anchorDefault
{
    return self.anchorCenter;
}

- (JHChainableAnimator *)anchorCenter
{
    [self makeAnchorFromX:0.5 Y:0.5];
    return self;
}

- (JHChainableAnimator *)anchorTopLeft
{
    [self makeAnchorFromX:0.0 Y:0.0];
    return self;
}

- (JHChainableAnimator *)anchorTopRight
{
    [self makeAnchorFromX:1.0 Y:0.0];
    return self;
}

- (JHChainableAnimator *)anchorBottomLeft
{

    [self makeAnchorFromX:0.0 Y:1.0];

    return self;
}

- (JHChainableAnimator *)anchorBottomRight
{
    [self makeAnchorFromX:1.0 Y:1.0];
    return self;
}

- (JHChainableAnimator *)anchorTop
{
    [self makeAnchorFromX:0.5 Y:0.0];
    return self;
}

- (JHChainableAnimator *)anchorBottom
{
    [self makeAnchorFromX:0.5 Y:1.0];
    return self;
}

- (JHChainableAnimator *)anchorLeft
{
    [self makeAnchorFromX:0.0 Y:0.5];
    return self;
}

- (JHChainableAnimator *)anchorRight
{
    [self makeAnchorFromX:1.0 Y:0.5];
    return self;
}

#pragma mark - Animations
- (void)addAnimationKeyframeCalculation:(JHKeyframeAnimationCalculationBlock)functionBlock
{
    [self.currentAnimationLink addAnimationCalculationAction:^(__weak UIView *view, __weak JHChainableAnimator *weakSelf) {
        [weakSelf.animationChainLinks.firstObject addAnimationKeyframeCalculation:functionBlock];
    }];
}

- (JHChainableAnimator *)easeIn
{
    return self.easeInQuad;
}

- (JHChainableAnimator *)easeOut
{
    return self.easeOutQuad;
}

- (JHChainableAnimator *)easeInOut
{
    return self.easeInOutQuad;
}

- (JHChainableAnimator *)easeBack
{
    return self.easeOutBack;
}

- (JHChainableAnimator *)spring
{
    return self.easeOutElastic;
}

- (JHChainableAnimator *)bounce
{
    return self.easeOutBounce;
}

- (JHChainableAnimator *)easeInQuad
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInQuad(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeOutQuad
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseOutQuad(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInOutQuad
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInOutQuad(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInCubic
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInCubic(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeOutCubic
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseOutCubic(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInOutCubic
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInOutCubic(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInQuart
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInQuart(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeOutQuart
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseOutQuart(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInOutQuart
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInOutQuart(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInQuint
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInQuint(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeOutQuint
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseOutQuint(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInOutQuint
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInOutQuint(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInSine
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInSine(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeOutSine
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseOutSine(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInOutSine
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInOutSine(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInExpo
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInExpo(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeOutExpo
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseOutExpo(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInOutExpo
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInOutExpo(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInCirc
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInCirc(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeOutCirc
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseOutCirc(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInOutCirc
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInOutCirc(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInElastic
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInElastic(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeOutElastic
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseOutElastic(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInOutElastic
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInOutElastic(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInBack
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInBack(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeOutBack
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseOutBack(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInOutBack
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInOutBack(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInBounce
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInBounce(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeOutBounce
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseOutBounce(t, b, c, d);
    }];
    return self;
}

- (JHChainableAnimator *)easeInOutBounce
{
    [self addAnimationKeyframeCalculation:^double(double t, double b, double c, double d) {
        return JHKeyframeAnimationFunctionEaseInOutBounce(t, b, c, d);
    }];
    return self;
}

- (JHChainableCustomKeyframeAnimationCalculation)customAnimationFunction
{
    JHChainableCustomKeyframeAnimationCalculation chainable = JHChainableCustomKeyframeAnimationCalculation(block) {
        [self addAnimationKeyframeCalculation:block];
        return self;
    };
    return chainable;
}

- (JHChainableTimeInterval)delay
{
    JHChainableTimeInterval chainable = JHChainableTimeInterval(t) {

        self.currentAnimationLink.animationDelay = t;

        return self;
    };
    return chainable;
}

- (JHChainableTimeInterval)wait
{
    JHChainableTimeInterval chainable = JHChainableTimeInterval(t) {

        return self.delay(t);
    };
    return chainable;
}
@end

redpajama_set_name: RedPajamaGithub


We usually are running new promotions to help our customers save on their wedding rings. At this time, we have one discount code.
Use SPRING18, which grants you an additional $5 off your total purchase. Thorsten Rings is an alternative precious metals wedding ring manufacturer which supports all of their items through a top-rated free life time warranty. This would mean should your ring finger size ever change or if perhaps the ring breaks or cracks whether or not this was your fault, they will exchange it at no cost to you.
We do provide a military discount open to military staff solely. It grants an extra 5% off the active promotional code. To get the military discount, simply just e-mail us through your military e-mail address or e-mail us a photo of your military ID card and we'll e-mail you back with this exclusive discount unavailable for civilians. For additional queries, please be sure to contact us and we'll provide you with a prompt reply during our regular business hours.
Sign up and receive a 10% discount on your purchase as well as regular updates and deals!


Horizontal Auger Drive Chain Requires a 13 Tooth Motor Sprocket with Key.
Horizontal Auger Drive Chain. Requires an additional Connecting Link 20-1330 and a Half Link 20-1370.
The Horizontal Auger Drive Chain Half Link.


text:
Karim Sayad – "Of Sheep and Men" – Episode 332
by david | Oct 18, 2017 | Film, International Development, podcasts, Social Justice | 0 comments
Karim Sayad and Face2Face host David Peck talk about his new film Of Sheep and Men, ram fighting, Eid al-Adha, Algeria, colonization, democracy and the Arab Spring, empathy and gender injustice.
Karim Sayad was born in Lausanne, Switzerland and completed a master's degree in international relations at Geneva's Graduate Institute of International and Development Studies. He has directed the short film Babor Casanova.
Of Sheep and Men is his debut feature documentary.
"Sixteen-year-old Habib dreams of training his prized sheep to become a fighting champion and middle-aged Samir just wants to sell enough sheep before Eid to make ends meet, in this emotive profile of two men in an impoverished Algerian community.
It's a tale of two men, one country, and many sheep. Habib is 16 years old, working as a bus conductor although he had once dreamed of becoming a veterinarian. He is the proud owner of El Bouq, a ram he hopes to raise to become a champion fighter. Samir is 42 years old, having seen enough hardship to know the cost of dreaming is high and that everything in life has the potential to bring a profit. He capitalizes on this ethos at Eid al-Adha (Feast of the Sacrifice) by buying and selling sheep for the coming slaughter. When El Bouq proves to be a far-from-illustrious combatant, and with Eid fast approaching, it looks as though the animal's fate might not be a favourable one.
Building on the style and themes explored in his award-winning short Babor Casanova, Karim Sayad explores in his documentary feature debut the current climate in Algeria following the Arab Spring with an allegorical and intricate eye. Shot in the Bab el Oued neighbourhood of Algiers, with a camera that sinks seamlessly into each scenario, the film captures Habib's, Samir's, and even the sheep's perspectives with an intriguing sense of intimacy that's rich with symbolism."
With thanks to Kiva Reardon – TIFF
Image Copyright: Karim Sayad and Close Up Films. Used with permission.
https://app.pippa.io/public/streams/5b604f084c43ac1347a3f13a/episodes/5b604f48e14127853f949f73.mp3
Violeta Ayala – Episode 312 Jonas Matzow Gulbrandsen- "Valley of Shadows" – Episode 324 Gustavo Salmerón -"Lots of Kids, A Monkey and a Castle" – Episode 333 Michael Matthews & Sean Drummond – "5 Fingers for Marseille" – Episode 328 Mouly Surya – Marlina: The Murderer in Four Acts – Episode 330

redpajama_set_name: RedPajamaCommonCrawl


Pharmboard > Topics > Non-Sales Pharma Positions > sales reps in other fields?
This topic contains 1 reply, has 2 voices, and was last updated by Clint Cora 11 years, 11 months ago.
Seeing as we're all selling machines in this field, aren't there other lucrative sales fields that we will find easy to get into?
My cousin just got a job in medical equipment sales. It's a different environment compared to pharma though.


I must admit that yesterday was not one of my favorite days. Early in the morning, a fleet of giant trucks came rumbling down the driveway and filled up the parking lot. One of them carried a huge crane that looked like it could reach the sky. They were coming to take down a white oak tree that was estimated by the city arborist to be 130 years old.
I had a love/hate relationship with this tree. It had been spared at the last minute when we built our house 31 years ago. My husband decided we could do without the planned carport and have a nice tree right up against the house instead.
In fall, the acorns would pound against the slate roof all night long for days on end. The leaves fell late, so the gutters had to be cleaned out multiple times during autumn and then a final time after Christmas. In 2000, an enormous limb fell and destroyed some limestone coping around a little boxwood garden outside the kitchen. Every time a wind would come up, I would shudder, thinking about the other giant limbs that hung over the house.
Still, the large, leafy canopy probably saved thousands in air-conditioning bills over the years.
The tree's fate was sealed last Sunday when a 40 foot long branch came crashing down and brought two other huge limbs with it. The arborist said the tree had to come down immediately.
So, yesterday the tree company had it down before noon. This morning, the main trunk is still lying on the ground in big chunks. I measured the diameter of the stump, which will be ground up today. Sixty-one inches. That's pretty big.
While they were sawing away, I went out on the back terrace and was greeted by the moon flowers you see in the photo above, the seeds a gift from a friend. I got closer and caught their sweet fragrance. I was immediately cheered by their pristine beauty. Unlike the oak that had lasted for more than a century, these very flowers wouldn't make it through the day. But, I could already see some more twisted buds ready to open during the night.
Moonflowers are annual vines, and like day lilies, the individual flowers last only a day (or rather a night and part of the next morning). But it is exciting to know that there will be more big, white blossoms the next day and the next.
Before I get deeper into this maudlin metaphor (I feel ashamed thinking of all the real suffering and loss out there), a word about growing moonvine (Ipomoea alba). I don't have a lot of foliage on either of my plants, which are growing in containers. A friend advised me not to use fertilizer because I would get too much foliage and not enough blooms, which is what happened to me before. Another gardener called to say she must have 40 flowers on her vines and that she had poured on a bloom buster type fertilizer with a high middle number.
I planted the seedlings (my friend had soaked the seeds and gave me small plants) back in May. Yesterday, I was happy to have the big, fragrant flowers to enjoy while the tree men buzzed away. Sometimes, there's nothing like a flower to make it all seem better.
Flowers do make a difference. This story came at a good time -- I needed a lift!!
So sorry you lost your tree - seems that's happening all over our area. We had to have the huge old oak removed from in front of the Norcross Garden Club last winter before it fell on someone in the street or the house. So sad, but we've moved on & planted lots of sun loving plants that we couldn't plant in the front of the Club while the tree was there. Love your moon vine - great picture.


text:
#TBT Series: Watch Video of a True 1990's Old School Rave
Welcome back to our weekly appointment with Throwback Thursday, our feature series that travels back in time to look at some of the most iconic, thought-provoking and nostalgic moments in dance music scene history.
This week our time machine takes us back to the early to mid 90's, the span of years that some still consider to be the heydays of the rave revolution. New York City had already gone through its disco years, Chicago House was being played all over the world and Detroit had soon followed suit in the late 80's to early 90's with techno. Across the pond Ibiza fever was taking over and, influenced by the Balearic parties and the sounds coming from the States, the rave scene in the United Kingdom was truly flourishing.
There was no Facebook, no Instagram, no Snapchat and definitely no way to tweet the night away during those years. Just the music, the crowd, a likely illegal venue, your mates and, let's be honest, some good old ecstasy or MDMA.
The video below, which has been making the rounds on Facebook and already has almost 170,000 views, depicts those years perfectly. Enjoy it and learn a thing or two from those dance moves!
Welcome back to our...


Fassine are set to release their debut album, Dialectik on May 2nd. Ploughing a furrow that is as mysterious and brooding as it is enlivening and joyous, the London trio's lyrically dark and musically complex operation possesses all the hallmarks of a typically British affair; cold and austere soundscapes being paired with the uplifting, in a fine execution of the juxtapositional. There are hints of Trent Reznor's influence, along with a smart ear for the cinematic – something that has not gone un-noticed, earning their track Bring The Weight Down a slot in Sky Atlantic's hit US show The Following. Fassine's sound is one of striking imagery that dares the listener to enter the group's own stark world.
As a body of work, Dialectik is, in the band's own words "about isolation, those treacherous thoughts that wedge themselves in the door – a journey from logic to absurdity and back again." On Dialectik, dark meets like and cold flirts with warmth, creating claustrophobic atmospheres that reflect their subject matter perfectly.
In the media, Fassine have had support from Spin, Q Magazine, The 405, The Guardian and more in the run up to their debut album. In addition to Sunshine it features the single Kelby. The band recently featured on artist Robot Koch's haunting new track, Spine – released on Monkeytown Records.


text:
Countdown - No. 2: Bill Walsh
2. Bill Walsh: Offensive patriarch
No. 2 - Bill Walsh (1:33)
Jerry Rice, Joe Montana and Sam Wyche discuss why Bill Walsh is one of the greatest coaches in NFL history. (1:33)
ESPN celebrates the 100th anniversary of Vince Lombardi's birth with the "Greatest Coaches in NFL History" series, saluting the finest innovators, motivators, tacticians, teachers and champions ever to stalk the sidelines. Follow along as we reveal our list of the top 20 coaches of all time and document the lineage of the league's most influential coaching trees.
Bill Walsh, considered an offensive genius and father of the West Coast offense, turned the San Francisco 49ers into a dynasty in the 1980s. In 10 seasons under Walsh, the 49ers won six NFC West titles and three Super Bowls.
The offensive philosophy that Walsh originally subscribed to was the Sid Gillman passing attack, which Walsh adopted from Gillman disciple Al Davis as a Raiders assistant in 1966. Walsh's big break, however, came in 1968, when he joined the staff of the legendary Paul Brown with the expansion Cincinnati Bengals after considering quitting football in favor of law school. In Cincinnati, Walsh modified Gillman's system and incorporated a heavy dose of short passes, creating what ultimately would become known as the West Coast offense in San Francisco.
The relationship between Walsh and Brown was icy and often punctuated by friction. Walsh felt constricted by Brown and believed his boss was holding him back from becoming a head coach. Following eight seasons in Cincinnati, Walsh left the Bengals when he was passed over to succeed Brown as head coach in 1976. Offensive line coach Bill Johnson got the job instead, and Walsh believed that Brown subsequently tried to blackball him in NFL circles.
After helping to develop future Hall of Fame quarterback Dan Fouts while serving as an assistant with the San Diego Chargers for one season, Walsh became head coach at Stanford, where he remained for two seasons. Walsh was named head coach and general manager of the 49ers in 1979. The previous season, the 49ers had gone 2-14, having won only 31 of their previous 86 games. The 1981 team won the Super Bowl, and the 1984 and 1988 teams repeated the feat.
Walsh retired after Super Bowl XXIII in January 1989, but the 49ers -- coached by his former defensive coordinator George Seifert -- returned to the Super Bowl the following season and won.
Mike Holmgren followed Seifert as another former Walsh assistant who went on to win the Super Bowl. The Walsh coaching tree boasts no fewer than 25 descendants, including Super Bowl champions Holmgren, Brian Billick, Jon Gruden and John Harbaugh.
-- Shawna Seed
WALSH THROUGH THE EYES OF A PLAYER: JERRY RICE
The thing about Bill Walsh is he had this certain swagger about him. I know they called him a genius for his play calling, but really it was the way he carried himself. If you stepped on that practice field and you didn't practice well, then you could tell by his body language, "Hey, we better pick it up. If we don't pick it up right now, we're probably going to start practice all over again."
The 49ers won three Super Bowls under Bill Walsh and another the season after he retired. AP Photo
Seriously, that happened sometimes. It's one of those scenarios where your father, if he gives you that certain look. You know the one I'm talking about, where it's, "You better straighten up and start acting right."
There was such a sense of urgency about him that you wanted to win football games not only for yourself but for him. I find that to be very special when a coach has swagger like that.
Bill always used to say this: If you don't put the time in during the week, you don't have a chance on that given Sunday or Monday night. That's why we practiced the way we played during the week. If you came to a 49ers practice, you would not believe the speed of play, the tempo, the attention to detail. We had done it during the week.
Now, all of a sudden, we didn't have to think about it when you get to the game. It's ingrained in you. You just go out there and do it. That's coaching right there.
-- Former 49ers wide receiver and Hall of Famer Jerry Rice, as told to Greg Garber
ESPN "Greatest Coaches in NFL History" voting panel: Chris Berman, Jeffri Chadiha, John Clayton, Colin Cowherd, Mike Ditka, Gregg Easterbrook, Herm Edwards, David Fleming, Ashley Fox, Greg Garber, Mike Golic, Suzy Kolber, Eric Mangini, Chris Mortensen, Sal Paolantonio, Bill Polian, Rick Reilly, Mike Sando, Adam Schefter, Ed Werder, Seth Wickersham, Trey Wingo.


text:
Currently residing in the Pacific Northwest, Greg blogs about his upcoming books in The Sy'Arrian Legacy Series. His posts also explore the creative process and what feeds our imaginations.
Gregory D. Current: Author of the Sy'Arrian Legacy Series
Mar 18 Bait and Ditch: The Gay Character Paradox
Gregory Current
Discussion, Writing
As a closeted gay teen growing up in the late 70's and early 80's, if you would have asked me there would be a time one could write about the treatment of gay characters in storytelling, I would have probably laughed nervously and carried on with my business. The evolution of gay characters in virtually all types of creative content has grown tremendously in the last few decades but their treatment has been a mixed bag.
In this entry, my intent is to point out what I like to refer to as the bait and ditch practice content creators use lavishly when introducing and maintaining gay characters. My initial research yielded a deluge of information, ranging from the very first introductions of queer characters to the widespread practice of queerbaiting. Links to specific references are listed below.
This technique ties loosely into my Creative Liberties and Keep It Simple posts, where the finely crafted reasonings for introducing a gay character by content creators is somehow lost in translation once the cameras start rolling or fingers begin flying across a keyboard. In my Keep It Simple post, I referenced the treatment of actor Chris Brochu's character Luke Parker in the CW's, The Vampire Diaries. Content creators stipulated he would be a prominent gay character but in reality, he was practically non-existent and forgettable. You can read more about the CW's handling of Luke by reading my prior post. I want to point out another popular series, whose treatment of gay characters is extremely better but fell short with one of its own. The series I am referring? Freeform's, The Fosters, previously seen on ABC Family.
Let me be abundantly clear. My husband and I love this series although it is ending in June 2018 with a three-part series wrap up and an untitled spin off series in development. The show has received numerous awards and accolades for its treatment of LGBTQ related issues. I always admired its direct, common sense and unabashed approach to issues affecting minority populations and society in general. Praise aside, there is always room for improvement.
Case in point. Characters Jude Adams Foster and Connor Stevens superbly played by actors Hayden Byerly and Gavin MacIntosh respectively, entered television's history books with the youngest onscreen same sex kiss. I was personally impressed considering both actors are straight and, based on what could be seen on the surface, handled the scene with poise and did not appear to have any serious hang ups over it. Both actors became allies of the LBGTQ community taking notable stances on bullying and an instance of censorship. Witnessing such behavior in the younger generations and noting the progressively fluid nature of sexual identity, has always given me hope the draconian and venomous ideologies seeking to divide us are slowly dying off even though they may have occasional resurgences. Yet, my main disagreement is not with the actors; you know my stance regarding these types of critiques if you have read my prior posts regarding the treatment of characters.
When I stumbled across The Fosters while conducting research for my own book, I was relieved to see its creators were Peter Paige and Bradley Bredeweg; the former I was well aware having seen him in Queer as Folk. One of the main reasons why my husband and I began watching the show was due to its main content creators being gay. Both of us were very tired of the standardized approach the entertainment industry takes towards appropriately introducing gay characters. Namely, if "the gays" make too much of a fuss, we'll appease them by sprinkling a few of them around and call it good. Give one a prominent lead role? Please.
Jude's and Connor's kiss received a truck load of attention thrusting both characters and the actors who portrayed them into a media circus where my own gay pessimism soon reared its head. The importance of the event was not lost on me, but I soon sat back and waited for the inevitable. Sure enough, in typified Hollywood fashion, I noticed a decline in these two characters interactions and the evolution of an "issue" eventually breaking them apart with one of them leaving the show altogether. Eventually Jude moves forward and begins a relationship with Noah beautifully portrayed by Kalama Epstein, who I believe provides the necessary balance in Jude's life.
In many of my prior posts, I point to being intellectually honest when we confront our bias and how we should respond when airing creative differences; I want to do that here. When you review The Fosters premise and impressive body of work, my observation is a tad, shall we say, weak. The two main leads, Stef and Lena Adams Foster are lesbian with a multiethnic, blended family. Me even hinting at some creative, duplicitous act would have many of your crinkling your brow and wondering if I left my brain in the pickle jar. Trust this is not my intent. My opinion stems more from a culmination of what I have seen in my lifetime than the diversity The Fosters clearly emulates.
Just like I mentioned in the introduction, I grew up with zero role models in the areas of what it meant to be gay. What you did see in film and television were blatant stereotypes. Gay characters always committed suicide, were mentioned but barely seen, or had such serious issues they would leave the show altogether. Even if a talented actor was found to play such a role they appeared so emasculated, all the roles they appeared in afterward focused on copious amounts of sex with women.
In the 1982 drama, Making Love staring Kate Jackson, Michael Ontkean, and Harry Hamlin where a successful young doctor begins to question his sexuality while in a seemingly happy relationship with his wife of eight years, a variety of actors were approached to play the gay role; Tom Berenger, Harrison Ford, and Michael Douglas to name a few. The movie's creators were essentially told by these actors to not even consider them for the role. The roadblocks allowing for gay characters to be accurately portrayed were numerous. Fortunately, nowadays, younger actors are not allowing themselves to be held back due to extreme views of what it means to be masculine, feminine and everything else in-between.
This is the reason why many who look at this issue through a lens similar to mine tend to react harshly when they experience the business as usual model regarding the evolution of gay characters in a particular story. Personally, I was looking forward to how Jude and Connor's characters were going to evolve and confront issues of simply being gay teenagers in today's world. When that evolution was cut short, I called foul.
Whether you agree with me or not regarding the handling of Jude's and Connor's characters, I am pointing to a systemic issue still fairly prevalent in content creation. Creator's often generate a lot of hype and fervor when announcing the introduction of a gay character but rarely deliver on their spoken word.
Do not even get me started on the double standard between being fine with showing a great deal of physical intimacy amongst two women; however, when it comes to two men, you'll see a peck on the lips, maybe a tad more but that is all. If you do want to find a more realistic representation, one has to obtain a premium service like HBO or Showtime.
Bottomline, we are consumers of intellectual and physical goods. If we continue to blindly consume the complimentary scraps of deficient content thrown our direction, should we realistically expect content creators to step up and provide us accurate, realistic portrayals of gay characters?
We need to stop settling for what content creators THINK we need and start demanding from them what we KNOW we want. Do not simply vote with your wallet but your intellect as well.
Feel free to post your questions, comments or concerns. I will respond, if need be, when I am able.
Bury Your Gays Article: http://www.huffingtonpost.ca/2017/06/30/queerbaiting-bury-your-gays-tv_a_23005000/
The Fosters: https://en.wikipedia.org/wiki/The_Fosters_(2013_TV_series)
History of Homosexuality in American Film (Wiki): https://en.wikipedia.org/wiki/History_of_homosexuality_in_American_film
Making Love: https://en.wikipedia.org/wiki/Making_Love
The Vampire Diaries: https://en.wikipedia.org/wiki/The_Vampire_Diaries
Writing, Television, Gay, Characters, Fosters, Vampire Diaries
Twitter LinkedIn0 StumbleUpon Reddit Tumblr 0 Likes
Mar 26 Character Portrait: Brakel Janus
Feb 24 Another Casualty: The Shannara Chronicles
Blog About Contact Links Archive


The spectacular tropical archipelago of the Bahamas beckons our bikini beauties to photo shoots in the Caribbean.
Come along as they explore the islands, subterranean caves, and some of the most spectacular geography on the face of the earth.
Gorgeous scenery and models await.