<commit_before>(function (root, factory) {
    if (typeof define !== 'undefined' && define.amd) {
        define(['jquery'], function ($) {
            return factory($);
        });
    } else {
        return factory(root.jQuery);
    }
}(this, function ($) {

    var NOTE_ON = 0x90,
        NOTE_OFF = 0x80,
        notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'],
        defaults = {
            startNote : 48, // C3
            noteOnVelocity : 0x7f, // 128
            noteOffVelocity : 0x40, // 64,
            channel : 0
        },
        pluginName = 'midiKeys',
        pluginDataName = 'plugin_' + pluginName,
        keys = 'ZSXDCVGBHNJM' + 'Q2W3ER5T6Y7U' + 'I9O0P',
        createMIDIEventData = function (status, note, velocity) {
            var data = new Uint8Array(3);
            data[0] = status;
            data[1] = note;
            data[2] = velocity;
            return data;
        },
        getEventCode = function (event) {
            var character = String.fromCharCode(event.keyCode),
                index = keys.indexOf(character);
            if (index < 0) {
                return;
            }
            return this.options.startNote + index;
        },
        createEventCallback = function (status, velocityKey) {
            return function (event) {
                var eventCode = getEventCode.call(this, event);
                if (!eventCode) {
                    return;
                }
                var velocity = this.options[velocityKey];
                if ($.isFunction(velocity)) {
                    velocity = velocity();
                }
                var data = createMIDIEventData(status | this.options.channel, eventCode, velocity);
                var midiEvent = new MIDIEvent(event.timeStamp, data);
                this.$element.trigger('midiEvent', midiEvent);
            };
        };

    function MIDIEvent(timestamp, data) {
        this.timestamp = timestamp;
        this.data = data;
    }

    MIDIEvent.prototype = {
        getChannel : function () {
            return this.data[0] & 0x0f;
        },
        getVelocity : function () {
            return this.data[2];
        },
        getOctave : function () {
            return Math.floor(this.data[1] / 12) - 1;
        },
        getNoteLetter : function () {
            return notes[this.data[1] % 12];
        },
        getNote : function () {
            return this.getNoteLetter() + this.getOctave();
        },
        toString : function () {
            var parts = [
                'type=' + this.getType(),
                'channel=' + this.getChannel(),
                'note=' + this.getNote(),
                'velocity=' + this.getVelocity()
            ];
            return 'MIDIEvent[' + parts.join(',') + ']';
        },
        getType : function () {
            var statusWithoutChannel = this.data[0] & 0xf0;
            if (statusWithoutChannel === NOTE_ON) {
                return 'NOTE ON';
            }
            if (statusWithoutChannel === NOTE_OFF) {
                return 'NOTE OFF';
            }
        }
    };

    function MIDIKeys (element, options) {
        this.element = element;
        this.$element = $(element);

        this.options = $.extend({}, defaults, options);

        this._defaults = defaults;

        this.init();
    }

    MIDIKeys.prototype = {
        init : function () {
            var keydown = $.proxy(createEventCallback(NOTE_ON, 'noteOnVelocity'), this),
                keyup = $.proxy(createEventCallback(NOTE_OFF, 'noteOffVelocity'), this);
            this.$element.on('keydown', keydown);
            this.$element.on('keyup', keyup);
            this.destroy = function () {
                this.$element.off('keydown', keydown);
                this.$element.off('keyup', keyup);
                this.$element.removeData(pluginDataName);
            };
        }
    };

    return $.fn[pluginName] = function (options) {
        var args = Array.prototype.slice.call(arguments, 1);
        return this.each(function () {
            var pluginData = $.data(this, pluginDataName);
            if (pluginData && $.isFunction(pluginData[options])) {
                pluginData[options].apply(pluginData, args);
            } else if (!pluginData) {
                $.data(this, pluginDataName, new MIDIKeys(this, options));
            }
        });
    };

}));<commit_msg>updated velocity functions to pass the event timestamp to allow time based effects<commit_after>(function (root, factory) {
    if (typeof define !== 'undefined' && define.amd) {
        define(['jquery'], function ($) {
            return factory($);
        });
    } else {
        return factory(root.jQuery);
    }
}(this, function ($) {

    var NOTE_ON = 0x90,
        NOTE_OFF = 0x80,
        notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'],
        defaults = {
            startNote : 48, // C3
            noteOnVelocity : 0x7f, // 128
            noteOffVelocity : 0x40, // 64,
            channel : 0
        },
        pluginName = 'midiKeys',
        pluginDataName = 'plugin_' + pluginName,
        keys = 'ZSXDCVGBHNJM' + 'Q2W3ER5T6Y7U' + 'I9O0P',
        createMIDIEventData = function (status, note, velocity) {
            var data = new Uint8Array(3);
            data[0] = status;
            data[1] = note;
            data[2] = velocity;
            return data;
        },
        getEventCode = function (event) {
            var character = String.fromCharCode(event.keyCode),
                index = keys.indexOf(character);
            if (index < 0) {
                return;
            }
            return this.options.startNote + index;
        },
        createEventCallback = function (status, velocityKey) {
            return function (event) {
                var eventCode = getEventCode.call(this, event);
                if (!eventCode) {
                    return;
                }
                var velocity = this.options[velocityKey];
                if ($.isFunction(velocity)) {
                    velocity = velocity(event.timeStamp);
                }
                var data = createMIDIEventData(status | this.options.channel, eventCode, velocity);
                var midiEvent = new MIDIEvent(event.timeStamp, data);
                this.$element.trigger('midiEvent', midiEvent);
            };
        };

    function MIDIEvent(timestamp, data) {
        this.timestamp = timestamp;
        this.data = data;
    }

    MIDIEvent.prototype = {
        getChannel : function () {
            return this.data[0] & 0x0f;
        },
        getVelocity : function () {
            return this.data[2];
        },
        getOctave : function () {
            return Math.floor(this.data[1] / 12) - 1;
        },
        getNoteLetter : function () {
            return notes[this.data[1] % 12];
        },
        getNote : function () {
            return this.getNoteLetter() + this.getOctave();
        },
        toString : function () {
            var parts = [
                'type=' + this.getType(),
                'channel=' + this.getChannel(),
                'note=' + this.getNote(),
                'velocity=' + this.getVelocity()
            ];
            return 'MIDIEvent[' + parts.join(',') + ']';
        },
        getType : function () {
            var statusWithoutChannel = this.data[0] & 0xf0;
            if (statusWithoutChannel === NOTE_ON) {
                return 'NOTE ON';
            }
            if (statusWithoutChannel === NOTE_OFF) {
                return 'NOTE OFF';
            }
        }
    };

    function MIDIKeys (element, options) {
        this.element = element;
        this.$element = $(element);

        this.options = $.extend({}, defaults, options);

        this._defaults = defaults;

        this.init();
    }

    MIDIKeys.prototype = {
        init : function () {
            var keydown = $.proxy(createEventCallback(NOTE_ON, 'noteOnVelocity'), this),
                keyup = $.proxy(createEventCallback(NOTE_OFF, 'noteOffVelocity'), this);
            this.$element.on('keydown', keydown);
            this.$element.on('keyup', keyup);
            this.destroy = function () {
                this.$element.off('keydown', keydown);
                this.$element.off('keyup', keyup);
                this.$element.removeData(pluginDataName);
            };
        }
    };

    return $.fn[pluginName] = function (options) {
        var args = Array.prototype.slice.call(arguments, 1);
        return this.each(function () {
            var pluginData = $.data(this, pluginDataName);
            if (pluginData && $.isFunction(pluginData[options])) {
                pluginData[options].apply(pluginData, args);
            } else if (!pluginData) {
                $.data(this, pluginDataName, new MIDIKeys(this, options));
            }
        });
    };

}));<|endoftext|>


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

