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

/**
 * An observable array of items.
 *
 * Because the array is observable, we must be careful about the possibility of concurrent-modification errors.
 * Any time we iterate over the array, we must iterate over a copy, because callback may be modifying the array.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( require => {
  'use strict';

  // modules
  const axon = require( 'AXON/axon' );
  const inherit = require( 'PHET_CORE/inherit' );
  const merge = require( 'PHET_CORE/merge' );
  const NumberProperty = require( 'AXON/NumberProperty' );
  const ObjectIO = require( 'TANDEM/types/ObjectIO' );
  const ObservableArrayIO = require( 'AXON/ObservableArrayIO' );
  const PhetioObject = require( 'TANDEM/PhetioObject' );
  const Tandem = require( 'TANDEM/Tandem' );

  // Factor out to reduce memory footprint, see https://github.com/phetsims/tandem/issues/71
  const DefaultObservableArrayIOType = ObservableArrayIO( ObjectIO );

  /**
   * @param {Object} [options]
   * @constructor
   */
  function ObservableArray( options ) {
    assert && assert( !Array.isArray( options ), 'ObservableArray cannot be initialized with values' );

    options = merge( {
      allowDuplicates: false, // are duplicate items allowed in the array?
      phetioType: DefaultObservableArrayIOType,
      tandem: Tandem.optional
    }, options );

    assert && assert( options.phetioType && options.phetioType.parameterType,
      'ObservableArray\'s phetioType should have a parameterType' );

    this.allowDuplicates = options.allowDuplicates; // @private

    this._options = options; // @private, for creating internal ObservableArray copies, see map/filter

    this._array = []; // @private internal, do not access directly
    this._addedListeners = []; // @private listeners called when an item is added
    this._removedListeners = []; // @private listeners called when an item is removed

    // @public (read-only) observe this, but don't set it
    this.lengthProperty = new NumberProperty( this._array.length, {
      numberType: 'Integer',
      tandem: options.tandem.createTandem( 'lengthProperty' ),
      phetioReadOnly: true
    } );

    PhetioObject.call( this, options );
  }

  axon.register( 'ObservableArray', ObservableArray );

  return inherit( PhetioObject, ObservableArray, {

    // @public
    dispose: function() {
      this.lengthProperty.dispose();
      PhetioObject.prototype.dispose.call( this );
    },

    /**
     * Restore the array back to its initial state
     * Note: if an item is in the current array and original array, it is removed and added back
     * This may or may not change in the future, see #4
     * @public
     */
    reset: function() {
      this.clear();
    },

    // @public
    get length() { return this._array.length; },

    /**
     * Adds a listener that will be notified when an item is added to the list.
     * @param listener function( item, observableArray )
     * @public
     */
    addItemAddedListener: function( listener ) {
      assert && assert( this._addedListeners.indexOf( listener ) === -1 ); // listener is not already registered
      this._addedListeners.push( listener );
    },

    /**
     * Removes a listener that was added via addItemAddedListener.
     * @param listener
     * @public
     */
    removeItemAddedListener: function( listener ) {
      const index = this._addedListeners.indexOf( listener );
      assert && assert( index !== -1, 'Listener is not found in item added listeners' ); // listener is registered
      this._addedListeners.splice( index, 1 );
    },

    /**
     * Adds a listener that will be notified when an item is removed from the list.
     * @param listener function( item, observableArray )
     * @public
     */
    addItemRemovedListener: function( listener ) {
      assert && assert( this._removedListeners.indexOf( listener ) === -1, 'Listener was already registered' ); // listener is not already registered
      this._removedListeners.push( listener );
    },

    /**
     * Removes a listener that was added via addItemRemovedListener.
     * @param listener
     * @public
     */
    removeItemRemovedListener: function( listener ) {
      const index = this._removedListeners.indexOf( listener );
      assert && assert( index !== -1, 'Listener is not found in item removed listeners' ); // listener is registered
      this._removedListeners.splice( index, 1 );
    },

    // @private called when an item is added.
    _fireItemAdded: function( item ) {
      const self = this;
      this.phetioStartEvent( 'itemAdded', function() {
        return self.phetioType.parameterType.toStateObject( item );
      } );

      //Signify that an item was added to the list
      const copy = this._addedListeners.slice( 0 ); // operate on a copy, firing could result in the listeners changing
      for ( let i = 0; i < copy.length; i++ ) {
        copy[ i ]( item, this );
      }

      this.phetioEndEvent();
    },

    // @private called when an item is removed.
    _fireItemRemoved: function( item ) {
      const self = this;
      this.phetioStartEvent( 'itemRemoved', function() {
        return self.phetioType.parameterType.toStateObject( item );
      } );

      //Signify that an item was removed from the list
      const copy = this._removedListeners.slice( 0 ); // operate on a copy, firing could result in the listeners changing
      for ( let i = 0; i < copy.length; i++ ) {
        copy[ i ]( item, this );
      }

      this.phetioEndEvent();
    },

    /**
     * Adds an item to the end of the array.
     * This is a convenience function, and is the same as push.
     * @param item
     * @public
     */
    add: function( item ) {
      this.push( item );
    },

    /**
     * Add items to the end of the array.
     * This is a convenience function, and is the same as push.
     * @param {Array} items
     * @public
     */
    addAll: function( items ) {
      for ( let i = 0; i < items.length; i++ ) {
        this.add( items[ i ] );
      }
    },

    /**
     * Removes the first occurrence of an item from the array.
     * If duplicates are allowed (see options.allowDuplicates) you may need to call this multiple
     * times to totally purge item from the array.
     * @param item
     * @public
     */
    remove: function( item ) {
      const index = this._array.indexOf( item );
      if ( index !== -1 ) {
        this._array.splice( index, 1 );
        this.lengthProperty.set( this._array.length );
        this._fireItemRemoved( item );
      }
    },

    /**
     * Removes the first occurrence of each item in the specified array.
     * @param {Array} array a list of items to remove
     * @see ObservableArray.remove
     * @public
     */
    removeAll: function( array ) {
      assert && assert( _.isArray( array ), 'array should be an array' );
      for ( let i = 0; i < array.length; i++ ) {
        this.remove( array[ i ] );
      }
    },

    /**
     * Pushes an item onto the end of the array.
     * @param item
     * @throws Error if duplicates are not allowed (see options.allowDuplicates) and item is already in the array
     * @public
     */
    push: function( item ) {
      if ( !this.allowDuplicates && this.contains( item ) ) {
        throw new Error( 'duplicates are not allowed' );
      }
      this._array.push( item );
      this.lengthProperty.set( this._array.length );
      this._fireItemAdded( item );
    },

    /**
     * Removes an item from the end of the array and returns it.
     * @returns {*}
     * @public
     */
    pop: function() {
      assert && assert( this.length > 0, 'cannot pop an empty ObservableArray' );

      const item = this._array.pop();
      if ( item !== undefined ) {
        this.lengthProperty.set( this._array.length );
        this._fireItemRemoved( item );
      }
      return item;
    },

    /**
     * Removes an item from the beginning of the array and returns it.
     * @returns {*}
     * @public
     */
    shift: function() {
      assert && assert( this.length > 0, 'cannot shift an empty ObservableArray' );

      const item = this._array.shift();
      if ( item !== undefined ) {
        this.lengthProperty.set( this._array.length );
        this._fireItemRemoved( item );
      }
      return item;
    },

    /**
     * Does the array contain the specified item?
     * @param item
     * @returns {boolean}
     * @public
     */
    contains: function( item ) {
      return this.indexOf( item ) !== -1;
    },

    /**
     * Gets an item at the specified index.
     * @param index
     * @returns {*} the item, or undefined if there is no item at the specified index
     * @public
     */
    get: function( index ) {
      return this._array[ index ];
    },

    /**
     * Gets the index of a specified item.
     * @param item
     * @returns {*} -1 if item is not in the array
     * @public
     */
    indexOf: function( item ) {
      return this._array.indexOf( item );
    },

    /**
     * Removes all items from the array.
     * @public
     */
    clear: function() {
      while ( this.length > 0 ) {
        this.pop();
      }
    },

    /**
     * Applies a callback function to each item in the array
     * @param callback function(item)
     * @public
     */
    forEach: function( callback ) {
      this._array.slice().forEach( callback ); // do this on a copy of the array, in case callbacks involve array modification
    },

    /**
     * Maps the values in this ObservableArray using the specified function, and returns a new ObservableArray for chaining.
     * @param {function(element:*):*} mapFunction - from Observable array element to something else
     * @returns {Object[]}
     * @public
     */
    map: function( mapFunction ) {
      return this._array.map( mapFunction );
    },

    /**
     * Filters the values in this ObservableArray using the predicate function, and returns a new ObservableArray for chaining.
     * @param {function(element):boolean} predicate
     * @returns {Object[]}
     * @public
     */
    filter: function( predicate ) {
      return this._array.filter( predicate );
    },

    /**
     * Count the number of items in this ObservableArray that satisfy the given Predicate.
     * @param {function} predicate
     * @returns {number}
     * @public
     */
    count: function( predicate ) {
      let count = 0;
      for ( let i = 0; i < this._array.length; i++ ) {
        if ( predicate( this._array[ i ] ) ) {
          count++;
        }
      }
      return count;
    },

    /**
     * Find the first element that matches the given predicate.
     * @param {function} predicate
     * @param {number} [fromIndex] - optional start index for the search
     */
    find: function( predicate, fromIndex ) {
      return _.find( this._array, predicate, fromIndex );
    },

    /**
     * Returns true if some element in this ObservableArray matches the predicate.
     * @param {function} predicate - the function to test elements
     * @returns {boolean}
     */
    some: function( predicate ) {
      return _.some( this._array, predicate );
    },

    /**
     * Starting with the initial value, combine values from this ObservableArray to come up with a composite result.
     * Same as foldLeft.  In underscore this is called _.reduce aka _.fold or _.inject
     * @param value
     * @param combiner
     * @returns {*}
     * @public
     */
    reduce: function( value, combiner ) {
      for ( let i = 0; i < this._array.length; i++ ) {
        value = combiner( value, this._array[ i ] );
      }
      return value;
    },

    /**
     * Return the underlying array
     * @returns {Array}
     * @public
     */
    getArray: function() {
      return this._array;
    },

    /**
     * Add/remove elements from any point in the array
     * @param {number} start - the index to start adding/removing items
     * @param {number} deleteCount - the number of items to delete
     * @param {Object} [item1] - an item to add
     * @param {Object} [item2] - an item to add
     * @param {Object} [etc] - varargs items to add etc.
     * @returns {Object[]} the items that were deleted.
     * @public
     */
    splice: function( start, deleteCount, item1, item2, etc ) {
      const deleted = this._array.splice.apply( this._array, arguments );
      const args = Array.prototype.slice.call( arguments );
      for ( let i = 0; i < deleted.length; i++ ) {
        this._fireItemRemoved( deleted[ i ] );
      }

      for ( let k = 2; k < args.length; k++ ) {
        this._fireItemAdded( args[ k ] );
      }
      return deleted;
    },

    /**
     * Changes the ordering of elements in the array.  Requires a Random source so that shuffles can be reproducible.
     * No items are added or removed, and this method does not send out any notifications.
     * @param {Random} random - from dot
     */
    shuffle: function( random ) {
      assert && assert( random, 'random must be supplied' );

      // preserve the same _array reference in case any clients got a reference to it with getArray()
      const shuffled = random.shuffle( this._array );
      this._array.length = 0;
      Array.prototype.push.apply( this._array, shuffled );
    }
  } );
} );<commit_msg>Add bounds checks for get and find, see https://github.com/phetsims/axon/issues/273<commit_after>// Copyright 2013-2019, University of Colorado Boulder

