<commit_before>import React, { PropTypes } from 'react';
import { findDOMNode } from 'react-dom';

import { shortUID, clamp, trimFloat, isMsBrowser, contains } from './utils.js';
import TWEEN from 'tween.js';

const EASING = {
  'ease':        TWEEN.Easing.Quadratic.InOut,
  'ease-in':     TWEEN.Easing.Cubic.In,
  'ease-out':    TWEEN.Easing.Cubic.Out,
  'ease-in-out': TWEEN.Easing.Cubic.InOut,
  'linear':      TWEEN.Easing.Linear.None,
  'step-start':  TWEEN.Easing.Bounce.In,   // not quite the same thing ;)
  'step-end':    TWEEN.Easing.Bounce.Out   // not quite the same thing ;)
};

export default class MtSvgLines extends React.Component {

  static propTypes = {
    className: PropTypes.string,            // custom CSS class (applied to svg elem)
    animate:   PropTypes.oneOfType([        // external animation trigger
      PropTypes.string,                     // - pass a unique string or true to (re)start animation
      PropTypes.number,                     // - pass a number to specify delay before the animation begins (ms)
      PropTypes.bool                        // - pass false (or omit) to draw static SVG (no animation)
    ]),
    duration:  PropTypes.number,            // total anim duration (ms)
    stagger:   PropTypes.number,            // delay between start times of each path (percentage)
    timing:    React.PropTypes.oneOf([      // easing type
      'ease',
      'ease-in',
      'ease-out',
      'ease-in-out',
      'linear',
      'step-start',
      'step-end'
    ]),
    playback:  PropTypes.string,            // iteration-count || direction || fill-mode (perhaps even play-state)
    fade:      PropTypes.bool,              // apply a fade-in to each path
    callback:  PropTypes.func,              // callback fn to run when when anim completes
    jsOnly:    PropTypes.bool               // apply JS animation, regardless of browser
  };

  // defaults
  static defaultProps = {
    className: 'mt-svg',
    animate:   false,
    duration:  1000,
    stagger:   0,
    timing:    'ease',
    playback:  'forwards',
    fade:      false,
    callback:  () => {},
    jsOnly:    false
  };

  constructor( props ) {
    super( props );

    this.state = {
      classKey:      '',      // unique class name for the wrapper, acts as internal "trigger" (re-generated each time anim is to run)
      css:           '',      // generated CSS
      tweenElapsed:  0,       // tween duration so far (ms)
      tweenProgress: 0        // tween completion (pct)
    };

    this._lastAnimate  = '';
    this._lastClassKey = '';

    this._animStart    = 0;    // anim start timestamp

    this._pathElems    = [];
    this._pathDataFrom = {};
    this._pathDataTo   = {};
    this._tweenData    = {};
  }

  componentWillMount() {
    this._setClassKey( this.props.animate );
  }

  componentWillReceiveProps( nextProps ) {
    this._setClassKey( nextProps.animate );
  }

  render() {
    // destruct all component-specific props, so '...rest' can be applied to wrapper
    // eslint-disable-next-line no-unused-vars
    const { className, animate, duration, stagger, timing, playback, fade, jsOnly, children, callback, ...rest } = this.props;
    const { classKey, css } = this.state;
    const isHidden = animate === 'hide';

    return (
       this._svgWrapper = c }
        className={ `${ className } ${ classKey }` }
        style={ { opacity: isHidden ? 0.01 : 1 } }
        { ...rest }
      >

        { children }

    );
  }

  componentDidMount() {
    this._animate();
  }

  componentDidUpdate() {
    this._animate();
  }

  // ------------------------------------------------------

  /*
   * Main animate handler, called after each render update
   */
  _animate() {
    const { animate, duration, stagger, timing, playback, jsOnly }  = this.props;
    const { classKey } = this.state;

    const isStartNewAnim = animate !== false && classKey !== this._lastClassKey;
    const isAnimJS       = isMsBrowser() || jsOnly;

    // >>> STARTING NEW ANIMATION...
    if ( isStartNewAnim ) {

      // set flags
      this._animStart    = Date.now();
      this._lastClassKey = classKey;

      // parse out vars common for both modes
      const startDelay = typeof animate === 'number' ? animate : 0;   // if numeric, treat as delay (ms)
      let numOfRepeats = parseInt( playback, 10 ) || 0;

      /* ----- JS MODE ----- */
      if ( isAnimJS ) {

        // parse props for use with Tween.js
        if ( numOfRepeats > 0 ) { numOfRepeats = numOfRepeats - 1; }
        if ( contains( playback, 'infinite' ) ) { numOfRepeats = Infinity; }
        const isYoYo = contains( playback, 'alternate' );

        // acquire path elems and generate to/from data sets
        this._pathElems    = this._selectPathElems();
        const pathData     = this._getPathData( this._pathElems );
        this._pathDataFrom = pathData.from;
        this._pathDataTo   = pathData.to;

        // TODO: if ( contains( playback, 'reverse' ) ) { ... };

        // init tweener object
        this._tweenData = { ...this._pathDataFrom };

        // set paths' offsets to start positions
        this._setStrokeDasharray( this._pathElems, this._pathDataFrom );
        this._setStrokeDashoffset( this._pathElems, this._tweenData );

        // init the tweener..
        const tween = new TWEEN.Tween( this._tweenData )
          .to( this._pathDataTo, duration )
          .easing( EASING[ timing ] )
          .repeat( numOfRepeats )
          .yoyo( isYoYo )
          .onUpdate( this._onTweenUpdate )
          .onComplete( this._onAnimComplete );

        // kick off JS tweening..
        const t = setTimeout( () => {
          tween.start();
          TWEEN.update();
          clearTimeout( t );
        }, Math.max( 1, startDelay ) );

      /* ----- CSS MODE ----- */
      } else {

        let css ='';

        // 1) determine number of path elems in svg
        const pathLenghts = this._getPathLengths();
        const pathQty     = pathLenghts.length || 1;

        // 2) calc all timing values
        const staggerMult      = clamp( stagger, 0, 100 ) / 100;                        // convert percentage to 0-1
        const pathStaggerDelay = ( stagger > 0 ? duration/pathQty * staggerMult : 0 );
        const pathDrawDuration = ( stagger > 0 ? duration - (( pathStaggerDelay * ( pathQty - 1 ) ) * ( 2 - staggerMult )) : duration );

        // 3) concat generated CSS, one path at a time..
        pathLenghts.forEach( ( length, index ) => {
          css += this._getPathCSS( index, length, startDelay, pathStaggerDelay, pathDrawDuration );
        });

        // set up on-complete timer
        const t = setTimeout( () => {
          clearTimeout( t );
          this._onAnimComplete();
        }, startDelay + duration * ( numOfRepeats ) );

        // set state (re-render)
        this.setState( { css } );
      }

    // >>> ONGOING ANIMATION...
    } else if ( this._animStart ) {

      /* ----- JS MODE ----- */
      if ( isAnimJS ) {
        requestAnimationFrame( TWEEN.update );

      /* ----- CSS MODE ----- */
      } else {
        // NOTE: nothing to do, browser does its thing...
      }
    }
  }

  /*
   * On each Tween update, set dash offsets to newly tweened values
   */
  _onTweenUpdate = () => {
    this._setStrokeDashoffset( this._pathElems, this._tweenData );
    this._animate();  // go again..
  }

  /*
   * When animation completes, run callback (if any), clear start timestamp
   */
  _onAnimComplete = () => {
    this.props.callback();
    this._animStart = 0;
  }

  /*
   * Acquire selection of SVG 'path' elems contained within
   */
  _selectPathElems() {
    const svgEl = findDOMNode( this._svgWrapper ).getElementsByTagName( 'svg' )[0];
    return svgEl ? svgEl.querySelectorAll( 'path' ) : [];
  }

  /*
   * Generate an object containing 'from' and 'to' sub-objects
   * Each object contains same set of indexed keys, per the path selection
   * The 'from' object values are the paths' lengths
   * The 'to' object values are 0 (unless 'skip' attr is present, then equal path's length)
   */
  _getPathData( pathElems ) {
    const pathData = { from: {}, to: {} };

    [].forEach.call( pathElems, ( pathEl, i ) => {
      if ( !this._hasSkipAttr( pathEl.attributes ) ) {
        const pathLengh  = trimFloat( pathEl.getTotalLength() );
        pathData.to[ i ]   = 0;
        pathData.from[ i ] = pathLengh;
      }
    });

    return pathData;
  }

  /*
   * Check path's attributes for data-mt="skip"
   */
  _hasSkipAttr( attributes ) {
    let result = false;

    // path.attributes is an obj with indexed keys, so we must iterate over them
    // { '0': { name: 'd', value: 'M37.063' }, '1': { name: 'data-mt', value: 'skip' }, ... }
    for( let key in attributes ) {
      const { name, value } = attributes[ key ];
      if ( !result && name === 'data-mt' && value === 'skip' ) {
        result = true;
        break;
      }
    }

    return result;
  }

  /*
   * Set style.strokeDasharray on all paths in selection, from the provided key-val object
   */
  _setStrokeDasharray( pathElems, pathData ) {
    [].forEach.call( pathElems, ( pathEl, i ) => {
      if ( pathData[ i ] ) { pathEl.style.strokeDasharray = pathData[ i ]; }
    });
  }

  /*
   * Set style.strokeDashoffset on all paths in selection, from the provided key-val object
   */
  _setStrokeDashoffset( pathElems, pathData ) {
    [].forEach.call( pathElems, ( pathEl, i ) => {
      if ( pathData[ i ] ) { pathEl.style.strokeDashoffset = pathData[ i ]; }
    });
  }

  /*
   * Return an array containing lengths of all path elems inside the SVG
   */
  _getPathLengths() {
    const pathElems = this._selectPathElems();
    return [].map.call( pathElems, ( pathEl ) => {
      return this._hasSkipAttr( pathEl.attributes ) ? 0 : trimFloat( pathEl.getTotalLength() );
    });
  }

  /*
   * Return CSS for a single path elem (using classKey and path index as the CSS selector)
   */
  _getPathCSS( index, length, startDelay, staggerDelay, duration ) {
    const { classKey } = this.state;
    const { timing, playback, fade } = this.props;

    const keysId       = `${ classKey }-${ index + 1 }`;
    const totalDelay   = length ? trimFloat( ( startDelay + staggerDelay * index ) / 1000 ) : 0;
    const startOpacity = fade ? 0.01 : 1;

    duration = duration ? trimFloat( duration / 1000 ) : 0;

    return `
      @keyframes ${ keysId } {
        0%   { stroke-dashoffset: ${ length }; opacity: ${ startOpacity }; }
        100% { stroke-dashoffset: 0; opacity: 1; }
      }
      .${ classKey } path:nth-of-type( ${ index + 1 } ) {
        opacity:                 0.01;
        stroke-dasharray:        ${ length };
        stroke-dashoffset:       ${ length };
        -webkit-animation:       ${ keysId } ${ duration }s ${ timing } ${ playback };
        animation:               ${ keysId } ${ duration }s ${ timing } ${ playback };
        -webkit-animation-delay: ${ totalDelay }s;
        animation-delay:         ${ totalDelay }s;
      }
    `;
  }

  /*
   * Check if animate flag is new, and if true, set a new classKey into state (trigger anim)
   */
  _setClassKey( animate ) {
    if ( animate !== this._lastAnimate ) {
      this._lastAnimate = animate;
      this.setState( { classKey: `mt-${ shortUID() }` } );
    }
  }
}
<commit_msg>prefix keyframes with -webkit- for other browsers<commit_after>import React, { PropTypes } from 'react';
import { findDOMNode } from 'react-dom';