/**
 * A carousel UI component.
 * A set of N items is divided into M 'pages', based on how many items are visible in the carousel.
 * Pressing the next and previous buttons moves through the pages.
 * Movement through the pages is animated, so that items appear to scroll by.
 *
 * Note that Carousel performs layout directly on the items (Nodes) that it is provided.
 * If those Nodes appear in multiple places in the scenegraph, then it's the client's
 * responsibility to provide the Carousel with wrapped Nodes.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var CarouselButton = require( 'SUN/buttons/CarouselButton' );
  var Dimension2 = require( 'DOT/Dimension2' );
  var HSeparator = require( 'SUN/HSeparator' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Property = require( 'AXON/Property' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Shape = require( 'KITE/Shape' );
  var sun = require( 'SUN/sun' );
  var Util = require( 'DOT/Util' );
  var VSeparator = require( 'SUN/VSeparator' );

  // constants
  var DEFAULT_OPTIONS = {

    // container
    orientation: 'horizontal', // {string} 'horizontal'|'vertical'
    fill: 'white', // {Color|string} background color of the carousel
    stroke: 'black', // {Color|string|null} color used to stroke the border of the carousel
    lineWidth: 1, // {number} width of the border around the carousel
    cornerRadius: 4, // {number} radius applied to the carousel and next/previous buttons
    defaultPageNumber: 0, // {number} page that is initially visible

    // items
    itemsPerPage: 4, // {number} number of items per page, or how many items are visible at a time in the carousel
    spacing: 10, // {number} spacing between items, between items and optional separators, and between items and buttons
    margin: 10, // {number} margin between items and the edges of the carousel

    // next/previous buttons
    buttonColor: 'rgba( 200, 200, 200, 0.5 )', // {Color|string} base color for the buttons
    buttonStroke: undefined, // {Color|string|null|undefined} stroke around the buttons (null is no stroke, undefined derives color from buttonColor)
    buttonLineWidth: 1, // {number} lineWidth of borders on buttons
    arrowSize: new Dimension2( 20, 7 ), // {Dimension2} size of the arrow, in 'up' directions
    arrowStroke: 'black', // {Color|string} color used for the arrow icons
    arrowLineWidth: 3, // {number} line width used to stroke the arrow icons
    hideDisabledButtons: false, // {boolean} whether to hide buttons when they are disabled

    // for dilating pointer areas of next/previous buttons such that they do not overlap with Carousel content
    buttonTouchAreaXDilation: 0, // {number} horizontal touchArea dilation
    buttonTouchAreaYDilation: 0, // {number} vertical touchArea dilation
    buttonMouseAreaXDilation: 0, // {number} horiztonal mouseArea dilation
    buttonMouseAreaYDilation: 0, // {number} vertical mouseArea dialation

    // item separators
    separatorsVisible: false, // {boolean} whether to put separators between items
    separatorColor: 'rgb( 180, 180, 180 )', // {Color|string} color for separators
    separatorLineWidth: 0.5, // {number} lineWidth for separators

    // animation
    animationEnabled: true // {boolean} is animation enabled when scrolling between pages?
  };
  assert && Object.freeze( DEFAULT_OPTIONS );

  /**
   * @param {Node[]} items - items in the carousel
   * @param {Object} [options]
   * @constructor
   */
  function Carousel( items, options ) {

    // Override defaults with specified options
    options = _.extend( {}, DEFAULT_OPTIONS, options );

    // Validate options
    assert && assert( _.contains( [ 'horizontal', 'vertical' ], options.orientation ), 'invalid orientation=' + options.orientation );

    // To improve readability
    var isHorizontal = ( options.orientation === 'horizontal' );

    // Dimensions of largest item
    var maxItemWidth = _.max( items, function( item ) { return item.width; } ).width;
    var maxItemHeight = _.max( items, function( item ) { return item.height; } ).height;

    // This quantity is used make some other computations independent of orientation.
    var maxItemLength = isHorizontal ? maxItemWidth : maxItemHeight;

    // Options common to both buttons
    var buttonOptions = {
      xMargin: 5,
      yMargin: 5,
      cornerRadius: options.cornerRadius,
      baseColor: options.buttonColor,
      disabledBaseColor: options.fill, // same as carousel background
      stroke: options.buttonStroke,
      lineWidth: options.buttonLineWidth,
      minWidth: isHorizontal ? 0 : maxItemWidth + ( 2 * options.margin ), // fill the width of a vertical carousel
      minHeight: isHorizontal ? maxItemHeight + ( 2 * options.margin ) : 0, // fill the height of a horizontal carousel
      arrowSize: options.arrowSize,
      arrowStroke: options.arrowStroke,
      arrowLineWidth: options.arrowLineWidth,
      touchAreaXDilation: options.buttonTouchAreaXDilation,
      touchAreaYDilation: options.buttonTouchAreaYDilation,
      mouseAreaXDilation: options.buttonMouseAreaXDilation,
      mouseAreaYDilation: options.buttonMouseAreaYDilation
    };

    // Next/previous buttons
    var nextButton = new CarouselButton( _.extend( { arrowDirection: isHorizontal ? 'right' : 'down' }, buttonOptions ) );
    var previousButton = new CarouselButton( _.extend( { arrowDirection: isHorizontal ? 'left' : 'up' }, buttonOptions ) );

    // Computations related to layout of items
    var numberOfSeparators = ( options.separatorsVisible ) ? ( items.length - 1 ) : 0;
    var scrollingLength = ( items.length * ( maxItemLength + options.spacing ) + ( numberOfSeparators * options.spacing) + options.spacing );
    var scrollingWidth = isHorizontal ? scrollingLength : ( maxItemWidth + 2 * options.margin );
    var scrollingHeight = isHorizontal ? ( maxItemHeight + 2 * options.margin ) : scrollingLength;
    var itemCenter = options.spacing + ( maxItemLength / 2 );

    // Options common to all separators
    var separatorOptions = {
      stroke: options.separatorColor,
      lineWidth: options.separatorLineWidth
    };

    // @private enables animation when scrolling between pages
    this._animationEnabled = options.animationEnabled;

    // All items, arranged in the proper orientation, with margins and spacing.
    // Horizontal carousel arrange items left-to-right, vertical is top-to-bottom.
    // Translation of this node will be animated to give the effect of scrolling through the items.
    var scrollingNode = new Rectangle( 0, 0, scrollingWidth, scrollingHeight );
    items.forEach( function( item ) {

      // add the item
      if ( isHorizontal ) {
        item.centerX = itemCenter;
        item.centerY = options.margin + ( maxItemHeight / 2 );
      }
      else {
        item.centerX = options.margin + ( maxItemWidth / 2 );
        item.centerY = itemCenter;
      }
      scrollingNode.addChild( item );

      // center for the next item
      itemCenter += ( options.spacing + maxItemLength );

      // add optional separator
      if ( options.separatorsVisible ) {
        var separator;
        if ( isHorizontal ) {

          // vertical separator, to the left of the item
          separator = new VSeparator( scrollingHeight, _.extend( {
            centerX: item.centerX + ( maxItemLength / 2 ) + options.spacing,
            centerY: item.centerY
          }, separatorOptions ) );
          scrollingNode.addChild( separator );

          // center for the next item
          itemCenter = separator.centerX + options.spacing + ( maxItemLength / 2 );
        }
        else {

          // horizontal separator, below the item
          separator = new HSeparator( scrollingWidth, _.extend( {
            centerX: item.centerX,
            centerY: item.centerY + ( maxItemLength / 2 ) + options.spacing
          }, separatorOptions ) );
          scrollingNode.addChild( separator );

          // center for the next item
          itemCenter = separator.centerY + options.spacing + ( maxItemLength / 2 );
        }
      }
    } );

    // How much to translate scrollingNode each time a next/previous button is pressed
    var scrollingDelta = options.itemsPerPage * ( maxItemLength + options.spacing );
    if ( options.separatorsVisible ) {
      scrollingDelta += ( options.itemsPerPage * options.spacing );
    }

    // Clipping window, to show one page at a time.
    // Clips at the midpoint of spacing between items so that you don't see any stray bits of the items that shouldn't be visible.
    var windowLength = ( scrollingDelta + options.spacing );
    if ( options.separatorsVisible ) {
      windowLength -= options.spacing;
    }
    var windowWidth = isHorizontal ? windowLength : scrollingNode.width;
    var windowHeight = isHorizontal ? scrollingNode.height : windowLength;
    var clipArea = isHorizontal ?
                   Shape.rectangle( options.spacing / 2, 0, windowWidth - options.spacing, windowHeight ) :
                   Shape.rectangle( 0, options.spacing / 2, windowWidth, windowHeight - options.spacing );
    var windowNode = new Node( {
      children: [ scrollingNode ],
      clipArea: clipArea
    } );

    // Background - displays the carousel's fill color
    var backgroundWidth = isHorizontal ? ( windowWidth + nextButton.width + previousButton.width ) : windowWidth;
    var backgroundHeight = isHorizontal ? windowHeight : ( windowHeight + nextButton.height + previousButton.height );
    var backgroundNode = new Rectangle( 0, 0, backgroundWidth, backgroundHeight, options.cornerRadius, options.cornerRadius, {
      fill: options.fill
    } );

    // Foreground - displays the carousel's outline, created as a separate node so that it can be placed on top of everything, for a clean look.
    var foregroundNode = new Rectangle( 0, 0, backgroundWidth, backgroundHeight, options.cornerRadius, options.cornerRadius, {
      stroke: options.stroke
    } );

    // Layout
    if ( isHorizontal ) {
      nextButton.centerY = previousButton.centerY = windowNode.centerY = backgroundNode.centerY;
      nextButton.right = backgroundNode.right;
      previousButton.left = backgroundNode.left;
      windowNode.centerX = backgroundNode.centerX;
    }
    else {
      nextButton.centerX = previousButton.centerX = windowNode.centerX = backgroundNode.centerX;
      nextButton.bottom = backgroundNode.bottom;
      previousButton.top = backgroundNode.top;
      windowNode.centerY = backgroundNode.centerY;
    }

    // Number of pages
    var numberOfPages = items.length / options.itemsPerPage;
    if ( !Util.isInteger( numberOfPages ) ) {
      numberOfPages = Math.floor( numberOfPages + 1 );
    }

    // Number of the page that is visible in the carousel.
    assert && assert( options.defaultPageNumber >= 0 && options.defaultPageNumber <= numberOfPages - 1,
      'defaultPageNumber is out of range: ' + options.defaultPageNumber );
    var pageNumberProperty = new Property( options.defaultPageNumber );

    // Change pages
    var thisCarousel = this;
    var scrollTween;
    pageNumberProperty.link( function( pageNumber ) {

      assert && assert( pageNumber >= 0 && pageNumber <= numberOfPages - 1, 'pageNumber out of range: ' + pageNumber );

      // button state
      nextButton.enabled = pageNumber < ( numberOfPages - 1 );
      previousButton.enabled = pageNumber > 0;
      if ( options.hideDisabledButtons ) {
        nextButton.visible = nextButton.enabled;
        previousButton.visible = previousButton.enabled;
      }

      // stop any animation that's in progress
      scrollTween && scrollTween.stop();

      if ( thisCarousel._animationEnabled ) {

        //TODO replace calls to Tween with a wrapper, see https://github.com/phetsims/tasks/issues/360
        // Set up the animation to scroll the items in the carousel.
        var parameters;
        var animationDuration = 400; // ms
        var easing = TWEEN.Easing.Cubic.InOut;
        if ( isHorizontal ) {
          parameters = { left: scrollingNode.left };
          scrollTween = new TWEEN.Tween( parameters )
            .easing( easing )
            .to( { left: -pageNumber * scrollingDelta }, animationDuration )
            .onUpdate( function() {
              scrollingNode.left = parameters.left;
            } )
            .start();
        }
        else {
          parameters = { top: scrollingNode.top };
          scrollTween = new TWEEN.Tween( parameters )
            .easing( easing )
            .to( { top: -pageNumber * scrollingDelta }, animationDuration )
            .onUpdate( function() {
              scrollingNode.top = parameters.top;
            } )
            .start();
        }
      }
      else {

        // animation disabled, move immediate to new page
        if ( isHorizontal ) {
          scrollingNode.left = -pageNumber * scrollingDelta;
        }
        else {
          scrollingNode.top = -pageNumber * scrollingDelta;
        }
      }
    } );

    // Buttons modify the page number
    nextButton.addListener( function() {
      pageNumberProperty.set( pageNumberProperty.get() + 1 );
    } );
    previousButton.addListener( function() {
      pageNumberProperty.set( pageNumberProperty.get() - 1 );
    } );

    // fields
    this.items = items; // @private
    this.itemsPerPage = options.itemsPerPage; // @private
    this.numberOfPages = numberOfPages; // @public (read-only) {number} number of pages in the carousel
    this.pageNumberProperty = pageNumberProperty; // @public {Property<number>} page number that is currently visible

    options.children = [ backgroundNode, windowNode, nextButton, previousButton, foregroundNode ];

    Node.call( this, options );
  }

  sun.register( 'Carousel', Carousel );

  return inherit( Node, Carousel, {

    /**
     * Resets the carousel to its initial state.
     *
     * @param {Object} [options]
     * @public
     */
    reset: function( options ) {

      options = _.extend( {
        animationEnabled: this.animationEnabled // {boolean} whether to enable animation during reset
      }, options );

      var saveAnimationEnabled = this.animationEnabled;
      this.animationEnabled = options.animationEnabled;
      this.pageNumberProperty.reset();
      this.animationEnabled = saveAnimationEnabled;
    },

    /**
     * Determines whether animation is enabled for scrolling between pages.
     * @param {boolean} animationEnabled
     * @public
     */
    setAnimationEnabled: function( animationEnabled ) {
      this._animationEnabled = animationEnabled;
    },
    set animationEnabled( value ) { this.setAnimationEnabled( value ); },

    /**
     * Is animation enabled for scrolling between pages?
     * @returns {boolean}
     * @public
     */
    getAnimationEnabled: function() {
      return this._animationEnabled;
    },
    get animationEnabled() { return this.getAnimationEnabled(); },

    /**
     * Given an item's index, scroll the carousel to the page that contains that item.
     * @param {number} itemIndex
     */
    scrollToItemIndex: function( itemIndex ) {
      this.pageNumberProperty.set( this.itemIndexToPageNumber( itemIndex ) );
    },

    /**
     * Given an item, scroll the carousel to the page that contains that item.
     * @param {Node} item
     * @public
     */
    scrollToItem: function( item ) {
      this.scrollToItemIndex( this.items.indexOf( item ) );
    },

    /**
     * Is the specified item currently visible in the carousel?
     * @param {Node} item
     * @returns {boolean}
     * @public
     */
    isItemVisible: function( item ) {
      var itemIndex = this.items.indexOf( item );
      assert && assert( itemIndex !== -1, 'item not found' );
      return ( this.pageNumberProperty.get() === this.itemIndexToPageNumber( itemIndex ) );
    },

    /**
     * Converts an item index to a page number.
     * @param {number} itemIndex
     * @returns {number}
     * @public
     */
    itemIndexToPageNumber: function( itemIndex ) {
      assert && assert( itemIndex >= 0 && itemIndex < this.items.length, 'itemIndex out of range: ' + itemIndex );
      return Math.floor( itemIndex / this.itemsPerPage );
    }
  }, {

    // @static @public (read-only)
    DEFAULT_OPTIONS: DEFAULT_OPTIONS
  } );
} );
<commit_msg>added handling for an issue where the coin terms on the carousels would overlap for the worst case expression configuration<commit_after>// Copyright 2015, University of Colorado Boulder

