<commit_before>// Copyright 2018, University of Colorado Boulder

/**
 * Status bar for games that have a finite number of challenges per level.
 * This was adapted from and replaces ScoreboardBar.
 * See https://github.com/phetsims/vegas/issues/66.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var ElapsedTimeNode = require( 'VEGAS/ElapsedTimeNode' );
  var inherit = require( 'PHET_CORE/inherit' );
  var HBox = require( 'SCENERY/nodes/HBox' );
  var PhetColorScheme = require( 'SCENERY_PHET/PhetColorScheme' );
  var ScoreDisplayLabeledNumber = require( 'VEGAS/ScoreDisplayLabeledNumber' );
  var ScoreDisplayLabeledStars = require( 'VEGAS/ScoreDisplayLabeledStars' );
  var StatusBar = require( 'VEGAS/StatusBar' );
  var StringUtils = require( 'PHETCOMMON/util/StringUtils' );
  var Tandem = require( 'TANDEM/Tandem' );
  var Text = require( 'SCENERY/nodes/Text' );
  var TextPushButton = require( 'SUN/buttons/TextPushButton' );
  var vegas = require( 'VEGAS/vegas' );

  // strings
  var labelLevelString = require( 'string!VEGAS/label.level' );
  var pattern0Challenge1MaxString = require( 'string!VEGAS/pattern.0challenge.1max' );
  var startOverString = require( 'string!VEGAS/startOver' );

  // constants
  var VALID_SCORE_DISPLAY_CONSTRUCTORS = [
    // Types that are relevant for this status bar. All constructors must have the same signature!
    ScoreDisplayLabeledNumber, ScoreDisplayLabeledStars
  ];

  /**
   * @param {Bounds2} layoutBounds
   * @param {Property.<Bounds2>} visibleBoundsProperty
   * @param {Property.<number>} scoreProperty
   * @param {Object} [options]
   * @constructor
   */
  function FiniteStatusBar( layoutBounds, visibleBoundsProperty, scoreProperty, options ) {

    options = _.extend( {

      // optional Properties
      challengeIndexProperty: null, // {Property.<number>|null}
      numberOfChallengesProperty: null, // {Property.<number>|null}
      levelProperty: null, // {Property.<number>|null}
      elapsedTimeProperty: null, // {Property.<number>|null}
      timerEnabledProperty: null, // {Property.<boolean>|null}

      // things that can be hidden
      levelVisible: true,
      challengeNumberVisible: true,

      // all text
      font: StatusBar.DEFAULT_FONT,
      textFill: 'black',

      // score display
      scoreDisplayConstructor: ScoreDisplayLabeledNumber,
      scoreDisplayOptions: null, // passed to scoreDisplayConstructor

      // nested options for 'Start Over' button, filled in below
      startOverButtonOptions: null,
      startOverButtonText: startOverString,

      // Timer
      clockIconRadius: 15,

      // spacing and margin for things in the bar
      xSpacing: 50,
      xMargin: 20,
      yMargin: 10,

      levelTextOptions: null, // passed to the "Level N" text
      challengeTextOptions: null, // passed to the "Challenge N of M" text

      // phet-io
      tandem: Tandem.required
    }, options );

    // nested options for score display
    options.scoreDisplayOptions = _.extend( {
      font: options.font,
      textFill: options.textFill
    }, options.scoreDisplayOptions );

    // nested options for 'Start Over' button
    options.startOverButtonOptions = _.extend( {
      font: options.font,
      textFill: options.textFill,
      baseColor: PhetColorScheme.BUTTON_YELLOW,
      xMargin: 10,
      yMargin: 8,
      listener: function() {},
      tandem: options.tandem.createTandem( 'startOverButton' ),
      maxWidth: 0.2 * layoutBounds.width
    }, options.startOverButtonOptions );

    assert && assert( _.includes( VALID_SCORE_DISPLAY_CONSTRUCTORS, options.scoreDisplayConstructor,
      'invalid scoreDisplayConstructor: ' + options.scoreDisplayConstructor ) );
    assert && assert( ( options.challengeIndexProperty && options.numberOfChallengesProperty ) ||
                      ( !options.challengeIndexProperty && !options.numberOfChallengesProperty ),
      'challengeIndexProperty and numberOfChallengesProperty are both or neither' );

    // nested options for 'Level N' text
    options.levelTextOptions = _.extend( {
      fill: options.textFill,
      font: options.font
    }, options.levelTextOptions );

    // nested options for 'Challenge N of M' text
    options.challengeTextOptions = _.extend( {
      fill: options.textFill,
      font: options.font
    }, options.challengeTextOptions );

    // Nodes on the left end of the bar
    var leftChildren = [];

    // Level N
    if ( options.levelProperty ) {
      var levelText = new Text( '', _.extend( {
        tandem: options.tandem.createTandem( 'levelText' )
      }, options.levelTextOptions ) );
      leftChildren.push( levelText );

      var levelListener = function( level ) {
        levelText.text = StringUtils.format( labelLevelString, level + 1 );
      };
      options.levelProperty.link( levelListener );
    }

    // Challenge N of M
    if ( options.challengeIndexProperty && options.numberOfChallengesProperty ) {
      var challengeNumberText = new Text( '', _.extend( {
        tandem: options.tandem.createTandem( 'challengeNumberText' )
      }, options.challengeTextOptions ) );
      leftChildren.push( challengeNumberText );

      var updateChallengeString = function() {
        challengeNumberText.text = StringUtils.format( pattern0Challenge1MaxString,
          options.challengeIndexProperty.get() + 1, options.numberOfChallengesProperty.get() );
      };
      options.challengeIndexProperty.link( updateChallengeString );
      options.numberOfChallengesProperty.link( updateChallengeString );
    }

    // Score
    var scoreDisplay = new options.scoreDisplayConstructor( scoreProperty, options.scoreDisplayOptions );
    leftChildren.push( scoreDisplay );

    // Timer
    if ( options.elapsedTimeProperty && options.timerEnabledProperty ) {

      var elapsedTimeNode = new ElapsedTimeNode( options.elapsedTimeProperty, {
        clockRadius: options.clockRadius,
        font: options.font,
        textFill: options.textFill
      } );
      leftChildren.push( elapsedTimeNode );

      var timerEnabledListener = function( timerEnabled ) {
        elapsedTimeNode.visible = ( options.timerEnabledProperty && timerEnabled );
      };
      options.timerEnabledProperty && options.timerEnabledProperty.link( timerEnabledListener );
    }

    // @private Nodes on the left end of the bar
    this.leftNodes = new HBox( {
      resize: false,
      spacing: options.xSpacing,
      children: leftChildren,
      maxWidth: 0.7 * layoutBounds.width
    } );

    // @private Start Over button
    this.startOverButton = new TextPushButton( options.startOverButtonText, options.startOverButtonOptions );

    assert && assert( !options.children, 'FiniteStatusBar sets children' );
    options.children = [ this.leftNodes, this.startOverButton ];

    assert && assert( !options.barHeight, 'FiniteStatusBar sets barHeight' );
    options.barHeight = Math.max( this.leftNodes.height, this.startOverButton.height ) + ( 2 * options.yMargin );

    StatusBar.call( this, layoutBounds, visibleBoundsProperty, options );

    // @private
    this.disposeFiniteStatusBar = function() {

      scoreDisplay.dispose();
      elapsedTimeNode && elapsedTimeNode.dispose();

      if ( options.levelProperty && options.levelProperty.hasListener( levelListener ) ) {
        options.levelProperty.unlink( levelListener );
      }

      if ( options.challengeIndexProperty && options.challengeIndexProperty.hasListener( updateChallengeString ) ) {
        options.challengeIndexProperty.unlink( updateChallengeString );
      }

      if ( options.numberOfChallengesProperty && options.numberOfChallengesProperty.hasListener( updateChallengeString ) ) {
        options.numberOfChallengesProperty.link( updateChallengeString );
      }

      if ( options.timerEnabledProperty && options.timerEnabledProperty.hasListener( timerEnabledListener ) ) {
        options.timerEnabledProperty.unlink( timerEnabledListener );
      }
    };
  }

  vegas.register( 'FiniteStatusBar', FiniteStatusBar );

  return inherit( StatusBar, FiniteStatusBar, {

    /**
     * @public
     * @override
     */
    dispose: function() {
      this.disposeFiniteStatusBar();
      StatusBar.prototype.dispose.call( this );
    },

    /**
     * Handles layout when the bar changes.
     * @param {number} leftEdge - the bar's left edge, compensated for xMargin
     * @param {number} rightEdge - the bar's right edge, compensated for xMargin
     * @param {number} centerY - the bar's vertical center
     * @protected
     * @override
     */
    updateLayoutProtected: function( leftEdge, rightEdge, centerY ) {

      // stuff on left end of bar
      this.leftNodes.left = leftEdge;
      this.leftNodes.centerY = centerY;

      // start over button on right end of bar
      this.startOverButton.right = rightEdge;
      this.startOverButton.centerY = centerY;
    }
  } );
} );<commit_msg>implement options.levelVisible, https://github.com/phetsims/vegas/issues/71<commit_after>// Copyright 2018, University of Colorado Boulder