/**
 * An observable array of items.
 *
 * Because the array is observable, we must be careful about the possibility of concurrent-modification errors.
 * Any time we iterate over the array, we must iterate over a copy, because callback may be modifying the array.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( require => {
  'use strict';

  // modules
  const axon = require( 'AXON/axon' );
  const inherit = require( 'PHET_CORE/inherit' );
  const merge = require( 'PHET_CORE/merge' );
  const NumberProperty = require( 'AXON/NumberProperty' );
  const ObjectIO = require( 'TANDEM/types/ObjectIO' );
  const ObservableArrayIO = require( 'AXON/ObservableArrayIO' );
  const PhetioObject = require( 'TANDEM/PhetioObject' );
  const Tandem = require( 'TANDEM/Tandem' );

  // Factor out to reduce memory footprint, see https://github.com/phetsims/tandem/issues/71
  const DefaultObservableArrayIOType = ObservableArrayIO( ObjectIO );

  /**
   * @param {Object} [options]
   * @constructor
   */
  function ObservableArray( options ) {
    assert && assert( !Array.isArray( options ), 'ObservableArray cannot be initialized with values' );

    options = merge( {
      allowDuplicates: false, // are duplicate items allowed in the array?
      phetioType: DefaultObservableArrayIOType,
      tandem: Tandem.optional
    }, options );

    assert && assert( options.phetioType && options.phetioType.parameterType,
      'ObservableArray\'s phetioType should have a parameterType' );

    this.allowDuplicates = options.allowDuplicates; // @private

    this._options = options; // @private, for creating internal ObservableArray copies, see map/filter

    this._array = []; // @private internal, do not access directly
    this._addedListeners = []; // @private listeners called when an item is added
    this._removedListeners = []; // @private listeners called when an item is removed

    // @public (read-only) observe this, but don't set it
    this.lengthProperty = new NumberProperty( this._array.length, {
      numberType: 'Integer',
      tandem: options.tandem.createTandem( 'lengthProperty' ),
      phetioReadOnly: true
    } );

    PhetioObject.call( this, options );
  }

  axon.register( 'ObservableArray', ObservableArray );

  return inherit( PhetioObject, ObservableArray, {

    // @public
    dispose: function() {
      this.lengthProperty.dispose();
      PhetioObject.prototype.dispose.call( this );
    },

    /**
     * Restore the array back to its initial state
     * Note: if an item is in the current array and original array, it is removed and added back
     * This may or may not change in the future, see #4
     * @public
     */
    reset: function() {
      this.clear();
    },

    // @public
    get length() { return this._array.length; },

    /**
     * Adds a listener that will be notified when an item is added to the list.
     * @param listener function( item, observableArray )
     * @public
     */
    addItemAddedListener: function( listener ) {
      assert && assert( this._addedListeners.indexOf( listener ) === -1 ); // listener is not already registered
      this._addedListeners.push( listener );
    },

    /**
     * Removes a listener that was added via addItemAddedListener.
     * @param listener
     * @public
     */
    removeItemAddedListener: function( listener ) {
      const index = this._addedListeners.indexOf( listener );
      assert && assert( index !== -1, 'Listener is not found in item added listeners' ); // listener is registered
      this._addedListeners.splice( index, 1 );
    },

    /**
     * Adds a listener that will be notified when an item is removed from the list.
     * @param listener function( item, observableArray )
     * @public
     */
    addItemRemovedListener: function( listener ) {
      assert && assert( this._removedListeners.indexOf( listener ) === -1, 'Listener was already registered' ); // listener is not already registered
      this._removedListeners.push( listener );
    },

    /**
     * Removes a listener that was added via addItemRemovedListener.
     * @param listener
     * @public
     */
    removeItemRemovedListener: function( listener ) {
      const index = this._removedListeners.indexOf( listener );
      assert && assert( index !== -1, 'Listener is not found in item removed listeners' ); // listener is registered
      this._removedListeners.splice( index, 1 );
    },

    // @private called when an item is added.
    _fireItemAdded: function( item ) {
      const self = this;
      this.phetioStartEvent( 'itemAdded', function() {
        return self.phetioType.parameterType.toStateObject( item );
      } );

      //Signify that an item was added to the list
      const copy = this._addedListeners.slice( 0 ); // operate on a copy, firing could result in the listeners changing
      for ( let i = 0; i < copy.length; i++ ) {
        copy[ i ]( item, this );
      }

      this.phetioEndEvent();
    },

    // @private called when an item is removed.
    _fireItemRemoved: function( item ) {
      const self = this;
      this.phetioStartEvent( 'itemRemoved', function() {
        return self.phetioType.parameterType.toStateObject( item );
      } );

      //Signify that an item was removed from the list
      const copy = this._removedListeners.slice( 0 ); // operate on a copy, firing could result in the listeners changing
      for ( let i = 0; i < copy.length; i++ ) {
        copy[ i ]( item, this );
      }

      this.phetioEndEvent();
    },

    /**
     * Adds an item to the end of the array.
     * This is a convenience function, and is the same as push.
     * @param item
     * @public
     */
    add: function( item ) {
      this.push( item );
    },

    /**
     * Add items to the end of the array.
     * This is a convenience function, and is the same as push.
     * @param {Array} items
     * @public
     */
    addAll: function( items ) {
      for ( let i = 0; i < items.length; i++ ) {
        this.add( items[ i ] );
      }
    },

    /**
     * Removes the first occurrence of an item from the array.
     * If duplicates are allowed (see options.allowDuplicates) you may need to call this multiple
     * times to totally purge item from the array.
     * @param item
     * @public
     */
    remove: function( item ) {
      const index = this._array.indexOf( item );
      if ( index !== -1 ) {
        this._array.splice( index, 1 );
        this.lengthProperty.set( this._array.length );
        this._fireItemRemoved( item );
      }
    },

    /**
     * Removes the first occurrence of each item in the specified array.
     * @param {Array} array a list of items to remove
     * @see ObservableArray.remove
     * @public
     */
    removeAll: function( array ) {
      assert && assert( _.isArray( array ), 'array should be an array' );
      for ( let i = 0; i < array.length; i++ ) {
        this.remove( array[ i ] );
      }
    },

    /**
     * Pushes an item onto the end of the array.
     * @param item
     * @throws Error if duplicates are not allowed (see options.allowDuplicates) and item is already in the array
     * @public
     */
    push: function( item ) {
      if ( !this.allowDuplicates && this.contains( item ) ) {
        throw new Error( 'duplicates are not allowed' );
      }
      this._array.push( item );
      this.lengthProperty.set( this._array.length );
      this._fireItemAdded( item );
    },

    /**
     * Removes an item from the end of the array and returns it.
     * @returns {*}
     * @public
     */
    pop: function() {
      assert && assert( this.length > 0, 'cannot pop an empty ObservableArray' );

      const item = this._array.pop();
      if ( item !== undefined ) {
        this.lengthProperty.set( this._array.length );
        this._fireItemRemoved( item );
      }
      return item;
    },

    /**
     * Removes an item from the beginning of the array and returns it.
     * @returns {*}
     * @public
     */
    shift: function() {
      assert && assert( this.length > 0, 'cannot shift an empty ObservableArray' );

      const item = this._array.shift();
      if ( item !== undefined ) {
        this.lengthProperty.set( this._array.length );
        this._fireItemRemoved( item );
      }
      return item;
    },

    /**
     * Does the array contain the specified item?
     * @param item
     * @returns {boolean}
     * @public
     */
    contains: function( item ) {
      return this.indexOf( item ) !== -1;
    },

    /**
     * Gets an item at the specified index.
     * @param index
     * @returns {*} the item
     * @throws assertion Error if the index is out of bounds
     * @public
     */
    get: function( index ) {
      assert && assert( index >= 0 && index < this.length, 'index out of bounds' );
      return this._array[ index ];
    },

    /**
     * Gets the index of a specified item.
     * @param item
     * @returns {*} -1 if item is not in the array
     * @public
     */
    indexOf: function( item ) {
      return this._array.indexOf( item );
    },

    /**
     * Removes all items from the array.
     * @public
     */
    clear: function() {
      while ( this.length > 0 ) {
        this.pop();
      }
    },

    /**
     * Applies a callback function to each item in the array
     * @param callback function(item)
     * @public
     */
    forEach: function( callback ) {
      this._array.slice().forEach( callback ); // do this on a copy of the array, in case callbacks involve array modification
    },

    /**
     * Maps the values in this ObservableArray using the specified function, and returns a new ObservableArray for chaining.
     * @param {function(element:*):*} mapFunction - from Observable array element to something else
     * @returns {Object[]}
     * @public
     */
    map: function( mapFunction ) {
      return this._array.map( mapFunction );
    },

    /**
     * Filters the values in this ObservableArray using the predicate function, and returns a new ObservableArray for chaining.
     * @param {function(element):boolean} predicate
     * @returns {Object[]}
     * @public
     */
    filter: function( predicate ) {
      return this._array.filter( predicate );
    },

    /**
     * Count the number of items in this ObservableArray that satisfy the given Predicate.
     * @param {function} predicate
     * @returns {number}
     * @public
     */
    count: function( predicate ) {
      let count = 0;
      for ( let i = 0; i < this._array.length; i++ ) {
        if ( predicate( this._array[ i ] ) ) {
          count++;
        }
      }
      return count;
    },

    /**
     * Find the first element that matches the given predicate.
     * @param {function} predicate
     * @param {number} [fromIndex] - optional start index for the search
     * @returns {*} the first element that matches the given predicate, or undefined if no matching item can be found
     * @throws assertion Error if the fromIndex is specified and out of range
     * @public
     */
    find: function( predicate, fromIndex ) {
      assert && typeof fromIndex === 'number' && assert( fromIndex >= 0 && fromIndex < this.length, 'fromIndex out of bounds' );
      return _.find( this._array, predicate, fromIndex );
    },

    /**
     * Returns true if some element in this ObservableArray matches the predicate.
     * @param {function} predicate - the function to test elements
     * @returns {boolean}
     */
    some: function( predicate ) {
      return _.some( this._array, predicate );
    },

    /**
     * Starting with the initial value, combine values from this ObservableArray to come up with a composite result.
     * Same as foldLeft.  In underscore this is called _.reduce aka _.fold or _.inject
     * @param value
     * @param combiner
     * @returns {*}
     * @public
     */
    reduce: function( value, combiner ) {
      for ( let i = 0; i < this._array.length; i++ ) {
        value = combiner( value, this._array[ i ] );
      }
      return value;
    },

    /**
     * Return the underlying array
     * @returns {Array}
     * @public
     */
    getArray: function() {
      return this._array;
    },

    /**
     * Add/remove elements from any point in the array
     * @param {number} start - the index to start adding/removing items
     * @param {number} deleteCount - the number of items to delete
     * @param {Object} [item1] - an item to add
     * @param {Object} [item2] - an item to add
     * @param {Object} [etc] - varargs items to add etc.
     * @returns {Object[]} the items that were deleted.
     * @public
     */
    splice: function( start, deleteCount, item1, item2, etc ) {
      const deleted = this._array.splice.apply( this._array, arguments );
      const args = Array.prototype.slice.call( arguments );
      for ( let i = 0; i < deleted.length; i++ ) {
        this._fireItemRemoved( deleted[ i ] );
      }

      for ( let k = 2; k < args.length; k++ ) {
        this._fireItemAdded( args[ k ] );
      }
      return deleted;
    },

    /**
     * Changes the ordering of elements in the array.  Requires a Random source so that shuffles can be reproducible.
     * No items are added or removed, and this method does not send out any notifications.
     * @param {Random} random - from dot
     */
    shuffle: function( random ) {
      assert && assert( random, 'random must be supplied' );

      // preserve the same _array reference in case any clients got a reference to it with getArray()
      const shuffled = random.shuffle( this._array );
      this._array.length = 0;
      Array.prototype.push.apply( this._array, shuffled );
    }
  } );
} );<|endoftext|>