/**
 * A carousel UI component.
 * A set of N items is divided into M 'pages', based on how many items are visible in the carousel.
 * Pressing the next and previous buttons moves through the pages.
 * Movement through the pages is animated, so that items appear to scroll by.
 *
 * Note that Carousel performs layout directly on the items (Nodes) that it is provided.
 * If those Nodes appear in multiple places in the scenegraph, then it's the client's
 * responsibility to provide the Carousel with wrapped Nodes.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var CarouselButton = require( 'SUN/buttons/CarouselButton' );
  var Dimension2 = require( 'DOT/Dimension2' );
  var HSeparator = require( 'SUN/HSeparator' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Property = require( 'AXON/Property' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Shape = require( 'KITE/Shape' );
  var sun = require( 'SUN/sun' );
  var Util = require( 'DOT/Util' );
  var VSeparator = require( 'SUN/VSeparator' );

  // constants
  var DEFAULT_OPTIONS = {

    // container
    orientation: 'horizontal', // {string} 'horizontal'|'vertical'
    fill: 'white', // {Color|string} background color of the carousel
    stroke: 'black', // {Color|string|null} color used to stroke the border of the carousel
    lineWidth: 1, // {number} width of the border around the carousel
    cornerRadius: 4, // {number} radius applied to the carousel and next/previous buttons
    defaultPageNumber: 0, // {number} page that is initially visible

    // items
    itemsPerPage: 4, // {number} number of items per page, or how many items are visible at a time in the carousel
    spacing: 10, // {number} spacing between items, between items and optional separators, and between items and buttons
    margin: 10, // {number} margin between items and the edges of the carousel

    // next/previous buttons
    buttonColor: 'rgba( 200, 200, 200, 0.5 )', // {Color|string} base color for the buttons
    buttonStroke: undefined, // {Color|string|null|undefined} stroke around the buttons (null is no stroke, undefined derives color from buttonColor)
    buttonLineWidth: 1, // {number} lineWidth of borders on buttons
    arrowSize: new Dimension2( 20, 7 ), // {Dimension2} size of the arrow, in 'up' directions
    arrowStroke: 'black', // {Color|string} color used for the arrow icons
    arrowLineWidth: 3, // {number} line width used to stroke the arrow icons
    hideDisabledButtons: false, // {boolean} whether to hide buttons when they are disabled

    // for dilating pointer areas of next/previous buttons such that they do not overlap with Carousel content
    buttonTouchAreaXDilation: 0, // {number} horizontal touchArea dilation
    buttonTouchAreaYDilation: 0, // {number} vertical touchArea dilation
    buttonMouseAreaXDilation: 0, // {number} horizontal mouseArea dilation
    buttonMouseAreaYDilation: 0, // {number} vertical mouseArea dilation

    // item separators
    separatorsVisible: false, // {boolean} whether to put separators between items
    separatorColor: 'rgb( 180, 180, 180 )', // {Color|string} color for separators
    separatorLineWidth: 0.5, // {number} lineWidth for separators

    // animation
    animationEnabled: true // {boolean} is animation enabled when scrolling between pages?
  };
  assert && Object.freeze( DEFAULT_OPTIONS );

  /**
   * @param {Node[]} items - items in the carousel
   * @param {Object} [options]
   * @constructor
   */
  function Carousel( items, options ) {

    // Override defaults with specified options
    options = _.extend( {}, DEFAULT_OPTIONS, options );

    // Validate options
    assert && assert( _.contains( [ 'horizontal', 'vertical' ], options.orientation ), 'invalid orientation=' + options.orientation );

    // To improve readability
    var isHorizontal = ( options.orientation === 'horizontal' );

    // Dimensions of largest item
    var maxItemWidth = _.max( items, function( item ) { return item.width; } ).width;
    var maxItemHeight = _.max( items, function( item ) { return item.height; } ).height;

    // This quantity is used make some other computations independent of orientation.
    var maxItemLength = isHorizontal ? maxItemWidth : maxItemHeight;

    // Options common to both buttons
    var buttonOptions = {
      xMargin: 5,
      yMargin: 5,
      cornerRadius: options.cornerRadius,
      baseColor: options.buttonColor,
      disabledBaseColor: options.fill, // same as carousel background
      stroke: options.buttonStroke,
      lineWidth: options.buttonLineWidth,
      minWidth: isHorizontal ? 0 : maxItemWidth + ( 2 * options.margin ), // fill the width of a vertical carousel
      minHeight: isHorizontal ? maxItemHeight + ( 2 * options.margin ) : 0, // fill the height of a horizontal carousel
      arrowSize: options.arrowSize,
      arrowStroke: options.arrowStroke,
      arrowLineWidth: options.arrowLineWidth,
      touchAreaXDilation: options.buttonTouchAreaXDilation,
      touchAreaYDilation: options.buttonTouchAreaYDilation,
      mouseAreaXDilation: options.buttonMouseAreaXDilation,
      mouseAreaYDilation: options.buttonMouseAreaYDilation
    };

    // Next/previous buttons
    var nextButton = new CarouselButton( _.extend( { arrowDirection: isHorizontal ? 'right' : 'down' }, buttonOptions ) );
    var previousButton = new CarouselButton( _.extend( { arrowDirection: isHorizontal ? 'left' : 'up' }, buttonOptions ) );

    // Computations related to layout of items
    var numberOfSeparators = ( options.separatorsVisible ) ? ( items.length - 1 ) : 0;
    var scrollingLength = ( items.length * ( maxItemLength + options.spacing ) + ( numberOfSeparators * options.spacing) + options.spacing );
    var scrollingWidth = isHorizontal ? scrollingLength : ( maxItemWidth + 2 * options.margin );
    var scrollingHeight = isHorizontal ? ( maxItemHeight + 2 * options.margin ) : scrollingLength;
    var itemCenter = options.spacing + ( maxItemLength / 2 );

    // Options common to all separators
    var separatorOptions = {
      stroke: options.separatorColor,
      lineWidth: options.separatorLineWidth
    };

    // @private enables animation when scrolling between pages
    this._animationEnabled = options.animationEnabled;

    // All items, arranged in the proper orientation, with margins and spacing.
    // Horizontal carousel arrange items left-to-right, vertical is top-to-bottom.
    // Translation of this node will be animated to give the effect of scrolling through the items.
    var scrollingNode = new Rectangle( 0, 0, scrollingWidth, scrollingHeight );
    items.forEach( function( item ) {

      // add the item
      if ( isHorizontal ) {
        item.centerX = itemCenter;
        item.centerY = options.margin + ( maxItemHeight / 2 );
      }
      else {
        item.centerX = options.margin + ( maxItemWidth / 2 );
        item.centerY = itemCenter;
      }
      scrollingNode.addChild( item );

      // center for the next item
      itemCenter += ( options.spacing + maxItemLength );

      // add optional separator
      if ( options.separatorsVisible ) {
        var separator;
        if ( isHorizontal ) {

          // vertical separator, to the left of the item
          separator = new VSeparator( scrollingHeight, _.extend( {
            centerX: item.centerX + ( maxItemLength / 2 ) + options.spacing,
            centerY: item.centerY
          }, separatorOptions ) );
          scrollingNode.addChild( separator );

          // center for the next item
          itemCenter = separator.centerX + options.spacing + ( maxItemLength / 2 );
        }
        else {

          // horizontal separator, below the item
          separator = new HSeparator( scrollingWidth, _.extend( {
            centerX: item.centerX,
            centerY: item.centerY + ( maxItemLength / 2 ) + options.spacing
          }, separatorOptions ) );
          scrollingNode.addChild( separator );

          // center for the next item
          itemCenter = separator.centerY + options.spacing + ( maxItemLength / 2 );
        }
      }
    } );

    // How much to translate scrollingNode each time a next/previous button is pressed
    var scrollingDelta = options.itemsPerPage * ( maxItemLength + options.spacing );
    if ( options.separatorsVisible ) {
      scrollingDelta += ( options.itemsPerPage * options.spacing );
    }

    // Clipping window, to show one page at a time.
    // Clips at the midpoint of spacing between items so that you don't see any stray bits of the items that shouldn't be visible.
    var windowLength = ( scrollingDelta + options.spacing );
    if ( options.separatorsVisible ) {
      windowLength -= options.spacing;
    }
    var windowWidth = isHorizontal ? windowLength : scrollingNode.width;
    var windowHeight = isHorizontal ? scrollingNode.height : windowLength;
    var clipArea = isHorizontal ?
                   Shape.rectangle( options.spacing / 2, 0, windowWidth - options.spacing, windowHeight ) :
                   Shape.rectangle( 0, options.spacing / 2, windowWidth, windowHeight - options.spacing );
    var windowNode = new Node( {
      children: [ scrollingNode ],
      clipArea: clipArea
    } );

    // Background - displays the carousel's fill color
    var backgroundWidth = isHorizontal ? ( windowWidth + nextButton.width + previousButton.width ) : windowWidth;
    var backgroundHeight = isHorizontal ? windowHeight : ( windowHeight + nextButton.height + previousButton.height );
    var backgroundNode = new Rectangle( 0, 0, backgroundWidth, backgroundHeight, options.cornerRadius, options.cornerRadius, {
      fill: options.fill
    } );

    // Foreground - displays the carousel's outline, created as a separate node so that it can be placed on top of everything, for a clean look.
    var foregroundNode = new Rectangle( 0, 0, backgroundWidth, backgroundHeight, options.cornerRadius, options.cornerRadius, {
      stroke: options.stroke
    } );

    // Layout
    if ( isHorizontal ) {
      nextButton.centerY = previousButton.centerY = windowNode.centerY = backgroundNode.centerY;
      nextButton.right = backgroundNode.right;
      previousButton.left = backgroundNode.left;
      windowNode.centerX = backgroundNode.centerX;
    }
    else {
      nextButton.centerX = previousButton.centerX = windowNode.centerX = backgroundNode.centerX;
      nextButton.bottom = backgroundNode.bottom;
      previousButton.top = backgroundNode.top;
      windowNode.centerY = backgroundNode.centerY;
    }

    // Number of pages
    var numberOfPages = items.length / options.itemsPerPage;
    if ( !Util.isInteger( numberOfPages ) ) {
      numberOfPages = Math.floor( numberOfPages + 1 );
    }

    // Number of the page that is visible in the carousel.
    assert && assert( options.defaultPageNumber >= 0 && options.defaultPageNumber <= numberOfPages - 1,
      'defaultPageNumber is out of range: ' + options.defaultPageNumber );
    var pageNumberProperty = new Property( options.defaultPageNumber );

    // Change pages
    var thisCarousel = this;
    var scrollTween;
    pageNumberProperty.link( function( pageNumber ) {

      assert && assert( pageNumber >= 0 && pageNumber <= numberOfPages - 1, 'pageNumber out of range: ' + pageNumber );

      // button state
      nextButton.enabled = pageNumber < ( numberOfPages - 1 );
      previousButton.enabled = pageNumber > 0;
      if ( options.hideDisabledButtons ) {
        nextButton.visible = nextButton.enabled;
        previousButton.visible = previousButton.enabled;
      }

      // stop any animation that's in progress
      scrollTween && scrollTween.stop();

      if ( thisCarousel._animationEnabled ) {

        //TODO replace calls to Tween with a wrapper, see https://github.com/phetsims/tasks/issues/360
        // Set up the animation to scroll the items in the carousel.
        var parameters;
        var animationDuration = 400; // ms
        var easing = TWEEN.Easing.Cubic.InOut;
        if ( isHorizontal ) {
          parameters = { left: scrollingNode.left };
          scrollTween = new TWEEN.Tween( parameters )
            .easing( easing )
            .to( { left: -pageNumber * scrollingDelta }, animationDuration )
            .onUpdate( function() {
              scrollingNode.left = parameters.left;
            } )
            .start();
        }
        else {
          parameters = { top: scrollingNode.top };
          scrollTween = new TWEEN.Tween( parameters )
            .easing( easing )
            .to( { top: -pageNumber * scrollingDelta }, animationDuration )
            .onUpdate( function() {
              scrollingNode.top = parameters.top;
            } )
            .start();
        }
      }
      else {

        // animation disabled, move immediate to new page
        if ( isHorizontal ) {
          scrollingNode.left = -pageNumber * scrollingDelta;
        }
        else {
          scrollingNode.top = -pageNumber * scrollingDelta;
        }
      }
    } );

    // Buttons modify the page number
    nextButton.addListener( function() {
      pageNumberProperty.set( pageNumberProperty.get() + 1 );
    } );
    previousButton.addListener( function() {
      pageNumberProperty.set( pageNumberProperty.get() - 1 );
    } );

    // fields
    this.items = items; // @private
    this.itemsPerPage = options.itemsPerPage; // @private
    this.numberOfPages = numberOfPages; // @public (read-only) {number} number of pages in the carousel
    this.pageNumberProperty = pageNumberProperty; // @public {Property<number>} page number that is currently visible

    options.children = [ backgroundNode, windowNode, nextButton, previousButton, foregroundNode ];

    Node.call( this, options );
  }

  sun.register( 'Carousel', Carousel );

  return inherit( Node, Carousel, {

    /**
     * Resets the carousel to its initial state.
     *
     * @param {Object} [options]
     * @public
     */
    reset: function( options ) {

      options = _.extend( {
        animationEnabled: this.animationEnabled // {boolean} whether to enable animation during reset
      }, options );

      var saveAnimationEnabled = this.animationEnabled;
      this.animationEnabled = options.animationEnabled;
      this.pageNumberProperty.reset();
      this.animationEnabled = saveAnimationEnabled;
    },

    /**
     * Determines whether animation is enabled for scrolling between pages.
     * @param {boolean} animationEnabled
     * @public
     */
    setAnimationEnabled: function( animationEnabled ) {
      this._animationEnabled = animationEnabled;
    },
    set animationEnabled( value ) { this.setAnimationEnabled( value ); },

    /**
     * Is animation enabled for scrolling between pages?
     * @returns {boolean}
     * @public
     */
    getAnimationEnabled: function() {
      return this._animationEnabled;
    },
    get animationEnabled() { return this.getAnimationEnabled(); },

    /**
     * Given an item's index, scroll the carousel to the page that contains that item.
     * @param {number} itemIndex
     */
    scrollToItemIndex: function( itemIndex ) {
      this.pageNumberProperty.set( this.itemIndexToPageNumber( itemIndex ) );
    },

    /**
     * Given an item, scroll the carousel to the page that contains that item.
     * @param {Node} item
     * @public
     */
    scrollToItem: function( item ) {
      this.scrollToItemIndex( this.items.indexOf( item ) );
    },

    /**
     * Is the specified item currently visible in the carousel?
     * @param {Node} item
     * @returns {boolean}
     * @public
     */
    isItemVisible: function( item ) {
      var itemIndex = this.items.indexOf( item );
      assert && assert( itemIndex !== -1, 'item not found' );
      return ( this.pageNumberProperty.get() === this.itemIndexToPageNumber( itemIndex ) );
    },

    /**
     * Converts an item index to a page number.
     * @param {number} itemIndex
     * @returns {number}
     * @public
     */
    itemIndexToPageNumber: function( itemIndex ) {
      assert && assert( itemIndex >= 0 && itemIndex < this.items.length, 'itemIndex out of range: ' + itemIndex );
      return Math.floor( itemIndex / this.itemsPerPage );
    }
  }, {

    // @static @public (read-only)
    DEFAULT_OPTIONS: DEFAULT_OPTIONS
  } );
} );
<|endoftext|>


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

