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

/**
 * The lattice is a 2D grid with a value in each cell that represents the wave amplitude at that point.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const Bounds2 = require( 'DOT/Bounds2' );
  const Emitter = require( 'AXON/Emitter' );
  const Matrix = require( 'DOT/Matrix' );
  const waveInterference = require( 'WAVE_INTERFERENCE/waveInterference' );

  // constants

  // The wave speed in the coordinate frame of the lattice, see http://www.mtnmath.com/whatth/node47.html. We tried
  // different values, but they do not have the properer emergent behavior.  WAVE_SPEED=1 propagates out as a diamond
  // rather than a circle, and WAVE_SPEED=0.1 is too slow and throws off the frequency of light.
  const WAVE_SPEED = 0.5;
  const WAVE_SPEED_SQUARED = WAVE_SPEED * WAVE_SPEED; // precompute to avoid work in the inner loop
  const NUMBER_OF_MATRICES = 3; // The discretized wave equation algorithm requires current value + 2 history points

  // This is the threshold for the wave value that determines if the light has visited.  If the value is higher,
  // it will track the wavefront of the light more accurately (and hence could be used for more accurate computation of
  // the speed of light), but will generate more artifacts in the initial wave.  If the value is lower, it will generate
  // fewer artifacts in the initial propagation, but will lead the initial wavefront by too far and make it seem like
  // light is faster than it should be measured (based on the propagation of wavefronts).
  const LIGHT_VISIT_THRESHOLD = 1E-3;

  class Lattice {

    /**
     * @param {number} width - width of the lattice
     * @param {number} height - height of the lattice
     * @param {number} dampX - number of cells on the left and again on the right to use for damping
     * @param {number} dampY - number of cells on the top and again on the bottom to use for damping
     */
    constructor( width, height, dampX, dampY ) {

      // @public (read-only) {number} - number of cells on the left and again on the right to use for damping
      this.dampX = dampX;

      // @public (read-only) {number} - number of cells on the top and again on the bottom to use for damping
      this.dampY = dampY;

      // @private {Matrix[]} - matrices for current value, previous value and value before previous
      this.matrices = [];
      for ( let i = 0; i < NUMBER_OF_MATRICES; i++ ) {
        this.matrices.push( new Matrix( width, height ) );
      }

      // @private - keeps track of which cells have been visited by the wave
      this.visitedMatrix = new Matrix( width, height );

      // @private - tracks which cells could have been activated by an source disturbance, as opposed to a numerical
      // artifact or reflection.  See TemporalMask.  Initialize to 1 to support plane waves, which is never masked.
      this.allowedMask = new Matrix( width, height, 1 );

      // @private {number} - indicates the current matrix. Previous matrix is one higher (with correct modulus)
      this.currentMatrixIndex = 0;

      // @public - sends a notification each time the lattice updates.
      this.changedEmitter = new Emitter();

      // @public (read-only) {number} - width of the lattice (includes damping regions)
      this.width = width;

      // @public (read-only) {number} - height of the lattice (includes damping regions)
      this.height = height;

      // @public {number} - Determines how far we have animated between the "last" and "current" matrices, so that we
      // can use getInterpolatedValue to update the view at 60fps even though the model is running at a slower rate.
      // See EventTimer.getRatio for more about this value.
      this.interpolationRatio = 0;

      // @public (read-only) - a Bounds2 representing the visible (non-damping) region of the lattice.
      this.visibleBounds = new Bounds2( this.dampX, this.dampY, this.width - this.dampX, this.height - this.dampY );
    }

    /**
     * Gets a Bounds2 representing the full region of the lattice, including damping regions.
     * @returns {Bounds2}
     */
    getBounds() {
      return new Bounds2( 0, 0, this.width, this.height );
    }

    /**
     * Returns true if the visible bounds contains the lattice coordinate
     * @param {number} i - integer for the horizontal coordinate
     * @param {number} j - integer for the vertical coordinate
     * @returns {boolean}
     * @public
     */
    visibleBoundsContains( i, j ) {
      const b = this.visibleBounds;

      // Note this differs from the standard Bounds2.containsCoordinate because we must exclude right and bottom edge
      // from reading one cell off the visible lattice, see https://github.com/phetsims/wave-interference/issues/86
      return b.minX <= i && i < b.maxX && b.minY <= j && j < b.maxY;
    }

    /**
     * Returns true if the given coordinate is within the lattice
     * @param {number} i - integer for the horizontal coordinate
     * @param {number} j - integer for the vertical coordinate
     * @returns {boolean}
     * @public
     */
    contains( i, j ) {
      return i >= 0 && i < this.width && j >= 0 && j < this.height;
    }

    /**
     * Read the values on the center line of the lattice (omits the out-of-bounds damping regions), for display in the
     * WaveAreaGraphNode
     * @param {number[]} array - array to fill with the values for performance/memory, will be resized if necessary
     * @public
     */
    getCenterLineValues( array ) {
      const samplingWidth = this.width - this.dampX * 2;

      // Resize array if necessary
      if ( array.length !== samplingWidth ) {
        array.length = 0;
      }
      const samplingVerticalLocation = Math.floor( this.height / 2 ); // 50.5 is the center, but we want 50.0
      for ( let i = 0; i < this.width - this.dampX * 2; i++ ) {
        array[ i ] = this.getCurrentValue( i + this.dampX, samplingVerticalLocation );
      }
    }

    /**
     * Returns the current value in the given cell, masked by the allowedMask.
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @returns {number}
     * @public
     */
    getCurrentValue( i, j ) {
      return this.allowedMask.get( i, j ) === 1 ? this.matrices[ this.currentMatrixIndex ].get( i, j ) : 0;
    }

    /**
     * Returns the interpolated value of the given cell, masked by the allowedMask.
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @returns {number}
     * @public
     */
    getInterpolatedValue( i, j ) {
      if ( this.allowedMask.get( i, j ) === 1 ) {
        const currentValue = this.getCurrentValue( i, j );
        const lastValue = this.getLastValue( i, j );
        return currentValue * this.interpolationRatio + lastValue * ( 1 - this.interpolationRatio );
      }
      else {
        return 0;
      }
    }

    /**
     * Sets the current value in the given cell
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @param {number} value
     * @public
     */
    setCurrentValue( i, j, value ) {
      this.matrices[ this.currentMatrixIndex ].set( i, j, value );
    }

    /**
     * Returns the previous value in the given cell
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @returns {number}
     * @private
     */
    getLastValue( i, j ) {
      return this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ].get( i, j );
    }

    /**
     * Sets the previous value in the given cell
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @param {number} value
     * @private
     */
    setLastValue( i, j, value ) {
      this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ].set( i, j, value );
    }

    /**
     * In order to prevent numerical artifacts in the point source scenes, we use TemporalMask to identify which cells
     * have a value because of the source oscillation.
     * @param {number} i
     * @param {number} j
     * @param {boolean} allowed - true if the temporal mask indicates that the value could have been caused by sources
     * @public
     */
    setAllowed( i, j, allowed ) {
      this.allowedMask.set( i, j, allowed ? 1 : 0 );
    }

    /**
     * Determines whether the incoming wave has reached the cell.
     * @param {number} i - horizontal coordinate to check
     * @param {number} j - vertical coordinate to check
     * @returns {boolean}
     * @public
     */
    hasCellBeenVisited( i, j ) {
      return this.visitedMatrix.get( i, j ) === 1 && this.allowedMask.get( i, j ) === 1;
    }

    /**
     * Resets all of the wave values to 0.
     * @public
     */
    clear() {
      this.clearRight( 0 );
    }

    /**
     * Clear everything at and to the right of the specified column.
     * @param {number} column - integer index of the column to start clearing at.
     * @public
     */
    clearRight( column ) {
      for ( let i = column; i < this.width; i++ ) {
        for ( let j = 0; j < this.height; j++ ) {
          this.setCurrentValue( i, j, 0 );
          this.setLastValue( i, j, 0 );
          this.visitedMatrix.set( i, j, 0 );
          this.allowedMask.set( i, j, 1 ); // Initialize to 1 to support plane waves, which is never masked.
        }
      }
      this.changedEmitter.emit();
    }

    /**
     * Gets the values on the right hand side of the wave (before the damping region), for determining intensity.
     * @returns {number[]}
     * @public
     */
    getOutputColumn() {

      // This could be implemented in garbage-free from by require preallocating the entire intensitySample matrix and
      // using an index pointer like a circular array.  However, profiling in Mac Chrome did not show a significant
      // amount of time spent in this function, hence we use the simpler implementation.
      const column = [];
      for ( let j = this.dampY; j < this.height - this.dampY; j++ ) {
        const a = this.getCurrentValue( this.width - this.dampX - 1, j );
        const b = this.getCurrentValue( this.width - this.dampX - 2, j );
        const v = ( a + b ) / 2;
        column.push( v );
      }
      return column;
    }

    /**
     * Propagates the wave by one step.  This is a discrete algorithm and cannot use dt.
     * @public
     */
    step() {

      // Move to the next matrix
      this.currentMatrixIndex = ( this.currentMatrixIndex - 1 + this.matrices.length ) % this.matrices.length;

      const matrix0 = this.matrices[ ( this.currentMatrixIndex + 0 ) % this.matrices.length ];
      const matrix1 = this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ];
      const matrix2 = this.matrices[ ( this.currentMatrixIndex + 2 ) % this.matrices.length ];
      const width = matrix0.getRowDimension();
      const height = matrix0.getColumnDimension();

      // Main loop, doesn't update cells on the edges
      for ( let i = 1; i < width - 1; i++ ) {
        for ( let j = 1; j < height - 1; j++ ) {
          const neighborSum = matrix1.get( i + 1, j ) +
                              matrix1.get( i - 1, j ) +
                              matrix1.get( i, j + 1 ) +
                              matrix1.get( i, j - 1 );
          const m1ij = matrix1.get( i, j );
          const value = m1ij * 2 - matrix2.get( i, j ) + WAVE_SPEED_SQUARED * ( neighborSum + m1ij * -4 );
          matrix0.set( i, j, value );

          if ( Math.abs( value ) > LIGHT_VISIT_THRESHOLD ) {
            this.visitedMatrix.set( i, j, 1 );
          }
        }
      }

      // Numerical computation of absorbing boundary conditions, under the assumption that the wave is perpendicular
      // to the edge, see https://www.phy.ornl.gov/csep/sw/node22.html.  This assumption does not hold everywhere, but
      // it is a helpful approximation.
      // Note there is a Fortran error on the top boundary and in the equations, replace:
      // u2 => matrix1.get
      // u1 => matrix2.get
      // cb => WAVE_SPEED

      // Left edge
      let i = 0;
      for ( let j = 0; j < height; j++ ) {
        const sum = matrix1.get( i, j ) + matrix1.get( i + 1, j ) - matrix2.get( i + 1, j ) + WAVE_SPEED *
                    ( matrix1.get( i + 1, j ) - matrix1.get( i, j ) + matrix2.get( i + 1, j ) - matrix2.get( i + 2, j ) );
        matrix0.set( i, j, sum );
      }

      // Right edge
      i = width - 1;
      for ( let j = 0; j < height; j++ ) {
        const sum = matrix1.get( i, j ) + matrix1.get( i - 1, j ) - matrix2.get( i - 1, j ) + WAVE_SPEED *
                    ( matrix1.get( i - 1, j ) - matrix1.get( i, j ) + matrix2.get( i - 1, j ) - matrix2.get( i - 2, j ) );
        matrix0.set( i, j, sum );
      }

      // Top edge
      let j = 0;
      for ( let i = 0; i < width; i++ ) {
        const sum = matrix1.get( i, j ) + matrix1.get( i, j + 1 ) - matrix2.get( i, j + 1 ) + WAVE_SPEED *
                    ( matrix1.get( i, j + 1 ) - matrix1.get( i, j ) + matrix2.get( i, j + 1 ) - matrix2.get( i, j + 2 ) );
        matrix0.set( i, j, sum );
      }

      // Bottom edge
      j = height - 1;
      for ( let i = 0; i < width; i++ ) {
        const sum = matrix1.get( i, j ) + matrix1.get( i, j - 1 ) - matrix2.get( i, j - 1 ) + WAVE_SPEED *
                    ( matrix1.get( i, j - 1 ) - matrix1.get( i, j ) + matrix2.get( i, j - 1 ) - matrix2.get( i, j - 2 ) );
        matrix0.set( i, j, sum );
      }
    }
  }

  // @public {number} - see docs above
  Lattice.WAVE_SPEED = WAVE_SPEED;

  return waveInterference.register( 'Lattice', Lattice );
} );<commit_msg>Temporary lattice column for testing, see https://github.com/phetsims/wave-interference/issues/143<commit_after>// Copyright 2017-2018, University of Colorado Boulder