<commit_before><commit_msg>moved audio creation into core_repo_init()<commit_after><|endoftext|>


<commit_before>var webpack = require("webpack");
var path = require("path");
module.exports = {
	context: __dirname,
	entry: "./assets/application.js",
	output: {
		path: path.join(__dirname, "public/assets/js"),
		publicPath: "assets/js", // relative path for github pages
		filename: "application.js", // no hash in main.js because index.html is a static page
		chunkFilename: "[hash]/js/[id].js",
	},
	module: {
		loaders: [
			//{ test: /\.svg$/,    loader: "file-loader?prefix=font/" },
                  {
                    test: /\.js?$/,
                    exclude: /(node_modules|bower_components)/,
                    loader: 'babel',
                    query: {
                      presets: ['es2015'],
                      plugins: ['transform-runtime']
                    }
                  }
		],
	},
	resolve: {
          modulesDirectories: ["node_modules", "assets"]
	},
	plugins: [
          new webpack.ProvidePlugin({
            'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
          }),
          //new webpack.optimize.UglifyJsPlugin({
            //compress: {
              //warnings: false
            //}
          //})
          //new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 20 })
	],
	fakeUpdateVersion: 0
};
<commit_msg>add minification for production mode<commit_after>var webpack = require("webpack");
var path = require("path");
module.exports = {
	context: __dirname,
	entry: "./assets/application.js",
	output: {
		path: path.join(__dirname, "public/assets/js"),
		publicPath: "assets/js", // relative path for github pages
		filename: "application.js", // no hash in main.js because index.html is a static page
		chunkFilename: "[hash]/js/[id].js",
	},
	module: {
		loaders: [
			//{ test: /\.svg$/,    loader: "file-loader?prefix=font/" },
                  {
                    test: /\.js?$/,
                    exclude: /(node_modules|bower_components)/,
                    loader: 'babel',
                    query: {
                      presets: ['es2015'],
                      plugins: ['transform-runtime']
                    }
                  }
		],
	},
	resolve: {
          modulesDirectories: ["node_modules", "assets"]
	},
	plugins: [
          new webpack.ProvidePlugin({
            'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
          }),
          //new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 20 })
	].concat(process.env['NODE_ENV'] === 'production'
                 ? new webpack.optimize.UglifyJsPlugin({
                     compress: {
                       warnings: false
                     }
                   })
                 : []),
	fakeUpdateVersion: 0
};
<|endoftext|>


<commit_before>const expect = require("expect");
const request = require("supertest");

const { app } = require("../server");
const { Todo } = require("../models/todo");

// Drop collections before each test
beforeEach((done) => {
    Todo.remove({}).then(() => done());
});

describe("POST /todos", () => {
    it("should create a new todo", (done) => {
        const text = "Test todo";

        request(app)
            .post("/todos")
            .send({ text })
            .expect(200)
            .expect((res) => {
                expect(res.body.text).toBe(text);
            })
            .end((err, res) => {
                if (err) {
                    return done(err);
                }

                Todo.find().where("text").equals("Test todo")
                    .then((todo) => {
                        expect(todo[0].text).toBe(text);
                        done();
                    }).catch((e) => done(e));
            });
    });

    it("should not create a new todo", (done) => {
        request(app)
            .post("/todos")
            .send({})
            .expect(400)
            .expect((res) => {
                expect(res.body.name).toEqual("ValidationError");
            })
            .end((err, res) => {
                if (err) {
                    return done(err);
                }

                Todo.find({})
                    .then((todo) => {
                        expect(todo.length).toBe(0);
                        done();
                    }).catch((e) => done(e));
            });
    });
});<commit_msg>Test GET /todos route<commit_after>const expect = require("expect");
const request = require("supertest");

const { app } = require("../server");
const { Todo } = require("../models/todo");

// Seed data for the test
const todos = [{
    text: "First todo"
}, {
    text: "Second todo"
}];

// Drop data and add seed data
beforeEach((done) => {
    Todo.remove({}).then(() => {
        Todo.insertMany(todos);
    }).then(() => done());
});

describe("POST /todos", () => {
    it("should create a new todo", (done) => {
        const text = "Test todo";

        request(app)
            .post("/todos")
            .send({ text })
            .expect(200)
            .expect((res) => {
                expect(res.body.text).toBe(text);
            })
            .end((err, res) => {
                if (err) {
                    return done(err);
                }

                Todo.find().where("text").equals("Test todo")
                    .then((todo) => {
                        expect(todo[0].text).toBe(text);
                        done();
                    }).catch((e) => done(e));
            });
    });

    it("should create a 2 todos", (done) => {
        request(app)
            .post("/todos")
            .send({})
            .expect(400)
            .expect((res) => {
                expect(res.body.name).toEqual("ValidationError");
            })
            .end((err, res) => {
                if (err) {
                    return done(err);
                }

                Todo.find({})
                    .then((todo) => {
                        expect(todo.length).toBe(2);
                        done();
                    }).catch((e) => done(e));
            });
    });
});

describe("GET /todos", () => {
    it("should get all todos in the db", (done) => {
        request(app)
            .get("/todos")
            .expect(200)
            .expect((res) => {
                expect(res.body.todos.length).toBe(2);
            }).end(done);
    });
});<|endoftext|>


<commit_before>"use strict";
/**
 * AddThis widget directive, Re-renders addthis buttons as angular changes views in our app since
 * the add this buttons only load by default on page load and not when the DOM is updated. based on:
 * {@link http://stackoverflow.com/questions/15593039/angularjs-and-addthis-social-plugin}
 * @example
 *  Usage:
 *
 *
 *
 *
 *
 *
 *
 *
 *   Share on Google+
 *
 * @class  snAddthisToolbox
 * @module sn.utils
 * @author SOON_
 */
angular.module("sn.utils").directive("snAddthisToolbox", [
    "$document",
    "$timeout",
    /**
     * @constructor
     * @param {Service} $document
     * @param {Service} $timeout
     */
    function ($document, $timeout) {
        return {
            restrict: "A",
            replace: false,
            scope: {
                url: "@",
                title: "@",
                description: "@",
            },
            link: function ($scope, $element, attrs) {

                /**
                 * Number of times to check for stock addthis buttons
                 * @property checksLeft
                 * @type     {Number}
                 * @default  10
                 */
                var checksLeft = 10;

                /**
                 * Store $timeout instance in variable to clear later
                 * @property checksLeft
                 * @type     {$timeout}
                 */
                var timer;

                /**
                 * {@link http://support.addthis.com/customer/portal/articles/1337994-the-addthis_config-variable}
                 * @property addthis_config
                 * @type     {Object}
                 */
                var addthis_config = addthis_config || {};

                /**
                 * {@link http://support.addthis.com/customer/portal/articles/1337996-the-addthis_share-variable}
                 * @property addthis_share
                 * @type     {Object}
                 */
                var addthis_share = {
                    url: $scope.url,
                    title : $scope.title,
                    description : $scope.description
                }

                /**
                 * Removes the stock addthis buttons
                 * @method removeStockButtons
                 */
                var removeStockButtons = function removeStockButtons() {
                    if (checksLeft > 0){
                        checksLeft--;

                        var addthisEl = $document[0].getElementById("atstbx");

                        if (addthisEl){
                            addthisEl.parentNode.removeChild(addthisEl);
                        } else {
                            if (timer) {
                                $timeout.cancel(timer)
                            }
                            timer = $timeout(removeStockButtons, 500);
                        }
                    }
                }

                /**
                 * Initialise the addthis buttons on directive load
                 * @method init
                 */
                var init = function init(){

                    if (timer) {
                        $timeout.cancel(timer)
                    }

                    addthis.init();
                    addthis.toolbox($element[0], addthis_config, addthis_share);

                    timer = $timeout(removeStockButtons, 1000);
                }

                init();

            }
        }
    }
]);
<|endoftext|>


<commit_before>angular.module('uiKonpasa', ['ngSanitize', 'uiKonpasaTemplates']);

angular.module('uiKonpasa').run(function() {

});
angular.module('uiKonpasa')
.directive('uiKonpasaBubbleArea', function()
{
	return {
		scope: {
			bubbles : '='
		},
		controller : function($scope, $element, $attrs) {

			this.pushBubble = function(bubble)
			{
				$scope.bubbles.push(bubble);
				delete $scope.bubble;
			}

			this.bubblesPop = function() {
				$scope.bubbles.pop();
			}

			$element.addClass('bubbleArea');
		}
	}
})

.directive('uiKonpasaBubble', function() {
	return {
		templateUrl: 'templates/bubble.tpl.html',
		replace: true,
		require: "^uiKonpasaBubbleArea",
		scope: {
			body : "@",
			type : "@"
		},
		link: function(scope, element, attrs, ctrl)
		{
			element.modal('show');
		}
	}
});
angular.module('uiKonpasa')

.directive('uiKonpasaGallery', function() {
	return {

		scope: {
			'gallery-id': "@",
			size: "@"
		},
		replace: false,
		controller: function($scope, $element, $attrs)
		{
			var gallery = [];
			var active = 0;
			var lightbox = undefined;

			this.registerGalleryItem = function(galleryItem, src)
			{
				gallery.push({
					'galleryItem'  : galleryItem,
					'source' : src
				});

			}

			this.openLightbox = function(idx) {
				var itemActive;

				console.log(idx);
				if (idx < 0 ) {
					idx = gallery.length+idx;
				};

				if (idx >= gallery.length) {
					idx =  idx - gallery.length;
				};

				for (i in gallery) {
					if (idx == i) {
						itemActive = gallery[i];
					};
				}

				lightbox.source = itemActive.source;

				active = itemActive.galleryItem.position;
			}

			this.registerLightbox = function(lbox) {
				lightbox = lbox;
			}

			this.getColSize = function()
			{
				return $scope.size;
			}

			this.getGalleryLength = function()
			{
				return gallery.length;
			}

			this.getActive = function()
			{
				return active;
			}

			$element.addClass('ui-konpasa-gallery');
		}
	}
})

.directive('uiKonpasaGalleryItem', function() {
	return {
		templateUrl: 'templates/galleryitem.tpl.html',
		transclude: true,
		scope: {
			title : '@',
		},
		require: '^uiKonpasaGallery',
		link: function(scope, element, attrs, ctrl) {

			angular.element(element).addClass('lightbox-thumbnail');
			angular.element(element).addClass('col-md-'+ctrl.getColSize());
			angular.element(element).addClass('col-sm-'+ctrl.getColSize());
			angular.element(element).addClass('col-xs-'+ctrl.getColSize());

			scope.position = ctrl.getGalleryLength();

			element.children().each(function(index, value) {
				var x = angular.element(value).children()[0];
				ctrl.registerGalleryItem( scope, x.src );
			});

			scope.openLightbox = function() {
				ctrl.openLightbox(scope.position);
			}
		}
	}
})

.directive('uiKonpasaLightbox', function() {
	return {
		templateUrl: 'templates/lightbox.tpl.html',
		replace: true,
		require: '^uiKonpasaGallery',
		scope: {
			'gallery': "@",
			title: "@",
			source: "@",
		},
		link: function(scope, element, attrs, ctrl) {
			ctrl.registerLightbox(scope);

			scope.previousImg = function() {
				ctrl.openLightbox(ctrl.getActive()-1);
			}

			scope.nextImg = function() {
				ctrl.openLightbox(ctrl.getActive()+1);
			}
		}
	}
});
angular.module('uiKonpasa')
.directive('uiKonpasaSidemenu', function() {
	return {
		templateUrl: 'templates/sidemenu.tpl.html',
		transclude: true,
		restrict: 'E',
		controller: function($scope, $element, $attrs)
		{
			$element.slide()
		}
	}
});
angular.module('uiKonpasaTemplates', ['templates/bubble.tpl.html', 'templates/galleryitem.tpl.html', 'templates/lightbox.tpl.html', 'templates/sidemenu.tpl.html']);