/**
 * Status bar for games that have a finite number of challenges per level.
 * This was adapted from and replaces ScoreboardBar.
 * See https://github.com/phetsims/vegas/issues/66.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var ElapsedTimeNode = require( 'VEGAS/ElapsedTimeNode' );
  var inherit = require( 'PHET_CORE/inherit' );
  var HBox = require( 'SCENERY/nodes/HBox' );
  var PhetColorScheme = require( 'SCENERY_PHET/PhetColorScheme' );
  var ScoreDisplayLabeledNumber = require( 'VEGAS/ScoreDisplayLabeledNumber' );
  var ScoreDisplayLabeledStars = require( 'VEGAS/ScoreDisplayLabeledStars' );
  var StatusBar = require( 'VEGAS/StatusBar' );
  var StringUtils = require( 'PHETCOMMON/util/StringUtils' );
  var Tandem = require( 'TANDEM/Tandem' );
  var Text = require( 'SCENERY/nodes/Text' );
  var TextPushButton = require( 'SUN/buttons/TextPushButton' );
  var vegas = require( 'VEGAS/vegas' );

  // strings
  var labelLevelString = require( 'string!VEGAS/label.level' );
  var pattern0Challenge1MaxString = require( 'string!VEGAS/pattern.0challenge.1max' );
  var startOverString = require( 'string!VEGAS/startOver' );

  // constants
  var VALID_SCORE_DISPLAY_CONSTRUCTORS = [
    // Types that are relevant for this status bar. All constructors must have the same signature!
    ScoreDisplayLabeledNumber, ScoreDisplayLabeledStars
  ];

  /**
   * @param {Bounds2} layoutBounds
   * @param {Property.<Bounds2>} visibleBoundsProperty
   * @param {Property.<number>} scoreProperty
   * @param {Object} [options]
   * @constructor
   */
  function FiniteStatusBar( layoutBounds, visibleBoundsProperty, scoreProperty, options ) {

    options = _.extend( {

      // optional Properties
      challengeIndexProperty: null, // {Property.<number>|null}
      numberOfChallengesProperty: null, // {Property.<number>|null}
      levelProperty: null, // {Property.<number>|null}
      elapsedTimeProperty: null, // {Property.<number>|null}
      timerEnabledProperty: null, // {Property.<boolean>|null}

      // things that can be hidden
      levelVisible: true,
      challengeNumberVisible: true,

      // all text
      font: StatusBar.DEFAULT_FONT,
      textFill: 'black',

      // score display
      scoreDisplayConstructor: ScoreDisplayLabeledNumber,
      scoreDisplayOptions: null, // passed to scoreDisplayConstructor

      // nested options for 'Start Over' button, filled in below
      startOverButtonOptions: null,
      startOverButtonText: startOverString,

      // Timer
      clockIconRadius: 15,

      // spacing and margin for things in the bar
      xSpacing: 50,
      xMargin: 20,
      yMargin: 10,

      levelTextOptions: null, // passed to the "Level N" text
      challengeTextOptions: null, // passed to the "Challenge N of M" text

      // phet-io
      tandem: Tandem.required
    }, options );

    // nested options for score display
    options.scoreDisplayOptions = _.extend( {
      font: options.font,
      textFill: options.textFill
    }, options.scoreDisplayOptions );

    // nested options for 'Start Over' button
    options.startOverButtonOptions = _.extend( {
      font: options.font,
      textFill: options.textFill,
      baseColor: PhetColorScheme.BUTTON_YELLOW,
      xMargin: 10,
      yMargin: 8,
      listener: function() {},
      tandem: options.tandem.createTandem( 'startOverButton' ),
      maxWidth: 0.2 * layoutBounds.width
    }, options.startOverButtonOptions );

    assert && assert( _.includes( VALID_SCORE_DISPLAY_CONSTRUCTORS, options.scoreDisplayConstructor,
      'invalid scoreDisplayConstructor: ' + options.scoreDisplayConstructor ) );
    assert && assert( ( options.challengeIndexProperty && options.numberOfChallengesProperty ) ||
                      ( !options.challengeIndexProperty && !options.numberOfChallengesProperty ),
      'challengeIndexProperty and numberOfChallengesProperty are both or neither' );

    // nested options for 'Level N' text
    options.levelTextOptions = _.extend( {
      fill: options.textFill,
      font: options.font
    }, options.levelTextOptions );

    // nested options for 'Challenge N of M' text
    options.challengeTextOptions = _.extend( {
      fill: options.textFill,
      font: options.font
    }, options.challengeTextOptions );

    // Nodes on the left end of the bar
    var leftChildren = [];

    // Level N
    if ( options.levelProperty && options.levelVisible ) {
      var levelText = new Text( '', _.extend( {
        tandem: options.tandem.createTandem( 'levelText' )
      }, options.levelTextOptions ) );
      leftChildren.push( levelText );

      var levelListener = function( level ) {
        levelText.text = StringUtils.format( labelLevelString, level + 1 );
      };
      options.levelProperty.link( levelListener );
    }

    // Challenge N of M
    if ( options.challengeIndexProperty && options.numberOfChallengesProperty ) {
      var challengeNumberText = new Text( '', _.extend( {
        tandem: options.tandem.createTandem( 'challengeNumberText' )
      }, options.challengeTextOptions ) );
      leftChildren.push( challengeNumberText );

      var updateChallengeString = function() {
        challengeNumberText.text = StringUtils.format( pattern0Challenge1MaxString,
          options.challengeIndexProperty.get() + 1, options.numberOfChallengesProperty.get() );
      };
      options.challengeIndexProperty.link( updateChallengeString );
      options.numberOfChallengesProperty.link( updateChallengeString );
    }

    // Score
    var scoreDisplay = new options.scoreDisplayConstructor( scoreProperty, options.scoreDisplayOptions );
    leftChildren.push( scoreDisplay );

    // Timer
    if ( options.elapsedTimeProperty && options.timerEnabledProperty ) {

      var elapsedTimeNode = new ElapsedTimeNode( options.elapsedTimeProperty, {
        clockRadius: options.clockRadius,
        font: options.font,
        textFill: options.textFill
      } );
      leftChildren.push( elapsedTimeNode );

      var timerEnabledListener = function( timerEnabled ) {
        elapsedTimeNode.visible = ( options.timerEnabledProperty && timerEnabled );
      };
      options.timerEnabledProperty && options.timerEnabledProperty.link( timerEnabledListener );
    }

    // @private Nodes on the left end of the bar
    this.leftNodes = new HBox( {
      resize: false,
      spacing: options.xSpacing,
      children: leftChildren,
      maxWidth: 0.7 * layoutBounds.width
    } );

    // @private Start Over button
    this.startOverButton = new TextPushButton( options.startOverButtonText, options.startOverButtonOptions );

    assert && assert( !options.children, 'FiniteStatusBar sets children' );
    options.children = [ this.leftNodes, this.startOverButton ];

    assert && assert( !options.barHeight, 'FiniteStatusBar sets barHeight' );
    options.barHeight = Math.max( this.leftNodes.height, this.startOverButton.height ) + ( 2 * options.yMargin );

    StatusBar.call( this, layoutBounds, visibleBoundsProperty, options );

    // @private
    this.disposeFiniteStatusBar = function() {

      scoreDisplay.dispose();
      elapsedTimeNode && elapsedTimeNode.dispose();

      if ( options.levelProperty && options.levelProperty.hasListener( levelListener ) ) {
        options.levelProperty.unlink( levelListener );
      }

      if ( options.challengeIndexProperty && options.challengeIndexProperty.hasListener( updateChallengeString ) ) {
        options.challengeIndexProperty.unlink( updateChallengeString );
      }

      if ( options.numberOfChallengesProperty && options.numberOfChallengesProperty.hasListener( updateChallengeString ) ) {
        options.numberOfChallengesProperty.link( updateChallengeString );
      }

      if ( options.timerEnabledProperty && options.timerEnabledProperty.hasListener( timerEnabledListener ) ) {
        options.timerEnabledProperty.unlink( timerEnabledListener );
      }
    };
  }

  vegas.register( 'FiniteStatusBar', FiniteStatusBar );

  return inherit( StatusBar, FiniteStatusBar, {

    /**
     * @public
     * @override
     */
    dispose: function() {
      this.disposeFiniteStatusBar();
      StatusBar.prototype.dispose.call( this );
    },

    /**
     * Handles layout when the bar changes.
     * @param {number} leftEdge - the bar's left edge, compensated for xMargin
     * @param {number} rightEdge - the bar's right edge, compensated for xMargin
     * @param {number} centerY - the bar's vertical center
     * @protected
     * @override
     */
    updateLayoutProtected: function( leftEdge, rightEdge, centerY ) {

      // stuff on left end of bar
      this.leftNodes.left = leftEdge;
      this.leftNodes.centerY = centerY;

      // start over button on right end of bar
      this.startOverButton.right = rightEdge;
      this.startOverButton.centerY = centerY;
    }
  } );
} );<|endoftext|>


<commit_before>// Copyright 2013-2020, University of Colorado Boulder

/**
 * LU decomposition, based on Jama (http://math.nist.gov/javanumerics/jama/).  Please note the arbitrary-precision
 * copy LUDecompositionDecimal which should be maintained with this file.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

import Matrix from './Matrix.js';
import dot from './dot.js';

const ArrayType = window.Float64Array || Array;

class LUDecomposition {
  constructor( matrix ) {
    let i;
    let j;
    let k;

    this.matrix = matrix;

    // TODO: size!
    this.LU = matrix.getArrayCopy();
    const LU = this.LU;
    this.m = matrix.getRowDimension();
    const m = this.m;
    this.n = matrix.getColumnDimension();
    const n = this.n;
    this.piv = new Uint32Array( m );
    for ( i = 0; i < m; i++ ) {
      this.piv[ i ] = i;
    }
    this.pivsign = 1;
    const LUcolj = new ArrayType( m );

    // Outer loop.

    for ( j = 0; j < n; j++ ) {

      // Make a copy of the j-th column to localize references.
      for ( i = 0; i < m; i++ ) {
        LUcolj[ i ] = LU[ matrix.index( i, j ) ];
      }

      // Apply previous transformations.

      for ( i = 0; i < m; i++ ) {
        // Most of the time is spent in the following dot product.
        const kmax = Math.min( i, j );
        let s = 0.0;
        for ( k = 0; k < kmax; k++ ) {
          const ik = matrix.index( i, k );
          s += LU[ ik ] * LUcolj[ k ];
        }

        LUcolj[ i ] -= s;
        LU[ matrix.index( i, j ) ] = LUcolj[ i ];
      }

      // Find pivot and exchange if necessary.

      let p = j;
      for ( i = j + 1; i < m; i++ ) {
        if ( Math.abs( LUcolj[ i ] ) > Math.abs( LUcolj[ p ] ) ) {
          p = i;
        }
      }
      if ( p !== j ) {
        for ( k = 0; k < n; k++ ) {
          const pk = matrix.index( p, k );
          const jk = matrix.index( j, k );
          const t = LU[ pk ];
          LU[ pk ] = LU[ jk ];
          LU[ jk ] = t;
        }
        k = this.piv[ p ];
        this.piv[ p ] = this.piv[ j ];
        this.piv[ j ] = k;
        this.pivsign = -this.pivsign;
      }

      // Compute multipliers.

      if ( j < m && LU[ this.matrix.index( j, j ) ] !== 0.0 ) {
        for ( i = j + 1; i < m; i++ ) {
          LU[ matrix.index( i, j ) ] /= LU[ matrix.index( j, j ) ];
        }
      }
    }
  }

  /**
   * @public
   *
   * @returns {boolean}
   */
  isNonsingular() {
    for ( let j = 0; j < this.n; j++ ) {
      const index = this.matrix.index( j, j );
      if ( this.LU[ index ] === 0 ) {
        return false;
      }
    }
    return true;
  }

  /**
   * @public
   *
   * @returns {Matrix}
   */
  getL() {
    const result = new Matrix( this.m, this.n );
    for ( let i = 0; i < this.m; i++ ) {
      for ( let j = 0; j < this.n; j++ ) {
        if ( i > j ) {
          result.entries[ result.index( i, j ) ] = this.LU[ this.matrix.index( i, j ) ];
        }
        else if ( i === j ) {
          result.entries[ result.index( i, j ) ] = 1.0;
        }
        else {
          result.entries[ result.index( i, j ) ] = 0.0;
        }
      }
    }
    return result;
  }

  /**
   * @public
   *
   * @returns {Matrix}
   */
  getU() {
    const result = new Matrix( this.n, this.n );
    for ( let i = 0; i < this.n; i++ ) {
      for ( let j = 0; j < this.n; j++ ) {
        if ( i <= j ) {
          result.entries[ result.index( i, j ) ] = this.LU[ this.matrix.index( i, j ) ];
        }
        else {
          result.entries[ result.index( i, j ) ] = 0.0;
        }
      }
    }
    return result;
  }

  /**
   * @public
   *
   * @returns {Uint32Array}
   */
  getPivot() {
    const p = new Uint32Array( this.m );
    for ( let i = 0; i < this.m; i++ ) {
      p[ i ] = this.piv[ i ];
    }
    return p;
  }

  /**
   * @public
   *
   * @returns {Float64Array}
   */
  getDoublePivot() {
    const vals = new ArrayType( this.m );
    for ( let i = 0; i < this.m; i++ ) {
      vals[ i ] = this.piv[ i ];
    }
    return vals;
  }

  /**
   * @public
   *
   * @returns {number}
   */
  det() {
    if ( this.m !== this.n ) {
      throw new Error( 'Matrix must be square.' );
    }
    let d = this.pivsign;
    for ( let j = 0; j < this.n; j++ ) {
      d *= this.LU[ this.matrix.index( j, j ) ];
    }
    return d;
  }

  /**
   * @public
   *
   * @param {Matrix} matrix
   * @returns {Matrix}
   */
  solve( matrix ) {
    let i;
    let j;
    let k;
    if ( matrix.getRowDimension() !== this.m ) {
      throw new Error( 'Matrix row dimensions must agree.' );
    }
    if ( !this.isNonsingular() ) {
      throw new Error( 'Matrix is singular.' );
    }

    // Copy right hand side with pivoting
    const nx = matrix.getColumnDimension();
    const Xmat = matrix.getArrayRowMatrix( this.piv, 0, nx - 1 );

    // Solve L*Y = B(piv,:)
    for ( k = 0; k < this.n; k++ ) {
      for ( i = k + 1; i < this.n; i++ ) {
        for ( j = 0; j < nx; j++ ) {
          Xmat.entries[ Xmat.index( i, j ) ] -= Xmat.entries[ Xmat.index( k, j ) ] * this.LU[ this.matrix.index( i, k ) ];
        }
      }
    }

    // Solve U*X = Y;
    for ( k = this.n - 1; k >= 0; k-- ) {
      for ( j = 0; j < nx; j++ ) {
        Xmat.entries[ Xmat.index( k, j ) ] /= this.LU[ this.matrix.index( k, k ) ];
      }
      for ( i = 0; i < k; i++ ) {
        for ( j = 0; j < nx; j++ ) {
          Xmat.entries[ Xmat.index( i, j ) ] -= Xmat.entries[ Xmat.index( k, j ) ] * this.LU[ this.matrix.index( i, k ) ];
        }
      }
    }
    return Xmat;
  }
}