import { shortUID, clamp, trimFloat, isMsBrowser, contains } from './utils.js';
import TWEEN from 'tween.js';

const EASING = {
  'ease':        TWEEN.Easing.Quadratic.InOut,
  'ease-in':     TWEEN.Easing.Cubic.In,
  'ease-out':    TWEEN.Easing.Cubic.Out,
  'ease-in-out': TWEEN.Easing.Cubic.InOut,
  'linear':      TWEEN.Easing.Linear.None,
  'step-start':  TWEEN.Easing.Bounce.In,   // not quite the same thing ;)
  'step-end':    TWEEN.Easing.Bounce.Out   // not quite the same thing ;)
};

export default class MtSvgLines extends React.Component {

  static propTypes = {
    className: PropTypes.string,            // custom CSS class (applied to svg elem)
    animate:   PropTypes.oneOfType([        // external animation trigger
      PropTypes.string,                     // - pass a unique string or true to (re)start animation
      PropTypes.number,                     // - pass a number to specify delay before the animation begins (ms)
      PropTypes.bool                        // - pass false (or omit) to draw static SVG (no animation)
    ]),
    duration:  PropTypes.number,            // total anim duration (ms)
    stagger:   PropTypes.number,            // delay between start times of each path (percentage)
    timing:    React.PropTypes.oneOf([      // easing type
      'ease',
      'ease-in',
      'ease-out',
      'ease-in-out',
      'linear',
      'step-start',
      'step-end'
    ]),
    playback:  PropTypes.string,            // iteration-count || direction || fill-mode (perhaps even play-state)
    fade:      PropTypes.bool,              // apply a fade-in to each path
    callback:  PropTypes.func,              // callback fn to run when when anim completes
    jsOnly:    PropTypes.bool               // apply JS animation, regardless of browser
  };

  // defaults
  static defaultProps = {
    className: 'mt-svg',
    animate:   false,
    duration:  1000,
    stagger:   0,
    timing:    'ease',
    playback:  'forwards',
    fade:      false,
    callback:  () => {},
    jsOnly:    false
  };

  constructor( props ) {
    super( props );

    this.state = {
      classKey:      '',      // unique class name for the wrapper, acts as internal "trigger" (re-generated each time anim is to run)
      css:           '',      // generated CSS
      tweenElapsed:  0,       // tween duration so far (ms)
      tweenProgress: 0        // tween completion (pct)
    };

    this._lastAnimate  = '';
    this._lastClassKey = '';

    this._animStart    = 0;    // anim start timestamp

    this._pathElems    = [];
    this._pathDataFrom = {};
    this._pathDataTo   = {};
    this._tweenData    = {};
  }

  componentWillMount() {
    this._setClassKey( this.props.animate );
  }

  componentWillReceiveProps( nextProps ) {
    this._setClassKey( nextProps.animate );
  }