/**
 * The lattice is a 2D grid with a value in each cell that represents the wave amplitude at that point.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const Bounds2 = require( 'DOT/Bounds2' );
  const Emitter = require( 'AXON/Emitter' );
  const Matrix = require( 'DOT/Matrix' );
  const waveInterference = require( 'WAVE_INTERFERENCE/waveInterference' );

  // constants

  // The wave speed in the coordinate frame of the lattice, see http://www.mtnmath.com/whatth/node47.html. We tried
  // different values, but they do not have the properer emergent behavior.  WAVE_SPEED=1 propagates out as a diamond
  // rather than a circle, and WAVE_SPEED=0.1 is too slow and throws off the frequency of light.
  const WAVE_SPEED = 0.5;
  const NUMBER_OF_MATRICES = 3; // The discretized wave equation algorithm requires current value + 2 history points

  // This is the threshold for the wave value that determines if the light has visited.  If the value is higher,
  // it will track the wavefront of the light more accurately (and hence could be used for more accurate computation of
  // the speed of light), but will generate more artifacts in the initial wave.  If the value is lower, it will generate
  // fewer artifacts in the initial propagation, but will lead the initial wavefront by too far and make it seem like
  // light is faster than it should be measured (based on the propagation of wavefronts).

  class Lattice {

    /**
     * @param {number} width - width of the lattice
     * @param {number} height - height of the lattice
     * @param {number} dampX - number of cells on the left and again on the right to use for damping
     * @param {number} dampY - number of cells on the top and again on the bottom to use for damping
     */
    constructor( width, height, dampX, dampY ) {

      // @public (read-only) {number} - number of cells on the left and again on the right to use for damping
      this.dampX = dampX;

      // @public (read-only) {number} - number of cells on the top and again on the bottom to use for damping
      this.dampY = dampY;

      // @private {Matrix[]} - matrices for current value, previous value and value before previous
      this.matrices = [];
      for ( let i = 0; i < NUMBER_OF_MATRICES; i++ ) {
        this.matrices.push( new Matrix( width, height ) );
      }

      // @private - keeps track of which cells have been visited by the wave
      this.visitedMatrix = new Matrix( width, height, 1 );

      // @private - tracks which cells could have been activated by an source disturbance, as opposed to a numerical
      // artifact or reflection.  See TemporalMask.  Initialize to 1 to support plane waves, which is never masked.
      this.allowedMask = new Matrix( width, height, 1 );

      // @private {number} - indicates the current matrix. Previous matrix is one higher (with correct modulus)
      this.currentMatrixIndex = 0;

      // @public - sends a notification each time the lattice updates.
      this.changedEmitter = new Emitter();

      // @public (read-only) {number} - width of the lattice (includes damping regions)
      this.width = width;

      // @public (read-only) {number} - height of the lattice (includes damping regions)
      this.height = height;

      // @public {number} - Determines how far we have animated between the "last" and "current" matrices, so that we
      // can use getInterpolatedValue to update the view at 60fps even though the model is running at a slower rate.
      // See EventTimer.getRatio for more about this value.
      this.interpolationRatio = 0;

      // @public (read-only) - a Bounds2 representing the visible (non-damping) region of the lattice.
      this.visibleBounds = new Bounds2( this.dampX, this.dampY, this.width - this.dampX, this.height - this.dampY );
    }

    /**
     * Gets a Bounds2 representing the full region of the lattice, including damping regions.
     * @returns {Bounds2}
     */
    getBounds() {
      return new Bounds2( 0, 0, this.width, this.height );
    }

    /**
     * Returns true if the visible bounds contains the lattice coordinate
     * @param {number} i - integer for the horizontal coordinate
     * @param {number} j - integer for the vertical coordinate
     * @returns {boolean}
     * @public
     */
    visibleBoundsContains( i, j ) {
      const b = this.visibleBounds;

      // Note this differs from the standard Bounds2.containsCoordinate because we must exclude right and bottom edge
      // from reading one cell off the visible lattice, see https://github.com/phetsims/wave-interference/issues/86
      return b.minX <= i && i < b.maxX && b.minY <= j && j < b.maxY;
    }

    /**
     * Returns true if the given coordinate is within the lattice
     * @param {number} i - integer for the horizontal coordinate
     * @param {number} j - integer for the vertical coordinate
     * @returns {boolean}
     * @public
     */
    contains( i, j ) {
      return i >= 0 && i < this.width && j >= 0 && j < this.height;
    }

    /**
     * Read the values on the center line of the lattice (omits the out-of-bounds damping regions), for display in the
     * WaveAreaGraphNode
     * @param {number[]} array - array to fill with the values for performance/memory, will be resized if necessary
     * @public
     */
    getCenterLineValues( array ) {
      const samplingWidth = this.width - this.dampX * 2;

      // Resize array if necessary
      if ( array.length !== samplingWidth ) {
        array.length = 0;
      }
      const samplingVerticalLocation = Math.floor( this.height / 2 ); // 50.5 is the center, but we want 50.0
      for ( let i = 0; i < this.width - this.dampX * 2; i++ ) {
        array[ i ] = this.getCurrentValue( i + this.dampX, samplingVerticalLocation );
      }
    }

    /**
     * Returns the current value in the given cell, masked by the allowedMask.
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @returns {number}
     * @public
     */
    getCurrentValue( i, j ) {
      return this.allowedMask.get( i, j ) === 1 ? this.matrices[ this.currentMatrixIndex ].get( i, j ) : 0;
    }

    /**
     * Returns the interpolated value of the given cell, masked by the allowedMask.
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @returns {number}
     * @public
     */
    getInterpolatedValue( i, j ) {
      if ( this.allowedMask.get( i, j ) === 1 ) {
        const currentValue = this.getCurrentValue( i, j );
        const lastValue = this.getLastValue( i, j );
        return currentValue * this.interpolationRatio + lastValue * ( 1 - this.interpolationRatio );
      }
      else {
        return 0;
      }
    }

    /**
     * Sets the current value in the given cell
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @param {number} value
     * @public
     */
    setCurrentValue( i, j, value ) {
      this.matrices[ this.currentMatrixIndex ].set( i, j, value );
    }

    /**
     * Returns the previous value in the given cell
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @returns {number}
     * @private
     */
    getLastValue( i, j ) {
      return this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ].get( i, j );
    }

    /**
     * Sets the previous value in the given cell
     * @param {number} i - horizontal integer coordinate
     * @param {number} j - vertical integer coordinate
     * @param {number} value
     * @private
     */
    setLastValue( i, j, value ) {
      this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ].set( i, j, value );
    }

    /**
     * In order to prevent numerical artifacts in the point source scenes, we use TemporalMask to identify which cells
     * have a value because of the source oscillation.
     * @param {number} i
     * @param {number} j
     * @param {boolean} allowed - true if the temporal mask indicates that the value could have been caused by sources
     * @public
     */
    setAllowed( i, j, allowed ) {
      // this.allowedMask.set( i, j, allowed ? 1 : 0 );
    }

    /**
     * Determines whether the incoming wave has reached the cell.
     * @param {number} i - horizontal coordinate to check
     * @param {number} j - vertical coordinate to check
     * @returns {boolean}
     * @public
     */
    hasCellBeenVisited( i, j ) {
      return this.visitedMatrix.get( i, j ) === 1 && this.allowedMask.get( i, j ) === 1;
    }

    /**
     * Resets all of the wave values to 0.
     * @public
     */
    clear() {
      this.clearRight( 0 );
    }

    /**
     * Clear everything at and to the right of the specified column.
     * @param {number} column - integer index of the column to start clearing at.
     * @public
     */
    clearRight( column ) {
      for ( let i = column; i < this.width; i++ ) {
        for ( let j = 0; j < this.height; j++ ) {
          this.setCurrentValue( i, j, 0 );
          this.setLastValue( i, j, 0 );
          this.visitedMatrix.set( i, j, 0 );
          this.allowedMask.set( i, j, 1 ); // Initialize to 1 to support plane waves, which is never masked.
        }
      }
      this.changedEmitter.emit();
    }

    /**
     * Gets the values on the right hand side of the wave (before the damping region), for determining intensity.
     * @returns {number[]}
     * @public
     */
    getOutputColumn() {

      // This could be implemented in garbage-free from by require preallocating the entire intensitySample matrix and
      // using an index pointer like a circular array.  However, profiling in Mac Chrome did not show a significant
      // amount of time spent in this function, hence we use the simpler implementation.
      const column = [];
      for ( let j = this.dampY; j < this.height - this.dampY; j++ ) {
        const a = this.getCurrentValue( this.width - this.dampX - 1, j );
        const b = this.getCurrentValue( this.width - this.dampX - 2, j );
        const v = ( a + b ) / 2;
        column.push( v );
      }
      return column;
    }

    /**
     * Propagates the wave by one step.  This is a discrete algorithm and cannot use dt.
     * @public
     */
    step() {

      // Move to the next matrix
      this.currentMatrixIndex = ( this.currentMatrixIndex - 1 + this.matrices.length ) % this.matrices.length;

      const matrix0 = this.matrices[ ( this.currentMatrixIndex + 0 ) % this.matrices.length ];
      const matrix1 = this.matrices[ ( this.currentMatrixIndex + 1 ) % this.matrices.length ];
      const matrix2 = this.matrices[ ( this.currentMatrixIndex + 2 ) % this.matrices.length ];
      const width = matrix0.getRowDimension();
      const height = matrix0.getColumnDimension();

      // Main loop, doesn't update cells on the edges
      for ( let i = 1; i < width - 1; i++ ) {
        for ( let j = 1; j < height - 1; j++ ) {
          matrix0.set( i, j, i === 75 ? 10 : 0 );
          // const neighborSum = matrix1.get( i + 1, j ) +
          //                     matrix1.get( i - 1, j ) +
          //                     matrix1.get( i, j + 1 ) +
          //                     matrix1.get( i, j - 1 );
          // const m1ij = matrix1.get( i, j );
          // const value = m1ij * 2 - matrix2.get( i, j ) + WAVE_SPEED_SQUARED * ( neighborSum + m1ij * -4 );
          // matrix0.set( i, j, value );
          //
          // if ( Math.abs( value ) > LIGHT_VISIT_THRESHOLD ) {
          //   this.visitedMatrix.set( i, j, 1 );
          // }
        }
      }

      // Numerical computation of absorbing boundary conditions, under the assumption that the wave is perpendicular
      // to the edge, see https://www.phy.ornl.gov/csep/sw/node22.html.  This assumption does not hold everywhere, but
      // it is a helpful approximation.
      // Note there is a Fortran error on the top boundary and in the equations, replace:
      // u2 => matrix1.get
      // u1 => matrix2.get
      // cb => WAVE_SPEED

      // Left edge
      let i = 0;
      for ( let j = 0; j < height; j++ ) {
        const sum = matrix1.get( i, j ) + matrix1.get( i + 1, j ) - matrix2.get( i + 1, j ) + WAVE_SPEED *
                    ( matrix1.get( i + 1, j ) - matrix1.get( i, j ) + matrix2.get( i + 1, j ) - matrix2.get( i + 2, j ) );
        matrix0.set( i, j, sum );
      }

      // Right edge
      i = width - 1;
      for ( let j = 0; j < height; j++ ) {
        const sum = matrix1.get( i, j ) + matrix1.get( i - 1, j ) - matrix2.get( i - 1, j ) + WAVE_SPEED *
                    ( matrix1.get( i - 1, j ) - matrix1.get( i, j ) + matrix2.get( i - 1, j ) - matrix2.get( i - 2, j ) );
        matrix0.set( i, j, sum );
      }

      // Top edge
      let j = 0;
      for ( let i = 0; i < width; i++ ) {
        const sum = matrix1.get( i, j ) + matrix1.get( i, j + 1 ) - matrix2.get( i, j + 1 ) + WAVE_SPEED *
                    ( matrix1.get( i, j + 1 ) - matrix1.get( i, j ) + matrix2.get( i, j + 1 ) - matrix2.get( i, j + 2 ) );
        matrix0.set( i, j, sum );
      }

      // Bottom edge
      j = height - 1;
      for ( let i = 0; i < width; i++ ) {
        const sum = matrix1.get( i, j ) + matrix1.get( i, j - 1 ) - matrix2.get( i, j - 1 ) + WAVE_SPEED *
                    ( matrix1.get( i, j - 1 ) - matrix1.get( i, j ) + matrix2.get( i, j - 1 ) - matrix2.get( i, j - 2 ) );
        matrix0.set( i, j, sum );
      }
    }
  }

  // @public {number} - see docs above
  Lattice.WAVE_SPEED = WAVE_SPEED;

  return waveInterference.register( 'Lattice', Lattice );
} );<|endoftext|>