/**
 * A face that can smile or frown.  This is generally used for indicating success or failure.
 * This was ported from a version that was originally written in Java.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 * @author John Blanco
 */

import DerivedProperty from '../../axon/js/DerivedProperty.js';
import Shape from '../../kite/js/Shape.js';
import InstanceRegistry from '../../phet-core/js/documentation/InstanceRegistry.js';
import inherit from '../../phet-core/js/inherit.js';
import merge from '../../phet-core/js/merge.js';
import Circle from '../../scenery/js/nodes/Circle.js';
import Node from '../../scenery/js/nodes/Node.js';
import Path from '../../scenery/js/nodes/Path.js';
import PaintColorProperty from '../../scenery/js/util/PaintColorProperty.js';
import sceneryPhet from './sceneryPhet.js';

/**
 * @param {number} headDiameter
 * @param {Object} [options]
 * @constructor
 */
function FaceNode( headDiameter, options ) {

  // default options
  options = merge( {
    headFill: 'yellow',
    eyeFill: 'black',
    mouthStroke: 'black',
    headLineWidth: 1
  }, options );

  // @private {Property.<Color>}
  this.headFillProperty = new PaintColorProperty( options.headFill );

  // The derived property listens to our headFillProperty which will be disposed. We don't need to keep a reference.
  options.headStroke = options.headStroke || new DerivedProperty( [ this.headFillProperty ], function( color ) {
    return color.darkerColor();
  } );

  Node.call( this );

  // Add head.
  this.addChild( new Circle( headDiameter / 2, {
    fill: options.headFill,
    stroke: options.headStroke,
    lineWidth: options.headLineWidth
  } ) );

  // Add the eyes.
  const eyeDiameter = headDiameter * 0.075;
  this.addChild( new Circle( eyeDiameter, {
    fill: options.eyeFill,
    centerX: -headDiameter * 0.2,
    centerY: -headDiameter * 0.1
  } ) );
  this.addChild( new Circle( eyeDiameter, {
    fill: options.eyeFill,
    centerX: headDiameter * 0.2,
    centerY: -headDiameter * 0.1
  } ) );

  // Add the mouths.
  const mouthLineWidth = headDiameter * 0.05;

  // @private
  this.smileMouth = new Path( new Shape().arc( 0, headDiameter * 0.05, headDiameter * 0.25, Math.PI * 0.2, Math.PI * 0.8 ), {
    stroke: options.mouthStroke,
    lineWidth: mouthLineWidth,
    lineCap: 'round'
  } );
  this.addChild( this.smileMouth );

  // @private
  this.frownMouth = new Path( new Shape().arc( 0, headDiameter * 0.4, headDiameter * 0.20, -Math.PI * 0.75, -Math.PI * 0.25 ), {
    stroke: options.mouthStroke,
    lineWidth: mouthLineWidth,
    lineCap: 'round'
  } );
  this.addChild( this.frownMouth );
  this.smile();

  // Pass through any options for positioning and such.
  this.mutate( options );

  // support for binder documentation, stripped out in builds and only runs when ?binder is specified
  assert && phet.chipper.queryParameters.binder && InstanceRegistry.registerDataURL( 'scenery-phet', 'FaceNode', this );
}

sceneryPhet.register( 'FaceNode', FaceNode );

inherit( Node, FaceNode, {

  // @public
  smile: function() {
    this.smileMouth.visible = true;
    this.frownMouth.visible = false;
    return this; // allow chaining
  },

  // @public
  frown: function() {
    this.smileMouth.visible = false;
    this.frownMouth.visible = true;
    return this; // allow chaining
  },

  /**
   * Releases references.
   * @public
   * @override
   */
  dispose: function() {
    this.headFillProperty.dispose();

    Node.prototype.dispose.call( this );
  }
} );