  render() {
    // destruct all component-specific props, so '...rest' can be applied to wrapper
    // eslint-disable-next-line no-unused-vars
    const { className, animate, duration, stagger, timing, playback, fade, jsOnly, children, callback, ...rest } = this.props;
    const { classKey, css } = this.state;
    const isHidden = animate === 'hide';

    return (
       this._svgWrapper = c }
        className={ `${ className } ${ classKey }` }
        style={ { opacity: isHidden ? 0.01 : 1 } }
        { ...rest }
      >

        { children }

    );
  }

  componentDidMount() {
    this._animate();
  }

  componentDidUpdate() {
    this._animate();
  }

  // ------------------------------------------------------

  /*
   * Main animate handler, called after each render update
   */
  _animate() {
    const { animate, duration, stagger, timing, playback, jsOnly }  = this.props;
    const { classKey } = this.state;

    const isStartNewAnim = animate !== false && classKey !== this._lastClassKey;
    const isAnimJS       = isMsBrowser() || jsOnly;

    // >>> STARTING NEW ANIMATION...
    if ( isStartNewAnim ) {

      // set flags
      this._animStart    = Date.now();
      this._lastClassKey = classKey;

      // parse out vars common for both modes
      const startDelay = typeof animate === 'number' ? animate : 0;   // if numeric, treat as delay (ms)
      let numOfRepeats = parseInt( playback, 10 ) || 0;

      /* ----- JS MODE ----- */
      if ( isAnimJS ) {

        // parse props for use with Tween.js
        if ( numOfRepeats > 0 ) { numOfRepeats = numOfRepeats - 1; }
        if ( contains( playback, 'infinite' ) ) { numOfRepeats = Infinity; }
        const isYoYo = contains( playback, 'alternate' );

        // acquire path elems and generate to/from data sets
        this._pathElems    = this._selectPathElems();
        const pathData     = this._getPathData( this._pathElems );
        this._pathDataFrom = pathData.from;
        this._pathDataTo   = pathData.to;

        // TODO: if ( contains( playback, 'reverse' ) ) { ... };

        // init tweener object
        this._tweenData = { ...this._pathDataFrom };

        // set paths' offsets to start positions
        this._setStrokeDasharray( this._pathElems, this._pathDataFrom );
        this._setStrokeDashoffset( this._pathElems, this._tweenData );

        // init the tweener..
        const tween = new TWEEN.Tween( this._tweenData )
          .to( this._pathDataTo, duration )
          .easing( EASING[ timing ] )
          .repeat( numOfRepeats )
          .yoyo( isYoYo )
          .onUpdate( this._onTweenUpdate )
          .onComplete( this._onAnimComplete );

        // kick off JS tweening..
        const t = setTimeout( () => {
          tween.start();
          TWEEN.update();
          clearTimeout( t );
        }, Math.max( 1, startDelay ) );

      /* ----- CSS MODE ----- */
      } else {

        let css ='';

        // 1) determine number of path elems in svg
        const pathLenghts = this._getPathLengths();
        const pathQty     = pathLenghts.length || 1;

        // 2) calc all timing values
        const staggerMult      = clamp( stagger, 0, 100 ) / 100;                        // convert percentage to 0-1
        const pathStaggerDelay = ( stagger > 0 ? duration/pathQty * staggerMult : 0 );
        const pathDrawDuration = ( stagger > 0 ? duration - (( pathStaggerDelay * ( pathQty - 1 ) ) * ( 2 - staggerMult )) : duration );

        // 3) concat generated CSS, one path at a time..
        pathLenghts.forEach( ( length, index ) => {
          css += this._getPathCSS( index, length, startDelay, pathStaggerDelay, pathDrawDuration );
        });

        // set up on-complete timer
        const t = setTimeout( () => {
          clearTimeout( t );
          this._onAnimComplete();
        }, startDelay + duration * ( numOfRepeats ) );

        // set state (re-render)
        this.setState( { css } );
      }

    // >>> ONGOING ANIMATION...
    } else if ( this._animStart ) {

      /* ----- JS MODE ----- */
      if ( isAnimJS ) {
        requestAnimationFrame( TWEEN.update );

      /* ----- CSS MODE ----- */
      } else {
        // NOTE: nothing to do, browser does its thing...
      }
    }
  }

  /*
   * On each Tween update, set dash offsets to newly tweened values
   */
  _onTweenUpdate = () => {
    this._setStrokeDashoffset( this._pathElems, this._tweenData );
    this._animate();  // go again..
  }

  /*
   * When animation completes, run callback (if any), clear start timestamp
   */
  _onAnimComplete = () => {
    this.props.callback();
    this._animStart = 0;
  }

  /*
   * Acquire selection of SVG 'path' elems contained within
   */
  _selectPathElems() {
    const svgEl = findDOMNode( this._svgWrapper ).getElementsByTagName( 'svg' )[0];
    return svgEl ? svgEl.querySelectorAll( 'path' ) : [];
  }

  /*
   * Generate an object containing 'from' and 'to' sub-objects
   * Each object contains same set of indexed keys, per the path selection
   * The 'from' object values are the paths' lengths
   * The 'to' object values are 0 (unless 'skip' attr is present, then equal path's length)
   */
  _getPathData( pathElems ) {
    const pathData = { from: {}, to: {} };

    [].forEach.call( pathElems, ( pathEl, i ) => {
      if ( !this._hasSkipAttr( pathEl.attributes ) ) {
        const pathLengh  = trimFloat( pathEl.getTotalLength() );
        pathData.to[ i ]   = 0;
        pathData.from[ i ] = pathLengh;
      }
    });

    return pathData;
  }

  /*
   * Check path's attributes for data-mt="skip"
   */
  _hasSkipAttr( attributes ) {
    let result = false;

    // path.attributes is an obj with indexed keys, so we must iterate over them
    // { '0': { name: 'd', value: 'M37.063' }, '1': { name: 'data-mt', value: 'skip' }, ... }
    for( let key in attributes ) {
      const { name, value } = attributes[ key ];
      if ( !result && name === 'data-mt' && value === 'skip' ) {
        result = true;
        break;
      }
    }

    return result;
  }

  /*
   * Set style.strokeDasharray on all paths in selection, from the provided key-val object
   */
  _setStrokeDasharray( pathElems, pathData ) {
    [].forEach.call( pathElems, ( pathEl, i ) => {
      if ( pathData[ i ] ) { pathEl.style.strokeDasharray = pathData[ i ]; }
    });
  }

  /*
   * Set style.strokeDashoffset on all paths in selection, from the provided key-val object
   */
  _setStrokeDashoffset( pathElems, pathData ) {
    [].forEach.call( pathElems, ( pathEl, i ) => {
      if ( pathData[ i ] ) { pathEl.style.strokeDashoffset = pathData[ i ]; }
    });
  }

  /*
   * Return an array containing lengths of all path elems inside the SVG
   */
  _getPathLengths() {
    const pathElems = this._selectPathElems();
    return [].map.call( pathElems, ( pathEl ) => {
      return this._hasSkipAttr( pathEl.attributes ) ? 0 : trimFloat( pathEl.getTotalLength() );
    });
  }

  /*
   * Return CSS for a single path elem (using classKey and path index as the CSS selector)
   */
  _getPathCSS( index, length, startDelay, staggerDelay, duration ) {
    const { classKey } = this.state;
    const { timing, playback, fade } = this.props;

    const keysId       = `${ classKey }-${ index + 1 }`;
    const totalDelay   = length ? trimFloat( ( startDelay + staggerDelay * index ) / 1000 ) : 0;
    const startOpacity = fade ? 0.01 : 1;

    duration = duration ? trimFloat( duration / 1000 ) : 0;

    return `
      @-webkit-keyframes ${ keysId } {
        0%   { stroke-dashoffset: ${ length }; opacity: ${ startOpacity }; }
        100% { stroke-dashoffset: 0; opacity: 1; }
      }
      @keyframes ${ keysId } {
        0%   { stroke-dashoffset: ${ length }; opacity: ${ startOpacity }; }
        100% { stroke-dashoffset: 0; opacity: 1; }
      }
      .${ classKey } path:nth-of-type( ${ index + 1 } ) {
        opacity:                 0.01;
        stroke-dasharray:        ${ length };
        stroke-dashoffset:       ${ length };
        -webkit-animation:       ${ keysId } ${ duration }s ${ timing } ${ playback };
        animation:               ${ keysId } ${ duration }s ${ timing } ${ playback };
        -webkit-animation-delay: ${ totalDelay }s;
        animation-delay:         ${ totalDelay }s;
      }
    `;
  }

  /*
   * Check if animate flag is new, and if true, set a new classKey into state (trigger anim)
   */
  _setClassKey( animate ) {
    if ( animate !== this._lastAnimate ) {
      this._lastAnimate = animate;
      this.setState( { classKey: `mt-${ shortUID() }` } );
    }
  }
}
<|endoftext|>


<commit_before>/**
 * A fixtures loader helper
 *
 * @author Daniil Filippov aka agile <filippovdaniil@gmail.com>
 */

var path = require('path');

function FixturesLoader () {}

FixturesLoader.prototype.fromLocalStorage = function (name) {
  var js = path.join(__dirname, 'fixtures/localstorage/' + name + '.js');
  var entity = require(js);
  return entity;
};

module.exports = FixturesLoader;
<commit_msg>(m) instance → static fixtures.fromLocalStorage()<commit_after>/**
 * A fixtures loader helper
 *
 * @author Daniil Filippov aka agile <filippovdaniil@gmail.com>
 */

var path = require('path');

function FixturesLoader () {}

/**
 * A static method for loading "localStorage" fixtures.
 *
 * @param {String} name A name of the entity
 * @return {String}
 */
FixturesLoader.fromLocalStorage = function (name) {
  var js = path.join(__dirname, 'fixtures/localstorage/' + name + '.js');
  var entity = require(js);
  return entity;
};

module.exports = FixturesLoader;
<|endoftext|>


<commit_before>var bowling = {};
bowling.players = [];

bowling.Player = function (optName) {
    this.name = optName || 'Player';
    this.frames = Array.range(10).map(function() { return [0, 0]; });
    this.bonusRolls = [0, 0];
    this.currentRoll = [0, 0]; // frame[0-10], roll[0-1]

    // In this implementation, every player has her own row. In the interest
    // of distributing concerns, an implementation with more future should
    // associate business logic, i.e. players with visualizations, i.e. HTML
    // nodes in another way.
    this.renderContext = bowling.createRow();
    this.color = Color.random();
    return this;
};

Object.extend(bowling.Player.prototype, {
    score: function() {
        return this.frames.reduceRight(function(acc, frame) {
            var sum = acc.sum,
                nextRolls = acc.nextRolls,
                frameResult = frame[0] + frame[1];
            if (frame[0] == 10) { // strike
                return {
                    sum: sum + 10 + nextRolls.reduce(Functions.plus),
                    nextRolls: [10, nextRolls[0]]};
            } else if (frameResult == 10) { //spare
                return {
                    sum: sum + 10 + nextRolls[0],
                    nextRolls: frame};
            } else {
                return {
                    sum: sum + frameResult,
                    nextRolls: frame};
            }
        }, { sum: 0, nextRolls: this.bonusRolls}).sum
    },
    render: function() {
        this.renderColor(this.renderContext.children[0]);
        var content = [this.name]
            .concat(this.frames)
            .concat([this.bonusRolls, this.score()]);
        content.forEach(function(ea, idx) {
            this.renderContext.children[idx + 1].innerHTML = content[idx];
        }, this);
    },
    renderColor: function(htmlNode) {
        while (htmlNode.lastChild) {
            htmlNode.removeChild(htmlNode.lastChild);
        }
        var color = document.createElement('colorLegend');
        color.style.backgroundColor = Color.intToStr(this.color);
        htmlNode.appendChild(color);
    }
});