<commit_before>/**
 *
 * Nav.react.js
 *
 * This component renders the navigation bar
 *
 */

import React, { Component } from 'react';
import { Link } from 'react-router';
import { logout } from '../actions/AppActions';
import LoadingIndicator from './LoadingIndicator.react';

class Nav extends Component {
  render() {
    // Render either the Log In and register buttons, or the logout button
    // based on the current authentication state.
    const navButtons = this.props.loggedIn ? (
        {this.props.currentlySending ? <LoadingIndicator /> : "Logout"}
      ) : (

          <Link to="/register" className="btn btn--login btn--nav">Register</Link>
          <Link to="/login" className="btn btn--login btn--nav">Log In</Link>

      );

    return(

          <Link to="/" className="nav__logo-wrapper">
Login Flow
</Link>
          { navButtons }

    );
  }

  _logout() {
    this.props.dispatch(logout());
  }
}

Nav.propTypes = {
  loggedIn: React.PropTypes.bool.isRequired
}

export default Nav;
<commit_msg>Add non-breaking spaces to navigation bar<commit_after>/**
 *
 * Nav.react.js
 *
 * This component renders the navigation bar
 *
 */

import React, { Component } from 'react';
import { Link } from 'react-router';
import { logout } from '../actions/AppActions';
import LoadingIndicator from './LoadingIndicator.react';

