<commit_before>// Parallax effect inspired by https://github.com/oblador/react-native-parallax/

import React, { Component } from 'react';
import { View, ViewPropTypes, Image, Animated, Easing, ActivityIndicator, findNodeHandle } from 'react-native';
import PropTypes from 'prop-types';
import styles from './ParallaxImage.style';

export default class ParallaxImage extends Component {

    static propTypes = {
        ...Image.propTypes,
        carouselRef: PropTypes.object, // passed from <Carousel />
        itemHeight: PropTypes.number, // passed from <Carousel />
        itemWidth: PropTypes.number, // passed from <Carousel />
        scrollPosition: PropTypes.object, // passed from <Carousel />
        sliderHeight: PropTypes.number, // passed from <Carousel />
        sliderWidth: PropTypes.number, // passed from <Carousel />
        vertical: PropTypes.bool, // passed from <Carousel />
        containerStyle: ViewPropTypes.style,
        dimensions: PropTypes.shape({
            width: PropTypes.number,
            height: PropTypes.number
        }),
        fadeDuration: PropTypes.number,
        parallaxFactor: PropTypes.number,
        showSpinner: PropTypes.bool,
        spinnerColor: PropTypes.string
    };

    static defaultProps = {
        containerStyle: {},
        fadeDuration: 500,
        parallaxFactor: 0.3,
        showSpinner: true,
        spinnerColor: 'rgba(0, 0, 0, 0.4)'
    }

    constructor (props) {
        super(props);
        this.state = {
            offset: 0,
            width: 0,
            height: 0,
            status: 1, // 1 -> loading; 2 -> loaded // 3 -> transition finished; 4 -> error
            animOpacity: new Animated.Value(0)
        };
        this._onLoad = this._onLoad.bind(this);
        this._onError = this._onError.bind(this);
    }

    setNativeProps (nativeProps) {
        this._container.setNativeProps(nativeProps);
    }

    componentDidMount () {
        this._mounted = true;

        setTimeout(() => {
            this._measureLayout();
        }, 0);
    }

    componentWillUnmount () {
        this._mounted = false;
    }

    _measureLayout () {
        if (this._container) {
            const {
                dimensions,
                vertical,
                carouselRef,
                sliderWidth,
                sliderHeight,
                itemWidth,
                itemHeight
            } = this.props;

            if (carouselRef) {
                this._container.measureLayout(
                    findNodeHandle(carouselRef),
                    (x, y, width, height, pageX, pageY) => {
                        const offset = vertical ?
                            y - ((sliderHeight - itemHeight) / 2) :
                            x - ((sliderWidth - itemWidth) / 2);

                        this.setState({
                            offset: offset,
                            width: dimensions && dimensions.width ?
                                dimensions.width :
                                Math.ceil(width),
                            height: dimensions && dimensions.height ?
                                dimensions.height :
                                Math.ceil(height)
                        });
                    }
                );
            }
        }
    }

    _onLoad () {
        const { animOpacity } = this.state;
        const { fadeDuration } = this.props;

        if (!this._mounted) {
            return;
        }

        this.setState({ status: 2 });

        Animated.timing(animOpacity, {
            toValue: 1,
            duration: fadeDuration,
            easing: Easing.out(Easing.quad),
            isInteraction: false,
            useNativeDriver: true
        }).start(() => {
            this.setState({ status: 3 });
        });
    }

    // If arg is missing from method signature, it just won't be called
    _onError (event) {
        this.setState({ status: 4 });
    }