angular.module("templates/bubble.tpl.html", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("templates/bubble.tpl.html",
    "
\n" +
    "
\n" +
    "
\n" +
    "			\n" +
    "
\n" +
    "
\n" +
    "
\n" +
    "
\n" +
    "
");
}]);

angular.module("templates/galleryitem.tpl.html", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("templates/galleryitem.tpl.html",
    "
\n" +
    "	\n" +
    "
");
}]);

angular.module("templates/lightbox.tpl.html", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("templates/lightbox.tpl.html",
    "
\n" +
    "
\n" +
    "
\n" +
    "
\n" +
    "
\n" +
    "					<button class=\"btn btn-default\" ng-click=\"previousImg()\">\n" +
    "						Prev\n" +
    "					</button>\n" +
    "					<button class=\"btn btn-primary\" data-dismiss=\"modal\">\n" +
    "						Close\n" +
    "					</button>\n" +
    "					<button class=\"btn btn-default\" ng-click=\"nextImg()\">\n" +
    "						Next\n" +
    "					</button>\n" +
    "
\n" +
    "
\n" +
    "
\n" +
    "				{{active}}\n" +
    "
\n" +
    "					\n" +
    "
\n" +
    "
\n" +
    "
\n" +
    "
\n" +
    "
");
}]);

angular.module("templates/sidemenu.tpl.html", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("templates/sidemenu.tpl.html",
    "
\n" +
    "	teste\n" +
    "	\n" +
    "
");
}]);
<commit_msg>Remove ngSanitizes<commit_after>angular.module('uiKonpasa', ['uiKonpasaTemplates']);

angular.module('uiKonpasa').run(function() {

});
angular.module('uiKonpasa')
.directive('uiKonpasaBubbleArea', function()
{
	return {
		scope: {
			bubbles : '='
		},
		controller : function($scope, $element, $attrs) {

			this.pushBubble = function(bubble)
			{
				$scope.bubbles.push(bubble);
				delete $scope.bubble;
			}

			this.bubblesPop = function() {
				$scope.bubbles.pop();
			}

			$element.addClass('bubbleArea');
		}
	}
})

.directive('uiKonpasaBubble', function() {
	return {
		templateUrl: 'templates/bubble.tpl.html',
		replace: true,
		require: "^uiKonpasaBubbleArea",
		scope: {
			body : "@",
			type : "@"
		},
		link: function(scope, element, attrs, ctrl)
		{
			element.modal('show');
		}
	}
});
angular.module('uiKonpasa')

.directive('uiKonpasaGallery', function() {
	return {

		scope: {
			'gallery-id': "@",
			size: "@"
		},
		replace: false,
		controller: function($scope, $element, $attrs)
		{
			var gallery = [];
			var active = 0;
			var lightbox = undefined;

			this.registerGalleryItem = function(galleryItem, src)
			{
				gallery.push({
					'galleryItem'  : galleryItem,
					'source' : src
				});

			}

			this.openLightbox = function(idx) {
				var itemActive;

				console.log(idx);
				if (idx < 0 ) {
					idx = gallery.length+idx;
				};

				if (idx >= gallery.length) {
					idx =  idx - gallery.length;
				};

				for (i in gallery) {
					if (idx == i) {
						itemActive = gallery[i];
					};
				}

				lightbox.source = itemActive.source;

				active = itemActive.galleryItem.position;
			}

			this.registerLightbox = function(lbox) {
				lightbox = lbox;
			}

			this.getColSize = function()
			{
				return $scope.size;
			}

			this.getGalleryLength = function()
			{
				return gallery.length;
			}

			this.getActive = function()
			{
				return active;
			}

			$element.addClass('ui-konpasa-gallery');
		}
	}
})

.directive('uiKonpasaGalleryItem', function() {
	return {
		templateUrl: 'templates/galleryitem.tpl.html',
		transclude: true,
		scope: {
			title : '@',
		},
		require: '^uiKonpasaGallery',
		link: function(scope, element, attrs, ctrl) {

			angular.element(element).addClass('lightbox-thumbnail');
			angular.element(element).addClass('col-md-'+ctrl.getColSize());
			angular.element(element).addClass('col-sm-'+ctrl.getColSize());
			angular.element(element).addClass('col-xs-'+ctrl.getColSize());

			scope.position = ctrl.getGalleryLength();

			element.children().each(function(index, value) {
				var x = angular.element(value).children()[0];
				ctrl.registerGalleryItem( scope, x.src );
			});

			scope.openLightbox = function() {
				ctrl.openLightbox(scope.position);
			}
		}
	}
})

.directive('uiKonpasaLightbox', function() {
	return {
		templateUrl: 'templates/lightbox.tpl.html',
		replace: true,
		require: '^uiKonpasaGallery',
		scope: {
			'gallery': "@",
			title: "@",
			source: "@",
		},
		link: function(scope, element, attrs, ctrl) {
			ctrl.registerLightbox(scope);

			scope.previousImg = function() {
				ctrl.openLightbox(ctrl.getActive()-1);
			}

			scope.nextImg = function() {
				ctrl.openLightbox(ctrl.getActive()+1);
			}
		}
	}
});
angular.module('uiKonpasa')
.directive('uiKonpasaSidemenu', function() {
	return {
		templateUrl: 'templates/sidemenu.tpl.html',
		transclude: true,
		restrict: 'E',
		controller: function($scope, $element, $attrs)
		{
			$element.slide()
		}
	}
});
angular.module('uiKonpasaTemplates', ['templates/bubble.tpl.html', 'templates/galleryitem.tpl.html', 'templates/lightbox.tpl.html', 'templates/sidemenu.tpl.html']);

angular.module("templates/bubble.tpl.html", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("templates/bubble.tpl.html",
    "
\n" +
    "
\n" +
    "
\n" +
    "			\n" +
    "
\n" +
    "
\n" +
    "
\n" +
    "
\n" +
    "
");
}]);

angular.module("templates/galleryitem.tpl.html", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("templates/galleryitem.tpl.html",
    "
\n" +
    "	\n" +
    "
");
}]);

angular.module("templates/lightbox.tpl.html", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("templates/lightbox.tpl.html",
    "
\n" +
    "
\n" +
    "
\n" +
    "
\n" +
    "
\n" +
    "					<button class=\"btn btn-default\" ng-click=\"previousImg()\">\n" +
    "						Prev\n" +
    "					</button>\n" +
    "					<button class=\"btn btn-primary\" data-dismiss=\"modal\">\n" +
    "						Close\n" +
    "					</button>\n" +
    "					<button class=\"btn btn-default\" ng-click=\"nextImg()\">\n" +
    "						Next\n" +
    "					</button>\n" +
    "
\n" +
    "
\n" +
    "
\n" +
    "				{{active}}\n" +
    "
\n" +
    "					\n" +
    "
\n" +
    "
\n" +
    "
\n" +
    "
\n" +
    "
");
}]);