dot.register( 'LUDecomposition', LUDecomposition );

export default LUDecomposition;<commit_msg>update copyright dates from daily grunt work<commit_after>// Copyright 2013-2021, University of Colorado Boulder

/**
 * LU decomposition, based on Jama (http://math.nist.gov/javanumerics/jama/).  Please note the arbitrary-precision
 * copy LUDecompositionDecimal which should be maintained with this file.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

import Matrix from './Matrix.js';
import dot from './dot.js';

const ArrayType = window.Float64Array || Array;

class LUDecomposition {
  constructor( matrix ) {
    let i;
    let j;
    let k;

    this.matrix = matrix;

    // TODO: size!
    this.LU = matrix.getArrayCopy();
    const LU = this.LU;
    this.m = matrix.getRowDimension();
    const m = this.m;
    this.n = matrix.getColumnDimension();
    const n = this.n;
    this.piv = new Uint32Array( m );
    for ( i = 0; i < m; i++ ) {
      this.piv[ i ] = i;
    }
    this.pivsign = 1;
    const LUcolj = new ArrayType( m );

    // Outer loop.

    for ( j = 0; j < n; j++ ) {

      // Make a copy of the j-th column to localize references.
      for ( i = 0; i < m; i++ ) {
        LUcolj[ i ] = LU[ matrix.index( i, j ) ];
      }

      // Apply previous transformations.

      for ( i = 0; i < m; i++ ) {
        // Most of the time is spent in the following dot product.
        const kmax = Math.min( i, j );
        let s = 0.0;
        for ( k = 0; k < kmax; k++ ) {
          const ik = matrix.index( i, k );
          s += LU[ ik ] * LUcolj[ k ];
        }

        LUcolj[ i ] -= s;
        LU[ matrix.index( i, j ) ] = LUcolj[ i ];
      }

      // Find pivot and exchange if necessary.

      let p = j;
      for ( i = j + 1; i < m; i++ ) {
        if ( Math.abs( LUcolj[ i ] ) > Math.abs( LUcolj[ p ] ) ) {
          p = i;
        }
      }
      if ( p !== j ) {
        for ( k = 0; k < n; k++ ) {
          const pk = matrix.index( p, k );
          const jk = matrix.index( j, k );
          const t = LU[ pk ];
          LU[ pk ] = LU[ jk ];
          LU[ jk ] = t;
        }
        k = this.piv[ p ];
        this.piv[ p ] = this.piv[ j ];
        this.piv[ j ] = k;
        this.pivsign = -this.pivsign;
      }

      // Compute multipliers.

      if ( j < m && LU[ this.matrix.index( j, j ) ] !== 0.0 ) {
        for ( i = j + 1; i < m; i++ ) {
          LU[ matrix.index( i, j ) ] /= LU[ matrix.index( j, j ) ];
        }
      }
    }
  }

  /**
   * @public
   *
   * @returns {boolean}
   */
  isNonsingular() {
    for ( let j = 0; j < this.n; j++ ) {
      const index = this.matrix.index( j, j );
      if ( this.LU[ index ] === 0 ) {
        return false;
      }
    }
    return true;
  }

  /**
   * @public
   *
   * @returns {Matrix}
   */
  getL() {
    const result = new Matrix( this.m, this.n );
    for ( let i = 0; i < this.m; i++ ) {
      for ( let j = 0; j < this.n; j++ ) {
        if ( i > j ) {
          result.entries[ result.index( i, j ) ] = this.LU[ this.matrix.index( i, j ) ];
        }
        else if ( i === j ) {
          result.entries[ result.index( i, j ) ] = 1.0;
        }
        else {
          result.entries[ result.index( i, j ) ] = 0.0;
        }
      }
    }
    return result;
  }

  /**
   * @public
   *
   * @returns {Matrix}
   */
  getU() {
    const result = new Matrix( this.n, this.n );
    for ( let i = 0; i < this.n; i++ ) {
      for ( let j = 0; j < this.n; j++ ) {
        if ( i <= j ) {
          result.entries[ result.index( i, j ) ] = this.LU[ this.matrix.index( i, j ) ];
        }
        else {
          result.entries[ result.index( i, j ) ] = 0.0;
        }
      }
    }
    return result;
  }

  /**
   * @public
   *
   * @returns {Uint32Array}
   */
  getPivot() {
    const p = new Uint32Array( this.m );
    for ( let i = 0; i < this.m; i++ ) {
      p[ i ] = this.piv[ i ];
    }
    return p;
  }

  /**
   * @public
   *
   * @returns {Float64Array}
   */
  getDoublePivot() {
    const vals = new ArrayType( this.m );
    for ( let i = 0; i < this.m; i++ ) {
      vals[ i ] = this.piv[ i ];
    }
    return vals;
  }

  /**
   * @public
   *
   * @returns {number}
   */
  det() {
    if ( this.m !== this.n ) {
      throw new Error( 'Matrix must be square.' );
    }
    let d = this.pivsign;
    for ( let j = 0; j < this.n; j++ ) {
      d *= this.LU[ this.matrix.index( j, j ) ];
    }
    return d;
  }

  /**
   * @public
   *
   * @param {Matrix} matrix
   * @returns {Matrix}
   */
  solve( matrix ) {
    let i;
    let j;
    let k;
    if ( matrix.getRowDimension() !== this.m ) {
      throw new Error( 'Matrix row dimensions must agree.' );
    }
    if ( !this.isNonsingular() ) {
      throw new Error( 'Matrix is singular.' );
    }

    // Copy right hand side with pivoting
    const nx = matrix.getColumnDimension();
    const Xmat = matrix.getArrayRowMatrix( this.piv, 0, nx - 1 );

    // Solve L*Y = B(piv,:)
    for ( k = 0; k < this.n; k++ ) {
      for ( i = k + 1; i < this.n; i++ ) {
        for ( j = 0; j < nx; j++ ) {
          Xmat.entries[ Xmat.index( i, j ) ] -= Xmat.entries[ Xmat.index( k, j ) ] * this.LU[ this.matrix.index( i, k ) ];
        }
      }
    }

    // Solve U*X = Y;
    for ( k = this.n - 1; k >= 0; k-- ) {
      for ( j = 0; j < nx; j++ ) {
        Xmat.entries[ Xmat.index( k, j ) ] /= this.LU[ this.matrix.index( k, k ) ];
      }
      for ( i = 0; i < k; i++ ) {
        for ( j = 0; j < nx; j++ ) {
          Xmat.entries[ Xmat.index( i, j ) ] -= Xmat.entries[ Xmat.index( k, j ) ] * this.LU[ this.matrix.index( i, k ) ];
        }
      }
    }
    return Xmat;
  }
}

dot.register( 'LUDecomposition', LUDecomposition );

export default LUDecomposition;<|endoftext|>


<commit_before>'use strict'

const Slapp = require('slapp')
const Context = require('slapp-context-beepboop')
const ConvoStore = require('slapp-convo-beepboop')