class Nav extends Component {
  render() {
    // Render either the Log In and register buttons, or the logout button
    // based on the current authentication state.
    const navButtons = this.props.loggedIn ? (
        {this.props.currentlySending ? <LoadingIndicator /> : "Logout"}
      ) : (

          <Link to="/register" className="btn btn--login btn--nav">Register</Link>
          <Link to="/login" className="btn btn--login btn--nav">Log In</Link>

      );

    return(

          <Link to="/" className="nav__logo-wrapper">
Login Flow
</Link>
          { navButtons }

    );
  }

  _logout() {
    this.props.dispatch(logout());
  }
}

Nav.propTypes = {
  loggedIn: React.PropTypes.bool.isRequired
}

export default Nav;
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'foam.graphics',
  name:  'Circle',

  extendsModel: 'foam.graphics.CView',

  properties: [
    {
      name:  'border',
      label: 'Border Color',
      type:  'String',
      defaultValue: undefined
    },
    {
      name:  'borderWidth',
      type:  'int',
      defaultValue: 1
    },
    {
      name: 'r',
      label: 'Radius',
      type: 'int',
      defaultValue: 20
    },
    {
      name: 'startAngle',
      defaultValue: 0
    },
    {
      name: 'endAngle',
      defaultValue: Math.PI*2
    },
    {
      name: 'width',
      defaultValueFn: function() { return 2*this.r; }
    },
    {
      name: 'height',
      defaultValueFn: function() { return 2*this.r; }
    }
  ],

  methods: [
    function paintSelf() {
      var c = this.canvas;
      if ( ! c ) return;

      if ( ! this.r ) return;

      if ( this.color) {
        c.beginPath();
        c.moveTo(0,0);
        c.arc(0, 0, this.r, -this.endAngle, -this.startAngle, false);
        c.closePath();

        c.fillStyle = this.color;
        c.fill();
      }

      this.paintBorder();
    },
    function paintBorder() {
      if ( this.border ) {
        var c = this.canvas;
        c.lineWidth = this.borderWidth;

        c.beginPath();
        c.arc(0, 0, this.r+this.borderWidth/2-1, -this.endAngle, -this.startAngle, false);
        c.closePath();

        c.strokeStyle = this.border;
        c.stroke();
      }
    },
    function intersects(c) {
      var r = this.r + c.r;
      if ( this.border ) r += this.borderWidth;
      if ( c.border    ) r += c.borderWidth;
      return Movement.distance(this.x-c.x, this.y-c.y) < r;
    }
  ]
});
<commit_msg>Formatting.<commit_after>/**
 * @license
 * Copyright 2014 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'foam.graphics',
  name: 'Circle',
  extendsModel: 'foam.graphics.CView',

  properties: [
    {
      name:  'border',
      label: 'Border Color',
      type:  'String',
      defaultValue: undefined
    },
    {
      name:  'borderWidth',
      type:  'int',
      defaultValue: 1
    },
    {
      name: 'r',
      label: 'Radius',
      type: 'int',
      defaultValue: 20
    },
    {
      name: 'startAngle',
      defaultValue: 0
    },
    {
      name: 'endAngle',
      defaultValue: Math.PI*2
    },
    {
      name: 'width',
      defaultValueFn: function() { return 2*this.r; }
    },
    {
      name: 'height',
      defaultValueFn: function() { return 2*this.r; }
    }
  ],

  methods: [
    function paintSelf() {
      var c = this.canvas;
      if ( ! c ) return;

      if ( ! this.r ) return;

      if ( this.color) {
        c.beginPath();
        c.moveTo(0,0);
        c.arc(0, 0, this.r, -this.endAngle, -this.startAngle, false);
        c.closePath();

        c.fillStyle = this.color;
        c.fill();
      }

      this.paintBorder();
    },
    function paintBorder() {
      if ( this.border ) {
        var c = this.canvas;
        c.lineWidth = this.borderWidth;

        c.beginPath();
        c.arc(0, 0, this.r+this.borderWidth/2-1, -this.endAngle, -this.startAngle, false);
        c.closePath();

        c.strokeStyle = this.border;
        c.stroke();
      }
    },
    function intersects(c) {
      var r = this.r + c.r;
      if ( this.border ) r += this.borderWidth;
      if ( c.border    ) r += c.borderWidth;
      return Movement.distance(this.x-c.x, this.y-c.y) < r;
    }
  ]
});
<|endoftext|>


<commit_before>!function($, Foundation){
  'use strict';
  Foundation.Nest = {
    Feather: function(menu, type){
      menu.attr('role', 'menubar');
      type = type || 'zf';
      var items = menu.find('li').attr({'role': 'menuitem'}),
          subMenuClass = 'is-' + type + '-submenu',
          subItemClass = subMenuClass + '-item',
          hasSubClass = 'is-' + type + '-submenu-parent';

      items.each(function(){
        var $item = $(this),
            $sub = $item.children('ul');
        if($sub.length){
          $item.addClass('has-submenu ' + hasSubClass);
          $sub.addClass('submenu ' + subMenuClass).attr('data-submenu', '');
        }
        if($item.parent('[data-submenu]').length){
          $item.addClass('is-submenu-item ' + subItemClass);
        }
      });
      return;
    },
    Burn: function(menu, type){
      var items = menu.find('li').removeAttr('tabindex'),
          subMenuClass = 'is-' + type + '-submenu',
          subItemClass = subMenuClass + '-item';

      // menu.find('.is-active').removeClass('is-active');
      menu.find('*')
      // menu.find('.' + subMenuClass + ', .' + subItemClass + ', .is-active, .has-submenu, .is-submenu-item, .submenu, [data-submenu]')
          .removeClass(subMenuClass + ' ' + subItemClass + ' has-submenu is-submenu-item submenu is-active')
          .removeAttr('data-submenu').css('display', '');

      // console.log(      menu.find('.' + subMenuClass + ', .' + subItemClass + ', .has-submenu, .is-submenu-item, .submenu, [data-submenu]')
      //           .removeClass(subMenuClass + ' ' + subItemClass + ' has-submenu is-submenu-item submenu')
      //           .removeAttr('data-submenu'));
      // items.each(function(){
      //   var $item = $(this),
      //       $sub = $item.children('ul');
      //   if($item.parent('[data-submenu]').length){
      //     $item.removeClass('is-submenu-item ' + subItemClass);
      //   }
      //   if($sub.length){
      //     $item.removeClass('has-submenu');
      //     $sub.removeClass('submenu ' + subMenuClass).removeAttr('data-submenu');
      //   }
      // });
    }
  };
}(jQuery, window.Foundation);
<commit_msg>removes stateful submenu class on Nest.Burn<commit_after>!function($, Foundation){
  'use strict';
  Foundation.Nest = {
    Feather: function(menu, type){
      menu.attr('role', 'menubar');
      type = type || 'zf';
      var items = menu.find('li').attr({'role': 'menuitem'}),
          subMenuClass = 'is-' + type + '-submenu',
          subItemClass = subMenuClass + '-item',
          hasSubClass = 'is-' + type + '-submenu-parent';

      items.each(function(){
        var $item = $(this),
            $sub = $item.children('ul');
        if($sub.length){
          $item.addClass('has-submenu ' + hasSubClass);
          $sub.addClass('submenu ' + subMenuClass).attr('data-submenu', '');
        }
        if($item.parent('[data-submenu]').length){
          $item.addClass('is-submenu-item ' + subItemClass);
        }
      });
      return;
    },
    Burn: function(menu, type){
      var items = menu.find('li').removeAttr('tabindex'),
          subMenuClass = 'is-' + type + '-submenu',
          subItemClass = subMenuClass + '-item',
          hasSubClass = 'is-' + type + '-submenu-parent';

      // menu.find('.is-active').removeClass('is-active');
      menu.find('*')
      // menu.find('.' + subMenuClass + ', .' + subItemClass + ', .is-active, .has-submenu, .is-submenu-item, .submenu, [data-submenu]')
          .removeClass(subMenuClass + ' ' + subItemClass + ' ' + hasSubClass + ' has-submenu is-submenu-item submenu is-active')
          .removeAttr('data-submenu').css('display', '');

      // console.log(      menu.find('.' + subMenuClass + ', .' + subItemClass + ', .has-submenu, .is-submenu-item, .submenu, [data-submenu]')
      //           .removeClass(subMenuClass + ' ' + subItemClass + ' has-submenu is-submenu-item submenu')
      //           .removeAttr('data-submenu'));
      // items.each(function(){
      //   var $item = $(this),
      //       $sub = $item.children('ul');
      //   if($item.parent('[data-submenu]').length){
      //     $item.removeClass('is-submenu-item ' + subItemClass);
      //   }
      //   if($sub.length){
      //     $item.removeClass('has-submenu');
      //     $sub.removeClass('submenu ' + subMenuClass).removeAttr('data-submenu');
      //   }
      // });
    }
  };
}(jQuery, window.Foundation);
<|endoftext|>


<commit_before>/*!
 * jQuery Plugin Creator @VERSION
 * https://github.com/OOPMan/jquery-plugin-creator
 *
 * Copyright 2014 Adam Jorgensen
 * Released under the MIT license.
 * http://github.com/OOPMan/jquery-plugin-creator/LICENSE
 *
 */