bowling.addPlayer = function(optName) {
    var newPlayer = new bowling.Player(optName);
    scores.appendChild(newPlayer.renderContext);
    bowling.players.push(newPlayer);
    updateVisualization()
    return newPlayer;
};

/*
   Visualization updates are implemented explicitly. There are more
   elegant solutions, e.g. obeserver pattern, ticking, or data bindings. But
   since the sources of change are well understood in this small example,
   explicit update calls should suffice
*/

updateVisualization = function() {
    // If players could be removed or their sequence changed, we'd have to
    // ensure that their HTML nodes are removed/reordered.
    bowling.players.invoke('render');

};
// setInterval(updateVisualization, 100);

bowling.createRow = function(optContent) {
    var tr = document.createElement('tr');
    Array.range(14).forEach(function(ea) {
        var td = document.createElement('td');
        td.appendChild(document.createTextNode(optContent && optContent[ea] || ''));
        tr.appendChild(td);
    })
    return tr;
};
<commit_msg>method for random rolls<commit_after>var bowling = {};
bowling.players = [];

bowling.Player = function (optName) {
    this.name = optName || 'Player';
    this.frames = Array.range(10).map(function() { return [0, 0]; });
    this.bonusRolls = [0, 0];
    this.currentRoll = [0, 0]; // frame[0-10], roll[0-1]

    // In this implementation, every player has her own row. In the interest
    // of distributing concerns, an implementation with more future should
    // associate business logic, i.e. players & rolls, with visualizations, i.e. HTML
    // nodes, in another way.
    this.renderContext = bowling.createRow();
    this.color = Color.random();
    return this;
};

Object.extend(bowling.Player.prototype, {
    score: function() {
        return this.frames.reduceRight(function(acc, frame) {
            var sum = acc.sum,
                nextRolls = acc.nextRolls,
                frameResult = frame[0] + frame[1];
            if (frame[0] == 10) { // strike
                return {
                    sum: sum + 10 + nextRolls.reduce(Functions.plus),
                    nextRolls: [10, nextRolls[0]]};
            } else if (frameResult == 10) { //spare
                return {
                    sum: sum + 10 + nextRolls[0],
                    nextRolls: frame};
            } else {
                return {
                    sum: sum + frameResult,
                    nextRolls: frame};
            }
        }, { sum: 0, nextRolls: this.bonusRolls}).sum
    },

    randomRoll: function() {
        var frameNr = this.currentRoll[0], roll = this.currentRoll[1];
        if (frameNr < 0) {
            throw new Error(this.name + ' can not roll again.') };
        var availablePins = roll == 0 ? 10 : (10 - this.frames[frameNr][0]);
        this.frames[frameNr][roll] = Math.floor(Math.random() * (availablePins + 1));

        // now, advance the currentRoll pointer
        if (roll == 0 && this.frames[frameNr].reduce(Functions.plus) != 10) {
            // advance rolls
            this.currentRoll[1] = 1;
            // if we are in bonus rolls and the last frame was not a strike
            if (frameNr == 10 && this.frames[9][1] != 0) {
                this.currentRoll = [-1, 0];
            }
        } else {
            // advance frames
            this.currentRoll = [frameNr + 1, 0];
            // if we are in bonus rolls and the last frame is neither strike nor spare
            if (frameNr == 9 && this.frames[9].reduce(Functions.plus) < 10) {
                this.currentRoll = [-1, 0];
            }
            if (frameNr == 10) {
                this.currentRoll = [-1, 0];
            }
        }
        updateVisualization();
    },

    render: function() {
        this.renderColor(this.renderContext.children[0]);
        var content = [this.name]
            .concat(this.frames)
            .concat([this.bonusRolls, this.score()]);
        content.forEach(function(ea, idx) {
            this.renderContext.children[idx + 1].innerHTML = content[idx];
        }, this);
    },
    renderColor: function(htmlNode) {
        while (htmlNode.lastChild) {
            htmlNode.removeChild(htmlNode.lastChild);
        }
        var color = document.createElement('colorLegend');
        color.style.backgroundColor = Color.intToStr(this.color);
        htmlNode.appendChild(color);
    }
});

bowling.addPlayer = function(optName) {
    var newPlayer = new bowling.Player(optName);
    scores.appendChild(newPlayer.renderContext);
    bowling.players.push(newPlayer);
    updateVisualization()
    return newPlayer;
};

/*
   Visualization updates are implemented explicitly. There are more
   elegant solutions, e.g. obeserver pattern, ticking, or data bindings. But
   since the sources of change are well understood in this small example,
   explicit update calls should suffice
*/

updateVisualization = function() {
    // If players could be removed or their sequence changed, we'd have to
    // ensure that their HTML nodes are removed/reordered.
    bowling.players.invoke('render');

};
// setInterval(updateVisualization, 100);

bowling.createRow = function(optContent) {
    var tr = document.createElement('tr');
    Array.range(14).forEach(function(ea) {
        var td = document.createElement('td');
        td.appendChild(document.createTextNode(optContent && optContent[ea] || ''));
        tr.appendChild(td);
    })
    return tr;
};
<|endoftext|>


<commit_before>'use strict';

const { toQueryString } = require('../../util/common');
const _trim = require('lodash/trim');
/**
 * 이재원: 로드를 새로 할때는 queue를 다시 만든다고 하네요 기존 코드는 제거 처리 하였습니다.
 * https://github.com/CreateJS/PreloadJS/issues/232#issuecomment-338739115
 *  */
Entry.AI_UTILIZE_BLOCK.tts = {
    name: 'tts',
    imageName: 'tts.png',
    sponserText: 'Powered by NAVER Clova',
    title: {
        ko: '읽어주기',
        en: 'read',
        jp: 'を読み上げる',
    },
    titleKey: 'template.tts_title_text',
    description: Lang.Msgs.ai_utilize_tts_description,
    descriptionKey: 'Msgs.ai_utilize_tts_description',
    isInitialized: false,
    api: '/api/expansionBlock/tts/read',
    sponsor: 'clovaNaver',
    sponsorLink: 'https://www.ncloud.com/product/aiService/css',
    sponsorText: 'Powered by NAVER Clova',
    loadQueue: [],
    init() {},
};