module.exports = (server, db) => {
  let app = Slapp({
    verify_token: process.env.SLACK_VERIFY_TOKEN,
    context: Context(),
    convo_store: ConvoStore()
  })

  // Middleware
  app.use((msg, next) => {
    if (msg.body.event.user)
    {
      db.saveConvo(msg.body.event.user, msg.body, (err, convo) => {
        console.log(err)
      })
    }
    next()
  })

  var HELP_TEXT = `
  I will respond to the following messages:
  \`help\` - to see this message
  \`hi\` - to demonstrate a conversation that tracks state.
  \`thanks\` - to demonstrate a simple response.
  \`<type-any-other-text>\` - to demonstrate a random emoticon response, some of the time :wink:.
  \`attachment\` - to see a Slack attachment message.
  `

//*********************************************
// Setup different handlers for messages
//*********************************************

  app.message('store', ['mention', 'direct_message'], (msg, text, greeting) => {

    msg
      .say(`Conversation model stored!`)
      .route('store_step_2')
  })

  app.route('store_step_2', (msg, state) => {
    var text = (msg.body.event && msg.body.event.text) || ''
    msg.say(['Okay'])
  })

  app.message('test', ['direct_mention', 'direct_message'], (msg, text, greeting) => {
    msg
      .say(`How are you?`)
      .route('handleHowAreYou')  // where to route the next msg in the conversation
  })

  // register a route handler
  app.route('handleHowAreYou', (msg) => {
    // respond with a random entry from array
    msg.say(['Me too', 'Noted', 'That is interesting'])
  })

  app
    .message('motivators', ['direct_mention', 'direct_message'], (msg, text) => {

      var motivators

      db.getMotivations(msg.body.event.user, (err, motivations) => {
        if (err) {
          console.error(err)
        }
        motivators = motivations
      })

      if (motivators)
      {
        msg
          .say('You have motivations set.')
      }
      else
      {
        msg
          .say(`What is something that motivates you to create?`)
          // sends next event from user to this route, passing along state
          .route('motivates')
      }

    })
  app.route('motivates', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("Whoops, I'm still waiting to hear a response.")
          .say('What is something that motivates you to create?')
          .route('motivates', state)
      }

      // add their response to state
      state.motivates = text

      msg
        .say(`Ok. And what's something that discourages you from creating?`)
        .route('discourages', state)
    })
  app.route('discourages', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("I'm eagerly awaiting to hear something that discourages you from creating.")
          .route('discourages', state)
      }

      // add their response to state
      state.discourages = text

      // Store the responses
      db.saveMotivations(msg.body.event.user, { 'motivates' : state.motivates , 'discourages' : state.discourages }, (err, convo) => {
        console.log(err)
      })

      msg
        .say('Thanks for sharing.')
        .say(`Do more of this: `+state.motivates+'. Do less of this: '+state.discourages)
      // At this point, since we don't route anywhere, the "conversation" is over
    })

  // response to the user typing "help"
  app.message('help', ['mention', 'direct_message'], (msg) => {
    msg.say(HELP_TEXT)
  })

  // Test response for boxes
  app.message('yesno', (msg) => {
    msg.say({
      text: '',
      attachments: [
        {
          text: '',
          fallback: 'Yes or No?',
          callback_id: 'yesno_callback',
          actions: [
            { name: 'answer', text: 'Yes', type: 'button', value: 'yes' },
            { name: 'answer', text: 'No',  type: 'button',  value: 'no' }
          ]
        }]
      })
  })

  app.action('yesno_callback', 'answer', (msg, value) => {
    msg.respond(msg.body.response_url, `${value} is a good choice!`)
  })

  // "Conversation" flow that tracks state - kicks off when user says hi, hello or hey
  app
    .message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
      msg
        .say(`${text}, how are you?`)
        // sends next event from user to this route, passing along state
        .route('how-are-you', { greeting: text })
    })
    .route('how-are-you', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("Whoops, I'm still waiting to hear how you're doing.")
          .say('How are you?')
          .route('how-are-you', state)
      }

      // add their response to state
      state.status = text

      msg
        .say(`Ok then. What's your favorite color?`)
        .route('color', state)
    })
    .route('color', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("I'm eagerly awaiting to hear your favorite color.")
          .route('color', state)
      }

      // add their response to state
      state.color = text

      msg
        .say('Thanks for sharing.')
        .say(`Here's what you've told me so far: \`\`\`${JSON.stringify(state)}\`\`\``)
      // At this point, since we don't route anywhere, the "conversation" is over
    })

  // Can use a regex as well
  app.message(/^(thanks|thank you)/i, ['mention', 'direct_message'], (msg) => {
    // You can provide a list of responses, and a random one will be chosen
    // You can also include slack emoji in your responses
    msg.say([
      "You're welcome :smile:",
      'You bet',
      ':+1: Of course',
      'Anytime :sun_with_face: :full_moon_with_face:'
    ])
  })

  // demonstrate returning an attachment...
  app.message('attachment', ['mention', 'direct_message'], (msg) => {
    msg.say({
      text: 'Check out this amazing attachment! :confetti_ball: ',
      attachments: [{
        text: 'Slapp is a robust open source library that sits on top of the Slack APIs',
        title: 'Slapp Library - Open Source',
        image_url: 'https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png',
        title_link: 'https://beepboophq.com/',
        color: '#7CD197'
      }]
    })
  })

  // response to the user asking "how did you get here?"
  app.message('how did you get here?', ['mention', 'direct_message'], (msg) => {
    msg.say('Team Rocket! :rocket:')
  })

  // "Creative motivators" flow

  app
    .message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
      msg
        .say(text + ', how are you?')
        .route('how-are-you', { text }, 60)
    })
    .route('how-are-you', (msg, state) => {
      var resp = msg.body.event && msg.body.event.text

      if (/good/i.test(resp)) {
        msg
          .say(['Great! Ready?', ':smile: Are you sure?'])
          .route('how-are-you', state, 60)
      } else {
        msg
          .say('Me too', () => msg.say(`Thanks for saying ${state.text}`))
      }
    })
    .message('^(thanks|thank you)', ['mention', 'direct_message'], (msg) => {
      msg.say(["You're welcome", 'Of course'])
    })
    .message('help', ['direct_message', 'direct_mention'], (msg) => {
      let help = `I will respond to the following messages:
\`@slapp hi||hello\` for a greeting.
\`mention me by name in a message\` to demonstrate detecting a mention.
\`@slapp help\` to see this again.`

      msg.say(help)
    })
    .message('fix it', 'ambient', (msg) => {
      msg.say('https://www.youtube.com/watch?v=yo3uxqwTxk0')
    })
    .message(/.*/, 'mention', (msg) => {
      msg.say('You really do care about me. :heart:')
    })

    app.attachToExpress(server)

  return app
}
<commit_msg>Console log motivations.<commit_after>'use strict'

const Slapp = require('slapp')
const Context = require('slapp-context-beepboop')
const ConvoStore = require('slapp-convo-beepboop')

module.exports = (server, db) => {
  let app = Slapp({
    verify_token: process.env.SLACK_VERIFY_TOKEN,
    context: Context(),
    convo_store: ConvoStore()
  })

  // Middleware
  app.use((msg, next) => {
    if (msg.body.event.user)
    {
      db.saveConvo(msg.body.event.user, msg.body, (err, convo) => {
        console.log(err)
      })
    }
    next()
  })

  var HELP_TEXT = `
  I will respond to the following messages:
  \`help\` - to see this message
  \`hi\` - to demonstrate a conversation that tracks state.
  \`thanks\` - to demonstrate a simple response.
  \`<type-any-other-text>\` - to demonstrate a random emoticon response, some of the time :wink:.
  \`attachment\` - to see a Slack attachment message.
  `

//*********************************************
// Setup different handlers for messages
//*********************************************

  app.message('store', ['mention', 'direct_message'], (msg, text, greeting) => {

    msg
      .say(`Conversation model stored!`)
      .route('store_step_2')
  })

  app.route('store_step_2', (msg, state) => {
    var text = (msg.body.event && msg.body.event.text) || ''
    msg.say(['Okay'])
  })

  app.message('test', ['direct_mention', 'direct_message'], (msg, text, greeting) => {
    msg
      .say(`How are you?`)
      .route('handleHowAreYou')  // where to route the next msg in the conversation
  })

  // register a route handler
  app.route('handleHowAreYou', (msg) => {
    // respond with a random entry from array
    msg.say(['Me too', 'Noted', 'That is interesting'])
  })

  app
    .message('motivators', ['direct_mention', 'direct_message'], (msg, text) => {

      var motivators

      db.getMotivations(msg.body.event.user, (err, motivations) => {
        if (err) {
          console.error(err)
        }
        motivators = motivations
        console.log(motivations)
      })

      if (motivators)
      {
        msg
          .say('You have motivations set.')
      }
      else
      {
        msg
          .say(`What is something that motivates you to create?`)
          // sends next event from user to this route, passing along state
          .route('motivates')
      }

    })
  app.route('motivates', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("Whoops, I'm still waiting to hear a response.")
          .say('What is something that motivates you to create?')
          .route('motivates', state)
      }

      // add their response to state
      state.motivates = text

      msg
        .say(`Ok. And what's something that discourages you from creating?`)
        .route('discourages', state)
    })
  app.route('discourages', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("I'm eagerly awaiting to hear something that discourages you from creating.")
          .route('discourages', state)
      }

      // add their response to state
      state.discourages = text

      // Store the responses
      db.saveMotivations(msg.body.event.user, { 'motivates' : state.motivates , 'discourages' : state.discourages }, (err, convo) => {
        console.log(err)
      })

      msg
        .say('Thanks for sharing.')
        .say(`Do more of this: `+state.motivates+'. Do less of this: '+state.discourages)
      // At this point, since we don't route anywhere, the "conversation" is over
    })

  // response to the user typing "help"
  app.message('help', ['mention', 'direct_message'], (msg) => {
    msg.say(HELP_TEXT)
  })

  // Test response for boxes
  app.message('yesno', (msg) => {
    msg.say({
      text: '',
      attachments: [
        {
          text: '',
          fallback: 'Yes or No?',
          callback_id: 'yesno_callback',
          actions: [
            { name: 'answer', text: 'Yes', type: 'button', value: 'yes' },
            { name: 'answer', text: 'No',  type: 'button',  value: 'no' }
          ]
        }]
      })
  })

  app.action('yesno_callback', 'answer', (msg, value) => {
    msg.respond(msg.body.response_url, `${value} is a good choice!`)
  })

  // "Conversation" flow that tracks state - kicks off when user says hi, hello or hey
  app
    .message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
      msg
        .say(`${text}, how are you?`)
        // sends next event from user to this route, passing along state
        .route('how-are-you', { greeting: text })
    })
    .route('how-are-you', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("Whoops, I'm still waiting to hear how you're doing.")
          .say('How are you?')
          .route('how-are-you', state)
      }

      // add their response to state
      state.status = text

      msg
        .say(`Ok then. What's your favorite color?`)
        .route('color', state)
    })
    .route('color', (msg, state) => {
      var text = (msg.body.event && msg.body.event.text) || ''

      // user may not have typed text as their next action, ask again and re-route
      if (!text) {
        return msg
          .say("I'm eagerly awaiting to hear your favorite color.")
          .route('color', state)
      }

      // add their response to state
      state.color = text

      msg
        .say('Thanks for sharing.')
        .say(`Here's what you've told me so far: \`\`\`${JSON.stringify(state)}\`\`\``)
      // At this point, since we don't route anywhere, the "conversation" is over
    })

  // Can use a regex as well
  app.message(/^(thanks|thank you)/i, ['mention', 'direct_message'], (msg) => {
    // You can provide a list of responses, and a random one will be chosen
    // You can also include slack emoji in your responses
    msg.say([
      "You're welcome :smile:",
      'You bet',
      ':+1: Of course',
      'Anytime :sun_with_face: :full_moon_with_face:'
    ])
  })

  // demonstrate returning an attachment...
  app.message('attachment', ['mention', 'direct_message'], (msg) => {
    msg.say({
      text: 'Check out this amazing attachment! :confetti_ball: ',
      attachments: [{
        text: 'Slapp is a robust open source library that sits on top of the Slack APIs',
        title: 'Slapp Library - Open Source',
        image_url: 'https://storage.googleapis.com/beepboophq/_assets/bot-1.22f6fb.png',
        title_link: 'https://beepboophq.com/',
        color: '#7CD197'
      }]
    })
  })

  // response to the user asking "how did you get here?"
  app.message('how did you get here?', ['mention', 'direct_message'], (msg) => {
    msg.say('Team Rocket! :rocket:')
  })

  // "Creative motivators" flow

  app
    .message('^(hi|hello|hey)$', ['direct_mention', 'direct_message'], (msg, text) => {
      msg
        .say(text + ', how are you?')
        .route('how-are-you', { text }, 60)
    })
    .route('how-are-you', (msg, state) => {
      var resp = msg.body.event && msg.body.event.text

      if (/good/i.test(resp)) {
        msg
          .say(['Great! Ready?', ':smile: Are you sure?'])
          .route('how-are-you', state, 60)
      } else {
        msg
          .say('Me too', () => msg.say(`Thanks for saying ${state.text}`))
      }
    })
    .message('^(thanks|thank you)', ['mention', 'direct_message'], (msg) => {
      msg.say(["You're welcome", 'Of course'])
    })
    .message('help', ['direct_message', 'direct_mention'], (msg) => {
      let help = `I will respond to the following messages:
\`@slapp hi||hello\` for a greeting.
\`mention me by name in a message\` to demonstrate detecting a mention.
\`@slapp help\` to see this again.`

      msg.say(help)
    })
    .message('fix it', 'ambient', (msg) => {
      msg.say('https://www.youtube.com/watch?v=yo3uxqwTxk0')
    })
    .message(/.*/, 'mention', (msg) => {
      msg.say('You really do care about me. :heart:')
    })

    app.attachToExpress(server)

  return app
}
<|endoftext|>