/**
 * TODO: Add checks to prevent over-writing plugins
 * TODO: Add ability to not specify plugin name and have a randomly generated name assigned
 */
"use strict";
(function () {
    /**
     *
     * @param {jQuery} jQuery
     * @returns {{addPlugin: addPlugin}}
     */
    function pluginCreatorFactory(jQuery) {
        var $ = jQuery,
            noOp = jQuery.noop,
            scopeName = "jquery-plugincreator-",
            /**
             *
             * @param {function} childMember
             * @param {function} parentMember
             * @returns {function}
             */
            parentWrapper = function (childMember, parentMember) {
                var parentMember = (parentMember == childMember ? noOp : parentMember);
                function parentGenerator (self) {
                    var _super = parentMember;
                    if (_super._isParentGenerator) _super = _super(self);
                    else _super = function () {
                        return parentMember.apply(self, arguments);
                    };
                    return function () {
                        var args = $.makeArray(arguments);
                        args.push(_super);
                        return childMember.apply(self, args);
                    };
                }
                parentGenerator._isParentGenerator = true;
                return parentGenerator;
            },
            /**
             *
             * @param {Object} childMembers
             * @param {Object} parentMembers
             * @param {Object} [self]
             * @returns {Object}
             */
            wrapParents = function (childMembers, parentMembers, self) {
                var childMembers = $.extend(true, {}, childMembers),
                    parentMembers = $.extend(true, {}, parentMembers);
                for (var memberName in parentMembers) {
                    if (childMembers[memberName]) {
                        if (typeof childMembers[memberName] == "function" && !childMembers[memberName]._isParentGenerator) {
                            childMembers[memberName] = parentWrapper(childMembers[memberName], parentMembers[memberName]);
                            if (self) childMembers[memberName] = childMembers[memberName](self);
                        }
                    } else {
                        childMembers[memberName] = parentMembers[memberName];
                    }
                }
                return childMembers;
            },
            /**
             *
             * @type {{addPlugin: addPlugin}}
             */
            pluginCreator = {
                /**
                 * Adds a new stateful plugin to jQuery.
                 *
                 * A stateful plugin consists of the following optional components:
                 * - An object defining default properties to be associated with a plugin instance via its options member
                 * - An object defining member functions and values to be associated with a plugin instances prototype.
                 *
                 * @param {string} name
                 * @param {Object} [defaults]
                 * @param {Object} [members]
                 * @return {string}
                 */
                addPlugin: function (name, defaults, members) {
                    var defaults = $.extend(true, {}, defaults || {}),
                        baseMembers = {
                            /**
                             * Default init function. Does nothing.
                             */
                            init: noOp,
                            /**
                             *
                             * @param {Object} [options]
                             * @returns {Object}
                             */
                            update: function (options) {
                                $.extend(true, this.options, options || {});
                                return this.options;
                            },
                            /**
                             *
                             * @param {Object} members
                             * @returns {boolean}
                             */
                            extend: function (members) {
                                $.extend(true, this, wrapParents(members, this, this));
                            },
                            /**
                             * Destructor function, performs the following:
                             *
                             * 1: Triggers a scopeName + name + ".destroy" (E.g. "jquery-plugincreator-myPlugin.destroy") event on the jQuery context (I.e. $(this) ) for the plugin instance.
                             * 2: Removes the scopeName + name (E.g. "jquery-plugincreator-myPlugin") class from the element linked to the plugin instance.
                             * 3: Removes the scopeName + name (E.g. "jquery-plugincreator-myPlugin") data value from the element linked to the plugin instance.
                             * 4: Removes the "data-" + scopeName + name (E.g. "data-jquery-plugincreator-myPlugin") attribute from the element linked to the plugin instance.
                             */
                            destroy: function () {
                                this.context
                                    .trigger(scopeName + name + ".destroy")
                                    .removeClass(scopeName + name)
                                    .removeData(scopeName + name)
                                    .removeAttr("data-" + scopeName + name);
                            }
                        },
                        members = $.isArray(members) ? members : [wrapParents(members, wrapParents(baseMembers, baseMembers))];

                    /**
                     * This function is used to instantiate an instance of the plugin on a given element.
                     *
                     * @param {Object} element
                     * @param {Object} [options]
                     * @param {Array} [initArguments]
                     */
                    function instantiatePlugin(element, options, initArguments) {
                        var options = options || {},
                            prototype = $.extend(true, {}, members[0]);

                        function pluginConstructor () {
                            this.element = element;
                            this.context = $(element).addClass(scopeName + name);
                            this.options = $.extend(true, {}, defaults, options);
                            for (var memberName in prototype) {
                                if (typeof prototype[memberName] == "function" && prototype[memberName]._isParentGenerator) {
                                    prototype[memberName] = prototype[memberName](this);
                                }
                            }
                            $.extend(true, this, prototype);
                            this.init.apply(this, initArguments);
                        }
                        $.data(element, scopeName + name, new pluginConstructor());
                    }

                    /**
                     *
                     * @param {Object|string} options
                     * @returns {jQuery}
                     */
                    $.fn[name] = function(options) {
                        var args = $.makeArray(arguments);
                        return this.each(function() {
                            var instance = $.data(this, scopeName + name);
                            if (instance) {
                                if (typeof options == "string" && typeof instance[options] == "function") { // call a method on the instance
                                    instance[options].apply(instance, args.slice(1));
                                } else if (typeof instance.update == "function" && $.isPlainObject(options)) { // call update on the instance
                                    instance.update.apply(instance, args);
                                } else {
                                    throw options + " is not a member of " + this;
                                }
                            } else {
                                instantiatePlugin(this, options, args.slice(1));
                            }
                        })
                    };

                    /**
                     *
                     * @type {Object}
                     */
                    $.fn[name].defaults = defaults;

                    /**
                     *
                     * @param {Object} options
                     * @returns {Object}
                     */
                    $.fn[name].updateDefaultsWith = function(options) {
                        $.extend(true, defaults, options);
                        return defaults;
                    };

                    /**
                     *
                     * @param {Object} childMembers
                     * @returns {function}
                     */
                    $.fn[name].extendMembersWith = function(childMembers) {
                        members.unshift(wrapParents(childMembers, members[0]));
                    };

                    /**
                     * Clones a plugin added using jQuery PluginCreator.
                     *
                     * While the defaults and members are cloned completely and are independent, the constructor is shared.
                     *
                     * @param {string} name
                     * @param {string} newName
                     * @returns {string}
                     */
                    $.fn[name].cloneTo = function (newName) {
                        return pluginCreator.addPlugin(newName, defaults, members);
                    };

                    /**
                     * Extends a plugin added using jQuery PluginCreator.
                     *
                     * Defaults and members are cloned and extended from the original plugin while the extended constructor
                     * functions such that the constructor for the original plugin is called first, then new new
                     * constructor, if present.
                     *
                     * @param {string} name
                     * @param {string} newName
                     * @param {Object} [childMembers]
                     * @return {string}
                     */
                     $.fn[name].extendTo = function (newName, childMembers) {
                        $.fn[name].cloneTo(newName);
                        $.fn[newName].extendMembersWith(childMembers);
                     };

                    return name;
                }
        };
        return pluginCreator;
    }
    // Export jQuery Plugin Creator
    if (typeof module !== "undefined") { // CommonJS
        var factory = function (jQuery) {
            var pluginCreator = pluginCreatorFactory(jQuery);
            jQuery.extend(jQuery, pluginCreator);
            return pluginCreator;
        };
        if (global.document) {
            module.exports = factory(require("jquery"));
        } else if (global.window) {
            module.exports = factory(require("jquery")(global.window));
        } else {
            module.exports = factory;

        }
    } else if (typeof define === "function" && define.amd) { // AMD Loader
        define(["jquery"], function(jQuery) {
            var pluginCreator = pluginCreatorFactory(jQuery);
            jQuery.extend(jQuery, pluginCreator);
            return pluginCreator;
        });
    } else if (typeof jQuery !== "undefined")  { // Global Browser Environment
        jQuery.extend(jQuery, pluginCreatorFactory(jQuery));
    } else {
        throw "jQuery not defined";
    }
})();