Entry.AI_UTILIZE_BLOCK.tts.getBlocks = function() {
    const params = {
        getSpeaker(isPython = false) {
            const param = {
                type: 'Dropdown',
                options: [
                    [Lang.Blocks.tts_female, 'kyuri'],
                    [Lang.Blocks.tts_male, 'jinho'],
                    [Lang.Blocks.tts_kind, 'hana'],
                    [Lang.Blocks.tts_sweet, 'dinna'],
                    [Lang.Blocks.tts_echo, 'brown'],
                    [Lang.Blocks.tts_mischievous, 'minions'],
                    [Lang.Blocks.tts_dainty, 'sally'],
                ],
                value: 'kyuri',
                fontSize: 11,
                bgColor: EntryStatic.colorSet.block.darken.AI_UTILIZE,
                arrowColor: EntryStatic.colorSet.common.WHITE,
            };
            if (isPython) {
                param.converter = Entry.block.converters.returnStringValue;
            }
            return param;
        },
        getSpeed(isPython = false) {
            const param = {
                type: 'Dropdown',
                options: [
                    [Lang.Blocks.tts_veryslow, '5'],
                    [Lang.Blocks.tts_slow, '3'],
                    [Lang.Blocks.tts_normal, '0'],
                    [Lang.Blocks.tts_fast, '-3'],
                    [Lang.Blocks.tts_veryfast, '-5'],
                ],
                value: '0',
                fontSize: 11,
                bgColor: EntryStatic.colorSet.block.darken.AI_UTILIZE,
                arrowColor: EntryStatic.colorSet.common.WHITE,
            };
            if (isPython) {
                param.converter = Entry.block.converters.returnStringValue;
            }
            return param;
        },
        getPitch(isPython = false) {
            const param = {
                type: 'Dropdown',
                options: [
                    [Lang.Blocks.tts_verylow, '5'],
                    [Lang.Blocks.tts_low, '3'],
                    [Lang.Blocks.tts_normal, '0'],
                    [Lang.Blocks.tts_high, '-3'],
                    [Lang.Blocks.tts_veryhigh, '-5'],
                ],
                value: '0',
                fontSize: 11,
                bgColor: EntryStatic.colorSet.block.darken.AI_UTILIZE,
                arrowColor: EntryStatic.colorSet.common.WHITE,
            };
            if (isPython) {
                param.converter = Entry.block.converters.returnStringValue;
            }
            return param;
        },
    };

    const hashCode = (s) =>
        s.split('').reduce(function(a, b) {
            a = (a << 5) - a + b.charCodeAt(0);
            return a & a;
        }, 0);

    const checkText = function(text) {
        const result = {
            result: false,
            message: Lang.Blocks.unknown_sentence,
        };

        if (!text) {
            result.message = Lang.Blocks.no_sentence;
            return result;
        }

        if (text.length > 2500) {
            result.message = Lang.Blocks.sentence_over_2500_charactor;
            return result;
        }

        return {
            result: true,
            message: _trim(text),
            hash: hashCode(text),
        };
    };
    const read = function({ message, hash, prop, callback }) {
        const tts = Entry.AI_UTILIZE_BLOCK.tts;
        const id = `tts-${hash}-${JSON.stringify(prop)}`;

        const soundQueue = new createjs.LoadQueue(true);
        soundQueue.installPlugin(createjs.Sound);
        soundQueue.setMaxConnections(8);
        soundQueue.maintainScriptOrder = true;
        const src = `${Entry.baseUrl}${Entry.AI_UTILIZE_BLOCK.tts.api}.mp3?${toQueryString({
            text: message,
            ...prop,
        })}`;
        const type = createjs.LoadQueue.SOUND;
        soundQueue.loadFile({ id, src, type, prop, callback, duration: message.length });
        tts.loadQueue.push(id);
        soundQueue.on('complete', ({ currentTarget }) => {
            const items = currentTarget.getItems().map(({ item }) => item);
            tts.loadQueue = tts.loadQueue.filter((id) => {
                const filtered = items.find((item) => item.id === id);
                if (filtered) {
                    const instance = Entry.Utils.playSound(id, filtered.prop);
                    instance.soundType = 'tts';
                    Entry.Utils.addSoundInstances(instance);
                    if (filtered.callback) {
                        const duration =
                            instance.duration > 0 ? instance.duration : filtered.duration * 300;
                        setTimeout(filtered.callback, duration);
                    }
                    return false;
                }
                return true;
            });
        });
    };

    return {
        tts_title: {
            skeleton: 'basic_text',
            color: EntryStatic.colorSet.common.TRANSPARENT,
            params: [
                {
                    type: 'Text',
                    text: Lang.template.tts_title_text,
                    color: EntryStatic.colorSet.common.TEXT,
                    align: 'center',
                },
            ],
            def: {
                type: 'tts_title',
            },
            class: 'tts',
            isNotFor: ['tts'],
            events: {},
        },
        read_text: {
            color: EntryStatic.colorSet.block.default.AI_UTILIZE,
            outerLine: EntryStatic.colorSet.block.darken.AI_UTILIZE,
            skeleton: 'basic',
            statements: [],
            params: [
                {
                    type: 'Block',
                    accept: 'string',
                },
                {
                    type: 'Indicator',
                    img: 'block_icon/ai_utilize_icon.svg',
                    size: 11,
                },
            ],
            events: {},
            def: {
                params: [
                    {
                        type: 'text',
                        params: [Lang.Blocks.entry],
                    },
                    null,
                ],
                type: 'read_text',
            },
            pyHelpDef: {
                params: ['A&value'],
                type: 'read_text',
            },
            paramsKeyMap: {
                TEXT: 0,
            },
            class: 'tts',
            isNotFor: ['tts'],
            func(sprite, script) {
                const { result, message, hash } = checkText(script.getStringValue('TEXT', script));
                if (result) {
                    const prop = sprite.getVoiceProp();
                    read({
                        message,
                        hash,
                        prop,
                    });
                }
                return script.callReturn();
            },
            syntax: {
                js: [],
                py: [
                    {
                        syntax: 'TTS.read(%1)',
                        blockType: 'param',
                        textParams: [
                            {
                                type: 'Block',
                                accept: 'string',
                            },
                        ],
                    },
                ],
            },
        },
        read_text_wait_with_block: {
            color: EntryStatic.colorSet.block.default.AI_UTILIZE,
            outerLine: EntryStatic.colorSet.block.darken.AI_UTILIZE,
            skeleton: 'basic',
            statements: [],
            params: [
                {
                    type: 'Block',
                    accept: 'string',
                },
                {
                    type: 'Indicator',
                    img: 'block_icon/ai_utilize_icon.svg',
                    size: 11,
                },
            ],
            events: {},
            def: {
                params: [
                    {
                        type: 'text',
                        params: [Lang.Blocks.entry],
                    },
                    null,
                ],
                type: 'read_text_wait_with_block',
            },
            pyHelpDef: {
                params: ['A&value'],
                type: 'read_text_wait_with_block',
            },
            paramsKeyMap: {
                TEXT: 0,
            },
            class: 'tts',
            isNotFor: ['tts'],
            func(sprite, script) {
                const { result, message, hash } = checkText(script.getStringValue('TEXT', script));
                const prop = sprite.getVoiceProp();
                if (result) {
                    if (!script.isPlay) {
                        script.isPlay = true;
                        script.playState = 1;
                        read({
                            message,
                            hash,
                            prop,
                            callback: () => {
                                script.playState = 0;
                            },
                        });
                        return script;
                    } else if (script.playState == 1) {
                        return script;
                    } else {
                        delete script.playState;
                        delete script.isPlay;
                        return script.callReturn();
                    }
                }
            },
            syntax: {
                js: [],
                py: [
                    {
                        passTest: true,
                        syntax: 'TTS.read_and_wait(%1)',
                    },
                ],
            },
        },
        set_tts_property: {
            color: EntryStatic.colorSet.block.default.AI_UTILIZE,
            outerLine: EntryStatic.colorSet.block.darken.AI_UTILIZE,
            skeleton: 'basic',
            statements: [],
            params: [
                params.getSpeaker(),
                params.getSpeed(),
                params.getPitch(),
                {
                    type: 'Indicator',
                    img: 'block_icon/ai_utilize_icon.svg',
                    size: 11,
                },
            ],
            events: {},
            def: {
                params: [
                    params.getSpeaker().value,
                    params.getSpeed().value,
                    params.getPitch().value,
                    null,
                ],
                type: 'set_tts_property',
            },
            pyHelpDef: {
                params: ['A&value', 'B&value', 'C&value'],
                type: 'set_tts_property',
            },
            paramsKeyMap: {
                SPEAKER: 0,
                SPEED: 1,
                PITCH: 2,
            },
            class: 'tts',
            isNotFor: ['tts'],
            func(sprite, script) {
                const speaker = script.getField('SPEAKER', script);
                const speed = script.getField('SPEED', script);
                const pitch = script.getField('PITCH', script);
                const volume = 1;
                sprite.setVoiceProp({ speaker, speed, pitch, volume });
                return script.callReturn();
            },
            syntax: {
                js: [],
                py: [
                    {
                        syntax: 'TTS.set(%1, %2, %3)',
                        blockType: 'param',
                        textParams: [
                            params.getSpeaker(true),
                            params.getSpeed(true),
                            params.getPitch(true),
                        ],
                    },
                ],
            },
        },
    };
};
<commit_msg>주석 수정<commit_after>'use strict';

const { toQueryString } = require('../../util/common');
const _trim = require('lodash/trim');
/**
 * wodnjs6512: 로드를 새로 할때는 queue를 다시 만든다고 하네요 기존 코드는 제거 처리 하였습니다.
 * https://github.com/CreateJS/PreloadJS/issues/232#issuecomment-338739115
 *  */
Entry.AI_UTILIZE_BLOCK.tts = {
    name: 'tts',
    imageName: 'tts.png',
    sponserText: 'Powered by NAVER Clova',
    title: {
        ko: '읽어주기',
        en: 'read',
        jp: 'を読み上げる',
    },
    titleKey: 'template.tts_title_text',
    description: Lang.Msgs.ai_utilize_tts_description,
    descriptionKey: 'Msgs.ai_utilize_tts_description',
    isInitialized: false,
    api: '/api/expansionBlock/tts/read',
    sponsor: 'clovaNaver',
    sponsorLink: 'https://www.ncloud.com/product/aiService/css',
    sponsorText: 'Powered by NAVER Clova',
    loadQueue: [],
    init() {},
};