<commit_before>// Copyright 2017, University of Colorado Boulder

/**
 * Wrapper type for scenery-phet's MultiLineText node.
 *
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var NodeIO = require( 'SCENERY/nodes/NodeIO' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );

  // phet-io modules
  var assertInstanceOf = require( 'ifphetio!PHET_IO/assertInstanceOf' );
  var phetioInherit = require( 'ifphetio!PHET_IO/phetioInherit' );
  var StringIO = require( 'ifphetio!PHET_IO/types/StringIO' );
  var VoidIO = require( 'ifphetio!PHET_IO/types/VoidIO' );

  /**
   * Wrapper type for scenery's Text node.
   * @param {Text} text
   * @param {string} phetioID
   * @constructor
   */
  function MultiLineTextIO( text, phetioID ) {
    assert && assertInstanceOf( text, phet.sceneryPhet.MultiLineText );
    NodeIO.call( this, text, phetioID );
  }

  phetioInherit( NodeIO, 'MultiLineTextIO', MultiLineTextIO, {
    setText: {
      returnType: VoidIO,
      parameterTypes: [ StringIO ],
      implementation: function( text ) {
        this.instance.text = text;
      },
      documentation: 'Set the text content'
    },

    getText: {
      returnType: StringIO,
      parameterTypes: [],
      implementation: function() {
        return this.instance.text;
      },
      documentation: 'Get the text content'
    }
  }, {
    documentation: 'The tandem wrapper type for the scenery phet\'s MultiLineText node',
    events: [ 'changed' ]
  } );

  sceneryPhet.register( 'MultiLineTextIO', MultiLineTextIO );

  return MultiLineTextIO;
} );<commit_msg>Added type checks in static methods, see https://github.com/phetsims/beers-law-lab/issues/215<commit_after>// Copyright 2017, University of Colorado Boulder

/**
 * Wrapper type for scenery-phet's MultiLineText node.
 *
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var NodeIO = require( 'SCENERY/nodes/NodeIO' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );

  // phet-io modules
  var assertInstanceOf = require( 'ifphetio!PHET_IO/assertInstanceOf' );
  var phetioInherit = require( 'ifphetio!PHET_IO/phetioInherit' );
  var StringIO = require( 'ifphetio!PHET_IO/types/StringIO' );
  var VoidIO = require( 'ifphetio!PHET_IO/types/VoidIO' );

  /**
   * Wrapper type for scenery's Text node.
   * @param {Text} multiLineText
   * @param {string} phetioID
   * @constructor
   */
  function MultiLineTextIO( multiLineText, phetioID ) {
    assert && assertInstanceOf( multiLineText, phet.sceneryPhet.MultiLineText );
    NodeIO.call( this, multiLineText, phetioID );
  }

  phetioInherit( NodeIO, 'MultiLineTextIO', MultiLineTextIO, {
    setText: {
      returnType: VoidIO,
      parameterTypes: [ StringIO ],
      implementation: function( text ) {
        this.instance.text = text;
      },
      documentation: 'Set the text content'
    },

    getText: {
      returnType: StringIO,
      parameterTypes: [],
      implementation: function() {
        return this.instance.text;
      },
      documentation: 'Get the text content'
    }
  }, {
    documentation: 'The tandem wrapper type for the scenery phet\'s MultiLineText node',
    events: [ 'changed' ]
  } );

  sceneryPhet.register( 'MultiLineTextIO', MultiLineTextIO );

  return MultiLineTextIO;
} );<|endoftext|>


<commit_before>// Copyright 2002-2014, University of Colorado Boulder

/**
 * Thermometer node, see https://github.com/phetsims/scenery-phet/issues/43
 *
 * @author Aaron Davis
 * @author Sam Reid
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var LinearGradient = require( 'SCENERY/util/LinearGradient' );
  var Shape = require( 'KITE/Shape' );
  var LinearFunction = require( 'DOT/LinearFunction' );

  /**
   *
   * @param {number} minTemperature
   * @param {number} maxTemperature
   * @param {Property.<number>} temperatureProperty
   * @param {Object} [options]
   * @constructor
   */
  function ThermometerNode( minTemperature, maxTemperature, temperatureProperty, options ) {

    options = _.extend( {
      bulbDiameter: 50,
      tubeWidth: 30,
      tubeHeight: 100,
      lineWidth: 4,
      outlineStroke: 'black',
      tickSpacing: 15,
      majorTickLength: 15,
      minorTickLength: 7.5,
      fluidSphereSpacing: 2, // the empty space between the fluid sphere and the thermometer outline
      fluidRectSpacing: 2, // the empty space between the fluid in the tube and the thermometer outline

      // leave as null to have a transparent background. If a color is given, then an extra Rectangle is created for the background
      backgroundColor: null,

      // all the default colors are shades of red
      fluidMainColor: '#850e0e', // the main color of the shaded sphere and the left side of the tube gradient
      fluidHighlightColor: '#ff7575', // the highlight color of the shaded sphere and the middle of the tube gradient
      fluidRightSideColor: '#c41515' // the right side of the tube gradient
    }, options );

    Node.call( this );

    // assume the center of the bulb is at (0, 0) and let the client code move to the correct place
    var bulbCenterX = 0;
    var bulbCenterY = 0;

    // Angles for the outline of the bulb
    var startAngle = -Math.acos( options.tubeWidth / options.bulbDiameter );
    var endAngle = Math.PI - startAngle;

    // Create the outline for the thermometer
    var shape = new Shape()
      .arc( bulbCenterX, bulbCenterY, options.bulbDiameter / 2, startAngle, endAngle )
      .verticalLineToRelative( -options.tubeHeight );

    var upperLeftCorner = shape.getLastPoint();
    shape.arc( bulbCenterX, upperLeftCorner.y, options.tubeWidth / 2, Math.PI, 0 ).close();

    shape.moveToPoint( upperLeftCorner ).moveToRelative( options.majorTickLength ).horizontalLineToRelative( -options.majorTickLength );
    for ( var i = 0; i < Math.floor( options.tubeHeight / options.tickSpacing ); i++ ) {
      if ( i % 2 === 0 ) {
        shape.moveToRelative( options.minorTickLength, options.tickSpacing ).horizontalLineToRelative( -options.minorTickLength );
      }
      else {
        shape.moveToRelative( options.majorTickLength, options.tickSpacing ).horizontalLineToRelative( -options.majorTickLength );
      }
    }

    var clipTubeRadius = ( options.tubeWidth - options.lineWidth - options.fluidRectSpacing ) / 2;
    var clipBulbRadius = ( options.bulbDiameter - options.lineWidth - options.fluidSphereSpacing ) / 2;
    var clipStartAngle = -Math.acos( clipTubeRadius / clipBulbRadius );
    var clipEndAngle = Math.PI - clipStartAngle;

    // Create clip area for the fluid
    var fluidClipShape = new Shape()
      .arc( bulbCenterX, bulbCenterY, clipBulbRadius, clipStartAngle, clipEndAngle )
      .verticalLineTo( upperLeftCorner.y );

    var clipUpperLeftCorner = fluidClipShape.getLastPoint();
    fluidClipShape.arc( bulbCenterX, clipUpperLeftCorner.y, clipTubeRadius, Math.PI, 0 ).close();

    var outline = new Path( shape,
      {
        stroke: options.outlineStroke,
        lineWidth: options.lineWidth
      } );

    // parameters for the fluid rectangle
    var fluidWidth = options.bulbDiameter;
    var rectangleX = bulbCenterX - options.bulbDiameter / 2;
    var tubeBase = upperLeftCorner.y + options.tubeHeight + options.fluidSphereSpacing;
    var fluidBase = bulbCenterY + options.bulbDiameter / 2;
    var fluidOffset = fluidBase - tubeBase; // the 0 temperature point of the rectangle
    var maxFluidHeight = options.tubeHeight + options.tubeWidth / 2; // max fluid height includes the rounded tip of the tube

    var fluidRectangleGradient = new LinearGradient( rectangleX, 0, rectangleX + fluidWidth, 0 ).
      addColorStop( 0, options.fluidMainColor ).
      addColorStop( 0.5, options.fluidHighlightColor ).
      addColorStop( 0.7, options.fluidHighlightColor ).
      addColorStop( 1, options.fluidRightSideColor );

    var fluidRectangle = new Rectangle( 0, 0, fluidWidth, 0, { fill: fluidRectangleGradient } );
    fluidRectangle.setClipArea( fluidClipShape );

    var temperatureLinearFunction = new LinearFunction( minTemperature, maxTemperature, fluidOffset, maxFluidHeight + fluidOffset );
    temperatureProperty.link( function( temp ) {
      var fluidHeight = temperatureLinearFunction( temp );
      fluidRectangle.setRect( rectangleX, fluidBase - fluidHeight, fluidWidth, fluidHeight );
    } );

    if ( options.backgroundColor ) {
      var backgroundY = upperLeftCorner.y - options.tubeWidth / 2;
      var backgroundHeight = options.tubeHeight + options.tubeWidth / 2;
      var backgroundRectangle = new Rectangle( upperLeftCorner.x, backgroundY, options.tubeWidth, backgroundHeight,
        { fill: options.backgroundColor } );
      backgroundRectangle.setClipArea( shape );
      this.addChild( backgroundRectangle );
    }
    this.addChild( fluidRectangle );
    this.addChild( outline );

    this.mutate( options );
  }

  return inherit( Node, ThermometerNode );
} );<commit_msg>Revert thermometer to using sphere and fix bugs, see #127, #128, #43<commit_after>// Copyright 2002-2014, University of Colorado Boulder