    get image () {
        const { status, animOpacity, offset, width, height } = this.state;
        const {
            scrollPosition,
            dimensions,
            vertical,
            sliderWidth,
            sliderHeight,
            parallaxFactor,
            style,
            ...other
        } = this.props;

        const parallaxPadding = (vertical ? height : width) * parallaxFactor;
        const dynamicStyles = {
            width: vertical ? width : width + parallaxPadding * 2,
            height: vertical ? height + parallaxPadding * 2 : height,
            opacity: animOpacity,
            transform: scrollPosition ? [
                {<commit_msg>refactor(ParallaxImage): expose 'onLoad' and 'onError'<commit_after>// Parallax effect inspired by https://github.com/oblador/react-native-parallax/

import React, { Component } from 'react';
import { View, ViewPropTypes, Image, Animated, Easing, ActivityIndicator, findNodeHandle } from 'react-native';
import PropTypes from 'prop-types';
import styles from './ParallaxImage.style';

export default class ParallaxImage extends Component {

    static propTypes = {
        ...Image.propTypes,
        carouselRef: PropTypes.object, // passed from <Carousel />
        itemHeight: PropTypes.number, // passed from <Carousel />
        itemWidth: PropTypes.number, // passed from <Carousel />
        scrollPosition: PropTypes.object, // passed from <Carousel />
        sliderHeight: PropTypes.number, // passed from <Carousel />
        sliderWidth: PropTypes.number, // passed from <Carousel />
        vertical: PropTypes.bool, // passed from <Carousel />
        containerStyle: ViewPropTypes.style,
        dimensions: PropTypes.shape({
            width: PropTypes.number,
            height: PropTypes.number
        }),
        fadeDuration: PropTypes.number,
        parallaxFactor: PropTypes.number,
        showSpinner: PropTypes.bool,
        spinnerColor: PropTypes.string
    };

    static defaultProps = {
        containerStyle: {},
        fadeDuration: 500,
        parallaxFactor: 0.3,
        showSpinner: true,
        spinnerColor: 'rgba(0, 0, 0, 0.4)'
    }

    constructor (props) {
        super(props);
        this.state = {
            offset: 0,
            width: 0,
            height: 0,
            status: 1, // 1 -> loading; 2 -> loaded // 3 -> transition finished; 4 -> error
            animOpacity: new Animated.Value(0)
        };
        this._onLoad = this._onLoad.bind(this);
        this._onError = this._onError.bind(this);
    }

    setNativeProps (nativeProps) {
        this._container.setNativeProps(nativeProps);
    }

    componentDidMount () {
        this._mounted = true;

        setTimeout(() => {
            this._measureLayout();
        }, 0);
    }

    componentWillUnmount () {
        this._mounted = false;
    }

    _measureLayout () {
        if (this._container) {
            const {
                dimensions,
                vertical,
                carouselRef,
                sliderWidth,
                sliderHeight,
                itemWidth,
                itemHeight
            } = this.props;

            if (carouselRef) {
                this._container.measureLayout(
                    findNodeHandle(carouselRef),
                    (x, y, width, height, pageX, pageY) => {
                        const offset = vertical ?
                            y - ((sliderHeight - itemHeight) / 2) :
                            x - ((sliderWidth - itemWidth) / 2);

                        this.setState({
                            offset: offset,
                            width: dimensions && dimensions.width ?
                                dimensions.width :
                                Math.ceil(width),
                            height: dimensions && dimensions.height ?
                                dimensions.height :
                                Math.ceil(height)
                        });
                    }
                );
            }
        }
    }

    _onLoad (event) {
        const { animOpacity } = this.state;
        const { fadeDuration, onLoad } = this.props;

        if (!this._mounted) {
            return;
        }

        this.setState({ status: 2 });

        if (onLoad) {
            onLoad(event);
        }

        Animated.timing(animOpacity, {
            toValue: 1,
            duration: fadeDuration,
            easing: Easing.out(Easing.quad),
            isInteraction: false,
            useNativeDriver: true
        }).start(() => {
            this.setState({ status: 3 });
        });
    }

    // If arg is missing from method signature, it just won't be called
    _onError (event) {
        const { onError } = this.props;

        this.setState({ status: 4 });

        if (onError) {
            onError(event);
        }
    }

    get image () {
        const { status, animOpacity, offset, width, height } = this.state;
        const {
            scrollPosition,
            dimensions,
            vertical,
            sliderWidth,
            sliderHeight,
            parallaxFactor,
            style,
            ...other
        } = this.props;

        const parallaxPadding = (vertical ? height : width) * parallaxFactor;
        const dynamicStyles = {
            width: vertical ? width : width + parallaxPadding * 2,
            height: vertical ? height + parallaxPadding * 2 : height,
            opacity: animOpacity,
            transform: scrollPosition ? [
                {
{'fraction_non_alphanumeric': 0.10137619497846412, 'fraction_numerical': 0.005882970900304654, 'mean_word_length': 1.4273329933707293, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 31, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4209511', 'n_tokens_mistral': 2488, 'n_tokens_neox': 2394, 'n_words': 569}