Entry.AI_UTILIZE_BLOCK.tts.getBlocks = function() {
    const params = {
        getSpeaker(isPython = false) {
            const param = {
                type: 'Dropdown',
                options: [
                    [Lang.Blocks.tts_female, 'kyuri'],
                    [Lang.Blocks.tts_male, 'jinho'],
                    [Lang.Blocks.tts_kind, 'hana'],
                    [Lang.Blocks.tts_sweet, 'dinna'],
                    [Lang.Blocks.tts_echo, 'brown'],
                    [Lang.Blocks.tts_mischievous, 'minions'],
                    [Lang.Blocks.tts_dainty, 'sally'],
                ],
                value: 'kyuri',
                fontSize: 11,
                bgColor: EntryStatic.colorSet.block.darken.AI_UTILIZE,
                arrowColor: EntryStatic.colorSet.common.WHITE,
            };
            if (isPython) {
                param.converter = Entry.block.converters.returnStringValue;
            }
            return param;
        },
        getSpeed(isPython = false) {
            const param = {
                type: 'Dropdown',
                options: [
                    [Lang.Blocks.tts_veryslow, '5'],
                    [Lang.Blocks.tts_slow, '3'],
                    [Lang.Blocks.tts_normal, '0'],
                    [Lang.Blocks.tts_fast, '-3'],
                    [Lang.Blocks.tts_veryfast, '-5'],
                ],
                value: '0',
                fontSize: 11,
                bgColor: EntryStatic.colorSet.block.darken.AI_UTILIZE,
                arrowColor: EntryStatic.colorSet.common.WHITE,
            };
            if (isPython) {
                param.converter = Entry.block.converters.returnStringValue;
            }
            return param;
        },
        getPitch(isPython = false) {
            const param = {
                type: 'Dropdown',
                options: [
                    [Lang.Blocks.tts_verylow, '5'],
                    [Lang.Blocks.tts_low, '3'],
                    [Lang.Blocks.tts_normal, '0'],
                    [Lang.Blocks.tts_high, '-3'],
                    [Lang.Blocks.tts_veryhigh, '-5'],
                ],
                value: '0',
                fontSize: 11,
                bgColor: EntryStatic.colorSet.block.darken.AI_UTILIZE,
                arrowColor: EntryStatic.colorSet.common.WHITE,
            };
            if (isPython) {
                param.converter = Entry.block.converters.returnStringValue;
            }
            return param;
        },
    };

    const hashCode = (s) =>
        s.split('').reduce(function(a, b) {
            a = (a << 5) - a + b.charCodeAt(0);
            return a & a;
        }, 0);

    const checkText = function(text) {
        const result = {
            result: false,
            message: Lang.Blocks.unknown_sentence,
        };

        if (!text) {
            result.message = Lang.Blocks.no_sentence;
            return result;
        }

        if (text.length > 2500) {
            result.message = Lang.Blocks.sentence_over_2500_charactor;
            return result;
        }

        return {
            result: true,
            message: _trim(text),
            hash: hashCode(text),
        };
    };
    const read = function({ message, hash, prop, callback }) {
        const tts = Entry.AI_UTILIZE_BLOCK.tts;
        const id = `tts-${hash}-${JSON.stringify(prop)}`;

        const soundQueue = new createjs.LoadQueue(true);
        soundQueue.installPlugin(createjs.Sound);
        soundQueue.setMaxConnections(8);
        soundQueue.maintainScriptOrder = true;
        const src = `${Entry.baseUrl}${Entry.AI_UTILIZE_BLOCK.tts.api}.mp3?${toQueryString({
            text: message,
            ...prop,
        })}`;
        const type = createjs.LoadQueue.SOUND;
        soundQueue.loadFile({ id, src, type, prop, callback, duration: message.length });
        tts.loadQueue.push(id);
        soundQueue.on('complete', ({ currentTarget }) => {
            const items = currentTarget.getItems().map(({ item }) => item);
            tts.loadQueue = tts.loadQueue.filter((id) => {
                const filtered = items.find((item) => item.id === id);
                if (filtered) {
                    const instance = Entry.Utils.playSound(id, filtered.prop);
                    instance.soundType = 'tts';
                    Entry.Utils.addSoundInstances(instance);
                    if (filtered.callback) {
                        const duration =
                            instance.duration > 0 ? instance.duration : filtered.duration * 300;
                        setTimeout(filtered.callback, duration);
                    }
                    return false;
                }
                return true;
            });
        });
    };

    return {
        tts_title: {
            skeleton: 'basic_text',
            color: EntryStatic.colorSet.common.TRANSPARENT,
            params: [
                {
                    type: 'Text',
                    text: Lang.template.tts_title_text,
                    color: EntryStatic.colorSet.common.TEXT,
                    align: 'center',
                },
            ],
            def: {
                type: 'tts_title',
            },
            class: 'tts',
            isNotFor: ['tts'],
            events: {},
        },
        read_text: {
            color: EntryStatic.colorSet.block.default.AI_UTILIZE,
            outerLine: EntryStatic.colorSet.block.darken.AI_UTILIZE,
            skeleton: 'basic',
            statements: [],
            params: [
                {
                    type: 'Block',
                    accept: 'string',
                },
                {
                    type: 'Indicator',
                    img: 'block_icon/ai_utilize_icon.svg',
                    size: 11,
                },
            ],
            events: {},
            def: {
                params: [
                    {
                        type: 'text',
                        params: [Lang.Blocks.entry],
                    },
                    null,
                ],
                type: 'read_text',
            },
            pyHelpDef: {
                params: ['A&value'],
                type: 'read_text',
            },
            paramsKeyMap: {
                TEXT: 0,
            },
            class: 'tts',
            isNotFor: ['tts'],
            func(sprite, script) {
                const { result, message, hash } = checkText(script.getStringValue('TEXT', script));
                if (result) {
                    const prop = sprite.getVoiceProp();
                    read({
                        message,
                        hash,
                        prop,
                    });
                }
                return script.callReturn();
            },
            syntax: {
                js: [],
                py: [
                    {
                        syntax: 'TTS.read(%1)',
                        blockType: 'param',
                        textParams: [
                            {
                                type: 'Block',
                                accept: 'string',
                            },
                        ],
                    },
                ],
            },
        },
        read_text_wait_with_block: {
            color: EntryStatic.colorSet.block.default.AI_UTILIZE,
            outerLine: EntryStatic.colorSet.block.darken.AI_UTILIZE,
            skeleton: 'basic',
            statements: [],
            params: [
                {
                    type: 'Block',
                    accept: 'string',
                },
                {
                    type: 'Indicator',
                    img: 'block_icon/ai_utilize_icon.svg',
                    size: 11,
                },
            ],
            events: {},
            def: {
                params: [
                    {
                        type: 'text',
                        params: [Lang.Blocks.entry],
                    },
                    null,
                ],
                type: 'read_text_wait_with_block',
            },
            pyHelpDef: {
                params: ['A&value'],
                type: 'read_text_wait_with_block',
            },
            paramsKeyMap: {
                TEXT: 0,
            },
            class: 'tts',
            isNotFor: ['tts'],
            func(sprite, script) {
                const { result, message, hash } = checkText(script.getStringValue('TEXT', script));
                const prop = sprite.getVoiceProp();
                if (result) {
                    if (!script.isPlay) {
                        script.isPlay = true;
                        script.playState = 1;
                        read({
                            message,
                            hash,
                            prop,
                            callback: () => {
                                script.playState = 0;
                            },
                        });
                        return script;
                    } else if (script.playState == 1) {
                        return script;
                    } else {
                        delete script.playState;
                        delete script.isPlay;
                        return script.callReturn();
                    }
                }
            },
            syntax: {
                js: [],
                py: [
                    {
                        passTest: true,
                        syntax: 'TTS.read_and_wait(%1)',
                    },
                ],
            },
        },
        set_tts_property: {
            color: EntryStatic.colorSet.block.default.AI_UTILIZE,
            outerLine: EntryStatic.colorSet.block.darken.AI_UTILIZE,
            skeleton: 'basic',
            statements: [],
            params: [
                params.getSpeaker(),
                params.getSpeed(),
                params.getPitch(),
                {
                    type: 'Indicator',
                    img: 'block_icon/ai_utilize_icon.svg',
                    size: 11,
                },
            ],
            events: {},
            def: {
                params: [
                    params.getSpeaker().value,
                    params.getSpeed().value,
                    params.getPitch().value,
                    null,
                ],
                type: 'set_tts_property',
            },
            pyHelpDef: {
                params: ['A&value', 'B&value', 'C&value'],
                type: 'set_tts_property',
            },
            paramsKeyMap: {
                SPEAKER: 0,
                SPEED: 1,
                PITCH: 2,
            },
            class: 'tts',
            isNotFor: ['tts'],
            func(sprite, script) {
                const speaker = script.getField('SPEAKER', script);
                const speed = script.getField('SPEED', script);
                const pitch = script.getField('PITCH', script);
                const volume = 1;
                sprite.setVoiceProp({ speaker, speed, pitch, volume });
                return script.callReturn();
            },
            syntax: {
                js: [],
                py: [
                    {
                        syntax: 'TTS.set(%1, %2, %3)',
                        blockType: 'param',
                        textParams: [
                            params.getSpeaker(true),
                            params.getSpeed(true),
                            params.getPitch(true),
                        ],
                    },
                ],
            },
        },
    };
};
<|endoftext|>