/**
 * Thermometer node, see https://github.com/phetsims/scenery-phet/issues/43
 *
 * @author Aaron Davis
 * @author Sam Reid
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var ShadedSphereNode = require( 'SCENERY_PHET/ShadedSphereNode' );
  var LinearGradient = require( 'SCENERY/util/LinearGradient' );
  var Shape = require( 'KITE/Shape' );
  var LinearFunction = require( 'DOT/LinearFunction' );

  /**
   *
   * @param {number} minTemperature
   * @param {number} maxTemperature
   * @param {Property.<number>} temperatureProperty
   * @param {Object} [options]
   * @constructor
   */
  function ThermometerNode( minTemperature, maxTemperature, temperatureProperty, options ) {

    options = _.extend( {
      bulbDiameter: 50,
      tubeWidth: 30,
      tubeHeight: 100,
      lineWidth: 4,
      outlineStroke: 'black',
      tickSpacing: 15,
      majorTickLength: 15,
      minorTickLength: 7.5,
      fluidSphereSpacing: 2, // the empty space between the fluid sphere and the thermometer outline
      fluidRectSpacing: 2, // the empty space between the fluid in the tube and the thermometer outline

      // leave as null to have a transparent background. If a color is given, then an extra Rectangle is created for the background
      backgroundColor: null,

      // all the default colors are shades of red
      fluidMainColor: '#850e0e', // the main color of the shaded sphere and the left side of the tube gradient
      fluidHighlightColor: '#ff7575', // the highlight color of the shaded sphere and the middle of the tube gradient
      fluidRightSideColor: '#c41515' // the right side of the tube gradient
    }, options );

    Node.call( this );

    // assume the center of the bulb is at (0, 0) and let the client code move to the correct place
    var bulbCenterX = 0;
    var bulbCenterY = 0;

    // Create a shaded sphere to act as the bulb fill
    var fluidSphereDiameter = options.bulbDiameter - options.lineWidth - options.fluidSphereSpacing;
    var fluidSphere = new ShadedSphereNode( fluidSphereDiameter,
      {
        centerX: bulbCenterX,
        centerY: bulbCenterY,
        mainColor: options.fluidMainColor,
        highlightColor: options.fluidHighlightColor,
        highlightXOffset: -0.2,
        highlightYOffset: -0.2,
        rotation: Math.PI / 2
      } );

    // Angles for the outline of the bulb
    var startAngle = -Math.acos( options.tubeWidth / options.bulbDiameter );
    var endAngle = Math.PI - startAngle;

    // Create the outline for the thermometer
    var shape = new Shape()
      .arc( bulbCenterX, bulbCenterY, options.bulbDiameter / 2, startAngle, endAngle )
      .verticalLineToRelative( -options.tubeHeight );

    var upperLeftCorner = shape.getLastPoint();
    shape.arc( upperLeftCorner.x + options.tubeWidth / 2, upperLeftCorner.y, options.tubeWidth / 2, Math.PI, 0 )
      .verticalLineToRelative( options.tubeHeight ).close();

    shape.moveToPoint( upperLeftCorner ).moveToRelative( options.majorTickLength ).horizontalLineToRelative( -options.majorTickLength );
    for ( var i = 0; i < Math.floor( options.tubeHeight / options.tickSpacing ); i++ ) {
      if ( i % 2 === 0 ) {
        shape.moveToRelative( options.minorTickLength, options.tickSpacing ).horizontalLineToRelative( -options.minorTickLength );
      }
      else {
        shape.moveToRelative( options.majorTickLength, options.tickSpacing ).horizontalLineToRelative( -options.majorTickLength );
      }
    }

    var fluidWidth = options.tubeWidth - options.lineWidth - options.fluidRectSpacing;
    var clipBulbRadius = ( options.bulbDiameter - options.lineWidth - options.fluidSphereSpacing ) / 2;
    var clipStartAngle = -Math.acos( ( fluidWidth / 2 ) / clipBulbRadius );
    var clipEndAngle = Math.PI - clipStartAngle;
    var fluidBottomCutoff = fluidSphereDiameter / 2  *  Math.sin( clipEndAngle );
    var rectangleX = -fluidWidth / 2;

    // Create clip area for the fluid
    var fluidClipShape = new Shape().moveTo( rectangleX, fluidBottomCutoff )
      .verticalLineTo( upperLeftCorner.y );

    var clipUpperLeftCorner = fluidClipShape.getLastPoint();
    fluidClipShape.arc( bulbCenterX, clipUpperLeftCorner.y, fluidWidth / 2, Math.PI, 0 )
      .lineTo( -rectangleX, fluidBottomCutoff ).close();

    // clip the top of the sphere so it's flat where it connects to the tube
    var sphereClipShape = Shape.rectangle( fluidBottomCutoff, -options.bulbDiameter / 2, options.bulbDiameter, options.bulbDiameter );
    fluidSphere.setClipArea( sphereClipShape );

    var outline = new Path( shape,
      {
        stroke: options.outlineStroke,
        lineWidth: options.lineWidth
      } );

    var fluidRectangleGradient = new LinearGradient( rectangleX, 0, rectangleX + fluidWidth, 0 ).
      addColorStop( 0, options.fluidMainColor ).
      addColorStop( 0.5, options.fluidHighlightColor ).
      addColorStop( 0.7, options.fluidHighlightColor ).
      addColorStop( 1, options.fluidRightSideColor );

    var fluidRectangle = new Rectangle( 0, 0, fluidWidth, 0, { fill: fluidRectangleGradient } );
    fluidRectangle.setClipArea( fluidClipShape );

    var height = new Path( fluidClipShape ).height;
    var maxFluidHeight = height - fluidBottomCutoff;
    var temperatureLinearFunction = new LinearFunction( minTemperature, maxTemperature, -fluidBottomCutoff, maxFluidHeight );
    temperatureProperty.link( function( temp ) {
      var fluidHeight = temperatureLinearFunction( temp );
      fluidRectangle.setRect( rectangleX, -fluidHeight, fluidWidth, fluidHeight );
    } );

    if ( options.backgroundColor ) {
      var backgroundY = upperLeftCorner.y - options.tubeWidth / 2;
      var backgroundHeight = options.tubeHeight + options.tubeWidth / 2;
      var backgroundRectangle = new Rectangle( upperLeftCorner.x, backgroundY, options.tubeWidth, backgroundHeight,
        { fill: options.backgroundColor } );
      backgroundRectangle.setClipArea( shape );
      this.addChild( backgroundRectangle );
    }
    this.addChild( fluidRectangle );
    this.addChild( fluidSphere );
    this.addChild( outline );

//    this.addChild( new Path( fluidClipShape, { stroke: 'green'}))

    this.mutate( options );
  }

  return inherit( Node, ThermometerNode );
} );<|endoftext|>


<commit_before>import {element} from 'deku';

import Editor from './editor';
import Menu from './menu';
import Panel from './panel';

function render({props, state}, setState) {
  console.log({props, state});

  if (!state.trees) {
    state.trees = props.globals.trees;
  }

  const updateTrees = (newTrees) => {
    setState({trees: newTrees});
  };

  return (

      <Panel
        trees={state.trees}
        updateTrees={updateTrees} />

      <Menu />
      <Editor />

  );
}

export default {render};
<commit_msg>Make good use of initialState<commit_after>import {element} from 'deku';

import Editor from './editor';
import Menu from './menu';
import Panel from './panel';

function initialState(props) {
  return {
    trees: props.globals.trees
  };
}

function render({props, state}, setState) {
  console.log({props, state});

  const updateTrees = (newTrees) => {
    setState({trees: newTrees});
  };

  return (

      <Panel
        trees={state.trees}
        updateTrees={updateTrees} />

      <Menu />
      <Editor />

  );
}

export default {initialState, render};
<|endoftext|>


<commit_before>define([ 'use!underscore' ], function(_) {
  describe("functions", function() {
    var sayIt = function(greeting, name, punctuation) {
          return greeting + ', ' + name + (punctuation || '!');
        },
        fn = function() {};

    it("you should be able to use an array as arguments when calling a function", function() {
      var result = fn([ 'Hello', 'Ellie', '!' ]);
      expect(result).to.be('Hello, Ellie!');
    });

    it("you should be able to change the context in which a function is called", function() {
      var speak = function() {
            return sayIt(this.greeting, this.name, '!!!');
          },
          obj = {
            greeting : 'Hello',
            name : 'Rebecca'
          };

      // define a function for fn that calls the speak function such that the
      // following test will pass
      expect(fn()).to.be('Hello, Rebecca!');
    });

    it("you should be able to return a function from a function", function() {
      // define a function for fn so that the following will pass
      expect(fn('Hello')('world')).to.be('Hello, world');
    });

    it("you should be able to create a 'partial' function", function() {
      // define a function for fn so that the following will pass
      var partial = fn(sayIt, 'Hello', 'Ellie');
      expect(partial('!!!')).to.be('Hello, Ellie!!!');
    });
  });
});
<commit_msg>updating expected results of 'change the context in which a function is called' as speak appends three exclamation marks.<commit_after>define([ 'use!underscore' ], function(_) {
  describe("functions", function() {
    var sayIt = function(greeting, name, punctuation) {
          return greeting + ', ' + name + (punctuation || '!');
        },
        fn = function() {};

    it("you should be able to use an array as arguments when calling a function", function() {
      var result = fn([ 'Hello', 'Ellie', '!' ]);
      expect(result).to.be('Hello, Ellie!');
    });

    it("you should be able to change the context in which a function is called", function() {
      var speak = function() {
            return sayIt(this.greeting, this.name, '!!!');
          },
          obj = {
            greeting : 'Hello',
            name : 'Rebecca'
          };

      // define a function for fn that calls the speak function such that the
      // following test will pass
      expect(fn()).to.be('Hello, Rebecca!!!');
    });

    it("you should be able to return a function from a function", function() {
      // define a function for fn so that the following will pass
      expect(fn('Hello')('world')).to.be('Hello, world');
    });

    it("you should be able to create a 'partial' function", function() {
      // define a function for fn so that the following will pass
      var partial = fn(sayIt, 'Hello', 'Ellie');
      expect(partial('!!!')).to.be('Hello, Ellie!!!');
    });
  });
});
<|endoftext|>