export default FaceNode;<commit_msg>convert FaceNode to ES6, https://github.com/phetsims/tasks/issues/1044<commit_after>// Copyright 2013-2020, University of Colorado Boulder

/**
 * A face that can smile or frown.  This is generally used for indicating success or failure.
 * This was ported from a version that was originally written in Java.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 * @author John Blanco
 */

import DerivedProperty from '../../axon/js/DerivedProperty.js';
import Shape from '../../kite/js/Shape.js';
import InstanceRegistry from '../../phet-core/js/documentation/InstanceRegistry.js';
import merge from '../../phet-core/js/merge.js';
import Circle from '../../scenery/js/nodes/Circle.js';
import Node from '../../scenery/js/nodes/Node.js';
import Path from '../../scenery/js/nodes/Path.js';
import PaintColorProperty from '../../scenery/js/util/PaintColorProperty.js';
import sceneryPhet from './sceneryPhet.js';

class FaceNode extends Node {

  /**
   * @param {number} headDiameter
   * @param {Object} [options]
   */
  constructor( headDiameter, options ) {

    // default options
    options = merge( {
      headFill: 'yellow',
      headStroke: null,
      eyeFill: 'black',
      mouthStroke: 'black',
      headLineWidth: 1
    }, options );

    super();

    // @private {Property.<Color>}
    this.headFillProperty = new PaintColorProperty( options.headFill );

    // The derived property listens to our headFillProperty which will be disposed. We don't need to keep a reference.
    options.headStroke = options.headStroke ||
                         new DerivedProperty( [ this.headFillProperty ],
                           color => color.darkerColor()
                         );

    // Add head.
    this.addChild( new Circle( headDiameter / 2, {
      fill: options.headFill,
      stroke: options.headStroke,
      lineWidth: options.headLineWidth
    } ) );

    // Add the eyes.
    const eyeDiameter = headDiameter * 0.075;
    this.addChild( new Circle( eyeDiameter, {
      fill: options.eyeFill,
      centerX: -headDiameter * 0.2,
      centerY: -headDiameter * 0.1
    } ) );
    this.addChild( new Circle( eyeDiameter, {
      fill: options.eyeFill,
      centerX: headDiameter * 0.2,
      centerY: -headDiameter * 0.1
    } ) );

    // Add the mouths.
    const mouthLineWidth = headDiameter * 0.05;

    // @private
    this.smileMouth = new Path( new Shape().arc( 0, headDiameter * 0.05, headDiameter * 0.25, Math.PI * 0.2, Math.PI * 0.8 ), {
      stroke: options.mouthStroke,
      lineWidth: mouthLineWidth,
      lineCap: 'round'
    } );
    this.addChild( this.smileMouth );

    // @private
    this.frownMouth = new Path( new Shape().arc( 0, headDiameter * 0.4, headDiameter * 0.20, -Math.PI * 0.75, -Math.PI * 0.25 ), {
      stroke: options.mouthStroke,
      lineWidth: mouthLineWidth,
      lineCap: 'round'
    } );
    this.addChild( this.frownMouth );
    this.smile();

    // Pass through any options for positioning and such.
    this.mutate( options );

    // support for binder documentation, stripped out in builds and only runs when ?binder is specified
    assert && phet.chipper.queryParameters.binder && InstanceRegistry.registerDataURL( 'scenery-phet', 'FaceNode', this );
  }

  // @public
  smile() {
    this.smileMouth.visible = true;
    this.frownMouth.visible = false;
    return this; // allow chaining
  }

  // @public
  frown() {
    this.smileMouth.visible = false;
    this.frownMouth.visible = true;
    return this; // allow chaining
  }

  /**
   * @public
   * @override
   */
  dispose() {
    this.headFillProperty.dispose();
    super.dispose();
  }
}

sceneryPhet.register( 'FaceNode', FaceNode );
export default FaceNode;<|endoftext|>


<commit_before>AAO.MainMenu = function(game){
  this.menuMusic_ = null;

  this.clouds_ = [];

  this.CLOUD_ANIMATION_SPEED = 80000; // MS to animate to one side
}

AAO.MainMenu.prototype.create = function() {
  // Add sprite and button
  this.game.add.sprite(0, 0, "menu-background");

  this.menuMusic_ = this.game.add.audio('menu-background');
  this.menuMusic_.play('',0,1,true);
  this.buttonClick_ = this.game.add.audio('select');

  this.clouds_.push(this.game.add.image(this.game.width/2,this.game.height,"smoke-test"));
  this.clouds_.push(this.game.add.image(-640,0,"smoke-test"));

  // Inverse first cloud
  this.clouds_[0].anchor.setTo(.5, 1);
  this.clouds_[0].scale.x = -1;

  // Animate clouds
  this.game.add.tween(this.clouds_[0]).to(
      {x: -640},
      this.CLOUD_ANIMATION_SPEED,
      Phaser.Easing.Linear.None,
      true,
      0,
      -1,
      true);
  this.game.add.tween(this.clouds_[1]).to(
      {x: 0},
      this.CLOUD_ANIMATION_SPEED*0.5,
      Phaser.Easing.Linear.None,
      true,
      0,
      -1,
      true);

  // Add button
  this.add.button(this.game.world.centerX,
      this.game.world.centerY + 200, 'start-button',
      this.startGame_.bind(this), this, 1, 0, 1, 1).anchor.setTo(0.5);
};

AAO.MainMenu.prototype.startGame_ = function() {
  this.state.start('Game');
  this.menuMusic_.stop();
  this.buttonClick_.play();
};
<commit_msg>Added smoke<commit_after>AAO.MainMenu = function(game){
  this.menuMusic_ = null;

  this.clouds_ = [];

  this.CLOUD_ANIMATION_SPEED = 80000; // MS to animate to one side
}

AAO.MainMenu.prototype.create = function() {
  // Add sprite and button
  this.game.add.sprite(0, 0, "menu-background");

  this.menuMusic_ = this.game.add.audio('menu-background');
  this.menuMusic_.play('',0,1,true);
  this.buttonClick_ = this.game.add.audio('select');

  this.clouds_.push(this.game.add.image(this.game.width/2,this.game.height,"smoke"));
  this.clouds_.push(this.game.add.image(-640,0,"smoke"));

  // Inverse first cloud
  this.clouds_[0].anchor.setTo(.5, 1);
  this.clouds_[0].scale.x = -1;

  // Animate clouds
  this.game.add.tween(this.clouds_[0]).to(
      {x: -640},
      this.CLOUD_ANIMATION_SPEED,
      Phaser.Easing.Linear.None,
      true,
      0,
      -1,
      true);
  this.game.add.tween(this.clouds_[1]).to(
      {x: 0},
      this.CLOUD_ANIMATION_SPEED*0.5,
      Phaser.Easing.Linear.None,
      true,
      0,
      -1,
      true);

  // Add button
  this.add.button(this.game.world.centerX,
      this.game.world.centerY + 200, 'start-button',
      this.startGame_.bind(this), this, 1, 0, 1, 1).anchor.setTo(0.5);
};

AAO.MainMenu.prototype.startGame_ = function() {
  this.state.start('Game');
  this.menuMusic_.stop();
  this.buttonClick_.play();
};
<|endoftext|>


<commit_before>/**
 * TODO: describe how organism works: mutations, memory, input, output,...
 * TODO: Add default values for config parameters
 *
 * Dependencies:
 *     Evo
 *     Evo.Mutator
 *     Evo.Code2Text
 *     Evo.Interpreter
 *
 * @author DeadbraiN
 */