<commit_msg>Added getInstance function to baseMembers. Added processPluginCall internal function. Use processPluginCall function to enable special plugin call behaviours relating to single instances and mapping.<commit_after>/*!
 * jQuery Plugin Creator @VERSION
 * https://github.com/OOPMan/jquery-plugin-creator
 *
 * Copyright 2014 Adam Jorgensen
 * Released under the MIT license.
 * http://github.com/OOPMan/jquery-plugin-creator/LICENSE
 *
 */
/**
 * TODO: Add checks to prevent over-writing plugins
 * TODO: Add ability to not specify plugin name and have a randomly generated name assigned
 */
"use strict";
(function () {
    /**
     *
     * @param {jQuery} jQuery
     * @returns {{addPlugin: addPlugin}}
     */
    function pluginCreatorFactory(jQuery) {
        var $ = jQuery,
            noOp = jQuery.noop,
            scopeName = "jquery-plugincreator-",
            /**
             *
             * @param {function} childMember
             * @param {function} parentMember
             * @returns {function}
             */
            parentWrapper = function (childMember, parentMember) {
                var parentMember = (parentMember == childMember ? noOp : parentMember);
                function parentGenerator (self) {
                    var _super = parentMember;
                    if (_super._isParentGenerator) _super = _super(self);
                    else _super = function () {
                        return parentMember.apply(self, arguments);
                    };
                    return function () {
                        var args = $.makeArray(arguments);
                        args.push(_super);
                        return childMember.apply(self, args);
                    };
                }
                parentGenerator._isParentGenerator = true;
                return parentGenerator;
            },
            /**
             *
             * @param {Object} childMembers
             * @param {Object} parentMembers
             * @param {Object} [self]
             * @returns {Object}
             */
            wrapParents = function (childMembers, parentMembers, self) {
                var childMembers = $.extend(true, {}, childMembers),
                    parentMembers = $.extend(true, {}, parentMembers);
                for (var memberName in parentMembers) {
                    if (childMembers[memberName]) {
                        if (typeof childMembers[memberName] == "function" && !childMembers[memberName]._isParentGenerator) {
                            childMembers[memberName] = parentWrapper(childMembers[memberName], parentMembers[memberName]);
                            if (self) childMembers[memberName] = childMembers[memberName](self);
                        }
                    } else {
                        childMembers[memberName] = parentMembers[memberName];
                    }
                }
                return childMembers;
            },
            /**
             *
             * @type {{addPlugin: addPlugin}}
             */
            pluginCreator = {
                /**
                 * Adds a new stateful plugin to jQuery.
                 *
                 * A stateful plugin consists of the following optional components:
                 * - An object defining default properties to be associated with a plugin instance via its options member
                 * - An object defining member functions and values to be associated with a plugin instances prototype.
                 *
                 * @param {string} name
                 * @param {Object} [defaults]
                 * @param {Object} [members]
                 * @return {string}
                 */
                addPlugin: function (name, defaults, members) {
                    var defaults = $.extend(true, {}, defaults || {}),
                        baseMembers = {
                            /**
                             * Default init function. Does nothing.
                             */
                            init: noOp,
                            /**
                             *
                             * @returns {Object}
                             */
                            getInstance: function () {
                                return this;
                            },
                            /**
                             *
                             * @param {Object} [options]
                             * @returns {Object}
                             */
                            update: function (options) {
                                $.extend(true, this.options, options || {});
                                return this.options;
                            },
                            /**
                             *
                             * @param {Object} members
                             * @returns {boolean}
                             */
                            extend: function (members) {
                                return $.extend(true, this, wrapParents(members, this, this));
                            },
                            /**
                             * Destructor function, performs the following:
                             *
                             * 1: Triggers a scopeName + name + ".destroy" (E.g. "jquery-plugincreator-myPlugin.destroy") event on the jQuery context (I.e. $(this) ) for the plugin instance.
                             * 2: Removes the scopeName + name (E.g. "jquery-plugincreator-myPlugin") class from the element linked to the plugin instance.
                             * 3: Removes the scopeName + name (E.g. "jquery-plugincreator-myPlugin") data value from the element linked to the plugin instance.
                             * 4: Removes the "data-" + scopeName + name (E.g. "data-jquery-plugincreator-myPlugin") attribute from the element linked to the plugin instance.
                             */
                            destroy: function () {
                                this.context
                                    .trigger(scopeName + name + ".destroy")
                                    .removeClass(scopeName + name)
                                    .removeData(scopeName + name)
                                    .removeAttr("data-" + scopeName + name);
                            }
                        },
                        members = $.isArray(members) ? members : [wrapParents(members, wrapParents(baseMembers, baseMembers))];

                    /**
                     * This function is used to instantiate an instance of the plugin on a given element.
                     *
                     * @param {Object} element
                     * @param {Object} [options]
                     * @param {Array} [initArguments]
                     */
                    function instantiatePlugin(element, options, initArguments) {
                        var options = options || {},
                            prototype = $.extend(true, {}, members[0]);

                        function pluginConstructor () {
                            this.element = element;
                            this.context = $(element).addClass(scopeName + name);
                            this.options = $.extend(true, {}, defaults, options);
                            for (var memberName in prototype) {
                                if (typeof prototype[memberName] == "function" && prototype[memberName]._isParentGenerator) {
                                    prototype[memberName] = prototype[memberName](this);
                                }
                            }
                            $.extend(true, this, prototype);
                            this.init.apply(this, initArguments);
                        }
                        $.data(element, scopeName + name, new pluginConstructor());
                    }

                    /**
                     * A function to handle the actual process of instantiating a plugin instance or calling a method on
                     * a given plugin instance.
                     *
                     * @param {Object} self
                     * @param {Object|String} options
                     * @param {Array} args
                     * @returns {*}
                     */
                    function processPluginCall(self, options, args) {
                        var instance = $.data(self, scopeName + name);
                        if (instance) {
                            if (typeof options == "string" && typeof instance[options] == "function") { // call a method on the instance
                                return instance[options].apply(instance, args.slice(1));
                            } else if (typeof instance.update == "function" && $.isPlainObject(options)) { // call update on the instance
                                return instance.update.apply(instance, args);
                            } else {
                                throw options + " is not a member of " + self;
                            }
                        } else {
                            return instantiatePlugin(self, options, args.slice(1));
                        }
                    }

                    /**
                     *
                     * @param {Object|string} options
                     * @returns {jQuery}
                     */
                    $.fn[name] = function(options) {
                        var args = $.makeArray(arguments);
                        if (this.length === 1) {
                            return processPluginCall(this[0], options, args);
                        } else {
                            if (options === "map") {
                                return this.map(function () {
                                    return processPluginCall(this, args[1], args.slice(2));
                                });
                            } else {
                                return this.each(function () {
                                    processPluginCall(this, options, args);
                                });
                            }
                        }
                    };

                    /**
                     *
                     * @type {Object}
                     */
                    $.fn[name].defaults = defaults;

                    /**
                     *
                     * @param {Object} options
                     * @returns {Object}
                     */
                    $.fn[name].updateDefaultsWith = function(options) {
                        $.extend(true, defaults, options);
                        return defaults;
                    };

                    /**
                     *
                     * @param {Object} childMembers
                     * @returns {function}
                     */
                    $.fn[name].extendMembersWith = function(childMembers) {
                        members.unshift(wrapParents(childMembers, members[0]));
                    };

                    /**
                     * Clones a plugin added using jQuery PluginCreator.
                     *
                     * While the defaults and members are cloned completely and are independent, the constructor is shared.
                     *
                     * @param {string} name
                     * @param {string} newName
                     * @returns {string}
                     */
                    $.fn[name].cloneTo = function (newName) {
                        return pluginCreator.addPlugin(newName, defaults, members);
                    };

                    /**
                     * Extends a plugin added using jQuery PluginCreator.
                     *
                     * Defaults and members are cloned and extended from the original plugin while the extended constructor
                     * functions such that the constructor for the original plugin is called first, then new new
                     * constructor, if present.
                     *
                     * @param {string} name
                     * @param {string} newName
                     * @param {Object} [childMembers]
                     * @return {string}
                     */
                     $.fn[name].extendTo = function (newName, childMembers) {
                        $.fn[name].cloneTo(newName);
                        $.fn[newName].extendMembersWith(childMembers);
                     };

                    return name;
                }
        };
        return pluginCreator;
    }
    // Export jQuery Plugin Creator
    if (typeof module !== "undefined") { // CommonJS
        var factory = function (jQuery) {
            var pluginCreator = pluginCreatorFactory(jQuery);
            jQuery.extend(jQuery, pluginCreator);
            return pluginCreator;
        };
        if (global.document) {
            module.exports = factory(require("jquery"));
        } else if (global.window) {
            module.exports = factory(require("jquery")(global.window));
        } else {
            module.exports = factory;

        }
    } else if (typeof define === "function" && define.amd) { // AMD Loader
        define(["jquery"], function(jQuery) {
            var pluginCreator = pluginCreatorFactory(jQuery);
            jQuery.extend(jQuery, pluginCreator);
            return pluginCreator;
        });
    } else if (typeof jQuery !== "undefined")  { // Global Browser Environment
        jQuery.extend(jQuery, pluginCreatorFactory(jQuery));
    } else {
        throw "jQuery not defined";
    }
})();