angular.module("templates/sidemenu.tpl.html", []).run(["$templateCache", function($templateCache) {
  $templateCache.put("templates/sidemenu.tpl.html",
    "
\n" +
    "	teste\n" +
    "	\n" +
    "
");
}]);
<|endoftext|>


<commit_before>$.composition = {
  defaults: {
    sortableOptions:{
      axis:                 'y',
      placeholder:          'ui-state-highlight',
      forcePlaceholderSize: true,
      over:                 function(event, ui){rm=false},
      out:                  function(event, ui){rm=true},
      beforeStop:           function(event, ui){if(true==rm) ui.item.remove()}
    },
    draggableOptions:{
      helper: 'clone',
      cursor: 'move'
    }
  }
};

(function($)
{
  var design_element_count = 0;

  $.fn.extend({
    compositionCanvas: function(options)
    {
      var options = $.extend({}, $.composition.defaults, options);

      $(this).sortable($.extend({ receive: updateDesignElement, update: updatePositions }, options.sortableOptions));

      designElementify();

      $('#design_element_source_list > li').draggable($.extend({
        connectToSortable:'.composition-canvas',
        start: showContainer,
        stop: hideContainer
      }, options.draggableOptions));

      $('.composition-canvas:empty').addClass('open');
      design_element_count = $('.composition-canvas > li').length;

      return this;
    }
  });

  function updateDesignElement(event, ui)
  {
    if(event)
    {
      var canvas = $(event.target);
      var current = canvas.children('.ui-draggable');
      canvas.find('.design-element-head').hide();
      canvas.find('.design-element-include').show();
      canvas.find('.design-element-form').show();
      current.find('input,select,textarea').each(function(i, element){
        element = $(element);
        element.attr('id', element.attr('id').replace(/_x_/, '_'+design_element_count+'_'));
        element.attr('name', element.attr('name').replace(/\[x\]/, '['+design_element_count+']'));
      });
      design_element_count += 1;
    }

    designElementify();
    updatePositions();
  }

  function updatePositions()
  {
    $('.composition-canvas .design-element').each(function(i, element){
      $(element).find('.design-element-form').children('input[id$=position]').val(i);
    });
  }

  function designElementify()
  {
    $('.design-element-canvas').compositionDesignElement();
  }

  function showContainer(event, ui)
  {
    $('.composition-canvas').addClass('open');
  }

  function hideContainer(event, ui)
  {
    $('.composition-canvas').removeClass('open');
  }
})(jQuery);
<commit_msg>* bugfix. updating ids and names of design elements now considers all elements<commit_after>$.composition = {
  defaults: {
    sortableOptions:{
      axis:                 'y',
      placeholder:          'ui-state-highlight',
      forcePlaceholderSize: true,
      over:                 function(event, ui){rm=false},
      out:                  function(event, ui){rm=true},
      beforeStop:           function(event, ui){if(true==rm) ui.item.remove()}
    },
    draggableOptions:{
      helper: 'clone',
      cursor: 'move'
    }
  }
};

(function($)
{
  var design_element_count = 0;

  $.fn.extend({
    compositionCanvas: function(options)
    {
      var options = $.extend({}, $.composition.defaults, options);

      $(this).sortable($.extend({ receive: updateDesignElement, update: updatePositions }, options.sortableOptions));

      designElementify();

      $('#design_element_source_list > li').draggable($.extend({
        connectToSortable:'.composition-canvas',
        start: showContainer,
        stop: hideContainer
      }, options.draggableOptions));

      $('.composition-canvas:empty').addClass('open');
      design_element_count = $('.composition-canvas > li').length;

      return this;
    }
  });

  function updateDesignElement(event, ui)
  {
    if(event)
    {
      var canvas = $(event.target);
      var current = canvas.children('.ui-draggable');
      canvas.find('.design-element-head').hide();
      canvas.find('.design-element-include').show();
      canvas.find('.design-element-form').show();
      design_element_count += 1;
    }

    designElementify();
    updatePositions();
  }

  function updatePositions()
  {
    $('.composition-canvas .design-element').each(function(i, element){
      $(element).find('.design-element-form').children('input[id$=position]').val(i);
      $(element).find('input,select,textarea').each(function(j, _element){
        _element = $(_element);
        _element.attr('id', _element.attr('id').replace(/_x_/, '_'+i+'_'));
        _element.attr('name', _element.attr('name').replace(/\[x\]/, '['+i+']'));
      });
    });
  }

  function designElementify()
  {
    $('.design-element-canvas').compositionDesignElement();
  }

  function showContainer(event, ui)
  {
    $('.composition-canvas').addClass('open');
  }

  function hideContainer(event, ui)
  {
    $('.composition-canvas').removeClass('open');
  }
})(jQuery);
<|endoftext|>


<commit_before>import React from 'react';

import ReferenceWorksList from '/imports/ui/components/referenceWorks/ReferenceWorksList';

const CommenterReferenceWorks = React.createClass({

	propTypes: {
		commenter: React.PropTypes.object.isRequired,
	},

	render() {
		const { commenter } = this.props;
		if (!commenter) {
			return null;
		}

		return (

						Reference Works

				<ReferenceWorksList
					commenterId={commenter._id}
				/>

		);
	},

});

export default CommenterReferenceWorks;
<commit_msg>refactor CommenterReferenceWorks<commit_after>import React from 'react';

import ReferenceWorksList from '/imports/ui/components/referenceWorks/ReferenceWorksList';

const CommenterReferenceWorks = ({ commenter }) => (

				Reference Works

		<ReferenceWorksList
			commenterId={commenter._id}
		/>

);
CommenterReferenceWorks.propTypes = {
	commenter: React.PropTypes.shape({
		_id: React.PropTypes.string.isRequired,
	}).isRequired,
};

export default CommenterReferenceWorks;
<|endoftext|>


<commit_before>var xpath = require("xpath");
var dom = require('xmldom').DOMParser;
var fs = require("fs");
var path = require('path');
var argv = require('yargs').argv;
var boss_models = require("./models.js").models;
var fight_mapping;
var time_mapping

var modelname;
if(!argv.model) {
    modelname = "nighthold";
} else {
    modelname = argv.model;
}
fight_mapping = boss_models.filter((m) => {
    return m.name == modelname;
})[0].model;

time_mapping = boss_models.filter((m) => {
    return m.name == modelname;
})[0].timeModel;

var sum = 0;
Object.keys(time_mapping).forEach(function(time) {
    sum += time_mapping[time];
})
console.log("Time calibration: " + sum);
sum = 0;
Object.keys(fight_mapping).forEach(function(fight) {
    sum += fight_mapping[fight];
})
console.log("Fight calibration: " + sum);

sum = 0;

var damage = 0;
var dps = 0;
var main = 0;
var mainLabel = '';
var mastery = 0;
var haste = 0;
var crit = 0;
var sp = 0;
var simname;
var vers = 0;

var spec;
var type;

var results = fs.readdirSync(path.join(".","/results"));

results.forEach(function(result){
    var xml = fs.readFileSync(path.join('.','results', result), "utf-8");
    var doc = new dom().parseFromString(xml);
    var res = result.match("^(.*)_([0-9]*)_(.*).simc.xml");
    var addRes = result.match("^([^_]*)_(.*).simc.xml");

    var time = res[2];
    var fight = res[3];
    var weight;

    if(result.includes('.csv')) {
        return;
    }

    if(result.includes('adds')) {
        weight = fight_mapping[addRes[2]];
    } else {
        weight = fight_mapping[fight]*time_mapping[time];
    }
	damage += xpath.select1("//simulationcraft/summary/dmg/@total", doc).value*weight;
	dps += xpath.select1("//simulationcraft/summary/dmg/@dps", doc).value*weight;
    var name = xpath.select1("//simulationcraft/players/player[1]/@name", doc).value

	spec = xpath.select("//simulationcraft/players/player[@name='"+name+"']/specialization", doc)[0].firstChild.data;
	type = xpath.select1("//simulationcraft/players/player[@name='"+name+"']/class/@type", doc).value;
	if(name != "sim_test") {
		name = xpath.select1("//simulationcraft/summary/player_by_dps/player/@name", doc).value;
        simname = name;
	}
    if(!argv.noweights) {
        try {
            main += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Int']/@value", doc).value*weight;
			mainLabel = "Intellect";
		}
		catch(err) {
            try {
                main += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Str']/@value", doc).value*weight;
                mainLabel = "Strength";
            } catch(err) {
                main += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Agi']/@value", doc).value*weight;
                mainLabel = "Agility";
            }
		}
        try {
            mastery += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Mastery']/@value", doc).value*weight;
        }
        catch(e) {}
        haste += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Haste']/@value", doc).value*weight;
        crit += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Crit']/@value", doc).value*weight;
        vers += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Vers']/@value", doc).value*weight;
    }
    sum += weight;
});

if(process.argv[2] == "sim_test") {
    simname = "simtest";
}
console.log("Total calibration: " + sum);
console.log("Damage (DPS): " + damage + " (" + dps.toFixed(2) + ")");
if(!argv.noweights) {
    var smain = main/main;
    var smastery = mastery/main;
    var shaste = haste/main;
    var scrit = crit/main;
    var svers = vers/main;
    spec = spec.charAt(0).toUpperCase() + spec.slice(1);
    type = type.charAt(0).toUpperCase() + type.slice(1);
    console.log("( Pawn: v1: \"" + simname + "_" + modelname + "_selfsim\": Class=" + type + ", Spec=" + spec + ", " + mainLabel+"=" + smain + ", Versatility="+ svers.toFixed(4) + ", HasteRating=" + shaste.toFixed(4) + ", MasteryRating=" + smastery.toFixed(4) + ", CritRating=" + scrit.toFixed(4) + " )");
    if(process.argv[2] == "sim_test") {
        console.log("Expected Values: ( Pawn: v1: \"SL 4-piece\": Intellect=1, MasteryRating=1.36, HasteRating=1.2, CritRating=1.02, Versatility=0.9 )")
        console.log("Differences: int=" + (1-smain) + ", mastery=" + (1.36-smastery) + ", haste=" + (1.2-shaste) + ", crit=" + (1.02-scrit) + ", vers=" + (0.9-svers));
    }
}

console.log("Note: If your calibration values vary greatly from 1, please verify weights/that all sims were run.");
<commit_msg>Update the analyzer for ToS.<commit_after>var xpath = require("xpath");
var dom = require('xmldom').DOMParser;
var fs = require("fs");
var path = require('path');
var argv = require('yargs').argv;
var boss_models = require("./models.js").models;
var fight_mapping;
var time_mapping

var modelname;
if(!argv.model) {
    modelname = "tos";
} else {
    modelname = argv.model;
}
fight_mapping = boss_models.filter((m) => {
    return m.name == modelname;
})[0].model;

time_mapping = boss_models.filter((m) => {
    return m.name == modelname;
})[0].timeModel;

var sum = 0;
Object.keys(time_mapping).forEach(function(time) {
    sum += time_mapping[time];
})
console.log("Time calibration: " + sum);
sum = 0;
Object.keys(fight_mapping).forEach(function(fight) {
    sum += fight_mapping[fight];
})
console.log("Fight calibration: " + sum);

sum = 0;

var damage = 0;
var dps = 0;
var main = 0;
var mainLabel = '';
var mastery = 0;
var haste = 0;
var crit = 0;
var sp = 0;
var simname;
var vers = 0;

var spec;
var type;

var results = fs.readdirSync(path.join(".","/results"));

results.forEach(function(result){
    var xml = fs.readFileSync(path.join('.','results', result), "utf-8");
    var doc = new dom().parseFromString(xml);
    var res = result.match("^(.*)_([0-9]*)_(.*).simc.xml");
    var addRes = result.match("^([^_]*)_(.*).simc.xml");

    if(res == null) {
        return;
    }

    var time = res[2];
    var fight = res[3];
    var weight;

    if(result.includes('.csv')) {
        return;
    }

    if(result.includes('adds')) {
        weight = fight_mapping[addRes[2]];
    } else {
        weight = fight_mapping[fight]*time_mapping[time];
    }
	damage += xpath.select1("//simulationcraft/summary/dmg/@total", doc).value*weight;
	dps += xpath.select1("//simulationcraft/summary/dmg/@dps", doc).value*weight;
    var name = xpath.select1("//simulationcraft/players/player[1]/@name", doc).value

	spec = xpath.select("//simulationcraft/players/player[@name='"+name+"']/specialization", doc)[0].firstChild.data;
	type = xpath.select1("//simulationcraft/players/player[@name='"+name+"']/class/@type", doc).value;
	if(name != "sim_test") {
		name = xpath.select1("//simulationcraft/summary/player_by_dps/player/@name", doc).value;
        simname = name;
	}
    if(!argv.noweights) {
        try {
            main += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Int']/@value", doc).value*weight;
			mainLabel = "Intellect";
		}
		catch(err) {
            try {
                main += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Str']/@value", doc).value*weight;
                mainLabel = "Strength";
            } catch(err) {
                main += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Agi']/@value", doc).value*weight;
                mainLabel = "Agility";
            }
		}
        try {
            mastery += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Mastery']/@value", doc).value*weight;
        }
        catch(e) {}
        haste += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Haste']/@value", doc).value*weight;
        crit += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Crit']/@value", doc).value*weight;
        vers += xpath.select1("//simulationcraft/players/player[@name='"+name+"']/scale_factors/metric[@name='" + name + " Damage Per Second']/weights/stat[@name='Vers']/@value", doc).value*weight;
    }
    sum += weight;
});

if(process.argv[2] == "sim_test") {
    simname = "simtest";
}
console.log("Total calibration: " + sum);
console.log("Damage (DPS): " + damage + " (" + dps.toFixed(2) + ")");
if(!argv.noweights) {
    var smain = main/main;
    var smastery = mastery/main;
    var shaste = haste/main;
    var scrit = crit/main;
    var svers = vers/main;
    spec = spec.charAt(0).toUpperCase() + spec.slice(1);
    type = type.charAt(0).toUpperCase() + type.slice(1);
    console.log("( Pawn: v1: \"" + simname + "_" + modelname + "_selfsim\": Class=" + type + ", Spec=" + spec + ", " + mainLabel+"=" + smain + ", Versatility="+ svers.toFixed(4) + ", HasteRating=" + shaste.toFixed(4) + ", MasteryRating=" + smastery.toFixed(4) + ", CritRating=" + scrit.toFixed(4) + " )");
    if(process.argv[2] == "sim_test") {
        console.log("Expected Values: ( Pawn: v1: \"SL 4-piece\": Intellect=1, MasteryRating=1.36, HasteRating=1.2, CritRating=1.02, Versatility=0.9 )")
        console.log("Differences: int=" + (1-smain) + ", mastery=" + (1.36-smastery) + ", haste=" + (1.2-shaste) + ", crit=" + (1.02-scrit) + ", vers=" + (0.9-svers));
    }
}

console.log("Note: If your calibration values vary greatly from 1, please verify weights/that all sims were run.");
<|endoftext|>


<commit_before>module.exports.Client = Client;
module.exports.Server = Server;
module.exports.ServerRooms = rooms;

var rtcconfig = {
	'iceServers': [{
		'url': 'stun:stun.l.google.com:19302'
	}]
};

var dataChannelOptions = {
	ordered: false,
	maxRetransmitTime: 1000
};

var conns;

function Client(iosocket, config, room, signallingCallback) {
    var self = this;

    self.iosocket = iosocket;

    self.id = self.iosocket.id;
    self.status = 'init';
    self.peers = {};
    self.connectioncount = 0;

    self.rtcPeerConnections = {};
    conns = self.rtcPeerConnections;

    self.messageCallback = null;

    // Set Client Config
    if( config ) {
        self.mode     = config.hasOwnProperty('mode') ? config.mode : 'single';
        self.maxpeers = config.hasOwnProperty('maxpeers') ? config.mode : 1;
    } else {
        self.mode = 'single';
        self.maxpeers = 1;
    }

    // Check Client Config <mode:maxpeers>
    if( self.mode === 'single' ) {

        // Mode is Single
        if( self.maxpeers != 1 ) {

            // ERROR: maxpeers in single has to be 1
            console.log("ERROR: maxpeers in single has to be 1");
        }
    } else if( self.mode === 'multi' ) {

        // Mode is Multi
        if( self.maxpeers < 2 ) {

            // ERROR: maxpeers in multi has to be bigger than 1
            console.log("ERROR: maxpeers in multi has to be bigger than 1");
        }
    } else if( clientConfig.mode.indexOf(self.mode) === -1 ) {

        // ERROR: unknown mode specified
        console.log("ERROR: unknown mode specified");
    } else {

        // ERROR: unknown error
        console.log("ERROR: unknown error");
    }

    console.log("I AM: " + self.iosocket.id);

    self.iosocket.on('signalling_message', function(data) {
        console.log("new signal");

        // Start Setting up connection after at least one other Peer
        // is connected to the Server.
        // @IMPORTANT We receive only Signallin messages from OTHER Peers
        newConnection();

        if( data.type === 'hereandready' ) {

            // New User Connected to game <init>
            // @TODO handle multiple users in Multi mode
            console.log("New User connected.");
        } else {

            // Connection Controlling Signal
            var message = JSON.parse(data.message);

            if( message.sdp ) {

                // SDP
                gotSDP(message);
            } else if( message.candidate){

                // ICE CANDIDATE
                addCandidate(message);
            } else {

                // ERROR: Unknown Singal
                // @TODO handle Errors properly, use log?!
                console.log("Unknown Signal: " + JSON.stringify(message));
            }
        }
      });

    // Send initial Signal to Server
    self.iosocket.emit('signal', {"type":"hereandready", "mode":self.mode, "room":"testroom"});

    // @IMPORTANT @TODO refactor this. Chaotic Callback Hell...
    function gotSDP(message) {
        console.log("Got SDP Message.");

        if( self.mode === 'single' ) {

            // Only one Connection in Single Mode
            var connobj = self.rtcPeerConnections[0];

            // Set Remote Description
            connobj.connection.setRemoteDescription(
                // Add SDP information from Remote to init new SessionDescription
                new RTCSessionDescription(message.sdp), function() {
                    // Answer to any offer in this Session
                    if( connobj.connection.remoteDescription.type == 'offer' ) {

                        // Create Answer to offer
                        connobj.connection.createAnswer(
                            function(desc) {
                                connobj.connection.setLocalDescription(desc,
                                    function() {
                                        console.log("Sending local description");

                                        // Emit Answer
                                        self.iosocket.emit('signal', {
                                                               "type":"SDP",
                                                               "message": JSON.stringify({
                                                                              'sdp': connobj.connection.localDescription
                                                                          }),
                                                               "room":"testroom"});
                                    },
                                    function(errormsg) {

                                        // @TODO replace Error Handling
                                        console.log("" + errormsg);
                                    }
                                );
                            },
                            function(errormsg) {

                                // @TODO replace Error Handling
                                console.log("" + errormsg);
                            }
                        );
                    }
                }
            );
        }
    }

    function addCandidate(message) {
        console.log("Adding ICE-Candidate")

        if( self.mode === 'single' ) {

            // Only one Connection in Single Mode
            var connobj = self.rtcPeerConnections[0];

            // Add ICE Candidate from Signalling Message
            connobj.connection.addIceCandidate(new RTCIceCandidate(message.candidate));
        }
    }

    function newConnection() {
        if( self.mode === 'single' && self.connectioncount == 0) {

            // @TODO replace by better checks
            self.connectioncount++;

            // Mode is SINGLE
            console.log("Mode is single");

            // Create empty Object
            // @TODO replace by own connection Class
            self.rtcPeerConnections[0] = { "connection":null, "datachannel":null };
            var connobj = self.rtcPeerConnections[0];

            // Create new RTCPeerConnection und Datachannel
            connobj.connection = new webkitRTCPeerConnection(rtcconfig, null);
            connobj.datachannel = connobj.connection.createDataChannel('textMessages', dataChannelOptions);

            // DataChannel open
            connobj.datachannel.onopen = function() {
                // DataChannel state changed

                if( connobj.datachannel.readyState === 'open' ) {

                    // DataChannel now open

                    connobj.datachannel.onmessage = function(message) {

                        //Received new message <WebRTC>
                        console.log("Datachannel: " + message.data);

                        if( self.messageCallback ) {
                            // Callback for messages
                            self.messageCallback(message.data);
                        }
                    };
                }
            };

            // DataChannel received
            connobj.connection.ondatachannel = function(data) {

                connobj.datachannel = data.channel;

                connobj.datachannel.onmessage = function(message) {

                    //Received new message <WebRTC>
                    console.log("Datachannel: " + message.data);

                    if( self.messageCallback ) {
                        // Callback for messages
                        self.messageCallback(message.data);
                    }
                };
            };

      // Send ICE-Candidates to the Server
      connobj.connection.onicecandidate = function(data) {
        if( data.candidate ) {
          iosocket.emit('signal', {"type":"ice candidate", "message": JSON.stringify({'candidate': data.candidate}), "room":"testroom"});
          console.log("Candidate: " + data.candidate);
        }
      };

      // Negotiationneeded
      connobj.connection.onnegotiationneeded = function() {
          console.log("Negotiaton needed.");
          connobj.connection.createOffer(
            function(desc) {
                connobj.connection.setLocalDescription(desc, function() {
                    console.log("Sending local description");
                    self.iosocket.emit('signal', {"type":"SDP", "message": JSON.stringify({'sdp': connobj.connection.localDescription }), "room":"testroom"});
                }, function(errormsg) {
                    console.log("" + errormsg);
                }
                );
            }, function(errormsg) {
                console.log("" + errormsg);
            }
          );
      };
    } else {
      // Mode is Multi
      var rtcPeerConn = { "connection":null, "datachannel":null };

      rtcPeerConn.connection = new webkitRTCPeerConnection(rtcconfig, null);
      rtcPeerConn.datachannel = rtcPeerConn.connection.createDataChannel('textMessages', dataChannelOptions);
    }
  }
}

Client.prototype.setMessageCallback = function(newCallback) {
    this.messageCallback = newCallback;
}

Client.prototype.sendMessage = function(msgType, data) {
    this.rtcPeerConnections[0].datachannel.send("PING");
}

Client.prototype.changeRoom = function(newroom) {
    // @TODO WIP
}

Client.prototype.setSignallingCallback = function(newCallback) {
    // @TODO WIP
}

var rooms = {
    'testroom': {
        users: [],
        userCount: 0,
        state: 'idle'
    }
};

function Server(iosocket, config) {
    var self = this;

    // Socket from Socket.io
    self.iosocket = iosocket;

    self.iosocket.on('disconnected', function(data) {
        console.log("Lost Connection to a Socket...");
        // @TODO handle disconnects
    });

    self.iosocket.on('signal', function(req) {
        console.log(self.iosocket.id + " => " + JSON.stringify(req));

        if( req.type === "hereandready" ) {
            newPeer(req.room);
        }

        rooms[req.room].users.forEach(function(user) {
            // console.log("" + user.id + " : " + self.iosocket.id);
            if( user.id != self.iosocket.id ) {

                // Redirect Signal
                console.log("Send to: " + user.id);
                user.emit('signalling_message', {
                              type: req.type,
                              message: req.message
                          }
                );
            }
        });
    });

    function newPeer(room){

        // @TODO check for multiple same peers
        console.log("User joined room: " + room);

        self.iosocket.join(room);
        rooms[room].users.push(self.iosocket);
    };
}

Server.prototype.sendStatus = function(room) {
    // @TODO WIP ?
}
<commit_msg>#23 Added Multi-/Single-Peer room logic.<commit_after>module.exports.Client = Client;
module.exports.Server = Server;
module.exports.ServerRooms = rooms;

var rtcconfig = {
	'iceServers': [{
		'url': 'stun:stun.l.google.com:19302'
	}]
};

var dataChannelOptions = {
	ordered: false,
	maxRetransmitTime: 1000
};

var conns;

function Client(iosocket, config, room, signallingCallback) {
    var self = this;

    self.iosocket = iosocket;

    self.id = self.iosocket.id;
    self.status = 'init';
    self.peers = {};
    self.connectioncount = 0;

    self.rtcPeerConnections = {};
    conns = self.rtcPeerConnections;

    self.messageCallback = null;

    // Set Client Config
    if( config ) {
        self.mode     = config.hasOwnProperty('mode') ? config.mode : 'single';
        self.maxpeers = config.hasOwnProperty('maxpeers') ? config.mode : 1;
    } else {
        self.mode = 'single';
        self.maxpeers = 1;
    }

    // Check Client Config <mode:maxpeers>
    if( self.mode === 'single' ) {

        // Mode is Single
        if( self.maxpeers != 1 ) {

            // ERROR: maxpeers in single has to be 1
            console.log("ERROR: maxpeers in single has to be 1");
        }
    } else if( self.mode === 'multi' ) {

        // Mode is Multi
        if( self.maxpeers < 2 ) {

            // ERROR: maxpeers in multi has to be bigger than 1
            console.log("ERROR: maxpeers in multi has to be bigger than 1");
        }
    } else if( clientConfig.mode.indexOf(self.mode) === -1 ) {

        // ERROR: unknown mode specified
        console.log("ERROR: unknown mode specified");
    } else {

        // ERROR: unknown error
        console.log("ERROR: unknown error");
    }

    console.log("I AM: " + self.iosocket.id);

    self.iosocket.on('signalling_message', function(data) {
        console.log("new signal");

        // Start Setting up connection after at least one other Peer
        // is connected to the Server.
        // @IMPORTANT We receive only Signallin messages from OTHER Peers
        newConnection();

        if( data.type === 'hereandready' ) {

            // New User Connected to game <init>
            // @TODO handle multiple users in Multi mode
            console.log("New User connected.");
        } else {

            // Connection Controlling Signal
            var message = JSON.parse(data.message);

            if( message.sdp ) {

                // SDP
                gotSDP(message);
            } else if( message.candidate){

                // ICE CANDIDATE
                addCandidate(message);
            } else {

                // ERROR: Unknown Singal
                // @TODO handle Errors properly, use log?!
                console.log("Unknown Signal: " + JSON.stringify(message));
            }
        }
      });

    // Send initial Signal to Server
    self.iosocket.emit('signal', {"type":"hereandready", "mode":self.mode, "room":"testroom"});

    // @IMPORTANT @TODO refactor this. Chaotic Callback Hell...
    function gotSDP(message) {
        console.log("Got SDP Message.");

        if( self.mode === 'single' ) {

            // Only one Connection in Single Mode
            var connobj = self.rtcPeerConnections[0];

            // Set Remote Description
            connobj.connection.setRemoteDescription(
                // Add SDP information from Remote to init new SessionDescription
                new RTCSessionDescription(message.sdp), function() {
                    // Answer to any offer in this Session
                    if( connobj.connection.remoteDescription.type == 'offer' ) {

                        // Create Answer to offer
                        connobj.connection.createAnswer(
                            function(desc) {
                                connobj.connection.setLocalDescription(desc,
                                    function() {
                                        console.log("Sending local description");

                                        // Emit Answer
                                        self.iosocket.emit('signal', {
                                                               "type":"SDP",
                                                               "message": JSON.stringify({
                                                                              'sdp': connobj.connection.localDescription
                                                                          }),
                                                               "room":"testroom"});
                                    },
                                    function(errormsg) {

                                        // @TODO replace Error Handling
                                        console.log("" + errormsg);
                                    }
                                );
                            },
                            function(errormsg) {

                                // @TODO replace Error Handling
                                console.log("" + errormsg);
                            }
                        );
                    }
                }
            );
        }
    }

    function addCandidate(message) {
        console.log("Adding ICE-Candidate")

        if( self.mode === 'single' ) {

            // Only one Connection in Single Mode
            var connobj = self.rtcPeerConnections[0];

            // Add ICE Candidate from Signalling Message
            connobj.connection.addIceCandidate(new RTCIceCandidate(message.candidate));
        }
    }

    function newConnection() {
        if( self.mode === 'single' && self.connectioncount == 0) {

            // @TODO replace by better checks
            self.connectioncount++;

            // Mode is SINGLE
            console.log("Mode is single");

            // Create empty Object
            // @TODO replace by own connection Class
            self.rtcPeerConnections[0] = { "connection":null, "datachannel":null };
            var connobj = self.rtcPeerConnections[0];

            // Create new RTCPeerConnection und Datachannel
            connobj.connection = new webkitRTCPeerConnection(rtcconfig, null);
            connobj.datachannel = connobj.connection.createDataChannel('textMessages', dataChannelOptions);

            // DataChannel open
            connobj.datachannel.onopen = function() {
                // DataChannel state changed

                if( connobj.datachannel.readyState === 'open' ) {

                    // DataChannel now open

                    connobj.datachannel.onmessage = function(message) {

                        //Received new message <WebRTC>
                        console.log("Datachannel: " + message.data);

                        if( self.messageCallback ) {
                            // Callback for messages
                            self.messageCallback(message.data);
                        }
                    };
                }
            };

            // DataChannel received
            connobj.connection.ondatachannel = function(data) {

                connobj.datachannel = data.channel;

                connobj.datachannel.onmessage = function(message) {

                    //Received new message <WebRTC>
                    console.log("Datachannel: " + message.data);

                    if( self.messageCallback ) {
                        // Callback for messages
                        self.messageCallback(message.data);
                    }
                };
            };

      // Send ICE-Candidates to the Server
      connobj.connection.onicecandidate = function(data) {
        if( data.candidate ) {
          iosocket.emit('signal', {"type":"ice candidate", "message": JSON.stringify({'candidate': data.candidate}), "room":"testroom"});
          console.log("Candidate: " + data.candidate);
        }
      };

      // Negotiationneeded
      connobj.connection.onnegotiationneeded = function() {
          console.log("Negotiaton needed.");
          connobj.connection.createOffer(
            function(desc) {
                connobj.connection.setLocalDescription(desc, function() {
                    console.log("Sending local description");
                    self.iosocket.emit('signal', {"type":"SDP", "message": JSON.stringify({'sdp': connobj.connection.localDescription }), "room":"testroom"});
                }, function(errormsg) {
                    console.log("" + errormsg);
                }
                );
            }, function(errormsg) {
                console.log("" + errormsg);
            }
          );
      };
    } else {
      // Mode is Multi
      var rtcPeerConn = { "connection":null, "datachannel":null };

      rtcPeerConn.connection = new webkitRTCPeerConnection(rtcconfig, null);
      rtcPeerConn.datachannel = rtcPeerConn.connection.createDataChannel('textMessages', dataChannelOptions);
    }
  }
}

Client.prototype.setMessageCallback = function(newCallback) {
    this.messageCallback = newCallback;
}

Client.prototype.sendMessage = function(msgType, data) {
    this.rtcPeerConnections[0].datachannel.send("PING");
}

Client.prototype.changeRoom = function(newroom) {
    // @TODO WIP
}

Client.prototype.setSignallingCallback = function(newCallback) {
    // @TODO WIP
}

var rooms = {
    'testroom': {
        users: [],
        userCount: 0,
        state: 'idle'
    }
};

function Server(iosocket, config) {
    var self = this;

    // Socket from Socket.io
    self.iosocket = iosocket;

    self.iosocket.on('disconnected', function(data) {
        console.log("Lost Connection to a Socket...");
        // @TODO handle disconnects
    });

    self.iosocket.on('signal', function(req) {
        console.log(self.iosocket.id + " => " + JSON.stringify(req));

        if( req.type === "hereandready" ) {
            newPeer(req.room, req.mode);
        }

        if( req.hasOwnProperty('to') ) {

            // 'to' defines a direct target
            var from = self.iosocket.id;
            var to = req.to;
            var room = req.room;
            var type = req.type;
            var message = req.message;

            // @TODO replace direct Array indexing by Object-Named indexing for direct user access
            rooms[room].users.forEach(function(user) {
                if( user.id === to ) {

                    // Send Signalling Message to single Peer
                    user.emit('signalling_message', {
                        from: from,
                        to: to,
                        message: message
                    });

                    break;
                }
            });
        } else {

            // Singalling Message will be broadcastet.
            // Currently no direct use?!
            // @TODO implement delayed connect between all Peers in Room.
            // -> Wait for room to be full?!
            rooms[req.room].users.forEach(function(user) {
                if( user.id != self.iosocket.id ) {

                    // Redirect Signal to Peer
                    console.log("Send to: " + user.id);
                    user.emit('signalling_message', {
                        from: self.iosocket.id,
                        type: req.type,
                        message: req.message
                    });
                }
            });
        }
    });

    function newPeer(room, mode){
        if( rooms.hasOwnProperty(roomname) ) {

            // Room exists, check if Single-Peer
            if( mode == 'single' ) {

                // All OK, connect to Room
                joinRoom(room);
            } else {

                // Multiple Multi-Peers currently not supported in one room
                // @TODO ERROR
            }
        } else {

            // Room doesnt exist
            if( mode == 'single' ) {

                // Singe-Peers can't create Rooms
                // @TODO ERROR
            } else {

                // All OK, create Room
                createRoom(room);
                joinRoom(room);
            }
        }
    };

    function createRoom(roomname) {

        console.log("New Room created: " + roomname);

        rooms[roomname] = {
            users: [],
            userCount: 0,
            state: 'idle'
        };
    };

    function joinRoom(room) {

        console.log("[" + self.iosocket.id + "] joined Room " + room);

        self.iosocket.join(room);
        rooms[room].users.push(self.iosocket);
    }
}

Server.prototype.sendStatus = function(room) {
    // @TODO WIP ?
}
<|endoftext|>


<commit_before>var normalize_ja = require('lib/natural/normalizers/normalizer_ja').normalize_ja;
var converters = require('lib/natural/normalizers/normalizer_ja').converters;

describe('normalize_ja', function() {
  it('should fix badly formed hiragana', function() {
    expect(normalize_ja('う゛か゛き゛く゛は゜ひ゜ふ゜')).toEqual('ゔがぎぐぱぴぷ');
    expect(normalize_ja('うﾞかﾞきﾞくﾞはﾟひﾟふﾟ')).toEqual('ゔがぎぐぱぴぷ');
  });

  it('should fix badly formed fullwidth katakana', function() {
    expect(normalize_ja('ウ゛カ゛キ゛ク゛ハ゜ヒ゜フ゜')).toEqual('ヴガギグパピプ');
    expect(normalize_ja('ウﾞカﾞキﾞクﾞハﾟヒﾟフﾟ')).toEqual('ヴガギグパピプ');
  });

  it('should fix badly formed halfwidth katakana', function() {
    expect(normalize_ja('ｳ゛ｶ゛ｷ゛ｸ゛ﾊ゜ﾋ゜ﾌ゜')).toEqual('ヴガギグパピプ');
    expect(normalize_ja('ｳﾞｶﾞｷﾞｸﾞﾊﾟﾋﾟﾌﾟ')).toEqual('ヴガギグパピプ');
  });

  it('should transform halfwidth katakana to fullwidth', function() {
    expect(normalize_ja('ｶﾀｶﾅ')).toEqual('カタカナ');
  });

  it('should transform fullwidth alphanumerical characters to halfwidth', function() {
    expect(normalize_ja('ＡＢＣ１２３')).toEqual('ABC123');
  });

  it('should transform fullwidth spaces to halfwidth', function() {
    expect(normalize_ja('空　空　空')).toEqual('空 空 空');
  });

  it('should transform halfwidth punctuation signs to fullwidth', function() {
    // Taken from http://unicode.org/cldr/trac/browser/trunk/common/main/ja.xml
    expect(normalize_ja('‾ _＿ -－ ‐ — ― 〜 ・ ･ ,， 、､ ;； :： !！ ?？ .． ‥ … 。｡ ＇＼ ‘ ’ "＂ “ ” (（ )） [［ ]］ {｛ }｝ 〈 〉 《 》 「｢ 」｣ 『 』 【 】 〔 〕 ‖ § ¶ @＠ +＋ ^＾ $＄ *＊ /／ ＼\\ &＆ #＃ %％ ‰ † ‡ ′ ″ 〃 ※'))
      .toEqual('‾ ＿＿ ─－ ‐ — ― 〜 ・ ・ ，， 、、 ；； ：： ！！ ？？ ．． ‥ … 。。 ＇＼ ‘ ’ ＂＂ “ ” （（ ）） ［［ ］］ ｛｛ ｝｝ 〈 〉 《 》 ｢｢ ｣｣ 『 』 【 】 〔 〕 ‖ § ¶ ＠＠ ＋＋ ＾＾ ＄＄ ＊＊ ／／ ＼＼ ＆＆ ＃＃ ％％ ‰ † ‡ ′ ″ 〃 ※');
  });
});

var sample = 'ABC ＡＢＣ　123１２３.,-．，-あいうえおはばぱｶｷｸｹｺﾊﾊﾞﾊﾟカキクケコハバパ';

describe('converters', function() {
  it('should all be reversible', function() {
    var sample = '半角カナ（はんかくカナ）とは、JIS X 0208など片仮名を含む他の文字集合と同時に運用される場合におけるJIS X 0201の片仮名文字集合の通称である。漢字を含む文字集合で定義された片仮名に対して、半分の文字幅で表示されることが一般的であったためこのように呼ばれる。JIS X 0201で規定される8ビット符号化およびShift_JISにおいて0xA1-0xDFの範囲の1バイト文字がこれにあたる。また、Shift_JISやEUC-JPなどの符号化方式やUnicodeでも互換性の目的でこの文字集合をもっている。';
    expect(converters.halfwidthToFullwidth.alphabet(converters.fullwidthToHalfwidth.alphabet(sample))).toEqual(converters.halfwidthToFullwidth.alphabet(sample));
    expect(converters.fullwidthToHalfwidth.alphabet(converters.halfwidthToFullwidth.alphabet(sample))).toEqual(converters.fullwidthToHalfwidth.alphabet(sample));
    expect(converters.halfwidthToFullwidth.numbers(converters.fullwidthToHalfwidth.numbers(sample))).toEqual(converters.halfwidthToFullwidth.numbers(sample));
    expect(converters.fullwidthToHalfwidth.numbers(converters.halfwidthToFullwidth.numbers(sample))).toEqual(converters.fullwidthToHalfwidth.numbers(sample));
    expect(converters.halfwidthToFullwidth.punctuation(converters.fullwidthToHalfwidth.punctuation(sample))).toEqual(converters.halfwidthToFullwidth.punctuation(sample));
    expect(converters.fullwidthToHalfwidth.punctuation(converters.halfwidthToFullwidth.punctuation(sample))).toEqual(converters.fullwidthToHalfwidth.punctuation(sample));
    expect(converters.halfwidthToFullwidth.katakana(converters.fullwidthToHalfwidth.katakana(sample))).toEqual(converters.halfwidthToFullwidth.katakana(sample));
    expect(converters.fullwidthToHalfwidth.katakana(converters.halfwidthToFullwidth.katakana(sample))).toEqual(converters.fullwidthToHalfwidth.katakana(sample));
  });

  describe('.fullwidthToHalfwidth', function() {
    describe('.alphabet', function() {
      it('should transform fullwidth roman characters and space to halfwidth', function() {
        expect(converters.fullwidthToHalfwidth.alphabet(sample)).toEqual('ABC ABC 123１２３.,-．，-あいうえおはばぱｶｷｸｹｺﾊﾊﾞﾊﾟカキクケコハバパ');
      });
    });

    describe('.numbers', function() {
      it('should transform fullwidth numerical characters to halfwidth', function() {
        expect(converters.fullwidthToHalfwidth.numbers(sample)).toEqual('ABC ＡＢＣ　123123.,-．，-あいうえおはばぱｶｷｸｹｺﾊﾊﾞﾊﾟカキクケコハバパ');
      });
    });

    describe('.punctuation', function() {
      it('should transform fullwidth punctuation signs to halfwidth', function() {
        expect(converters.fullwidthToHalfwidth.punctuation(sample)).toEqual('ABC ＡＢＣ　123１２３.,-.,-あいうえおはばぱｶｷｸｹｺﾊﾊﾞﾊﾟカキクケコハバパ');
      });
    });

    describe('.katakana', function() {
      it('should transform fullwidth katakana to halfwidth', function() {
        expect(converters.fullwidthToHalfwidth.katakana(sample)).toEqual('ABC ＡＢＣ　123１２３.,-．，-あいうえおはばぱｶｷｸｹｺﾊﾊﾞﾊﾟｶｷｸｹｺﾊﾊﾞﾊﾟ');
      });
    });
  });

  describe('.halfwidthToFullwidth', function() {
    describe('.alphabet', function() {
      it('should transform halfwidth roman characters and space to fullwidth', function() {
        expect(converters.halfwidthToFullwidth.alphabet(sample)).toEqual('ＡＢＣ　ＡＢＣ　123１２３.,-．，-あいうえおはばぱｶｷｸｹｺﾊﾊﾞﾊﾟカキクケコハバパ');
      });
    });

    describe('.numbers', function() {
      it('should transform halfwidth numerical characters to fullwidth', function() {
        expect(converters.halfwidthToFullwidth.numbers(sample)).toEqual('ABC ＡＢＣ　１２３１２３.,-．，-あいうえおはばぱｶｷｸｹｺﾊﾊﾞﾊﾟカキクケコハバパ');
      });
    });

    describe('.punctuation', function() {
      it('should transform halfwidth punctuation signs to fullwidth', function() {
        expect(converters.halfwidthToFullwidth.punctuation(sample)).toEqual('ABC ＡＢＣ　123１２３．，─．，─あいうえおはばぱｶｷｸｹｺﾊﾊﾞﾊﾟカキクケコハバパ');
      });
    });

    describe('.katakana', function() {
      it('should transform halfwidth katakana to fullwidth', function() {
        expect(converters.halfwidthToFullwidth.katakana(sample)).toEqual('ABC ＡＢＣ　123１２３.,-．，-あいうえおはばぱカキクケコハバパカキクケコハバパ');
      });
    });
  });
});
<commit_msg>Add a test for fixing small `n`<commit_after>var normalize_ja = require('lib/natural/normalizers/normalizer_ja').normalize_ja;
var converters = require('lib/natural/normalizers/normalizer_ja').converters;

describe('normalize_ja', function() {
  it('should fix badly formed hiragana', function() {
    expect(normalize_ja('う゛か゛き゛く゛は゜ひ゜ふ゜')).toEqual('ゔがぎぐぱぴぷ');
    expect(normalize_ja('うﾞかﾞきﾞくﾞはﾟひﾟふﾟ')).toEqual('ゔがぎぐぱぴぷ');
    expect(normalize_ja('まっなか')).toEqual('まんなか');
  });

  it('should fix badly formed fullwidth katakana', function() {
    expect(normalize_ja('ウ゛カ゛キ゛ク゛ハ゜ヒ゜フ゜')).toEqual('ヴガギグパピプ');
    expect(normalize_ja('ウﾞカﾞキﾞクﾞハﾟヒﾟフﾟ')).toEqual('ヴガギグパピプ');
  });

  it('should fix badly formed halfwidth katakana', function() {
    expect(normalize_ja('ｳ゛ｶ゛ｷ゛ｸ゛ﾊ゜ﾋ゜ﾌ゜')).toEqual('ヴガギグパピプ');
    expect(normalize_ja('ｳﾞｶﾞｷﾞｸﾞﾊﾟﾋﾟﾌﾟ')).toEqual('ヴガギグパピプ');
  });

  it('should transform halfwidth katakana to fullwidth', function() {
    expect(normalize_ja('ｶﾀｶﾅ')).toEqual('カタカナ');
  });

  it('should transform fullwidth alphanumerical characters to halfwidth', function() {
    expect(normalize_ja('ＡＢＣ１２３')).toEqual('ABC123');
  });

  it('should transform fullwidth spaces to halfwidth', function() {
    expect(normalize_ja('空　空　空')).toEqual('空 空 空');
  });

  it('should transform halfwidth punctuation signs to fullwidth', function() {
    // Taken from http://unicode.org/cldr/trac/browser/trunk/common/main/ja.xml
    expect(normalize_ja('‾ _＿ -－ ‐ — ― 〜 ・ ･ ,， 、､ ;； :： !！ ?？ .． ‥ … 。｡ ＇＼ ‘ ’ "＂ “ ” (（ )） [［ ]］ {｛ }｝ 〈 〉 《 》 「｢ 」｣ 『 』 【 】 〔 〕 ‖ § ¶ @＠ +＋ ^＾ $＄ *＊ /／ ＼\\ &＆ #＃ %％ ‰ † ‡ ′ ″ 〃 ※'))
      .toEqual('‾ ＿＿ ─－ ‐ — ― 〜 ・ ・ ，， 、、 ；； ：： ！！ ？？ ．． ‥ … 。。 ＇＼ ‘ ’ ＂＂ “ ” （（ ）） ［［ ］］ ｛｛ ｝｝ 〈 〉 《 》 ｢｢ ｣｣ 『 』 【 】 〔 〕 ‖ § ¶ ＠＠ ＋＋ ＾＾ ＄＄ ＊＊ ／／ ＼＼ ＆＆ ＃＃ ％％ ‰ † ‡ ′ ″ 〃 ※');
  });
});

var sample = 'ABC ＡＢＣ　123１２３.,-．，-あいうえおはばぱｶｷｸｹｺﾊﾊﾞﾊﾟカキクケコハバパ';

describe('converters', function() {
  it('should all be reversible', function() {
    var sample = '半角カナ（はんかくカナ）とは、JIS X 0208など片仮名を含む他の文字集合と同時に運用される場合におけるJIS X 0201の片仮名文字集合の通称である。漢字を含む文字集合で定義された片仮名に対して、半分の文字幅で表示されることが一般的であったためこのように呼ばれる。JIS X 0201で規定される8ビット符号化およびShift_JISにおいて0xA1-0xDFの範囲の1バイト文字がこれにあたる。また、Shift_JISやEUC-JPなどの符号化方式やUnicodeでも互換性の目的でこの文字集合をもっている。';
    expect(converters.halfwidthToFullwidth.alphabet(converters.fullwidthToHalfwidth.alphabet(sample))).toEqual(converters.halfwidthToFullwidth.alphabet(sample));
    expect(converters.fullwidthToHalfwidth.alphabet(converters.halfwidthToFullwidth.alphabet(sample))).toEqual(converters.fullwidthToHalfwidth.alphabet(sample));
    expect(converters.halfwidthToFullwidth.numbers(converters.fullwidthToHalfwidth.numbers(sample))).toEqual(converters.halfwidthToFullwidth.numbers(sample));
    expect(converters.fullwidthToHalfwidth.numbers(converters.halfwidthToFullwidth.numbers(sample))).toEqual(converters.fullwidthToHalfwidth.numbers(sample));
    expect(converters.halfwidthToFullwidth.punctuation(converters.fullwidthToHalfwidth.punctuation(sample))).toEqual(converters.halfwidthToFullwidth.punctuation(sample));
    expect(converters.fullwidthToHalfwidth.punctuation(converters.halfwidthToFullwidth.punctuation(sample))).toEqual(converters.fullwidthToHalfwidth.punctuation(sample));
    expect(converters.halfwidthToFullwidth.katakana(converters.fullwidthToHalfwidth.katakana(sample))).toEqual(converters.halfwidthToFullwidth.katakana(sample));
    expect(converters.fullwidthToHalfwidth.katakana(converters.halfwidthToFullwidth.katakana(sample))).toEqual(converters.fullwidthToHalfwidth.katakana(sample));
  });

  describe('.fullwidthToHalfwidth', function() {
    describe('.alphabet', function() {
      it('should transform fullwidth roman characters and space to halfwidth', function() {
        expect(converters.fullwidthToHalfwidth.alphabet(sample)).toEqual('ABC ABC 123１２３.,-．，-あいうえおはばぱｶｷｸｹｺﾊﾊﾞﾊﾟカキクケコハバパ');
      });
    });

    describe('.numbers', function() {
      it('should transform fullwidth numerical characters to halfwidth', function() {
        expect(converters.fullwidthToHalfwidth.numbers(sample)).toEqual('ABC ＡＢＣ　123123.,-．，-あいうえおはばぱｶｷｸｹｺﾊﾊﾞﾊﾟカキクケコハバパ');
      });
    });

    describe('.punctuation', function() {
      it('should transform fullwidth punctuation signs to halfwidth', function() {
        expect(converters.fullwidthToHalfwidth.punctuation(sample)).toEqual('ABC ＡＢＣ　123１２３.,-.,-あいうえおはばぱｶｷｸｹｺﾊﾊﾞﾊﾟカキクケコハバパ');
      });
    });

    describe('.katakana', function() {
      it('should transform fullwidth katakana to halfwidth', function() {
        expect(converters.fullwidthToHalfwidth.katakana(sample)).toEqual('ABC ＡＢＣ　123１２３.,-．，-あいうえおはばぱｶｷｸｹｺﾊﾊﾞﾊﾟｶｷｸｹｺﾊﾊﾞﾊﾟ');
      });
    });
  });

  describe('.halfwidthToFullwidth', function() {
    describe('.alphabet', function() {
      it('should transform halfwidth roman characters and space to fullwidth', function() {
        expect(converters.halfwidthToFullwidth.alphabet(sample)).toEqual('ＡＢＣ　ＡＢＣ　123１２３.,-．，-あいうえおはばぱｶｷｸｹｺﾊﾊﾞﾊﾟカキクケコハバパ');
      });
    });

    describe('.numbers', function() {
      it('should transform halfwidth numerical characters to fullwidth', function() {
        expect(converters.halfwidthToFullwidth.numbers(sample)).toEqual('ABC ＡＢＣ　１２３１２３.,-．，-あいうえおはばぱｶｷｸｹｺﾊﾊﾞﾊﾟカキクケコハバパ');
      });
    });

    describe('.punctuation', function() {
      it('should transform halfwidth punctuation signs to fullwidth', function() {
        expect(converters.halfwidthToFullwidth.punctuation(sample)).toEqual('ABC ＡＢＣ　123１２３．，─．，─あいうえおはばぱｶｷｸｹｺﾊﾊﾞﾊﾟカキクケコハバパ');
      });
    });

    describe('.katakana', function() {
      it('should transform halfwidth katakana to fullwidth', function() {
        expect(converters.halfwidthToFullwidth.katakana(sample)).toEqual('ABC ＡＢＣ　123１２３.,-．，-あいうえおはばぱカキクケコハバパカキクケコハバパ');
      });
    });
  });
});
<|endoftext|>