Evo.Organism = function Organism() {
    /**
     * {Uint16Array} Binary code of the organism. This code will be
     * changed by Mutator module.
     */
    var _code = null;
    /**
     * {Number} Amount of mutations for current data set. It is
     * reset every time, then new data set has handled.
     */
    var _curMutations = 0;
    /**
     * {Number} Total amount of all mutations from the beginning
     */
    var _allMutations = 0;
    /**
     * {Array} Reference to last data set, which was passed or not.
     * It's used for obtaining memory, output and variables of organism.
     */
    var _lastData = null;
    /**
     * {Evo.Mutator} Personal mutator instance. It's uses for binary
     * code mutation.
     */
    var _mutator = new Evo.Mutator();
    /**
     * {Evo.Interpreter} An interpreter for binary script
     */
    var _interpreter = new Evo.Interpreter();
    /**
     * {Evo.Code2Text} Utility class for representing of binary code
     * in human readable manner.
     */
    var _code2text  = new Evo.Code2Text();

    /**
     * This method is used for reallocation of memory, which is used
     * for binary code. After mutations script size is growing all the
     * time, so we need to allocate more memory for it.
     */
    function _reallocateCode() {
        var codeLen = _mutator.getCodeLen();
        var code    = new Uint16Array(codeLen + codeLen);

        code.set(_code, 0);
        _code = code;
        return codeLen + codeLen;
    }

    return {
        /**
         * TODO: describe logic about: mutation -> prev. data checks -> revert -> loop
         * Starts organism to leave on. Live means pass all data sets (tests) by
         * finding specific binary script obtained by mutations.
         * @param {Object} config          Start configuration of the organism
         *        {String} colorCode       Color of the text script
         *        {Number} maxNumber       Maximum available number in script
         *        {Number} blockIterations Amount of iterations, which will be run in background without breaking
         *        {Number} memSize         Organism's memory size in words (2 * memSize byte)
         *        {Number} codePadding     Padding of text code for every column: command, arg1, arg2, arg3
         *        {Number} energy          Amount of energy which is inside the organism from the beginning
         *        {Number} energyDecrease  Value, which is decrease an energy after every script run
         *        {Number} mutationSpeed   Speed of new mutations: 1 - one mutation per one script run, 2  - 1
         *                                 mutation per 2 script running and so on. As bigger this value is, as
         *                                 slower the mutations are.
         */
        live: function (config) {
            //
            // All this section is only for run speed increasing,
            // because local variables are faster, then global.
            //
            var maxNumber  = config.maxNumber;
            var backAmount = config.blockIterations;
            var mem        = new Uint16Array(config.memSize);
            var code       = new Uint16Array(maxNumber);
            var out        = [];
            var varsLen    = _interpreter.VARS_AMOUNT;
            var energy     = config.energy;
            var energyDec  = config.energyDecrease;
            var mutSpeed   = config.mutationSpeed;
            var m          = mutSpeed;
            var clever;
            var len;
            var b;

            _code = code;

            /**
             * This method calls as a background thread. As you know
             * JavaScript doesn't support multithreading. So we need to
             * use setTimeout() for this. As a result we may type different
             * commands in console, while organism is leaving.
             */
            function backgroundLive() {
                b = 0;
                m = mutSpeed;
                //
                // This is a main loop. Here organism checks if
                // last mutation do the job: generates correct
                // output.
                //
                while (!clever && b++ < backAmount) {
                    //
                    // This is how we control the mutations speed. As big
                    // config.mutationSpeed as slow the mutations are.
                    //
                    if (m-- < 1) {
                        _mutator.mutate(code, varsLen, _mutator.getCodeLen());
                        _curMutations++;
                        len = _mutator.getCodeLen();
                        //
                        // When all allocated memory for binary script is reached, we need
                        // to reallocate it new bigger size.
                        //
                        if (len === maxNumber) {
                            maxNumber = _reallocateCode();
                            code = _code;
                        }
                        //
                        // Mutations also decrease the energy resource
                        //
                        energy -= energyDec;
                        m = mutSpeed;
                    }
                    //
                    // We don't need to change memory between the iterations.
                    // Organism should remember it's previous experience. Thw
                    // same about output.
                    //
                    _interpreter.run(code, mem, out, len);
                    //
                    // After every script run, we need to decrease organism's energy.
                    // As big the script is, as more the energy we need.
                    //
                    energy -= (energyDec * len);
                }
                //
                // Calculation of all mutations should be here, outside
                // the loop, because of performance issue.
                //
                _allMutations += _curMutations;
                _curMutations = 0;
                //
                // This line is very important, because it decrease CPU
                // load and current thread works "silently"
                //
                setTimeout(backgroundLive, 0);
            }

            _allMutations = _curMutations = 0;
            //
            // This is an entry point of living process.
            // All other looping will be in background
            // and used may type different commands
            //
            backgroundLive();
        },
        /**
         * Returns organism's code in different formats. This method may contain unoptimized
         * code, because it's used rare.
         * @param {String|Boolean=} skipFormat true to return formatted human readable code,
         * false to return binary code. 'text' to show code using console.log() and
         * without return value. 'textNoLines' to show code without line numbers. It's possible
         * to combine these parameters like this: 'text, textNoLines'
         * @param {Number=} padWidth Width in symbols for every code segment
         * @returns {Uint16Array|String} Final generated binary script of organism
         */
        getCode: function (skipFormat, padWidth) {
            //
            // We need to exclude an ability to create a reference to the
            // binary code, because organism is leaving now and mutator
            // is changing binary code right now. So our changing of this
            // code here may affect it in organism.
            //
            var code = new Uint16Array(_code.subarray(0, _interpreter.getCodeLen()));

            padWidth = padWidth || config.codePadding;

            if (skipFormat === true || skipFormat === undefined) {
                return code;
            }
            if (skipFormat.indexOf('text') !== -1) {
                console.log('%c' + _code2text.format(_code2text.convert(code), padWidth, skipFormat.indexOf('textNoLines') !== -1), 'color: ' + config.colorCode);
                return undefined;
            }

            return _code2text.format(_code2text.convert(code), padWidth, skipFormat.indexOf('textNoLines') !== -1);
        },
        /**
         * Returns memory dump of organism according to actual binary script. It's
         * important, that we use a copy of the memory and not it's original one.
         * @returns {Uint16Array}
         */
        getMemory: function () {
            var mem = new Uint16Array(config.maxNumber);
            var out = [];

            mem.set(_lastData, 0);
            _interpreter.run(_code, mem, out, _interpreter.getCodeLen());

            return mem;
        },
        /**
         * Returns output stream of organism. It uses echo command for output.
         * This method creates a copy of output array to prevent it's modification
         * from outside.
         * @returns {Array}
         */
        getOutput: function () {
            var mem = new Uint16Array(config.maxNumber);
            var out = [];

            mem.set(_lastData, 0);
            _interpreter.run(_code, mem, out, _interpreter.getCodeLen());

            return out;
        },
        /**
         * Returns amount of mutations for current data set. This parameter is reset
         * for every new data set in Evo.Data.
         * @return {Number}
         */
        getMutations: function() {
            return _curMutations;
        },
        /**
         * Returns amount of all mutations of organism from the beginning of leaving.
         * We need to add all mutations and current, because all mutations field is
         * updated only between data sets are passed.
         * @return {Number}
         */
        getAllMutations: function() {
            return _allMutations + _curMutations;
        }
    };
};<commit_msg>* Small fixe<commit_after>/**
 * TODO: describe how organism works: mutations, memory, input, output,...
 * TODO: Add default values for config parameters
 *
 * Dependencies:
 *     Evo
 *     Evo.Mutator
 *     Evo.Code2Text
 *     Evo.Interpreter
 *
 * @author DeadbraiN
 */
Evo.Organism = function Organism() {
    /**
     * {Uint16Array} Binary code of the organism. This code will be
     * changed by Mutator module.
     */
    var _code = null;
    /**
     * {Number} Amount of mutations for current data set. It is
     * reset every time, then new data set has handled.
     */
    var _curMutations = 0;
    /**
     * {Number} Total amount of all mutations from the beginning
     */
    var _allMutations = 0;
    /**
     * {Array} Reference to last data set, which was passed or not.
     * It's used for obtaining memory, output and variables of organism.
     */
    var _lastData = null;
    /**
     * {Evo.Mutator} Personal mutator instance. It's uses for binary
     * code mutation.
     */
    var _mutator = new Evo.Mutator();
    /**
     * {Evo.Interpreter} An interpreter for binary script
     */
    var _interpreter = new Evo.Interpreter();
    /**
     * {Evo.Code2Text} Utility class for representing of binary code
     * in human readable manner.
     */
    var _code2text  = new Evo.Code2Text();

    /**
     * This method is used for reallocation of memory, which is used
     * for binary code. After mutations script size is growing all the
     * time, so we need to allocate more memory for it.
     */
    function _reallocateCode() {
        var codeLen = _mutator.getCodeLen();
        var code    = new Uint16Array(codeLen + codeLen);

        code.set(_code, 0);
        _code = code;
        return codeLen + codeLen;
    }

    return {
        /**
         * TODO: describe logic about: mutation -> prev. data checks -> revert -> loop
         * Starts organism to leave on. Live means pass all data sets (tests) by
         * finding specific binary script obtained by mutations.
         * @param {Object} config          Start configuration of the organism
         *        {String} colorCode       Color of the text script
         *        {Number} maxNumber       Maximum available number in script
         *        {Number} blockIterations Amount of iterations, which will be run in background without breaking
         *        {Number} memSize         Organism's memory size in words (2 * memSize byte)
         *        {Number} codePadding     Padding of text code for every column: command, arg1, arg2, arg3
         *        {Number} energy          Amount of energy which is inside the organism from the beginning
         *        {Number} energyDecrease  Value, which is decrease an energy after every script run
         *        {Number} mutationSpeed   Speed of new mutations: 1 - one mutation per one script run, 2  - 1
         *                                 mutation per 2 script running and so on. As bigger this value is, as
         *                                 slower the mutations are.
         */
        live: function (config) {
            //
            // All this section is only for run speed increasing,
            // because local variables are faster, then global.
            //
            var maxNumber  = config.maxNumber;
            var backAmount = config.blockIterations;
            var mem        = new Uint16Array(config.memSize);
            var code       = new Uint16Array(maxNumber);
            var out        = [];
            var varsLen    = _interpreter.VARS_AMOUNT;
            var energy     = config.energy;
            var energyDec  = config.energyDecrease;
            var mutSpeed   = config.mutationSpeed;
            var m          = 0;
            var clever;
            var len;
            var b;

            _code = code;

            /**
             * This method calls as a background thread. As you know
             * JavaScript doesn't support multithreading. So we need to
             * use setTimeout() for this. As a result we may type different
             * commands in console, while organism is leaving.
             */
            function backgroundLive() {
                b = 0;
                //
                // This is a main loop. Here organism checks if
                // last mutation do the job: generates correct
                // output.
                //
                while (!clever && b++ < backAmount) {
                    //
                    // This is how we control the mutations speed. As big
                    // config.mutationSpeed as slow the mutations are.
                    //
                    if (m-- < 1) {
                        _mutator.mutate(code, varsLen, _mutator.getCodeLen());
                        _curMutations++;
                        len = _mutator.getCodeLen();
                        //
                        // When all allocated memory for binary script is reached, we need
                        // to reallocate it new bigger size.
                        //
                        if (len === maxNumber) {
                            maxNumber = _reallocateCode();
                            code = _code;
                        }
                        //
                        // Mutations also decrease the energy resource
                        //
                        energy -= energyDec;
                        m = mutSpeed;
                    }
                    //
                    // We don't need to change memory between the iterations.
                    // Organism should remember it's previous experience. Thw
                    // same about output.
                    //
                    _interpreter.run(code, mem, out, len);
                    //
                    // After every script run, we need to decrease organism's energy.
                    // As big the script is, as more the energy we need.
                    //
                    energy -= (energyDec * len);
                }
                //
                // Calculation of all mutations should be here, outside
                // the loop, because of performance issue.
                //
                _allMutations += _curMutations;
                _curMutations = 0;
                //
                // This line is very important, because it decrease CPU
                // load and current thread works "silently"
                //
                setTimeout(backgroundLive, 0);
            }

            _allMutations = _curMutations = 0;
            //
            // This is an entry point of living process.
            // All other looping will be in background
            // and used may type different commands
            //
            backgroundLive();
        },
        /**
         * Returns organism's code in different formats. This method may contain unoptimized
         * code, because it's used rare.
         * @param {String|Boolean=} skipFormat true to return formatted human readable code,
         * false to return binary code. 'text' to show code using console.log() and
         * without return value. 'textNoLines' to show code without line numbers. It's possible
         * to combine these parameters like this: 'text, textNoLines'
         * @param {Number=} padWidth Width in symbols for every code segment
         * @returns {Uint16Array|String} Final generated binary script of organism
         */
        getCode: function (skipFormat, padWidth) {
            //
            // We need to exclude an ability to create a reference to the
            // binary code, because organism is leaving now and mutator
            // is changing binary code right now. So our changing of this
            // code here may affect it in organism.
            //
            var code = new Uint16Array(_code.subarray(0, _interpreter.getCodeLen()));

            padWidth = padWidth || config.codePadding;

            if (skipFormat === true || skipFormat === undefined) {
                return code;
            }
            if (skipFormat.indexOf('text') !== -1) {
                console.log('%c' + _code2text.format(_code2text.convert(code), padWidth, skipFormat.indexOf('textNoLines') !== -1), 'color: ' + config.colorCode);
                return undefined;
            }

            return _code2text.format(_code2text.convert(code), padWidth, skipFormat.indexOf('textNoLines') !== -1);
        },
        /**
         * Returns memory dump of organism according to actual binary script. It's
         * important, that we use a copy of the memory and not it's original one.
         * @returns {Uint16Array}
         */
        getMemory: function () {
            var mem = new Uint16Array(config.maxNumber);
            var out = [];

            mem.set(_lastData, 0);
            _interpreter.run(_code, mem, out, _interpreter.getCodeLen());

            return mem;
        },
        /**
         * Returns output stream of organism. It uses echo command for output.
         * This method creates a copy of output array to prevent it's modification
         * from outside.
         * @returns {Array}
         */
        getOutput: function () {
            var mem = new Uint16Array(config.maxNumber);
            var out = [];

            mem.set(_lastData, 0);
            _interpreter.run(_code, mem, out, _interpreter.getCodeLen());

            return out;
        },
        /**
         * Returns amount of mutations for current data set. This parameter is reset
         * for every new data set in Evo.Data.
         * @return {Number}
         */
        getMutations: function() {
            return _curMutations;
        },
        /**
         * Returns amount of all mutations of organism from the beginning of leaving.
         * We need to add all mutations and current, because all mutations field is
         * updated only between data sets are passed.
         * @return {Number}
         */
        getAllMutations: function() {
            return _allMutations + _curMutations;
        }
    };
};<|endoftext|>


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