<commit_before>"use strict";

var url = require("url");

var parseArgs = {
    "jsonGraph": true,
    "callPath": true,
    "arguments": true,
    "pathSuffixes": true,
    "paths": true
};

module.exports = function requestToContext(req) {
    var queryMap = req.method === "POST" ? req.body : url.parse(req.url, true).query;

    var context = {};
    if (queryMap) {
        Object.keys(queryMap).forEach(function(key) {
            var arg = queryMap[key];
            if (parseArgs[key] && arg != null) {
                context[key] = JSON.parse(arg);
            } else {
                context[key] = arg;
            }
        });
    }

    return context;
};
<commit_msg>Fix eslint error: no-eq-null<commit_after>"use strict";

var url = require("url");

var parseArgs = {
    "jsonGraph": true,
    "callPath": true,
    "arguments": true,
    "pathSuffixes": true,
    "paths": true
};

module.exports = function requestToContext(req) {
    var queryMap = req.method === "POST" ? req.body : url.parse(req.url, true).query;

    var context = {};
    if (queryMap) {
        Object.keys(queryMap).forEach(function(key) {
            var arg = queryMap[key];

            if (parseArgs[key] && arg) {
                context[key] = JSON.parse(arg);
            } else {
                context[key] = arg;
            }
        });
    }

    return context;
};
<|endoftext|>


<commit_before>var request = require('request');
var xml2js = require('xml2js');

// Small wrapper for request

var Requests = function() {

    this.json = function(options, params) {
        options = this.urlToOptions(options);
        options.json = true;
        this.get(options, params);
    };

    this.xml = function(options, params) {
        this.get(options, {
            success: function() {
                xml2js.parseString(xml, function (err, result) {
                    params.success(result);
                });
            },
            error: params.error
        });
    };

    this.get = function(options, params) {
        options = this.urlToOptions(options);
        if(typeof options.headers  == 'undefined') {
            options.headers = {'User-Agent': 'Online Notiwire (https://github.com/appKom/notiwire2)'};
        }
        request(options, function(error, response, body) {
            if(!error && response.statusCode == 200) {
                params.success(body);
            }
            else {
                params.error(error, body);
            }
        });
    };

    this.urlToOptions = function(options) {
        if(typeof options === 'string') {
            options = {url: options};
        }
        return options;
    };
};

module.exports = new Requests();
<commit_msg>Super minor code cleanup<commit_after>var request = require('request');
var xml2js = require('xml2js');

// Small wrapper for request

var Requests = function() {

    this.json = function(options, params) {
        options = this.urlToOptions(options);
        options.json = true;
        this.get(options, params);
    };

    this.xml = function(options, params) {
        this.get(options, {
            success: function() {
                xml2js.parseString(xml, function (err, result) {
                    params.success(result);
                });
            },
            error: params.error
        });
    };

    this.get = function(options, params) {
        options = this.urlToOptions(options);
        if(options.headers  == undefined) {
            options.headers = {'User-Agent': 'Online Notiwire (https://github.com/appKom/notiwire2)'};
        }
        request(options, function(error, response, body) {
            if(!error && response.statusCode == 200) {
                params.success(body);
            }
            else {
                params.error(error, body);
            }
        });
    };

    this.urlToOptions = function(options) {
        if(typeof options === 'string') {
            options = {url: options};
        }
        return options;
    };
};

module.exports = new Requests();
<|endoftext|>


<commit_before>import {
  Address,
  Item,
  ItemLine,
  ItemDepartment,
  ItemCategory,
  Transaction,
  TransactionItem,
  TransactionCategory,
  TransactionLine,
  MasterList,
  MasterListLine,
  Name,
  Requisition,
  RequisitionItem,
  RequisitionLine,
  Setting,
  SyncOut,
  Stocktake,
  StocktakeItem,
  StocktakeLine,
  User,
} from './DataTypes';

Address.schema = {
  name: 'Address',
  primaryKey: 'id',
  properties: {
    id: 'string',
    line1: { type: 'string', optional: true },
    line2: { type: 'string', optional: true },
    line3: { type: 'string', optional: true },
    line4: { type: 'string', optional: true },
    zipCode: { type: 'string', optional: true },
  },
};

Item.schema = {
  name: 'Item',
  primaryKey: 'id',
  properties: {
    id: 'string',
    code: 'string',
    name: 'string',
    defaultPackSize: 'double',
    lines: { type: 'list', objectType: 'ItemLine' },
    department: { type: 'ItemDepartment', optional: true },
    description: { type: 'string', optional: true },
    category: { type: 'ItemCategory', optional: true },
    defaultPrice: { type: 'double', optional: true },
  },
};

ItemCategory.schema = {
  name: 'ItemCategory',
  primaryKey: 'id',
  properties: {
    id: 'string',
    name: 'string',
    parentCategory: { type: 'ItemCategory', optional: true },
  },
};

ItemDepartment.schema = {
  name: 'ItemDepartment',
  primaryKey: 'id',
  properties: {
    id: 'string',
    name: 'string',
    parentDepartment: { type: 'ItemDepartment', optional: true },
  },
};

ItemLine.schema = {
  name: 'ItemLine',
  primaryKey: 'id',
  properties: {
    id: 'string',
    item: { type: 'Item', optional: true },
    packSize: 'double',
    numberOfPacks: 'double',
    expiryDate: 'date',
    batch: 'string',
    costPrice: 'double',
    sellPrice: 'double',
    supplier: { type: 'Name', optional: true },
  },
};

MasterList.schema = {
  name: 'MasterList',
  primaryKey: 'id',
  properties: {
    id: 'string',
    name: 'string',
    note: { type: 'string', optional: true },
    lines: { type: 'list', objectType: 'MasterListLine' },
  },
};

MasterListLine.schema = {
  name: 'MasterListLine',
  primaryKey: 'id',
  properties: {
    id: 'string',
    masterList: 'MasterList',
    item: 'Item',
    imprestQuantity: { type: 'double', optional: true },
  },
};

Name.schema = {
  name: 'Name',
  primaryKey: 'id',
  properties: {
    id: 'string',
    name: 'string',
    code: 'string',
    phoneNumber: { type: 'string', optional: true },
    billingAddress: { type: 'Address', optional: true },
    emailAddress: { type: 'string', optional: true },
    type: 'string',
    useMasterList: 'boolean',
    masterList: { type: 'MasterList', optional: true },
    transactions: { type: 'list', objectType: 'Transaction' },
  },
};

Requisition.schema = {
  name: 'Requisition',
  primaryKey: 'id',
  properties: {
    id: 'string',
    status: 'string',
    type: 'string', // imprest, forecast or request (request only used in mobile)
    entryDate: 'date',
    daysToSupply: 'double',
    serialNumber: 'string',
    user: { type: 'User', optional: true },
    items: { type: 'list', objectType: 'RequisitionItem' },
  },
};

RequisitionItem.schema = {
  name: 'RequisitionItem',
  primaryKey: 'id',
  properties: {
    id: 'string',
    item: 'Item',
    requisition: 'Requisition',
    lines: { type: 'list', objectType: 'RequisitionLine' },
  },
};

RequisitionLine.schema = {
  name: 'RequisitionLine',
  primaryKey: 'id',
  properties: {
    id: 'string',
    requisition: { type: 'Requisition', optional: true },
    item: { type: 'Item', optional: true },
    stockOnHand: 'double',
    dailyUsage: { type: 'double', optional: true },
    suggestedQuantity: 'double',
    imprestQuantity: { type: 'double', optional: true },
    requiredQuantity: { type: 'double', optional: true },
    comment: { type: 'string', optional: true },
    sortIndex: { type: 'int', optional: true },
  },
};

Setting.schema = {
  name: 'Setting',
  primaryKey: 'key',
  properties: {
    key: 'string', // Includes the user's UUID if it is per-user
    value: 'string',
    user: { type: 'User', optional: true },
  },
};

Stocktake.schema = {
  name: 'Stocktake',
  primaryKey: 'id',
  properties: {
    id: 'string',
    name: 'string',
    createdDate: 'date', // Includes time
    stocktakeDate: { type: 'date', optional: true },
    status: 'string',
    createdBy: { type: 'User', optional: true },
    finalisedBy: { type: 'User', optional: true },
    comment: { type: 'string', optional: true },
    serialNumber: 'string',
    items: { type: 'list', objectType: 'StocktakeItem' },
    additions: { type: 'Transaction', optional: true },
    reductions: { type: 'Transaction', optional: true },
  },
};

StocktakeItem.schema = {
  name: 'StocktakeItem',
  primaryKey: 'id',
  properties: {
    id: 'string',
    item: 'Item',
    stocktake: 'Stocktake',
    lines: { type: 'list', objectType: 'StocktakeLine' },
  },
};

StocktakeLine.schema = {
  name: 'StocktakeLine',
  primaryKey: 'id',
  properties: {
    id: 'string',
    stocktake: 'Stocktake',
    itemLine: 'ItemLine',
    snapshotNumberOfPacks: 'double',
    packSize: 'double',
    expiryDate: 'date',
    batch: 'string',
    costPrice: 'double',
    sellPrice: 'double',
    countedNumberOfPacks: { type: 'double', optional: true },
    sortIndex: { type: 'int', optional: true },
  },
};

SyncOut.schema = {
  name: 'SyncOut',
  primaryKey: 'id',
  properties: {
    id: 'string',
    changeTime: 'int', // In seconds since the epoch
    changeType: 'string', // create, update, or delete
    recordType: 'string', // i.e. Table name
    recordId: 'string',
  },
};

Transaction.schema = {
  name: 'Transaction',
  primaryKey: 'id',
  properties: {
    id: 'string',
    serialNumber: 'string',
    otherParty: { type: 'Name', optional: true },
    comment: { type: 'string', optional: true },
    entryDate: 'date',
    type: 'string',
    status: 'string',
    confirmDate: { type: 'date', optional: true },
    enteredBy: { type: 'User', optional: true },
    theirRef: { type: 'string', optional: true }, // An external reference code
    category: { type: 'TransactionCategory', optional: true },
    items: { type: 'list', objectType: 'TransactionItem' },
  },
};