<|endoftext|>


<commit_before>/**
 * --------------------------------------------------------------------------
 * Bootstrap (v4.3.1): dom/eventHandler.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */

import {
  jQuery as $
} from '../util/index'
import Polyfill from './polyfill'

/**
 * ------------------------------------------------------------------------
 * Constants
 * ------------------------------------------------------------------------
 */

const namespaceRegex = /[^.]*(?=\..*)\.|.*/
const stripNameRegex = /\..*/
const keyEventRegex = /^key/
const stripUidRegex = /::\d+$/
const eventRegistry = {} // Events storage
let uidEvent = 1
const customEvents = {
  mouseenter: 'mouseover',
  mouseleave: 'mouseout'
}
const nativeEvents = [
  'click',
  'dblclick',
  'mouseup',
  'mousedown',
  'contextmenu',
  'mousewheel',
  'DOMMouseScroll',
  'mouseover',
  'mouseout',
  'mousemove',
  'selectstart',
  'selectend',
  'keydown',
  'keypress',
  'keyup',
  'orientationchange',
  'touchstart',
  'touchmove',
  'touchend',
  'touchcancel',
  'pointerdown',
  'pointermove',
  'pointerup',
  'pointerleave',
  'pointercancel',
  'gesturestart',
  'gesturechange',
  'gestureend',
  'focus',
  'blur',
  'change',
  'reset',
  'select',
  'submit',
  'focusin',
  'focusout',
  'load',
  'unload',
  'beforeunload',
  'resize',
  'move',
  'DOMContentLoaded',
  'readystatechange',
  'error',
  'abort',
  'scroll'
]

/**
 * ------------------------------------------------------------------------
 * Private methods
 * ------------------------------------------------------------------------
 */

function getUidEvent(element, uid) {
  return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++
}

function getEvent(element) {
  const uid = getUidEvent(element)
  element.uidEvent = uid

  return eventRegistry[uid] = eventRegistry[uid] || {}
}

function fixEvent(event, element) {
  // Add which for key events
  if (event.which === null && keyEventRegex.test(event.type)) {
    event.which = event.charCode === null ? event.keyCode : event.charCode
  }

  event.delegateTarget = element
}

function bootstrapHandler(element, fn) {
  return function handler(event) {
    fixEvent(event, element)
    if (handler.oneOff) {
      EventHandler.off(element, event.type, fn)
    }

    return fn.apply(element, [event])
  }
}

function bootstrapDelegationHandler(element, selector, fn) {
  return function handler(event) {
    const domElements = element.querySelectorAll(selector)

    for (let { target } = event; target && target !== this; target = target.parentNode) {
      for (let i = domElements.length; i--;) {
        if (domElements[i] === target) {
          fixEvent(event, target)

          if (handler.oneOff) {
            EventHandler.off(element, event.type, fn)
          }

          return fn.apply(target, [event])
        }
      }
    }

    // To please ESLint
    return null
  }
}

function findHandler(events, handler, delegationSelector = null) {
  const uidList = Object.keys(events)

  for (let i = 0; i < uidList.length; i++) {
    const uid = uidList[i]
    const event = events[uid]

    if (event.originalHandler === handler && event.delegationSelector === delegationSelector) {
      return events[uid]
    }
  }

  return null
}

function normalizeParams(originalTypeEvent, handler, delegationFn) {
  const delegation = typeof handler === 'string'
  const originalHandler = delegation ? delegationFn : handler

  // allow to get the native events from namespaced events ('click.bs.button' --> 'click')
  let typeEvent = originalTypeEvent.replace(stripNameRegex, '')
  const custom = customEvents[typeEvent]

  if (custom) {
    typeEvent = custom
  }

  const isNative = nativeEvents.indexOf(typeEvent) > -1

  if (!isNative) {
    typeEvent = originalTypeEvent
  }

  return [delegation, originalHandler, typeEvent]
}

function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) {
  if (typeof originalTypeEvent !== 'string' || !element) {
    return
  }

  if (!handler) {
    handler = delegationFn
    delegationFn = null
  }

  const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)
  const events = getEvent(element)
  const handlers = events[typeEvent] || (events[typeEvent] = {})
  const previousFn = findHandler(handlers, originalHandler, delegation ? handler : null)

  if (previousFn) {
    previousFn.oneOff = previousFn.oneOff && oneOff

    return
  }

  const uid = getUidEvent(originalHandler, originalTypeEvent.replace(namespaceRegex, ''))
  const fn = delegation ? bootstrapDelegationHandler(element, handler, delegationFn) : bootstrapHandler(element, handler)

  fn.delegationSelector = delegation ? handler : null
  fn.originalHandler = originalHandler
  fn.oneOff = oneOff
  fn.uidEvent = uid
  handlers[uid] = fn

  element.addEventListener(typeEvent, fn, delegation)
}

function removeHandler(element, events, typeEvent, handler, delegationSelector) {
  const fn = findHandler(events[typeEvent], handler, delegationSelector)

  if (fn === null) {
    return
  }

  element.removeEventListener(typeEvent, fn, Boolean(delegationSelector))
  delete events[typeEvent][fn.uidEvent]
}

function removeNamespacedHandlers(element, events, typeEvent, namespace) {
  const storeElementEvent = events[typeEvent] || {}

  Object.keys(storeElementEvent)
    .forEach(handlerKey => {
      if (handlerKey.indexOf(namespace) > -1) {
        const event = storeElementEvent[handlerKey]

        removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)
      }
    })
}

const EventHandler = {
  on(element, event, handler, delegationFn) {
    addHandler(element, event, handler, delegationFn, false)
  },

  one(element, event, handler, delegationFn) {
    addHandler(element, event, handler, delegationFn, true)
  },

  off(element, originalTypeEvent, handler, delegationFn) {
    if (typeof originalTypeEvent !== 'string' || !element) {
      return
    }

    const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)
    const inNamespace = typeEvent !== originalTypeEvent
    const events = getEvent(element)
    const isNamespace = originalTypeEvent.charAt(0) === '.'

    if (typeof originalHandler !== 'undefined') {
      // Simplest case: handler is passed, remove that listener ONLY.
      if (!events || !events[typeEvent]) {
        return
      }

      removeHandler(element, events, typeEvent, originalHandler, delegation ? handler : null)
      return
    }

    if (isNamespace) {
      Object.keys(events)
        .forEach(elementEvent => {
          removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.substr(1))
        })
    }

    const storeElementEvent = events[typeEvent] || {}
    Object.keys(storeElementEvent)
      .forEach(keyHandlers => {
        const handlerKey = keyHandlers.replace(stripUidRegex, '')

        if (!inNamespace || originalTypeEvent.indexOf(handlerKey) > -1) {
          const event = storeElementEvent[keyHandlers]

          removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)
        }
      })
  },

  trigger(element, event, args) {
    if (typeof event !== 'string' || !element) {
      return null
    }

    const typeEvent = event.replace(stripNameRegex, '')
    const inNamespace = event !== typeEvent
    const isNative = nativeEvents.indexOf(typeEvent) > -1

    let jQueryEvent
    let bubbles = true
    let nativeDispatch = true
    let defaultPrevented = false
    let evt = null

    if (inNamespace && typeof $ !== 'undefined') {
      jQueryEvent = $.Event(event, args)

      $(element).trigger(jQueryEvent)
      bubbles = !jQueryEvent.isPropagationStopped()
      nativeDispatch = !jQueryEvent.isImmediatePropagationStopped()
      defaultPrevented = jQueryEvent.isDefaultPrevented()
    }

    if (isNative) {
      evt = document.createEvent('HTMLEvents')
      evt.initEvent(typeEvent, bubbles, true)
    } else {
      evt = new CustomEvent(event, {
        bubbles,
        cancelable: true
      })
    }

    // merge custom informations in our event
    if (typeof args !== 'undefined') {
      Object.keys(args)
        .forEach(key => {
          Object.defineProperty(evt, key, {
            get() {
              return args[key]
            }
          })
        })
    }

    if (defaultPrevented) {
      evt.preventDefault()

      if (!Polyfill.defaultPreventedPreservedOnDispatch) {
        Object.defineProperty(evt, 'defaultPrevented', {
          get: () => true
        })
      }
    }

    if (nativeDispatch) {
      element.dispatchEvent(evt)
    }

    if (evt.defaultPrevented && typeof jQueryEvent !== 'undefined') {
      jQueryEvent.preventDefault()
    }

    return evt
  }
}

