<commit_before>/**
 * Animation main class, dispatch and manage all animation controllers
 *
 */
// TODO Additive animation
// http://iosoteric.com/additive-animations-animatewithduration-in-ios-8/
// https://developer.apple.com/videos/wwdc2014/#236

import * as util from '../core/util';
import Eventful from '../core/Eventful';
import requestAnimationFrame from './requestAnimationFrame';
import Animator from './Animator';
import Clip from './Clip';


interface Stage {
    update?: () => void
}
type OnframeCallback = (deltaTime: number) => void

interface AnimationOption {
    stage?: Stage
    onframe?: OnframeCallback
}
/**
 * @example
 *     const animation = new Animation();
 *     const obj = {
 *         x: 100,
 *         y: 100
 *     };
 *     animation.animate(node.position)
 *         .when(1000, {
 *             x: 500,
 *             y: 500
 *         })
 *         .when(2000, {
 *             x: 100,
 *             y: 100
 *         })
 *         .start('spline');
 */

export default class Animation extends Eventful {

    stage: Stage

    onframe: OnframeCallback

    // Use linked list to store clip
    private _clipsHead: Clip
    private _clipsTail: Clip

    private _running: boolean = false

    private _time: number = 0
    private _pausedTime: number = 0
    private _pauseStart: number = 0

    private _paused = false;

    constructor(opts?: AnimationOption) {
        super();

        opts = opts || {};

        this.stage = opts.stage || {};

        this.onframe = opts.onframe || function () {};
    }

    /**
     * Add clip
     */
    addClip(clip: Clip) {
        if (clip.animation) {
            // Clip has been added
            this.removeClip(clip);
        }

        if (!this._clipsHead) {
            this._clipsHead = this._clipsTail = clip;
        }
        else {
            this._clipsTail.next = clip;
            clip.prev = this._clipsTail;
            clip.next = null;
            this._clipsTail = clip;
        }
        clip.animation = this;
    }
    /**
     * Add animator
     */
    addAnimator(animator: Animator<any>) {
        animator.animation = this;
        const clip = animator.getClip();
        if (clip) {
            this.addClip(clip);
        }
    }
    /**
     * Delete animation clip
     */
    removeClip(clip: Clip) {
        if (!clip.animation) {
            return;
        }
        const prev = clip.prev;
        const next = clip.next;
        if (prev) {
            prev.next = next;
        }
        else {
            // Is head
            this._clipsHead = next;
        }
        if (next) {
            next.prev = prev;
        }
        else {
            // Is tail
            this._clipsTail = prev;
        }
        clip.next = clip.prev = clip.animation = null;
    }

    /**
     * Delete animation clip
     */
    removeAnimator(animator: Animator<any>) {
        const clip = animator.getClip();
        if (clip) {
            this.removeClip(clip);
        }
        animator.animation = null;
    }

    update(notTriggerStageUpdate?: boolean) {
        const time = new Date().getTime() - this._pausedTime;
        const delta = time - this._time;
        let clip = this._clipsHead;

        while (clip) {
            // Save the nextClip before step.
            // So the loop will not been affected if the clip is removed in the callback
            const nextClip = clip.next;
            let finished = clip.step(time, delta);
            if (finished) {
                clip.ondestroy && clip.ondestroy();
                this.removeClip(clip);
                clip = nextClip;
            }
            else {
                clip = nextClip;
            }
        }

        this._time = time;

        this.onframe(delta);

        // 'frame' should be triggered before stage, because upper application
        // depends on the sequence (e.g., echarts-stream and finish
        // event judge)
        this.trigger('frame', delta);

        if (this.stage.update && !notTriggerStageUpdate) {
            this.stage.update();
        }
    }

    _startLoop() {
        const self = this;

        this._running = true;

        function step() {
            if (self._running) {

                requestAnimationFrame(step);

                !self._paused && self.update();
            }
        }

        requestAnimationFrame(step);
    }