<commit_before>/*eslint-disable*/
import React from 'react';
import {AppRegistry} from 'react-native';
import platformSpecific from './deprecated/platformSpecificDeprecated';
import {Screen} from './Screen';

import PropRegistry from './PropRegistry';

const registeredScreens = {};
const _allNavigatorEventHandlers = {};
const currentScreen = null;

function registerScreen(screenID, generator) {
  registeredScreens[screenID] = generator;
  AppRegistry.registerComponent(screenID, generator);
}

function registerComponent(screenID, generator, store = undefined, Provider = undefined, options = {}) {
  if (store && Provider) {
    return _registerComponentRedux(screenID, generator, store, Provider, options);
  } else {
    return _registerComponentNoRedux(screenID, generator);
  }
}

function _registerComponentNoRedux(screenID, generator) {
  const generatorWrapper = function() {
    const InternalComponent = generator();
    if (!InternalComponent) {
      console.error(`Navigation: ${screenID} registration result is 'undefined'`);
    }

    return class extends Screen {
      static navigatorStyle = InternalComponent.navigatorStyle || {};
      static navigatorButtons = InternalComponent.navigatorButtons || {};

      constructor(props) {
        super(props);
        this.state = {
          internalProps: {...props, ...PropRegistry.load(props.screenInstanceID || props.passPropsKey)}
        }
      }

      componentWillReceiveProps(nextProps) {
        this.setState({
          internalProps: {...PropRegistry.load(this.props.screenInstanceID || this.props.passPropsKey), ...nextProps}
        })
      }

      render() {
        return (
          <InternalComponent testID={screenID} navigator={this.navigator} {...this.state.internalProps} />
        );
      }
    };
  };
  registerScreen(screenID, generatorWrapper);
  return generatorWrapper;
}

function _registerComponentRedux(screenID, generator, store, Provider, options) {
  const generatorWrapper = function() {
    const InternalComponent = generator();
    return class extends Screen {
      static navigatorStyle = InternalComponent.navigatorStyle || {};
      static navigatorButtons = InternalComponent.navigatorButtons || {};

      constructor(props) {
        super(props);
        this.state = {
          internalProps: {...props, ...PropRegistry.load(props.screenInstanceID || props.passPropsKey)}
        }

        this.navigator.setOnNavigatorEvent(this.onNavigatorEvent.bind(this));
      }

      onNavigatorEvent(event) {
        switch(event.id) {
          case 'willAppear':
            console.log("SAYFA", screenID),
            currentScreen = this.navigator;
            break;

          default:
            break;
        }
      }

      componentWillReceiveProps(nextProps) {
        this.setState({
          internalProps: {...PropRegistry.load(this.props.screenInstanceID || this.props.passPropsKey), ...nextProps}
        })
      }

      render() {
        return (
          <Provider store={store} {...options}>
            <InternalComponent testID={screenID} navigator={this.navigator} {...this.state.internalProps} />
          </Provider>
        );
      }
    };
  };
  registerScreen(screenID, generatorWrapper);
  return generatorWrapper;
}

function getRegisteredScreen(screenID) {
  const generator = registeredScreens[screenID];
  if (!generator) {
    console.error(`Navigation.getRegisteredScreen: ${screenID} used but not yet registered`);
    return undefined;
  }
  return generator();
}

function showModal(params = {}) {
  return platformSpecific.showModal(params);
}

function dismissModal(params = {}) {
  return platformSpecific.dismissModal(params);
}

function dismissAllModals(params = {}) {
  return platformSpecific.dismissAllModals(params);
}

function showSnackbar(params = {}) {
  return platformSpecific.showSnackbar(params);
}

function showLightBox(params = {}) {
  return platformSpecific.showLightBox(params);
}

function dismissLightBox(params = {}) {
  return platformSpecific.dismissLightBox(params);
}

function showInAppNotification(params = {}) {
  return platformSpecific.showInAppNotification(params);
}

function dismissInAppNotification(params = {}) {
  return platformSpecific.dismissInAppNotification(params);
}

async function startTabBasedApp(params) {
  try {
    return await platformSpecific.startTabBasedApp(params);
  } catch(e) {
    console.error(`Error while starting app: ${e}`);
  }
}

async function startSingleScreenApp(params) {
  try {
    return await platformSpecific.startSingleScreenApp(params);
  } catch(e) {
    console.error(`Error while starting app: ${e}`);
  }
}

function setEventHandler(navigatorEventID, eventHandler) {
  _allNavigatorEventHandlers[navigatorEventID] = eventHandler;
}

function clearEventHandler(navigatorEventID) {
  delete _allNavigatorEventHandlers[navigatorEventID];
}

function handleDeepLink(params = {}) {
  const { link, payload } = params;

  if (!link) return;

  const event = {
    type: 'DeepLink',
    link,
    ...(payload ? { payload } : {})
  };
  for (let i in _allNavigatorEventHandlers) {
    _allNavigatorEventHandlers[i](event);
  }
}

async function isAppLaunched() {
  return await platformSpecific.isAppLaunched();
}

async function isRootLaunched() {
  return await platformSpecific.isRootLaunched();
}

function getCurrentlyVisibleScreenId() {
  return platformSpecific.getCurrentlyVisibleScreenId();
}

async function getLaunchArgs() {
  return await platformSpecific.getLaunchArgs();
}

function getCurrentScreen() {
  return currentScreen;
}

export default {
  getRegisteredScreen,
  getCurrentlyVisibleScreenId,
  registerComponent,
  showModal: showModal,
  dismissModal: dismissModal,
  dismissAllModals: dismissAllModals,
  showSnackbar: showSnackbar,
  showLightBox: showLightBox,
  dismissLightBox: dismissLightBox,
  showInAppNotification: showInAppNotification,
  dismissInAppNotification: dismissInAppNotification,
  startTabBasedApp: startTabBasedApp,
  startSingleScreenApp: startSingleScreenApp,
  setEventHandler: setEventHandler,
  clearEventHandler: clearEventHandler,
  handleDeepLink: handleDeepLink,
  isAppLaunched: isAppLaunched,
  isRootLaunched: isRootLaunched,
  getCurrentScreen: getCurrentScreen,
  isRootLaunched: isRootLaunched,
  getLaunchArgs
};
<commit_msg>Current screen listener update.<commit_after>/*eslint-disable*/
import React from 'react';
import {AppRegistry} from 'react-native';
import platformSpecific from './deprecated/platformSpecificDeprecated';
import {Screen} from './Screen';

import PropRegistry from './PropRegistry';

const registeredScreens = {};
const _allNavigatorEventHandlers = {};
const currentScreen = null;

function registerScreen(screenID, generator) {
  registeredScreens[screenID] = generator;
  AppRegistry.registerComponent(screenID, generator);
}

function registerComponent(screenID, generator, store = undefined, Provider = undefined, options = {}) {
  if (store && Provider) {
    return _registerComponentRedux(screenID, generator, store, Provider, options);
  } else {
    return _registerComponentNoRedux(screenID, generator);
  }
}

function _registerComponentNoRedux(screenID, generator) {
  const generatorWrapper = function() {
    const InternalComponent = generator();
    if (!InternalComponent) {
      console.error(`Navigation: ${screenID} registration result is 'undefined'`);
    }

    return class extends Screen {
      static navigatorStyle = InternalComponent.navigatorStyle || {};
      static navigatorButtons = InternalComponent.navigatorButtons || {};

      constructor(props) {
        super(props);
        this.state = {
          internalProps: {...props, ...PropRegistry.load(props.screenInstanceID || props.passPropsKey)}
        }
      }

      componentWillReceiveProps(nextProps) {
        this.setState({
          internalProps: {...PropRegistry.load(this.props.screenInstanceID || this.props.passPropsKey), ...nextProps}
        })
      }

      render() {
        return (
          <InternalComponent testID={screenID} navigator={this.navigator} {...this.state.internalProps} />
        );
      }
    };
  };
  registerScreen(screenID, generatorWrapper);
  return generatorWrapper;
}