export default EventHandler
<commit_msg>Remove assignment from return.<commit_after>/**
 * --------------------------------------------------------------------------
 * Bootstrap (v4.3.1): dom/eventHandler.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */

import {
  jQuery as $
} from '../util/index'
import Polyfill from './polyfill'

/**
 * ------------------------------------------------------------------------
 * Constants
 * ------------------------------------------------------------------------
 */

const namespaceRegex = /[^.]*(?=\..*)\.|.*/
const stripNameRegex = /\..*/
const keyEventRegex = /^key/
const stripUidRegex = /::\d+$/
const eventRegistry = {} // Events storage
let uidEvent = 1
const customEvents = {
  mouseenter: 'mouseover',
  mouseleave: 'mouseout'
}
const nativeEvents = [
  'click',
  'dblclick',
  'mouseup',
  'mousedown',
  'contextmenu',
  'mousewheel',
  'DOMMouseScroll',
  'mouseover',
  'mouseout',
  'mousemove',
  'selectstart',
  'selectend',
  'keydown',
  'keypress',
  'keyup',
  'orientationchange',
  'touchstart',
  'touchmove',
  'touchend',
  'touchcancel',
  'pointerdown',
  'pointermove',
  'pointerup',
  'pointerleave',
  'pointercancel',
  'gesturestart',
  'gesturechange',
  'gestureend',
  'focus',
  'blur',
  'change',
  'reset',
  'select',
  'submit',
  'focusin',
  'focusout',
  'load',
  'unload',
  'beforeunload',
  'resize',
  'move',
  'DOMContentLoaded',
  'readystatechange',
  'error',
  'abort',
  'scroll'
]

/**
 * ------------------------------------------------------------------------
 * Private methods
 * ------------------------------------------------------------------------
 */

function getUidEvent(element, uid) {
  return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++
}

function getEvent(element) {
  const uid = getUidEvent(element)

  element.uidEvent = uid
  eventRegistry[uid] = eventRegistry[uid] || {}

  return eventRegistry[uid]
}

function fixEvent(event, element) {
  // Add which for key events
  if (event.which === null && keyEventRegex.test(event.type)) {
    event.which = event.charCode === null ? event.keyCode : event.charCode
  }

  event.delegateTarget = element
}

function bootstrapHandler(element, fn) {
  return function handler(event) {
    fixEvent(event, element)
    if (handler.oneOff) {
      EventHandler.off(element, event.type, fn)
    }

    return fn.apply(element, [event])
  }
}

function bootstrapDelegationHandler(element, selector, fn) {
  return function handler(event) {
    const domElements = element.querySelectorAll(selector)

    for (let { target } = event; target && target !== this; target = target.parentNode) {
      for (let i = domElements.length; i--;) {
        if (domElements[i] === target) {
          fixEvent(event, target)

          if (handler.oneOff) {
            EventHandler.off(element, event.type, fn)
          }

          return fn.apply(target, [event])
        }
      }
    }

    // To please ESLint
    return null
  }
}

function findHandler(events, handler, delegationSelector = null) {
  const uidList = Object.keys(events)

  for (let i = 0; i < uidList.length; i++) {
    const uid = uidList[i]
    const event = events[uid]

    if (event.originalHandler === handler && event.delegationSelector === delegationSelector) {
      return events[uid]
    }
  }

  return null
}

function normalizeParams(originalTypeEvent, handler, delegationFn) {
  const delegation = typeof handler === 'string'
  const originalHandler = delegation ? delegationFn : handler

  // allow to get the native events from namespaced events ('click.bs.button' --> 'click')
  let typeEvent = originalTypeEvent.replace(stripNameRegex, '')
  const custom = customEvents[typeEvent]

  if (custom) {
    typeEvent = custom
  }

  const isNative = nativeEvents.indexOf(typeEvent) > -1

  if (!isNative) {
    typeEvent = originalTypeEvent
  }

  return [delegation, originalHandler, typeEvent]
}

function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) {
  if (typeof originalTypeEvent !== 'string' || !element) {
    return
  }

  if (!handler) {
    handler = delegationFn
    delegationFn = null
  }

  const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)
  const events = getEvent(element)
  const handlers = events[typeEvent] || (events[typeEvent] = {})
  const previousFn = findHandler(handlers, originalHandler, delegation ? handler : null)

  if (previousFn) {
    previousFn.oneOff = previousFn.oneOff && oneOff

    return
  }

  const uid = getUidEvent(originalHandler, originalTypeEvent.replace(namespaceRegex, ''))
  const fn = delegation ? bootstrapDelegationHandler(element, handler, delegationFn) : bootstrapHandler(element, handler)

  fn.delegationSelector = delegation ? handler : null
  fn.originalHandler = originalHandler
  fn.oneOff = oneOff
  fn.uidEvent = uid
  handlers[uid] = fn

  element.addEventListener(typeEvent, fn, delegation)
}

function removeHandler(element, events, typeEvent, handler, delegationSelector) {
  const fn = findHandler(events[typeEvent], handler, delegationSelector)

  if (fn === null) {
    return
  }

  element.removeEventListener(typeEvent, fn, Boolean(delegationSelector))
  delete events[typeEvent][fn.uidEvent]
}

function removeNamespacedHandlers(element, events, typeEvent, namespace) {
  const storeElementEvent = events[typeEvent] || {}

  Object.keys(storeElementEvent)
    .forEach(handlerKey => {
      if (handlerKey.indexOf(namespace) > -1) {
        const event = storeElementEvent[handlerKey]

        removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)
      }
    })
}

const EventHandler = {
  on(element, event, handler, delegationFn) {
    addHandler(element, event, handler, delegationFn, false)
  },

  one(element, event, handler, delegationFn) {
    addHandler(element, event, handler, delegationFn, true)
  },

  off(element, originalTypeEvent, handler, delegationFn) {
    if (typeof originalTypeEvent !== 'string' || !element) {
      return
    }

    const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)
    const inNamespace = typeEvent !== originalTypeEvent
    const events = getEvent(element)
    const isNamespace = originalTypeEvent.charAt(0) === '.'

    if (typeof originalHandler !== 'undefined') {
      // Simplest case: handler is passed, remove that listener ONLY.
      if (!events || !events[typeEvent]) {
        return
      }

      removeHandler(element, events, typeEvent, originalHandler, delegation ? handler : null)
      return
    }

    if (isNamespace) {
      Object.keys(events)
        .forEach(elementEvent => {
          removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.substr(1))
        })
    }

    const storeElementEvent = events[typeEvent] || {}
    Object.keys(storeElementEvent)
      .forEach(keyHandlers => {
        const handlerKey = keyHandlers.replace(stripUidRegex, '')

        if (!inNamespace || originalTypeEvent.indexOf(handlerKey) > -1) {
          const event = storeElementEvent[keyHandlers]

          removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)
        }
      })
  },

  trigger(element, event, args) {
    if (typeof event !== 'string' || !element) {
      return null
    }

    const typeEvent = event.replace(stripNameRegex, '')
    const inNamespace = event !== typeEvent
    const isNative = nativeEvents.indexOf(typeEvent) > -1

    let jQueryEvent
    let bubbles = true
    let nativeDispatch = true
    let defaultPrevented = false
    let evt = null

    if (inNamespace && typeof $ !== 'undefined') {
      jQueryEvent = $.Event(event, args)

      $(element).trigger(jQueryEvent)
      bubbles = !jQueryEvent.isPropagationStopped()
      nativeDispatch = !jQueryEvent.isImmediatePropagationStopped()
      defaultPrevented = jQueryEvent.isDefaultPrevented()
    }

    if (isNative) {
      evt = document.createEvent('HTMLEvents')
      evt.initEvent(typeEvent, bubbles, true)
    } else {
      evt = new CustomEvent(event, {
        bubbles,
        cancelable: true
      })
    }

    // merge custom informations in our event
    if (typeof args !== 'undefined') {
      Object.keys(args)
        .forEach(key => {
          Object.defineProperty(evt, key, {
            get() {
              return args[key]
            }
          })
        })
    }

    if (defaultPrevented) {
      evt.preventDefault()

      if (!Polyfill.defaultPreventedPreservedOnDispatch) {
        Object.defineProperty(evt, 'defaultPrevented', {
          get: () => true
        })
      }
    }

    if (nativeDispatch) {
      element.dispatchEvent(evt)
    }

    if (evt.defaultPrevented && typeof jQueryEvent !== 'undefined') {
      jQueryEvent.preventDefault()
    }

    return evt
  }
}

export default EventHandler
<|endoftext|>