/**
 * An observable property, notifies registered observers when the value changes.
 *
 * Uses the 'Constructor' pattern for object creation, which has the downside that
 * all properties are created once for each instance. It would be nice if our functions
 * were shared. But since the only way to create private fields is in the constructor,
 * and the functions need access to those private fields, there doesn't seem to be
 * any choice but to define the functions in the constructor.
 *
 * @author Sam Reid
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var axon = require( 'AXON/axon' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Events = require( 'AXON/Events' );

  // Also requires Multilink and DerivedProperty, but cannot reference them here or it will create a
  // circular dependency.  So they are loaded through axon.Multilink and axon.DerivedProperty.

  /**
   * @param {*} value - the initial value of the property
   * @param {Object} [options] - options
   * @constructor
   */
  axon.Property = function Property( value, options ) {

    options = _.extend( { tandem: null }, options );

    // @private Internal Events for sending startedCallbacksForChanged & endedCallbacksForChanged
    this.events = new Events();

    //Store the internal value and the initial value
    this._value = value;
    this.storeInitialValue( value ); // typically sets this._initialValue
    this._observers = []; // @private

    options.tandem && options.tandem.addInstance( this );

    // @private
    this.disposeProperty = function() {
      options.tandem && options.tandem.removeInstance( this );
      while ( this._observers.length > 0 ) {
        this.unlink( this._observers[ 0 ] );
      }
    };
  };

  return inherit( Object, axon.Property, {

      /**
       * Gets the value.  You can also use the es5 getter (property.value) but this means is provided for inner loops or internal code that must be fast.
       * @return {*}
       * @public
       */
      get: function() {
        return this._value;
      },

      /**
       * Sets the value and notifies registered observers.  You can also use the es5 getter (property.value) but this means is provided for inner loops or internal code that must be fast.
       * If the value hasn't changed, this is a no-op.
       *
       * @param {*} value
       * @public
       */
      set: function( value ) {
        if ( !this.equalsValue( value ) ) {
          this._setAndNotifyObservers( value );
        }
        return this;
      },

      // @public whether this property will not "change" when the passed-in value is set
      equalsValue: function( value ) {
        return value === this._value;
      },

      // @public store the initial value
      storeInitialValue: function( value ) {
        this._initialValue = value;
      },

      // @public
      get initialValue() {
        return this._initialValue;
      },

      // @private
      _setAndNotifyObservers: function( value ) {
        var oldValue = this.get();
        this._value = value;
        this._notifyObservers( oldValue );
      },

      // @private
      _notifyObservers: function( oldValue ) {

        // Note the current value, since it will be sent to possibly multiple observers.
        var value = this.get();

        // TODO: Should Property extend or compose Events?  Would extending Events broaden its interface too much?
        this.events.trigger2( 'startedCallbacksForChanged', value, oldValue );

        var observersCopy = this._observers.slice(); // make a copy, in case notification results in removeObserver
        for ( var i = 0; i < observersCopy.length; i++ ) {
          observersCopy[ i ]( value, oldValue );
        }

        this.events.trigger0( 'endedCallbacksForChanged' );
      },

      /**
       * Use this method when mutating a value (not replacing with a new instance) and you want to send notifications about the change.
       * This is different from the normal axon strategy, but may be necessary to prevent memory allocations.
       * This method is unsafe for removing observers because it assumes the observer list not modified, to save another allocation
       * Only provides the new reference as a callback (no oldvalue)
       * See https://github.com/phetsims/axon/issues/6
       * @public
       */
      notifyObserversStatic: function() {
        var value = this.get();
        for ( var i = 0; i < this._observers.length; i++ ) {
          this._observers[ i ]( value );
        }
      },

      /**
       * Resets the value to the initial value.
       * @public
       */
      reset: function() {
        this.set( this._initialValue );
      },

      /**
       * This function returns a bound function that sets the specified value.  For use in creating closures e.g. with gui classes.
       * For instance, to have a button that sets a property to true, instead of using
       * button.click(function(){property.set(true);});
       * you could use
       * button.click(property._set(true));
       * @param value the value to use when the setter is called.
       * @return a function that can be used to set the specified value.
       * @public TODO really?
       */
      _set: function( value ) {
        return this.set.bind( this, value );
      },

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

      // @public
      set value( newValue ) { this.set( newValue ); },

      /**
       * Adds an observer and notifies it immediately.
       * If observer is already registered, this is a no-op.
       * The initial notification provides the current value for newValue and null for oldValue.
       *
       * @param {function} observer a function of the form observer(newValue,oldValue)
       * @public
       */
      link: function( observer ) {
        if ( this._observers.indexOf( observer ) === -1 ) {
          this._observers.push( observer );
          observer( this.get(), null ); // null should be used when an object is expected but unavailable
        }
      },

      /**
       * Add an observer to the Property, without calling it back right away.
       * This is used when you need to register a observer without an immediate callback.
       *
       * @param {function} observer - a function with a single argument, which is the value of the property at the time the function is called.
       * @public
       */
      lazyLink: function( observer ) {
        if ( this._observers.indexOf( observer ) === -1 ) {
          this._observers.push( observer );
        }
      },

      /**
       * Removes an observer.
       * If observer is not registered, this is a no-op.
       *
       * @param {function} observer
       * @public
       */
      unlink: function( observer ) {
        var index = this._observers.indexOf( observer );
        if ( index !== -1 ) {
          this._observers.splice( index, 1 );
        }
      },

      /**
       * Links an object's named attribute to this property.  Returns a handle so it can be removed using Property.unlink();
       * Example: modelVisibleProperty.linkAttribute(view,'visible');
       *
       * @param object
       * @param attributeName
       * @public
       */
      linkAttribute: function( object, attributeName ) {
        var handle = function( value ) {object[ attributeName ] = value;};
        this.link( handle );
        return handle;
      },

      /**
       * Unlink an observer added with linkAttribute.  Note: the args of linkAttribute do not match the args of
       * unlinkAttribute: here, you must pass the observer handle returned by linkAttribute rather than object and attributeName
       *
       * @param observer
       * @public
       */
      unlinkAttribute: function( observer ) {
        this.unlink( observer );
      },

      // @public Provide toString for console debugging, see http://stackoverflow.com/questions/2485632/valueof-vs-tostring-in-javascript
      toString: function() {return 'Property{' + this.get() + '}'; },

      // @public
      valueOf: function() {return this.toString();},

      /**
       * Add an observer so that it will only fire once (and not on registration)
       *
       * I can see two ways to implement this:
       * (a) add a field to the observer so after notifications it can be checked and possibly removed. Disadvantage: will make everything slower even if not using 'once'
       * (b) wrap the observer in a new function which will call the observer and then remove itself.  Disadvantage: cannot remove an observer added using 'once'
       * To avoid possible performance problems, use a wrapper function, and return it as a handle in case the 'once' observer must be removed before it is called once
       *
       * @param observer the observer which should be called back only for one property change (and not on registration)
       * @returns {function} the wrapper handle in case the wrapped function needs to be removed with 'unlink' before it is called once
       * @public
       */
      once: function( observer ) {
        var property = this;
        var wrapper = function( newValue, oldValue ) {
          property.unlink( wrapper );
          observer( newValue, oldValue );
        };
        this.lazyLink( wrapper );
        return wrapper;
      },

      /**
       * Convenience function for debugging a property values.  It prints the new value on registration and when changed.
       * @param name debug name to be printed on the console
       * @returns {function} the handle to the linked observer in case it needs to be removed later
       * @public
       */
      debug: function( name ) {
        var observer = function( value ) { console.log( name, value ); };
        this.link( observer );
        return observer;
      },

      /**
       * Returns a function that can be used to toggle the property (using !)
       * @returns {function}
       * @public
       */
      get toggleFunction() {
        return this.toggle.bind( this );
      },

      /**
       * Modifies the value of this Property with the ! operator.  Works for booleans and non-booleans.
       * @public
       */
      toggle: function() {
        this.value = !this.value;
      },

      /**
       * Adds an observer that is fired when the property takes the specified value.  If the property has the value already,
       * the observer is called back immediately.  A reference to the observer is returned so that it can be removed.
       *
       * @param value the value to match
       * @param observer the observer that is called when this Property
       * @public
       */
      onValue: function( value, observer ) {
        var onValueObserver = function( v ) {
          if ( v === value ) {
            observer();
          }
        };
        this.link( onValueObserver );
        return onValueObserver;
      },

      // @public Ensures that the Property is eligible for GC
      dispose: function() {
        this.disposeProperty();
      }
    },

    //statics
    {

      /**
       * Registers an observer with multiple properties, then notifies the observer immediately.
       * @param {Property[]} properties
       * @param {function} observer no params, returns nothing
       * @static
       */
      multilink: function( properties, observer ) {
        return new axon.Multilink( properties, observer, false );
      },

      lazyMultilink: function( properties, observer ) {
        return new axon.Multilink( properties, observer, true );
      },

      /**
       * Removes the multilinked observer from this Property.
       * Same as calling dispose() on the handle (which happens to be a DerivedProperty instance)
       * @param {DerivedProperty} derivedProperty
       */
      unmultilink: function( derivedProperty ) {
        derivedProperty.dispose();
      },

      /**
       * Set up a PropertySet-like property on any object (see https://github.com/phetsims/axon/issues/42).
       *
       * @param {Object} object - The object that the property will be placed on
       * @param {string} propertyName - Name of the property
       * @param {*} initialValue - The initial value of the property
       */
      addProperty: function( object, propertyName, initialValue ) {
        // defines the property
        var property = object[ propertyName + 'Property' ] = new axon.Property( initialValue );

        // defines ES5 getter/setter
        Object.defineProperty( object, propertyName, {
          get: function() { return property.get(); },
          set: function( value ) { property.set( value ); },

          // Make it configurable and enumerable so it's easy to override...
          configurable: true,
          enumerable: true
        } );
      }
    } );
} );
<commit_msg>remove Property.storeInitialValue, see https://github.com/phetsims/axon/issues/67<commit_after>// Copyright 2002-2015, University of Colorado Boulder