function _registerComponentRedux(screenID, generator, store, Provider, options) {
  const generatorWrapper = function() {
    const InternalComponent = generator();
    return class extends Screen {
      static navigatorStyle = InternalComponent.navigatorStyle || {};
      static navigatorButtons = InternalComponent.navigatorButtons || {};

      constructor(props) {
        super(props);
        this.state = {
          internalProps: {...props, ...PropRegistry.load(props.screenInstanceID || props.passPropsKey)}
        }

        this.navigator.addOnNavigatorEvent(this.onNavigatorEvent.bind(this));
      }

      onNavigatorEvent(event) {
        switch(event.id) {
          case 'willAppear':
            console.log("SAYFA", screenID),
            currentScreen = this.navigator;
            break;

          default:
            break;
        }
      }

      componentWillReceiveProps(nextProps) {
        this.setState({
          internalProps: {...PropRegistry.load(this.props.screenInstanceID || this.props.passPropsKey), ...nextProps}
        })
      }

      render() {
        return (
          <Provider store={store} {...options}>
            <InternalComponent testID={screenID} navigator={this.navigator} {...this.state.internalProps} />
          </Provider>
        );
      }
    };
  };
  registerScreen(screenID, generatorWrapper);
  return generatorWrapper;
}

function getRegisteredScreen(screenID) {
  const generator = registeredScreens[screenID];
  if (!generator) {
    console.error(`Navigation.getRegisteredScreen: ${screenID} used but not yet registered`);
    return undefined;
  }
  return generator();
}

function showModal(params = {}) {
  return platformSpecific.showModal(params);
}

function dismissModal(params = {}) {
  return platformSpecific.dismissModal(params);
}

function dismissAllModals(params = {}) {
  return platformSpecific.dismissAllModals(params);
}

function showSnackbar(params = {}) {
  return platformSpecific.showSnackbar(params);
}

function showLightBox(params = {}) {
  return platformSpecific.showLightBox(params);
}

function dismissLightBox(params = {}) {
  return platformSpecific.dismissLightBox(params);
}

function showInAppNotification(params = {}) {
  return platformSpecific.showInAppNotification(params);
}

function dismissInAppNotification(params = {}) {
  return platformSpecific.dismissInAppNotification(params);
}

async function startTabBasedApp(params) {
  try {
    return await platformSpecific.startTabBasedApp(params);
  } catch(e) {
    console.error(`Error while starting app: ${e}`);
  }
}

async function startSingleScreenApp(params) {
  try {
    return await platformSpecific.startSingleScreenApp(params);
  } catch(e) {
    console.error(`Error while starting app: ${e}`);
  }
}

function setEventHandler(navigatorEventID, eventHandler) {
  _allNavigatorEventHandlers[navigatorEventID] = eventHandler;
}

function clearEventHandler(navigatorEventID) {
  delete _allNavigatorEventHandlers[navigatorEventID];
}

function handleDeepLink(params = {}) {
  const { link, payload } = params;

  if (!link) return;

  const event = {
    type: 'DeepLink',
    link,
    ...(payload ? { payload } : {})
  };
  for (let i in _allNavigatorEventHandlers) {
    _allNavigatorEventHandlers[i](event);
  }
}

async function isAppLaunched() {
  return await platformSpecific.isAppLaunched();
}

async function isRootLaunched() {
  return await platformSpecific.isRootLaunched();
}

function getCurrentlyVisibleScreenId() {
  return platformSpecific.getCurrentlyVisibleScreenId();
}

async function getLaunchArgs() {
  return await platformSpecific.getLaunchArgs();
}

function getCurrentScreen() {
  return currentScreen;
}

export default {
  getRegisteredScreen,
  getCurrentlyVisibleScreenId,
  registerComponent,
  showModal: showModal,
  dismissModal: dismissModal,
  dismissAllModals: dismissAllModals,
  showSnackbar: showSnackbar,
  showLightBox: showLightBox,
  dismissLightBox: dismissLightBox,
  showInAppNotification: showInAppNotification,
  dismissInAppNotification: dismissInAppNotification,
  startTabBasedApp: startTabBasedApp,
  startSingleScreenApp: startSingleScreenApp,
  setEventHandler: setEventHandler,
  clearEventHandler: clearEventHandler,
  handleDeepLink: handleDeepLink,
  isAppLaunched: isAppLaunched,
  isRootLaunched: isRootLaunched,
  getCurrentScreen: getCurrentScreen,
  isRootLaunched: isRootLaunched,
  getLaunchArgs
};
<|endoftext|>


<commit_before>
import {EventEmitter} from 'events'

import invariant from 'invariant'
import uuid from 'node-uuid'
import createQueue from '../queue'

import {race, select, call, put} from 'redux-saga/effects'
import {log, opts} from './log'

import * as actions from '../../actions'
import download from '../../tasks/download'

export function * startDownload (downloadOpts) {
  invariant(downloadOpts, 'startDownload cannot have null opts')
  invariant(downloadOpts.reason, 'startDownload must have a reason')
  invariant(downloadOpts.game, 'startDownload must have a game')
  invariant(typeof downloadOpts.totalSize === 'number', 'startDownload must have a total size')

  const existing = yield select((state) => state.tasks.downloadsByGameId[downloadOpts.game.id])
  if (existing && !existing.finished) {
    log(opts, `Not starting another download for ${downloadOpts.game.title}`)
    yield put(actions.navigate('downloads'))
    return
  }

  const {upload, downloadKey} = downloadOpts
  log(opts, `Should download ${upload.id}, dl key ? ${downloadKey}`)

  const id = uuid.v4()
  // FIXME: wasteful but easy
  yield put(actions.downloadStarted({id, ...downloadOpts, downloadOpts}))

  let err
  try {
    const queue = createQueue(`download-${id}`)

    const out = new EventEmitter()
    out.on('progress', (progress) => {
      queue.dispatch(actions.downloadProgress({id, progress}))
    })

    const credentials = yield select((state) => state.session.credentials)
    const extendedOpts = {
      ...opts,
      ...downloadOpts,
      credentials
    }

    log(opts, 'Starting download...')
    yield race({
      task: call(download, out, extendedOpts),
      queue: call(queue.exhaust)
    })
  } catch (e) {
    log(opts, 'Download threw')
    err = e.task || e
  } finally {
    err = err ? err.message || err : null
    log(opts, `Download ended, err: ${err || '<none>'}`)
    yield put(actions.downloadEnded({id, err, downloadOpts}))
  }

  log(opts, 'Download done!')
}
<commit_msg>:bug:<commit_after>
import {EventEmitter} from 'events'

import invariant from 'invariant'
import uuid from 'node-uuid'
import createQueue from '../queue'

import {race, select, call, put} from 'redux-saga/effects'
import {log, opts} from './log'

import * as actions from '../../actions'
import download from '../../tasks/download'

export function * startDownload (downloadOpts) {
  invariant(downloadOpts, 'startDownload cannot have null opts')
  invariant(downloadOpts.reason, 'startDownload must have a reason')
  invariant(downloadOpts.game, 'startDownload must have a game')
  invariant(typeof downloadOpts.totalSize === 'number', 'startDownload must have a total size')

  const existing = yield select((state) => state.tasks.downloadsByGameId[downloadOpts.game.id])
  if (existing && !existing.finished) {
    log(opts, `Not starting another download for ${downloadOpts.game.title}`)
    yield put(actions.navigate('downloads'))
    return
  }

  const {upload, downloadKey} = downloadOpts
  log(opts, `Should download ${upload.id}, has dl key ? ${!!downloadKey}`)

  const id = uuid.v4()
  // FIXME: wasteful but easy
  yield put(actions.downloadStarted({id, ...downloadOpts, downloadOpts}))

  let err
  try {
    const queue = createQueue(`download-${id}`)

    const out = new EventEmitter()
    out.on('progress', (progress) => {
      queue.dispatch(actions.downloadProgress({id, progress}))
    })

    const credentials = yield select((state) => state.session.credentials)
    const extendedOpts = {
      ...opts,
      ...downloadOpts,
      credentials
    }

    log(opts, 'Starting download...')
    yield race({
      task: call(download, out, extendedOpts),
      queue: call(queue.exhaust)
    })
  } catch (e) {
    log(opts, 'Download threw')
    err = e.task || e
  } finally {
    err = err ? err.message || err : null
    log(opts, `Download ended, err: ${err || '<none>'}`)
    yield put(actions.downloadEnded({id, err, downloadOpts}))
  }

  log(opts, 'Download done!')
}
<|endoftext|>