    /**
     * Start animation.
     */
    start() {
        if (this._running) {
            return;
        }
<commit_msg>fix: make manually update animation not trigger "frame" callback, otherwise it make cause dead loop if manual update calling is from one of the "frame" callback.<commit_after>/**
 * Animation main class, dispatch and manage all animation controllers
 *
 */
// TODO Additive animation
// http://iosoteric.com/additive-animations-animatewithduration-in-ios-8/
// https://developer.apple.com/videos/wwdc2014/#236

import * as util from '../core/util';
import Eventful from '../core/Eventful';
import requestAnimationFrame from './requestAnimationFrame';
import Animator from './Animator';
import Clip from './Clip';


interface Stage {
    update?: () => void
}
type OnframeCallback = (deltaTime: number) => void

interface AnimationOption {
    stage?: Stage
    onframe?: OnframeCallback
}
/**
 * @example
 *     const animation = new Animation();
 *     const obj = {
 *         x: 100,
 *         y: 100
 *     };
 *     animation.animate(node.position)
 *         .when(1000, {
 *             x: 500,
 *             y: 500
 *         })
 *         .when(2000, {
 *             x: 100,
 *             y: 100
 *         })
 *         .start('spline');
 */

export default class Animation extends Eventful {

    stage: Stage

    onframe: OnframeCallback

    // Use linked list to store clip
    private _clipsHead: Clip
    private _clipsTail: Clip

    private _running: boolean = false

    private _time: number = 0
    private _pausedTime: number = 0
    private _pauseStart: number = 0

    private _paused = false;

    constructor(opts?: AnimationOption) {
        super();

        opts = opts || {};

        this.stage = opts.stage || {};

        this.onframe = opts.onframe || function () {};
    }

    /**
     * Add clip
     */
    addClip(clip: Clip) {
        if (clip.animation) {
            // Clip has been added
            this.removeClip(clip);
        }

        if (!this._clipsHead) {
            this._clipsHead = this._clipsTail = clip;
        }
        else {
            this._clipsTail.next = clip;
            clip.prev = this._clipsTail;
            clip.next = null;
            this._clipsTail = clip;
        }
        clip.animation = this;
    }
    /**
     * Add animator
     */
    addAnimator(animator: Animator<any>) {
        animator.animation = this;
        const clip = animator.getClip();
        if (clip) {
            this.addClip(clip);
        }
    }
    /**
     * Delete animation clip
     */
    removeClip(clip: Clip) {
        if (!clip.animation) {
            return;
        }
        const prev = clip.prev;
        const next = clip.next;
        if (prev) {
            prev.next = next;
        }
        else {
            // Is head
            this._clipsHead = next;
        }
        if (next) {
            next.prev = prev;
        }
        else {
            // Is tail
            this._clipsTail = prev;
        }
        clip.next = clip.prev = clip.animation = null;
    }

    /**
     * Delete animation clip
     */
    removeAnimator(animator: Animator<any>) {
        const clip = animator.getClip();
        if (clip) {
            this.removeClip(clip);
        }
        animator.animation = null;
    }

    update(notTriggerFrameAndStageUpdate?: boolean) {
        const time = new Date().getTime() - this._pausedTime;
        const delta = time - this._time;
        let clip = this._clipsHead;

        while (clip) {
            // Save the nextClip before step.
            // So the loop will not been affected if the clip is removed in the callback
            const nextClip = clip.next;
            let finished = clip.step(time, delta);
            if (finished) {
                clip.ondestroy && clip.ondestroy();
                this.removeClip(clip);
                clip = nextClip;
            }
            else {
                clip = nextClip;
            }
        }

        this._time = time;

        if (!notTriggerFrameAndStageUpdate) {
            this.onframe(delta);

            // 'frame' should be triggered before stage, because upper application
            // depends on the sequence (e.g., echarts-stream and finish
            // event judge)
            this.trigger('frame', delta);

            this.stage.update && this.stage.update();
        }
    }

    _startLoop() {
        const self = this;

        this._running = true;

        function step() {
            if (self._running) {

                requestAnimationFrame(step);

                !self._paused && self.update();
            }
        }

        requestAnimationFrame(step);
    }

    /**
     * Start animation.
     */
    start() {
        if (this._running) {
            return;
        }

{'fraction_non_alphanumeric': 0.11326685454151311, 'fraction_numerical': 0.00802081075222198, 'mean_word_length': 1.5107482993197279, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 11, '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': '3365721', 'n_tokens_mistral': 2697, 'n_tokens_neox': 2542, 'n_words': 653}