/**
 * An observable property, notifies registered observers when the value changes.
 *
 * Uses the 'Constructor' pattern for object creation, which has the downside that
 * all properties are created once for each instance. It would be nice if our functions
 * were shared. But since the only way to create private fields is in the constructor,
 * and the functions need access to those private fields, there doesn't seem to be
 * any choice but to define the functions in the constructor.
 *
 * @author Sam Reid
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var axon = require( 'AXON/axon' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Events = require( 'AXON/Events' );

  // Also requires Multilink and DerivedProperty, but cannot reference them here or it will create a
  // circular dependency.  So they are loaded through axon.Multilink and axon.DerivedProperty.

  /**
   * @param {*} value - the initial value of the property
   * @param {Object} [options] - options
   * @constructor
   */
  axon.Property = function Property( value, options ) {

    options = _.extend( { tandem: null }, options );

    // @private Internal Events for sending startedCallbacksForChanged & endedCallbacksForChanged
    this.events = new Events();

    // @private - Store the internal value and the initial value
    this._value = value;

    // @private - Initial value
    this._initialValue = value;

    // @private - the property observers that get a callback when the value changes (or on link)
    this._observers = []; // @private

    options.tandem && options.tandem.addInstance( this );

    // @private
    this.disposeProperty = function() {
      options.tandem && options.tandem.removeInstance( this );
      while ( this._observers.length > 0 ) {
        this.unlink( this._observers[ 0 ] );
      }
    };
  };

  return inherit( Object, axon.Property, {

      /**
       * Gets the value.  You can also use the es5 getter (property.value) but this means is provided for inner loops or internal code that must be fast.
       * @return {*}
       * @public
       */
      get: function() {
        return this._value;
      },

      /**
       * Sets the value and notifies registered observers.  You can also use the es5 getter (property.value) but this means is provided for inner loops or internal code that must be fast.
       * If the value hasn't changed, this is a no-op.
       *
       * @param {*} value
       * @public
       */
      set: function( value ) {
        if ( !this.equalsValue( value ) ) {
          this._setAndNotifyObservers( value );
        }
        return this;
      },

      // @public whether this property will not "change" when the passed-in value is set
      equalsValue: function( value ) {
        return value === this._value;
      },

      // @public
      get initialValue() {
        return this._initialValue;
      },

      // @private
      _setAndNotifyObservers: function( value ) {
        var oldValue = this.get();
        this._value = value;
        this._notifyObservers( oldValue );
      },

      // @private
      _notifyObservers: function( oldValue ) {

        // Note the current value, since it will be sent to possibly multiple observers.
        var value = this.get();

        // TODO: Should Property extend or compose Events?  Would extending Events broaden its interface too much?
        this.events.trigger2( 'startedCallbacksForChanged', value, oldValue );

        var observersCopy = this._observers.slice(); // make a copy, in case notification results in removeObserver
        for ( var i = 0; i < observersCopy.length; i++ ) {
          observersCopy[ i ]( value, oldValue );
        }

        this.events.trigger0( 'endedCallbacksForChanged' );
      },

      /**
       * Use this method when mutating a value (not replacing with a new instance) and you want to send notifications about the change.
       * This is different from the normal axon strategy, but may be necessary to prevent memory allocations.
       * This method is unsafe for removing observers because it assumes the observer list not modified, to save another allocation
       * Only provides the new reference as a callback (no oldvalue)
       * See https://github.com/phetsims/axon/issues/6
       * @public
       */
      notifyObserversStatic: function() {
        var value = this.get();
        for ( var i = 0; i < this._observers.length; i++ ) {
          this._observers[ i ]( value );
        }
      },

      /**
       * Resets the value to the initial value.
       * @public
       */
      reset: function() {
        this.set( this._initialValue );
      },

      /**
       * This function returns a bound function that sets the specified value.  For use in creating closures e.g. with gui classes.
       * For instance, to have a button that sets a property to true, instead of using
       * button.click(function(){property.set(true);});
       * you could use
       * button.click(property._set(true));
       * @param value the value to use when the setter is called.
       * @return a function that can be used to set the specified value.
       * @public TODO really?
       */
      _set: function( value ) {
        return this.set.bind( this, value );
      },

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

      // @public
      set value( newValue ) { this.set( newValue ); },

      /**
       * Adds an observer and notifies it immediately.
       * If observer is already registered, this is a no-op.
       * The initial notification provides the current value for newValue and null for oldValue.
       *
       * @param {function} observer a function of the form observer(newValue,oldValue)
       * @public
       */
      link: function( observer ) {
        if ( this._observers.indexOf( observer ) === -1 ) {
          this._observers.push( observer );
          observer( this.get(), null ); // null should be used when an object is expected but unavailable
        }
      },

      /**
       * Add an observer to the Property, without calling it back right away.
       * This is used when you need to register a observer without an immediate callback.
       *
       * @param {function} observer - a function with a single argument, which is the value of the property at the time the function is called.
       * @public
       */
      lazyLink: function( observer ) {
        if ( this._observers.indexOf( observer ) === -1 ) {
          this._observers.push( observer );
        }
      },

      /**
       * Removes an observer.
       * If observer is not registered, this is a no-op.
       *
       * @param {function} observer
       * @public
       */
      unlink: function( observer ) {
        var index = this._observers.indexOf( observer );
        if ( index !== -1 ) {
          this._observers.splice( index, 1 );
        }
      },

      /**
       * Links an object's named attribute to this property.  Returns a handle so it can be removed using Property.unlink();
       * Example: modelVisibleProperty.linkAttribute(view,'visible');
       *
       * @param object
       * @param attributeName
       * @public
       */
      linkAttribute: function( object, attributeName ) {
        var handle = function( value ) {object[ attributeName ] = value;};
        this.link( handle );
        return handle;
      },

      /**
       * Unlink an observer added with linkAttribute.  Note: the args of linkAttribute do not match the args of
       * unlinkAttribute: here, you must pass the observer handle returned by linkAttribute rather than object and attributeName
       *
       * @param observer
       * @public
       */
      unlinkAttribute: function( observer ) {
        this.unlink( observer );
      },

      // @public Provide toString for console debugging, see http://stackoverflow.com/questions/2485632/valueof-vs-tostring-in-javascript
      toString: function() {return 'Property{' + this.get() + '}'; },

      // @public
      valueOf: function() {return this.toString();},

      /**
       * Add an observer so that it will only fire once (and not on registration)
       *
       * I can see two ways to implement this:
       * (a) add a field to the observer so after notifications it can be checked and possibly removed. Disadvantage: will make everything slower even if not using 'once'
       * (b) wrap the observer in a new function which will call the observer and then remove itself.  Disadvantage: cannot remove an observer added using 'once'
       * To avoid possible performance problems, use a wrapper function, and return it as a handle in case the 'once' observer must be removed before it is called once
       *
       * @param observer the observer which should be called back only for one property change (and not on registration)
       * @returns {function} the wrapper handle in case the wrapped function needs to be removed with 'unlink' before it is called once
       * @public
       */
      once: function( observer ) {
        var property = this;
        var wrapper = function( newValue, oldValue ) {
          property.unlink( wrapper );
          observer( newValue, oldValue );
        };
        this.lazyLink( wrapper );
        return wrapper;
      },

      /**
       * Convenience function for debugging a property values.  It prints the new value on registration and when changed.
       * @param name debug name to be printed on the console
       * @returns {function} the handle to the linked observer in case it needs to be removed later
       * @public
       */
      debug: function( name ) {
        var observer = function( value ) { console.log( name, value ); };
        this.link( observer );
        return observer;
      },

      /**
       * Returns a function that can be used to toggle the property (using !)
       * @returns {function}
       * @public
       */
      get toggleFunction() {
        return this.toggle.bind( this );
      },

      /**
       * Modifies the value of this Property with the ! operator.  Works for booleans and non-booleans.
       * @public
       */
      toggle: function() {
        this.value = !this.value;
      },

      /**
       * Adds an observer that is fired when the property takes the specified value.  If the property has the value already,
       * the observer is called back immediately.  A reference to the observer is returned so that it can be removed.
       *
       * @param value the value to match
       * @param observer the observer that is called when this Property
       * @public
       */
      onValue: function( value, observer ) {
        var onValueObserver = function( v ) {
          if ( v === value ) {
            observer();
          }
        };
        this.link( onValueObserver );
        return onValueObserver;
      },

      // @public Ensures that the Property is eligible for GC
      dispose: function() {
        this.disposeProperty();
      }
    },

    //statics
    {

      /**
       * Registers an observer with multiple properties, then notifies the observer immediately.
       * @param {Property[]} properties
       * @param {function} observer no params, returns nothing
       * @static
       */
      multilink: function( properties, observer ) {
        return new axon.Multilink( properties, observer, false );
      },

      lazyMultilink: function( properties, observer ) {
        return new axon.Multilink( properties, observer, true );
      },

      /**
       * Removes the multilinked observer from this Property.
       * Same as calling dispose() on the handle (which happens to be a DerivedProperty instance)
       * @param {DerivedProperty} derivedProperty
       */
      unmultilink: function( derivedProperty ) {
        derivedProperty.dispose();
      },

      /**
       * Set up a PropertySet-like property on any object (see https://github.com/phetsims/axon/issues/42).
       *
       * @param {Object} object - The object that the property will be placed on
       * @param {string} propertyName - Name of the property
       * @param {*} initialValue - The initial value of the property
       */
      addProperty: function( object, propertyName, initialValue ) {
        // defines the property
        var property = object[ propertyName + 'Property' ] = new axon.Property( initialValue );

        // defines ES5 getter/setter
        Object.defineProperty( object, propertyName, {
          get: function() { return property.get(); },
          set: function( value ) { property.set( value ); },

          // Make it configurable and enumerable so it's easy to override...
          configurable: true,
          enumerable: true
        } );
      }
    } );
} );
<|endoftext|>