TransactionCategory.schema = {
  name: 'TransactionCategory',
  primaryKey: 'id',
  properties: {
    id: 'string',
    name: 'string',
    code: 'string',
    type: 'string',
    parentCategory: { type: 'TransactionCategory', optional: true },
  },
};

TransactionItem.schema = {
  name: 'TransactionItem',
  primaryKey: 'id',
  properties: {
    id: 'string',
    item: 'Item',
    transaction: 'Transaction',
    lines: { type: 'list', objectType: 'TransactionLine' },
  },
};

TransactionLine.schema = {
  name: 'TransactionLine',
  primaryKey: 'id',
  properties: {
    id: 'string',
    itemId: 'string',
    itemName: 'string',
    itemLine: 'ItemLine',
    batch: 'string',
    expiryDate: 'date',
    packSize: 'double',
    numberOfPacks: 'double',
    numberOfPacksSent: { type: 'double', optional: true }, // For supplier invoices
    transaction: 'Transaction',
    note: { type: 'string', optional: true },
    costPrice: 'double',
    sellPrice: 'double',
    sortIndex: { type: 'int', optional: true },
  },
};

User.schema = {
  name: 'User',
  primaryKey: 'id',
  properties: {
    id: 'string',
    username: 'string',
    lastLogin: { type: 'date', optional: true },
    firstName: { type: 'string', optional: true },
    lastName: { type: 'string', optional: true },
    email: { type: 'string', optional: true },
    passwordHash: 'string',
    salt: { type: 'string', optional: true },
  },
};

export const schema =
  {
    schema: [
      Address,
      Item,
      ItemLine,
      ItemDepartment,
      ItemCategory,
      Transaction,
      TransactionItem,
      TransactionLine,
      TransactionCategory,
      MasterList,
      MasterListLine,
      Name,
      Requisition,
      RequisitionItem,
      RequisitionLine,
      Setting,
      SyncOut,
      Stocktake,
      StocktakeItem,
      StocktakeLine,
      User,
    ],
    schemaVersion: 1,
  };
<commit_msg>Sync is happy now<commit_after>import {
  Address,
  Item,
  ItemLine,
  ItemDepartment,
  ItemCategory,
  Transaction,
  TransactionItem,
  TransactionCategory,
  TransactionLine,
  MasterList,
  MasterListLine,
  Name,
  Requisition,
  RequisitionItem,
  RequisitionLine,
  Setting,
  SyncOut,
  Stocktake,
  StocktakeItem,
  StocktakeLine,
  User,
} from './DataTypes';

Address.schema = {
  name: 'Address',
  primaryKey: 'id',
  properties: {
    id: 'string',
    line1: { type: 'string', optional: true },
    line2: { type: 'string', optional: true },
    line3: { type: 'string', optional: true },
    line4: { type: 'string', optional: true },
    zipCode: { type: 'string', optional: true },
  },
};

Item.schema = {
  name: 'Item',
  primaryKey: 'id',
  properties: {
    id: 'string',
    code: 'string',
    name: 'string',
    defaultPackSize: 'double',
    lines: { type: 'list', objectType: 'ItemLine' },
    department: { type: 'ItemDepartment', optional: true },
    description: { type: 'string', optional: true },
    category: { type: 'ItemCategory', optional: true },
    defaultPrice: { type: 'double', optional: true },
  },
};

ItemCategory.schema = {
  name: 'ItemCategory',
  primaryKey: 'id',
  properties: {
    id: 'string',
    name: 'string',
    parentCategory: { type: 'ItemCategory', optional: true },
  },
};

ItemDepartment.schema = {
  name: 'ItemDepartment',
  primaryKey: 'id',
  properties: {
    id: 'string',
    name: 'string',
    parentDepartment: { type: 'ItemDepartment', optional: true },
  },
};

ItemLine.schema = {
  name: 'ItemLine',
  primaryKey: 'id',
  properties: {
    id: 'string',
    item: { type: 'Item', optional: true },
    packSize: 'double',
    numberOfPacks: 'double',
    expiryDate: 'date',
    batch: 'string',
    costPrice: 'double',
    sellPrice: 'double',
    supplier: { type: 'Name', optional: true },
  },
};

MasterList.schema = {
  name: 'MasterList',
  primaryKey: 'id',
  properties: {
    id: 'string',
    name: 'string',
    note: { type: 'string', optional: true },
    lines: { type: 'list', objectType: 'MasterListLine' },
  },
};

MasterListLine.schema = {
  name: 'MasterListLine',
  primaryKey: 'id',
  properties: {
    id: 'string',
    masterList: 'MasterList',
    item: 'Item',
    imprestQuantity: { type: 'double', optional: true },
  },
};

Name.schema = {
  name: 'Name',
  primaryKey: 'id',
  properties: {
    id: 'string',
    name: 'string',
    code: 'string',
    phoneNumber: { type: 'string', optional: true },
    billingAddress: { type: 'Address', optional: true },
    emailAddress: { type: 'string', optional: true },
    type: 'string',
    useMasterList: { type: 'bool', optional: true },
    masterList: { type: 'MasterList', optional: true },
    transactions: { type: 'list', objectType: 'Transaction' },
  },
};

Requisition.schema = {
  name: 'Requisition',
  primaryKey: 'id',
  properties: {
    id: 'string',
    status: 'string',
    type: 'string', // imprest, forecast or request (request only used in mobile)
    entryDate: 'date',
    daysToSupply: 'double',
    serialNumber: 'string',
    user: { type: 'User', optional: true },
    items: { type: 'list', objectType: 'RequisitionItem' },
  },
};

RequisitionItem.schema = {
  name: 'RequisitionItem',
  primaryKey: 'id',
  properties: {
    id: 'string',
    item: 'Item',
    requisition: 'Requisition',
    lines: { type: 'list', objectType: 'RequisitionLine' },
  },
};

RequisitionLine.schema = {
  name: 'RequisitionLine',
  primaryKey: 'id',
  properties: {
    id: 'string',
    requisition: { type: 'Requisition', optional: true },
    item: { type: 'Item', optional: true },
    stockOnHand: 'double',
    dailyUsage: { type: 'double', optional: true },
    suggestedQuantity: 'double',
    imprestQuantity: { type: 'double', optional: true },
    requiredQuantity: { type: 'double', optional: true },
    comment: { type: 'string', optional: true },
    sortIndex: { type: 'int', optional: true },
  },
};

Setting.schema = {
  name: 'Setting',
  primaryKey: 'key',
  properties: {
    key: 'string', // Includes the user's UUID if it is per-user
    value: 'string',
    user: { type: 'User', optional: true },
  },
};

Stocktake.schema = {
  name: 'Stocktake',
  primaryKey: 'id',
  properties: {
    id: 'string',
    name: 'string',
    createdDate: 'date', // Includes time
    stocktakeDate: { type: 'date', optional: true },
    status: 'string',
    createdBy: { type: 'User', optional: true },
    finalisedBy: { type: 'User', optional: true },
    comment: { type: 'string', optional: true },
    serialNumber: 'string',
    items: { type: 'list', objectType: 'StocktakeItem' },
    additions: { type: 'Transaction', optional: true },
    reductions: { type: 'Transaction', optional: true },
  },
};

StocktakeItem.schema = {
  name: 'StocktakeItem',
  primaryKey: 'id',
  properties: {
    id: 'string',
    item: 'Item',
    stocktake: 'Stocktake',
    lines: { type: 'list', objectType: 'StocktakeLine' },
  },
};

StocktakeLine.schema = {
  name: 'StocktakeLine',
  primaryKey: 'id',
  properties: {
    id: 'string',
    stocktake: 'Stocktake',
    itemLine: 'ItemLine',
    snapshotNumberOfPacks: 'double',
    packSize: 'double',
    expiryDate: 'date',
    batch: 'string',
    costPrice: 'double',
    sellPrice: 'double',
    countedNumberOfPacks: { type: 'double', optional: true },
    sortIndex: { type: 'int', optional: true },
  },
};

SyncOut.schema = {
  name: 'SyncOut',
  primaryKey: 'id',
  properties: {
    id: 'string',
    changeTime: 'int', // In seconds since the epoch
    changeType: 'string', // create, update, or delete
    recordType: 'string', // i.e. Table name
    recordId: 'string',
  },
};

Transaction.schema = {
  name: 'Transaction',
  primaryKey: 'id',
  properties: {
    id: 'string',
    serialNumber: 'string',
    otherParty: { type: 'Name', optional: true },
    comment: { type: 'string', optional: true },
    entryDate: 'date',
    type: 'string',
    status: 'string',
    confirmDate: { type: 'date', optional: true },
    enteredBy: { type: 'User', optional: true },
    theirRef: { type: 'string', optional: true }, // An external reference code
    category: { type: 'TransactionCategory', optional: true },
    items: { type: 'list', objectType: 'TransactionItem' },
  },
};

TransactionCategory.schema = {
  name: 'TransactionCategory',
  primaryKey: 'id',
  properties: {
    id: 'string',
    name: 'string',
    code: 'string',
    type: 'string',
    parentCategory: { type: 'TransactionCategory', optional: true },
  },
};

TransactionItem.schema = {
  name: 'TransactionItem',
  primaryKey: 'id',
  properties: {
    id: 'string',
    item: 'Item',
    transaction: 'Transaction',
    lines: { type: 'list', objectType: 'TransactionLine' },
  },
};

TransactionLine.schema = {
  name: 'TransactionLine',
  primaryKey: 'id',
  properties: {
    id: 'string',
    itemId: 'string',
    itemName: 'string',
    itemLine: 'ItemLine',
    batch: 'string',
    expiryDate: 'date',
    packSize: 'double',
    numberOfPacks: 'double',
    numberOfPacksSent: { type: 'double', optional: true }, // For supplier invoices
    transaction: 'Transaction',
    note: { type: 'string', optional: true },
    costPrice: 'double',
    sellPrice: 'double',
    sortIndex: { type: 'int', optional: true },
  },
};

User.schema = {
  name: 'User',
  primaryKey: 'id',
  properties: {
    id: 'string',
    username: 'string',
    lastLogin: { type: 'date', optional: true },
    firstName: { type: 'string', optional: true },
    lastName: { type: 'string', optional: true },
    email: { type: 'string', optional: true },
    passwordHash: 'string',
    salt: { type: 'string', optional: true },
  },
};

export const schema =
  {
    schema: [
      Address,
      Item,
      ItemLine,
      ItemDepartment,
      ItemCategory,
      Transaction,
      TransactionItem,
      TransactionLine,
      TransactionCategory,
      MasterList,
      MasterListLine,
      Name,
      Requisition,
      RequisitionItem,
      RequisitionLine,
      Setting,
      SyncOut,
      Stocktake,
      StocktakeItem,
      StocktakeLine,
      User,
    ],
    schemaVersion: 1,
  };
<|endoftext|>