<commit_before>jQuery.fn.QuickDateSelect = function() {
  return this.each(function(){
    jQuery.QuickDateSelect.activate(this);
  });
};

/**
 * @constructor
 * @name jQuery.QuickDateSelect
 * @param date_field the <input> field
 */
jQuery.QuickDateSelect = {
  activate: function(date_field) {

    var WEEKDAYS = ['S','M','T','W','T','F','S'];
    var MONTHS   = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'];

    var button  = createButton();
    var chooser = createChooser();
    var years   = $(chooser).find('.years');
    var months  = $(chooser).find('.months');
    var days    = $(chooser).find('.days');

    $(button).bind('click', function(e) {
      showChooser(e.pageX, e.pageY);
    });

    $(chooser).bind('mouseleave', function (e) {
        hideChooser();
    });

    $(years).children("li").bind('mouseenter', function (e) {
        $(this).addClass('sel').siblings().removeClass('sel');
        setYear($(this).text());
    });

    $(months).children("li").bind('mouseenter', function (e) {
        $(this).addClass('sel').siblings().removeClass('sel');
        setMonth($(this).text());
    });

    function createButton() {
      $(date_field).after("");
      return $(date_field).next();
    }

    function createChooser() {
      $(date_field).after(buildChooser());
      var chooser = $(date_field).next();
      return chooser
    }

    // make the chooser visible
    function showChooser(x, y) {
      var left = x - (years.width() / 2);
      if (left < 0) left = 0;

      var top = y - (years.height() / 2);
      if (top < 0) top = 0;

      $(chooser).css('top', top).css('left', left);

      $(years).show();
    }

    function hideChooser() {
      $(chooser).find('.sel').removeClass('sel');
      $(chooser).find('.selector').hide();
    }

    function setYear(year) {
      $(months).show();
      setMonth(-1);
    };

    function setMonth(month) {
      if (month === -1) {
        $(months).find('.sel').removeClass('sel');
        $(days).hide();
      } else {
        $(days).show();
        updateCal();
      }
    }

    function updateCal() {
      var year = $(years).find('.sel').text();
      var month = $(months).find('.sel').attr('month');

      if (month != -1) {
        // update contents
        $(days).find('.title').text(getTitle(year, month));
        $(days).find('.body').html(buildCal(year, month));

        // adjust position
        var mh = $(months).height();
        var dh = $(days).height();
        var space = (mh-dh)/11;
        var top = space * month;
        $(days).css('top', top);

        $(days).find('a')
          .bind('click', function(e) {
            var day = $(this).text();
            updateDateField(year, month, day);
            hideChooser();
          });
      }
    }

    function updateDateField(year, month, day) {
      $(date_field).attr("value", dateToString(new Date(year, month, day)));
    }

    function parseDateField() {
      return stringToDate($(date_field).attr("value"));
    }

    // calendar title
    function getTitle(year, month, day) {
      return MONTHS[month] + " " + year;
    }

    function daysInMonth(year, month) {
      return 32 - new Date(year, month, 32).getDate();
    }

    // 0 = Sun, 1 = Mon ... 6 = Sat
    function startOfMonth(year, month) {
      return new Date(year, month).getDay();
    }

    // build the outer markup for the chooser
    function buildChooser() {
      var html = '
'
      html += buildYears();
      html += buildMonths();
      html += buildDays();
      html += '
';
      return html;
    }

    // build the year selector
    function buildYears() {
      var html = '
';
      var thisYear = parseDateField().getFullYear();
      var startYear = thisYear - 6;
      var endYear = thisYear + 5;
      for (var i=startYear; i <= endYear; i++) {
          html += '
';
          html += i;
          html += '
';
      }
      html += '
';
      return html;
    }

    // build the month selector
    function buildMonths() {
      var html = '
';
      for (var i=0; i < 12; i++) {
          html += '
';
          html += MONTHS[i];
          html += '
';
      }
      html += '
';
      return html;
    }

    // build the day selector
    function buildDays() {
      var html = '
'
      html += '
[title]

';
      for (var i=0; i < WEEKDAYS.length; i++) {
        html += '';
        html += WEEKDAYS[i];
        html += '';
      }
      // div.body will be filled with buildCal() contents
      html +=	'

[calendar]

';
      return html
    }

    // build the inner markup for the calendar
    function buildCal(y, m) {
      var html = '
'
      var days = daysInMonth(y, m)
      var pad = startOfMonth(y,m)
      for (var i=0; i < pad; i++) {
        html += ' '
      }
      for (var i=1; i <= days; i++) {
        html += '' + i + ''
        if ((pad + i) % 7 == 0) {
          html += '

'
        }
      }
      html += '
'
      return html
    }

    function dateToString(date) {
      return date.getMonth()+1 + "/" + date.getDate() + "/" + date.getFullYear();
    }

    function stringToDate(str) {
      var pattern = /(\d+)\/(\d+)\/(\d+)/;
      var arr = str.match(pattern);
      return (arr == null) ? new Date() : new Date(arr[3]-1, arr[1], arr[2]);
    }

  }
}
<commit_msg>Fix date parsing bug.<commit_after>jQuery.fn.QuickDateSelect = function() {
  return this.each(function(){
    jQuery.QuickDateSelect.activate(this);
  });
};

/**
 * @constructor
 * @name jQuery.QuickDateSelect
 * @param date_field the <input> field
 */
jQuery.QuickDateSelect = {
  activate: function(date_field) {

    var WEEKDAYS = ['S','M','T','W','T','F','S'];
    var MONTHS   = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'];

    var button  = createButton();
    var chooser = createChooser();
    var years   = $(chooser).find('.years');
    var months  = $(chooser).find('.months');
    var days    = $(chooser).find('.days');

    $(button).bind('click', function(e) {
      showChooser(e.pageX, e.pageY);
    });

    $(chooser).bind('mouseleave', function (e) {
        hideChooser();
    });

    $(years).children("li").bind('mouseenter', function (e) {
        $(this).addClass('sel').siblings().removeClass('sel');
        setYear($(this).text());
    });

    $(months).children("li").bind('mouseenter', function (e) {
        $(this).addClass('sel').siblings().removeClass('sel');
        setMonth($(this).text());
    });

    function createButton() {
      $(date_field).after("");
      return $(date_field).next();
    }

    function createChooser() {
      $(date_field).after(buildChooser());
      var chooser = $(date_field).next();
      return chooser
    }

    // make the chooser visible
    function showChooser(x, y) {
      var left = x - (years.width() / 2);
      if (left < 0) left = 0;

      var top = y - (years.height() / 2);
      if (top < 0) top = 0;

      $(chooser).css('top', top).css('left', left);

      $(years).show();
    }

    function hideChooser() {
      $(chooser).find('.sel').removeClass('sel');
      $(chooser).find('.selector').hide();
    }

    function setYear(year) {
      $(months).show();
      setMonth(-1);
    };

    function setMonth(month) {
      if (month === -1) {
        $(months).find('.sel').removeClass('sel');
        $(days).hide();
      } else {
        $(days).show();
        updateCal();
      }
    }

    function updateCal() {
      var year = $(years).find('.sel').text();
      var month = $(months).find('.sel').attr('month');

      if (month != -1) {
        // update contents
        $(days).find('.title').text(getTitle(year, month));
        $(days).find('.body').html(buildCal(year, month));

        // adjust position
        var mh = $(months).height();
        var dh = $(days).height();
        var space = (mh-dh)/11;
        var top = space * month;
        $(days).css('top', top);

        $(days).find('a')
          .bind('click', function(e) {
            var day = $(this).text();
            updateDateField(year, month, day);
            hideChooser();
          });
      }
    }

    function updateDateField(year, month, day) {
      $(date_field).attr("value", dateToString(new Date(year, month, day)));
    }

    function parseDateField() {
      return stringToDate($(date_field).attr("value"));
    }

    // calendar title
    function getTitle(year, month, day) {
      return MONTHS[month] + " " + year;
    }

    function daysInMonth(year, month) {
      return 32 - new Date(year, month, 32).getDate();
    }

    // 0 = Sun, 1 = Mon ... 6 = Sat
    function startOfMonth(year, month) {
      return new Date(year, month).getDay();
    }

    // build the outer markup for the chooser
    function buildChooser() {
      var html = '
'
      html += buildYears();
      html += buildMonths();
      html += buildDays();
      html += '
';
      return html;
    }

    // build the year selector
    function buildYears() {
      var html = '
';
      var thisYear = parseDateField().getFullYear();
      var startYear = thisYear - 6;
      var endYear = thisYear + 5;
      for (var i=startYear; i <= endYear; i++) {
          html += '
';
          html += i;
          html += '
';
      }
      html += '
';
      return html;
    }

    // build the month selector
    function buildMonths() {
      var html = '
';
      for (var i=0; i < 12; i++) {
          html += '
';
          html += MONTHS[i];
          html += '
';
      }
      html += '
';
      return html;
    }

    // build the day selector
    function buildDays() {
      var html = '
'
      html += '
[title]

';
      for (var i=0; i < WEEKDAYS.length; i++) {
        html += '';
        html += WEEKDAYS[i];
        html += '';
      }
      // div.body will be filled with buildCal() contents
      html +=	'

[calendar]

';
      return html
    }

    // build the inner markup for the calendar
    function buildCal(y, m) {
      var html = '
'
      var days = daysInMonth(y, m)
      var pad = startOfMonth(y,m)
      for (var i=0; i < pad; i++) {
        html += ' '
      }
      for (var i=1; i <= days; i++) {
        html += '' + i + ''
        if ((pad + i) % 7 == 0) {
          html += '

'
        }
      }
      html += '
'
      return html
    }

    function dateToString(date) {
      return date.getMonth()+1 + "/" + date.getDate() + "/" + date.getFullYear();
    }

    function stringToDate(str) {
      var pattern = /(\d+)\/(\d+)\/(\d+)/;
      var arr = str.match(pattern);
      return (arr == null) ? new Date() : new Date(arr[3], arr[1]-1, arr[2]);
    }

  }
}
<|endoftext|>


<commit_before>/*!
 * jQuery special event "tap" using tap.js
 * Released under MIT license
 */
(function ($) {
  'use strict';

  $.event.special.tap = (function () {

    // Fallback to click events in old IE
    if (!document.addEventListener) return { bindType: 'click', delegateType: 'click' };

    var Tap = window.Tap;
    var dataKey = 'tap.js';

    return {
      setup: function () {
        $.data(this, dataKey, new Tap(this));
      },
      teardown: function () {
        var tap = $.data(this, dataKey);
        if (tap && tap.destroy) {
          tap.destroy();
          $.removeData(this, dataKey);
        }
      },
      add: function (handleObj) {
        this.addEventListener('tap', handleObj.handler, false);
      },
      remove: function (handleObj) {
        this.removeEventListener('tap', handleObj.handler, false);
      }
    };
  }());
}(window.jQuery));
<commit_msg>Fix indent in jQuery file<commit_after>/*!
 * jQuery special event "tap" using tap.js
 * Released under MIT license
 */
(function ($) {
    'use strict';

    $.event.special.tap = (function () {

        // Fallback to click events in old IE
        if (!document.addEventListener) return { bindType: 'click', delegateType: 'click' };

        var Tap = window.Tap;
        var dataKey = 'tap.js';

        return {
            setup: function () {
                $.data(this, dataKey, new Tap(this));
            },
            teardown: function () {
                var tap = $.data(this, dataKey);
                if (tap && tap.destroy) {
                    tap.destroy();
                    $.removeData(this, dataKey);
                }
            },
            add: function (handleObj) {
                this.addEventListener('tap', handleObj.handler, false);
            },
            remove: function (handleObj) {
                this.removeEventListener('tap', handleObj.handler, false);
            }
        };
    }());
}(window.jQuery));
<|endoftext|>


<commit_before>var fs = require('fs');
var babel = require('babel');

function generateSourceMap(map){
  function toBase64(string){
    return new Buffer(string).toString('base64');
  }

  var mapStr = JSON.stringify(map);

  // align `?instr` in filename to fold 4
  mapStr = mapStr.replace(/(,"sources":\[)("[^"]+")/, function(m, prefix, filename){
    while (filename.length % 3 != 1)
      filename = ' ' + filename;
    return prefix + filename;
  });

  // return comment for inject to js
  return (
    '\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,' +
    toBase64(mapStr)
  );
}

module.exports = function(options){
  var registratorName = options.registratorName;

  return function instrumentCode(content, filename, cb){
    content = String(content || '');
    filename = filename || 'unknown';

    var instrumentedCode = babel.transform(content, {
      filename: filename,
      sourceMaps: true,
      sourceFileName: filename + '?instr',
      plugins: [
        require('babel-plugin-source-wrapper')({
          registratorName: registratorName
        })
      ],
      whitelist: [],         // prevent use any other transformers
      blacklist: ['strict']  // prevent add "use strict" to sources
    });

    cb(null, instrumentedCode.code +
      generateSourceMap({
        version: instrumentedCode.map.version,
        sections: [{
          offset: { line: 0, column: 0 },
          map: instrumentedCode.map
        }]
      })
    );
  }
};
<commit_msg>report about error if any<commit_after>var fs = require('fs');
var babel = require('babel');

function generateSourceMap(map){
  function toBase64(string){
    return new Buffer(string).toString('base64');
  }

  var mapStr = JSON.stringify(map);

  // align `?instr` in filename to fold 4
  mapStr = mapStr.replace(/(,"sources":\[)("[^"]+")/, function(m, prefix, filename){
    while (filename.length % 3 != 1)
      filename = ' ' + filename;
    return prefix + filename;
  });

  // return comment for inject to js
  return (
    '\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,' +
    toBase64(mapStr)
  );
}

module.exports = function(options){
  var registratorName = options.registratorName;

  return function instrumentCode(content, filename, cb){
    content = String(content || '');
    filename = filename || 'unknown';

    try {
      var instrumentedCode = babel.transform(content, {
        filename: filename,
        sourceMaps: true,
        sourceFileName: filename + '?instr',
        plugins: [
          require('babel-plugin-source-wrapper')({
            registratorName: registratorName
          })
        ],
        whitelist: [],         // prevent use any other transformers
        blacklist: ['strict']  // prevent add "use strict" to sources
      });

      cb(null, instrumentedCode.code +
        generateSourceMap({
          version: instrumentedCode.map.version,
          sections: [{
            offset: { line: 0, column: 0 },
            map: instrumentedCode.map
          }]
        })
      );
    } catch(e) {
      cb(e);
    }
  }
};
<|endoftext|>


<commit_before>// Copyright 2002-2012, University of Colorado

/**
 * A 2D rectangle-shaped bounded area (bounding box)
 *
 * @author Jonathan Olson <olsonsjc@gmail.com>
 */

define( function( require ) {
  "use strict";

  var assert = require( 'ASSERT/assert' )( 'dot' );

  var dot = require( 'DOT/dot' );

  require( 'DOT/Vector2' );

  // not using x,y,width,height so that it can handle infinity-based cases in a better way
  dot.Bounds2 = function Bounds2( minX, minY, maxX, maxY ) {
    assert && assert( maxY !== undefined, 'Bounds2 requires 4 parameters' );
    this.minX = minX;
    this.minY = minY;
    this.maxX = maxX;
    this.maxY = maxY;
  };
  var Bounds2 = dot.Bounds2;

  Bounds2.prototype = {
    constructor: Bounds2,

    /*---------------------------------------------------------------------------*
    * Properties
    *----------------------------------------------------------------------------*/

    getWidth: function() { return this.maxX - this.minX; },
    get width() { return this.getWidth(); },

    getHeight: function() { return this.maxY - this.minY; },
    get height() { return this.getHeight(); },

    getX: function() { return this.minX; },
    get x() { return this.getX(); },

    getY: function() { return this.minY; },
    get y() { return this.getY(); },

    getCenter: function() { return new dot.Vector2( this.getCenterX(), this.getCenterY() ); },
    get center() { return this.getCenter(); },

    getCenterX: function() { return ( this.maxX + this.minX ) / 2; },
    get centerX() { return this.getCenterX(); },

    getCenterY: function() { return ( this.maxY + this.minY ) / 2; },
    get centerY() { return this.getCenterY(); },

    getMinX: function() { return this.minX; },
    getMinY: function() { return this.minY; },
    getMaxX: function() { return this.maxX; },
    getMaxY: function() { return this.maxY; },

    isEmpty: function() { return this.getWidth() < 0 || this.getHeight() < 0; },

    isFinite: function() {
      return isFinite( this.minX ) && isFinite( this.minY ) && isFinite( this.maxX ) && isFinite( this.maxY );
    },

    isValid: function() {
      return !this.isEmpty() && this.isFinite();
    },

    // whether the coordinates are inside the bounding box (or on the boundary)
    containsCoordinates: function( x, y ) {
      return this.minX <= x && x <= this.maxX && this.minY <= y && y <= this.maxY;
    },

    // whether the point is inside the bounding box (or on the boundary)
    containsPoint: function( point ) {
      return this.containsCoordinates( point.x, point.y );
    },

    // whether this bounding box completely contains the argument bounding box
    containsBounds: function( bounds ) {
      return this.minX <= bounds.minX && this.maxX >= bounds.maxX && this.minY <= bounds.minY && this.maxY >= bounds.maxY;
    },

    // whether the intersection is non-empty (if they share any part of a boundary, this will be true)
    intersectsBounds: function( bounds ) {
      // TODO: more efficient way of doing this?
      return !this.intersection( bounds ).isEmpty();
    },

    toString: function() {
      return '[x:(' + this.minX + ',' + this.maxX + '),y:(' + this.minY + ',' + this.maxY + ')]';
    },

    equals: function( other ) {
      return this.minX === other.minX && this.minY === other.minY && this.maxX === other.maxX && this.maxY === other.maxY;
    },

    equalsEpsilon: function( other, epsilon ) {
      epsilon = epsilon || 0;
      return Math.abs( this.minX - other.minX ) < epsilon &&
             Math.abs( this.minY - other.minY ) < epsilon &&
             Math.abs( this.maxX - other.maxX ) < epsilon &&
             Math.abs( this.maxY - other.maxY ) < epsilon;
    },

    /*---------------------------------------------------------------------------*
    * Immutable operations
    *----------------------------------------------------------------------------*/

    copy: function() {
      return new Bounds2( this.minX, this.minY, this.maxX, this.maxY );
    },

    // immutable operations (bounding-box style handling, so that the relevant bounds contain everything)
    union: function( bounds ) {
      return new Bounds2(
        Math.min( this.minX, bounds.minX ),
        Math.min( this.minY, bounds.minY ),
        Math.max( this.maxX, bounds.maxX ),
        Math.max( this.maxY, bounds.maxY )
      );
    },
    intersection: function( bounds ) {
      return new Bounds2(
        Math.max( this.minX, bounds.minX ),
        Math.max( this.minY, bounds.minY ),
        Math.min( this.maxX, bounds.maxX ),
        Math.min( this.maxY, bounds.maxY )
      );
    },
    // TODO: difference should be well-defined, but more logic is needed to compute

    withCoordinates: function( x, y ) {
      return new Bounds2(
        Math.min( this.minX, x ),
        Math.min( this.minY, y ),
        Math.max( this.maxX, x ),
        Math.max( this.maxY, y )
      );
    },

    // like a union with a point-sized bounding box
    withPoint: function( point ) {
      return this.withCoordinates( point.x, point.y );
    },

    withMinX: function( minX ) { return new Bounds2( minX, this.minY, this.maxX, this.maxY ); },
    withMinY: function( minY ) { return new Bounds2( this.minX, minY, this.maxX, this.maxY ); },
    withMaxX: function( maxX ) { return new Bounds2( this.minX, this.minY, maxX, this.maxY ); },
    withMaxY: function( maxY ) { return new Bounds2( this.minX, this.minY, this.maxX, maxY ); },

    // copy rounded to integral values, expanding where necessary
    roundedOut: function() {
      return new Bounds2(
        Math.floor( this.minX ),
        Math.floor( this.minY ),
        Math.ceil( this.maxX ),
        Math.ceil( this.maxY )
      );
    },

    // copy rounded to integral values, contracting where necessary
    roundedIn: function() {
      return new Bounds2(
        Math.ceil( this.minX ),
        Math.ceil( this.minY ),
        Math.floor( this.maxX ),
        Math.floor( this.maxY )
      );
    },

    // transform a bounding box.
    // NOTE that box.transformed( matrix ).transformed( inverse ) may be larger than the original box
    transformed: function( matrix ) {
      return this.copy().transform( matrix );
    },

    // returns copy expanded on all sides by length d
    dilated: function( d ) {
      return new Bounds2( this.minX - d, this.minY - d, this.maxX + d, this.maxY + d );
    },

    // returns copy contracted on all sides by length d
    eroded: function( d ) {
      return this.dilated( -d );
    },

    shiftedX: function( x ) {
      return new Bounds2( this.minX + x, this.minY, this.maxX + x, this.maxY );
    },

    shiftedY: function( y ) {
      return new Bounds2( this.minX, this.minY + y, this.maxX, this.maxY + y );
    },

    shifted: function( x, y ) {
      return new Bounds2( this.minX + x, this.minY + y, this.maxX + x, this.maxY + y );
    },

    /*---------------------------------------------------------------------------*
    * Mutable operations
    *----------------------------------------------------------------------------*/

    set: function( minX, minY, maxX, maxY ) {
      this.minX = minX;
      this.minY = minY;
      this.maxX = maxX;
      this.maxY = maxY;
      return this;
    },

    setBounds: function( bounds ) {
      return this.set( bounds.minX, bounds.minY, bounds.maxX, bounds.maxY );
    },

    // mutable union
    includeBounds: function( bounds ) {
      this.minX = Math.min( this.minX, bounds.minX );
      this.minY = Math.min( this.minY, bounds.minY );
      this.maxX = Math.max( this.maxX, bounds.maxX );
      this.maxY = Math.max( this.maxY, bounds.maxY );
      return this;
    },

    // mutable intersection
    constrainBounds: function( bounds ) {
      this.minX = Math.max( this.minX, bounds.minX );
      this.minY = Math.max( this.minY, bounds.minY );
      this.maxX = Math.min( this.maxX, bounds.maxX );
      this.maxY = Math.min( this.maxY, bounds.maxY );
      return this;
    },

    addCoordinates: function( x, y ) {
      this.minX = Math.min( this.minX, x );
      this.minY = Math.min( this.minY, y );
      this.maxX = Math.max( this.maxX, x );
      this.maxY = Math.max( this.maxY, y );
      return this;
    },

    addPoint: function( point ) {
      return this.addCoordinates( point.x, point.y );
    },

    setMinX: function( minX ) { this.minX = minX; return this; },
    setMinY: function( minY ) { this.minY = minY; return this; },
    setMaxX: function( maxX ) { this.maxX = maxX; return this; },
    setMaxY: function( maxY ) { this.maxY = maxY; return this; },

    // round to integral values, expanding where necessary
    roundOut: function() {
      this.minX = Math.floor( this.minX );
      this.minY = Math.floor( this.minY );
      this.maxX = Math.ceil( this.maxX );
      this.maxY = Math.ceil( this.maxY );
      return this;
    },

    // round to integral values, contracting where necessary
    roundIn: function() {
      this.minX = Math.ceil( this.minX );
      this.minY = Math.ceil( this.minY );
      this.maxX = Math.floor( this.maxX );
      this.maxY = Math.floor( this.maxY );
      return this;
    },

    // transform a bounding box.
    // NOTE that box.transformed( matrix ).transformed( inverse ) may be larger than the original box
    transform: function( matrix ) {
      // do nothing
      if ( this.isEmpty() ) {
        return this;
      }
      var minX = this.minX;
      var minY = this.minY;
      var maxX = this.maxX;
      var maxY = this.maxY;

      // using mutable vector so we don't create excessive instances of Vector2 during this
      // make sure all 4 corners are inside this transformed bounding box
      var vector = new dot.Vector2();
      this.setBounds( Bounds2.NOTHING );
      this.addPoint( matrix.multiplyVector2( vector.set( minX, minY ) ) );
      this.addPoint( matrix.multiplyVector2( vector.set( minX, maxY ) ) );
      this.addPoint( matrix.multiplyVector2( vector.set( maxX, minY ) ) );
      this.addPoint( matrix.multiplyVector2( vector.set( maxX, maxY ) ) );
      return this;
    },

    // expands on all sides by length d
    dilate: function( d ) {
      return this.set( this.minX - d, this.minY - d, this.maxX + d, this.maxY + d );
    },

    // contracts on all sides by length d
    erode: function( d ) {
      return this.dilate( -d );
    },

    shiftX: function( x ) {
      return this.setMinX( this.minX + x ).setMaxX( this.maxX + x );
    },

    shiftY: function( y ) {
      return this.setMinY( this.minY + y ).setMaxY( this.maxY + y );
    },

    shift: function( x, y ) {
      return this.shiftX( x ).shiftY( y );
    }
  };

  // specific bounds useful for operations
  Bounds2.EVERYTHING = new Bounds2( Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY );
  Bounds2.NOTHING = new Bounds2( Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY );

  return Bounds2;
} );
<commit_msg>Adding Bounds2.rect for convenience<commit_after>// Copyright 2002-2012, University of Colorado

/**
 * A 2D rectangle-shaped bounded area (bounding box)
 *
 * @author Jonathan Olson <olsonsjc@gmail.com>
 */

define( function( require ) {
  "use strict";

  var assert = require( 'ASSERT/assert' )( 'dot' );

  var dot = require( 'DOT/dot' );

  require( 'DOT/Vector2' );

  // not using x,y,width,height so that it can handle infinity-based cases in a better way
  dot.Bounds2 = function Bounds2( minX, minY, maxX, maxY ) {
    assert && assert( maxY !== undefined, 'Bounds2 requires 4 parameters' );
    this.minX = minX;
    this.minY = minY;
    this.maxX = maxX;
    this.maxY = maxY;
  };
  var Bounds2 = dot.Bounds2;

  Bounds2.prototype = {
    constructor: Bounds2,

    /*---------------------------------------------------------------------------*
    * Properties
    *----------------------------------------------------------------------------*/

    getWidth: function() { return this.maxX - this.minX; },
    get width() { return this.getWidth(); },

    getHeight: function() { return this.maxY - this.minY; },
    get height() { return this.getHeight(); },

    getX: function() { return this.minX; },
    get x() { return this.getX(); },

    getY: function() { return this.minY; },
    get y() { return this.getY(); },

    getCenter: function() { return new dot.Vector2( this.getCenterX(), this.getCenterY() ); },
    get center() { return this.getCenter(); },

    getCenterX: function() { return ( this.maxX + this.minX ) / 2; },
    get centerX() { return this.getCenterX(); },

    getCenterY: function() { return ( this.maxY + this.minY ) / 2; },
    get centerY() { return this.getCenterY(); },

    getMinX: function() { return this.minX; },
    getMinY: function() { return this.minY; },
    getMaxX: function() { return this.maxX; },
    getMaxY: function() { return this.maxY; },

    isEmpty: function() { return this.getWidth() < 0 || this.getHeight() < 0; },

    isFinite: function() {
      return isFinite( this.minX ) && isFinite( this.minY ) && isFinite( this.maxX ) && isFinite( this.maxY );
    },

    isValid: function() {
      return !this.isEmpty() && this.isFinite();
    },

    // whether the coordinates are inside the bounding box (or on the boundary)
    containsCoordinates: function( x, y ) {
      return this.minX <= x && x <= this.maxX && this.minY <= y && y <= this.maxY;
    },

    // whether the point is inside the bounding box (or on the boundary)
    containsPoint: function( point ) {
      return this.containsCoordinates( point.x, point.y );
    },

    // whether this bounding box completely contains the argument bounding box
    containsBounds: function( bounds ) {
      return this.minX <= bounds.minX && this.maxX >= bounds.maxX && this.minY <= bounds.minY && this.maxY >= bounds.maxY;
    },

    // whether the intersection is non-empty (if they share any part of a boundary, this will be true)
    intersectsBounds: function( bounds ) {
      // TODO: more efficient way of doing this?
      return !this.intersection( bounds ).isEmpty();
    },

    toString: function() {
      return '[x:(' + this.minX + ',' + this.maxX + '),y:(' + this.minY + ',' + this.maxY + ')]';
    },

    equals: function( other ) {
      return this.minX === other.minX && this.minY === other.minY && this.maxX === other.maxX && this.maxY === other.maxY;
    },

    equalsEpsilon: function( other, epsilon ) {
      epsilon = epsilon || 0;
      return Math.abs( this.minX - other.minX ) < epsilon &&
             Math.abs( this.minY - other.minY ) < epsilon &&
             Math.abs( this.maxX - other.maxX ) < epsilon &&
             Math.abs( this.maxY - other.maxY ) < epsilon;
    },

    /*---------------------------------------------------------------------------*
    * Immutable operations
    *----------------------------------------------------------------------------*/

    copy: function() {
      return new Bounds2( this.minX, this.minY, this.maxX, this.maxY );
    },

    // immutable operations (bounding-box style handling, so that the relevant bounds contain everything)
    union: function( bounds ) {
      return new Bounds2(
        Math.min( this.minX, bounds.minX ),
        Math.min( this.minY, bounds.minY ),
        Math.max( this.maxX, bounds.maxX ),
        Math.max( this.maxY, bounds.maxY )
      );
    },
    intersection: function( bounds ) {
      return new Bounds2(
        Math.max( this.minX, bounds.minX ),
        Math.max( this.minY, bounds.minY ),
        Math.min( this.maxX, bounds.maxX ),
        Math.min( this.maxY, bounds.maxY )
      );
    },
    // TODO: difference should be well-defined, but more logic is needed to compute

    withCoordinates: function( x, y ) {
      return new Bounds2(
        Math.min( this.minX, x ),
        Math.min( this.minY, y ),
        Math.max( this.maxX, x ),
        Math.max( this.maxY, y )
      );
    },

    // like a union with a point-sized bounding box
    withPoint: function( point ) {
      return this.withCoordinates( point.x, point.y );
    },

    withMinX: function( minX ) { return new Bounds2( minX, this.minY, this.maxX, this.maxY ); },
    withMinY: function( minY ) { return new Bounds2( this.minX, minY, this.maxX, this.maxY ); },
    withMaxX: function( maxX ) { return new Bounds2( this.minX, this.minY, maxX, this.maxY ); },
    withMaxY: function( maxY ) { return new Bounds2( this.minX, this.minY, this.maxX, maxY ); },

    // copy rounded to integral values, expanding where necessary
    roundedOut: function() {
      return new Bounds2(
        Math.floor( this.minX ),
        Math.floor( this.minY ),
        Math.ceil( this.maxX ),
        Math.ceil( this.maxY )
      );
    },

    // copy rounded to integral values, contracting where necessary
    roundedIn: function() {
      return new Bounds2(
        Math.ceil( this.minX ),
        Math.ceil( this.minY ),
        Math.floor( this.maxX ),
        Math.floor( this.maxY )
      );
    },

    // transform a bounding box.
    // NOTE that box.transformed( matrix ).transformed( inverse ) may be larger than the original box
    transformed: function( matrix ) {
      return this.copy().transform( matrix );
    },

    // returns copy expanded on all sides by length d
    dilated: function( d ) {
      return new Bounds2( this.minX - d, this.minY - d, this.maxX + d, this.maxY + d );
    },

    // returns copy contracted on all sides by length d
    eroded: function( d ) {
      return this.dilated( -d );
    },

    shiftedX: function( x ) {
      return new Bounds2( this.minX + x, this.minY, this.maxX + x, this.maxY );
    },

    shiftedY: function( y ) {
      return new Bounds2( this.minX, this.minY + y, this.maxX, this.maxY + y );
    },

    shifted: function( x, y ) {
      return new Bounds2( this.minX + x, this.minY + y, this.maxX + x, this.maxY + y );
    },

    /*---------------------------------------------------------------------------*
    * Mutable operations
    *----------------------------------------------------------------------------*/

    set: function( minX, minY, maxX, maxY ) {
      this.minX = minX;
      this.minY = minY;
      this.maxX = maxX;
      this.maxY = maxY;
      return this;
    },

    setBounds: function( bounds ) {
      return this.set( bounds.minX, bounds.minY, bounds.maxX, bounds.maxY );
    },

    // mutable union
    includeBounds: function( bounds ) {
      this.minX = Math.min( this.minX, bounds.minX );
      this.minY = Math.min( this.minY, bounds.minY );
      this.maxX = Math.max( this.maxX, bounds.maxX );
      this.maxY = Math.max( this.maxY, bounds.maxY );
      return this;
    },

    // mutable intersection
    constrainBounds: function( bounds ) {
      this.minX = Math.max( this.minX, bounds.minX );
      this.minY = Math.max( this.minY, bounds.minY );
      this.maxX = Math.min( this.maxX, bounds.maxX );
      this.maxY = Math.min( this.maxY, bounds.maxY );
      return this;
    },

    addCoordinates: function( x, y ) {
      this.minX = Math.min( this.minX, x );
      this.minY = Math.min( this.minY, y );
      this.maxX = Math.max( this.maxX, x );
      this.maxY = Math.max( this.maxY, y );
      return this;
    },

    addPoint: function( point ) {
      return this.addCoordinates( point.x, point.y );
    },

    setMinX: function( minX ) { this.minX = minX; return this; },
    setMinY: function( minY ) { this.minY = minY; return this; },
    setMaxX: function( maxX ) { this.maxX = maxX; return this; },
    setMaxY: function( maxY ) { this.maxY = maxY; return this; },

    // round to integral values, expanding where necessary
    roundOut: function() {
      this.minX = Math.floor( this.minX );
      this.minY = Math.floor( this.minY );
      this.maxX = Math.ceil( this.maxX );
      this.maxY = Math.ceil( this.maxY );
      return this;
    },

    // round to integral values, contracting where necessary
    roundIn: function() {
      this.minX = Math.ceil( this.minX );
      this.minY = Math.ceil( this.minY );
      this.maxX = Math.floor( this.maxX );
      this.maxY = Math.floor( this.maxY );
      return this;
    },

    // transform a bounding box.
    // NOTE that box.transformed( matrix ).transformed( inverse ) may be larger than the original box
    transform: function( matrix ) {
      // do nothing
      if ( this.isEmpty() ) {
        return this;
      }
      var minX = this.minX;
      var minY = this.minY;
      var maxX = this.maxX;
      var maxY = this.maxY;

      // using mutable vector so we don't create excessive instances of Vector2 during this
      // make sure all 4 corners are inside this transformed bounding box
      var vector = new dot.Vector2();
      this.setBounds( Bounds2.NOTHING );
      this.addPoint( matrix.multiplyVector2( vector.set( minX, minY ) ) );
      this.addPoint( matrix.multiplyVector2( vector.set( minX, maxY ) ) );
      this.addPoint( matrix.multiplyVector2( vector.set( maxX, minY ) ) );
      this.addPoint( matrix.multiplyVector2( vector.set( maxX, maxY ) ) );
      return this;
    },

    // expands on all sides by length d
    dilate: function( d ) {
      return this.set( this.minX - d, this.minY - d, this.maxX + d, this.maxY + d );
    },

    // contracts on all sides by length d
    erode: function( d ) {
      return this.dilate( -d );
    },

    shiftX: function( x ) {
      return this.setMinX( this.minX + x ).setMaxX( this.maxX + x );
    },

    shiftY: function( y ) {
      return this.setMinY( this.minY + y ).setMaxY( this.maxY + y );
    },

    shift: function( x, y ) {
      return this.shiftX( x ).shiftY( y );
    }
  };

  Bounds2.rect = function( x, y, width, height ) {
    return new Bounds2( x, y, x + width, y + height );
  };

  // specific bounds useful for operations
  Bounds2.EVERYTHING = new Bounds2( Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY );
  Bounds2.NOTHING = new Bounds2( Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY );

  return Bounds2;
} );
<|endoftext|>


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

/**
 * Lightweight event & listener abstraction for a single event type.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var axon = require( 'AXON/axon' );
  var EmitterIO = require( 'AXON/EmitterIO' );
  var inherit = require( 'PHET_CORE/inherit' );
  var PhetioObject = require( 'TANDEM/PhetioObject' );

  /**
   * @param {Object} [options]
   * @constructor
   */
  function Emitter( options ) {

    options = _.extend( {
      phetioState: false,
      phetioType: EmitterIO( [] ) // subtypes can override with EmitterIO([...])
    }, options );

    // @private {Function[]} - the listeners to emit to
    this.listeners = [];

    // @private - during emit() keep track of which listeners should receive events in order to manage removal of
    //          - listeners during emit()
    this.listenersToEmitTo = [];

    PhetioObject.call( this, options );
  }

  axon.register( 'Emitter', Emitter );

  return inherit( PhetioObject, Emitter, {

    /**
     * Dispose an Emitter that is no longer used.  Like Property.dispose, this method checks that there are no leaked
     * listeners.
     */
    dispose: function() {

      // See https://github.com/phetsims/axon/issues/124
      this.listeners.length = 0;

      PhetioObject.prototype.dispose.call( this );
    },

    /**
     * Adds a listener
     * @param {function} listener
     * @public
     */
    addListener: function( listener ) {

      assert && assert( this.listeners.indexOf( listener ) === -1, 'Cannot add the same listener twice' );

      // If callbacks are in progress, make a copy of the current list of listeners--the newly added listener
      // will be available for the next emit() but not the one in progress.  This is to match behavior with removeListener
      this.defendCallbacks();

      this.listeners.push( listener );
    },

    /**
     * Removes a listener
     * @param {function} listener
     * @public
     */
    removeListener: function( listener ) {

      var index = this.listeners.indexOf( listener );
      assert && assert( index >= 0, 'tried to removeListener on something that wasn\'t a listener' );

      // If callbacks are in progress, make a copy of the current list of listeners--the removed listener
      // will remain in the list and receive a callback for this emit call, see #72
      this.defendCallbacks();

      this.listeners.splice( index, 1 );
    },

    /**
     * Removes all the listeners
     * @public
     */
    removeAllListeners: function() {
      while ( this.listeners.length > 0 ) {
        this.removeListener( this.listeners[ 0 ] );
      }
    },

    /**
     * If processing callbacks during an emit() call and addListener/removeListener() is called,
     * make a defensive copy of the array of listener before changing the array, and use it for
     * the rest of the callbacks until the emit call has completed.
     * @private
     */
    defendCallbacks: function() {

      for ( var i = this.listenersToEmitTo.length - 1; i >= 0; i-- ) {

        // Once we meet a level that was already defended, we can stop, since all previous levels are also defended
        if ( this.listenersToEmitTo[ i ].defended ) {
          break;
        }
        else {
          var defendedListeners = this.listeners.slice();

          // Mark copies as 'defended' so that it will use the original listeners when emit started and not the modified list.
          defendedListeners.defended = true;
          this.listenersToEmitTo[ i ] = defendedListeners;
        }
      }
    },

    /**
     * Emits a single event.
     * This method is called many times in a simulation and must be well-optimized.
     * @public
     */
    emit: function() {
      this.startEvent( 'model', 'emitted' );
      this.listenersToEmitTo.push( this.listeners );
      var lastEntry = this.listenersToEmitTo.length - 1;

      for ( var i = 0; i < this.listenersToEmitTo[ lastEntry ].length; i++ ) {
        this.listenersToEmitTo[ lastEntry ][ i ]();
      }

      this.listenersToEmitTo.pop();
      this.endEvent();
    },

    /**
     * Emits a single event with one argument.  This is a copy-paste of emit() for performance reasons.
     * @param {*} arg0
     * @public
     */
    emit1: function( arg0 ) {

      // TODO: name the args for the data stream
      this.tandem.isSuppliedAndEnabled() && this.startEvent( 'model', 'emitted', {
        args: [ this.phetioType.parameterTypes[ 0 ].toStateObject( arg0 ) ]
      } );
      this.listenersToEmitTo.push( this.listeners );
      var lastEntry = this.listenersToEmitTo.length - 1;

      for ( var i = 0; i < this.listenersToEmitTo[ lastEntry ].length; i++ ) {
        this.listenersToEmitTo[ lastEntry ][ i ]( arg0 );
      }

      this.listenersToEmitTo.pop();
      this.tandem.isSuppliedAndEnabled() && this.endEvent();
    },

    /**
     * Emits a single event with two arguments.  This is a copy-paste of emit() for performance reasons.
     * @param {*} arg0
     * @param {*} arg1
     * @public
     */
    emit2: function( arg0, arg1 ) {
      this.tandem.isSuppliedAndEnabled() && this.startEvent( 'model', 'emitted', {
        args: [
          this.phetioType.parameterTypes[ 0 ].toStateObject( arg0 ),
          this.phetioType.parameterTypes[ 1 ].toStateObject( arg1 )
        ]
      } );
      this.listenersToEmitTo.push( this.listeners );
      var lastEntry = this.listenersToEmitTo.length - 1;

      for ( var i = 0; i < this.listenersToEmitTo[ lastEntry ].length; i++ ) {
        this.listenersToEmitTo[ lastEntry ][ i ]( arg0, arg1 );
      }

      this.listenersToEmitTo.pop();
      this.tandem.isSuppliedAndEnabled() && this.endEvent();
    },

    /**
     * Emits a single event with three arguments.  This is a copy-paste of emit() for performance reasons.
     * @param {*} arg0
     * @param {*} arg1
     * @param {*} arg2
     * @public
     */
    emit3: function( arg0, arg1, arg2 ) {
      this.tandem.isSuppliedAndEnabled() && this.startEvent( 'model', 'emitted', {
        args: [
          this.phetioType.parameterTypes[ 0 ].toStateObject( arg0 ),
          this.phetioType.parameterTypes[ 1 ].toStateObject( arg1 ),
          this.phetioType.parameterTypes[ 2 ].toStateObject( arg2 )
        ]
      } );
      this.listenersToEmitTo.push( this.listeners );
      var lastEntry = this.listenersToEmitTo.length - 1;

      for ( var i = 0; i < this.listenersToEmitTo[ lastEntry ].length; i++ ) {
        this.listenersToEmitTo[ lastEntry ][ i ]( arg0, arg1, arg2 );
      }

      this.listenersToEmitTo.pop();
      this.tandem.isSuppliedAndEnabled() && this.endEvent();
    },

    /**
     * Emits a single event with three arguments.  This is a copy-paste of emit() for performance reasons.
     * @param {*} arg0
     * @param {*} arg1
     * @param {*} arg2
     * @param {*} arg3
     * @public
     */
    emit4: function( arg0, arg1, arg2, arg3 ) {
      this.tandem.isSuppliedAndEnabled() && this.startEvent( 'model', 'emitted', {
        args: [
          this.phetioType.parameterTypes[ 0 ].toStateObject( arg0 ),
          this.phetioType.parameterTypes[ 1 ].toStateObject( arg1 ),
          this.phetioType.parameterTypes[ 2 ].toStateObject( arg2 ),
          this.phetioType.parameterTypes[ 3 ].toStateObject( arg3 )
        ]
      } );
      this.listenersToEmitTo.push( this.listeners );
      var lastEntry = this.listenersToEmitTo.length - 1;

      for ( var i = 0; i < this.listenersToEmitTo[ lastEntry ].length; i++ ) {
        this.listenersToEmitTo[ lastEntry ][ i ]( arg0, arg1, arg2, arg3 );
      }

      this.listenersToEmitTo.pop();
      this.tandem.isSuppliedAndEnabled() && this.endEvent();
    },

    /**
     * Checks whether a listener is registered with this Emitter
     * @param {function} listener
     * @returns {boolean}
     * @public
     */
    hasListener: function( listener ) {
      assert && assert( arguments.length === 1, 'Emitter.hasListener should be called with 1 argument' );
      return this.listeners.indexOf( listener ) >= 0;
    },

    /**
     * Returns true if there are any listeners.
     * @returns {boolean}
     * @public
     */
    hasListeners: function() {
      assert && assert( arguments.length === 0, 'Emitter.hasListeners should be called without arguments' );
      return this.listeners.length > 0;
    },

    /**
     * Returns the number of listeners.
     * @returns {number}
     * @public
     */
    getListenerCount: function() {
      return this.listeners.length;
    }
  } );
} );<commit_msg>Declare attributes in the constructor, see https://github.com/phetsims/phet-io/issues/1304<commit_after>// Copyright 2015, University of Colorado Boulder

/**
 * Lightweight event & listener abstraction for a single event type.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var axon = require( 'AXON/axon' );
  var EmitterIO = require( 'AXON/EmitterIO' );
  var inherit = require( 'PHET_CORE/inherit' );
  var PhetioObject = require( 'TANDEM/PhetioObject' );
  var Tandem = require( 'TANDEM/Tandem' );

  /**
   * @param {Object} [options]
   * @constructor
   */
  function Emitter( options ) {

    options = _.extend( {
      tandem: Tandem.optional,
      phetioState: false,
      phetioType: EmitterIO( [] ) // subtypes can override with EmitterIO([...])
    }, options );

    // @private {Function[]} - the listeners to emit to
    this.listeners = [];

    // @private - during emit() keep track of which listeners should receive events in order to manage removal of
    //          - listeners during emit()
    this.listenersToEmitTo = [];

    PhetioObject.call( this, options );
  }

  axon.register( 'Emitter', Emitter );

  return inherit( PhetioObject, Emitter, {

    /**
     * Dispose an Emitter that is no longer used.  Like Property.dispose, this method checks that there are no leaked
     * listeners.
     */
    dispose: function() {

      // See https://github.com/phetsims/axon/issues/124
      this.listeners.length = 0;

      PhetioObject.prototype.dispose.call( this );
    },

    /**
     * Adds a listener
     * @param {function} listener
     * @public
     */
    addListener: function( listener ) {

      assert && assert( this.listeners.indexOf( listener ) === -1, 'Cannot add the same listener twice' );

      // If callbacks are in progress, make a copy of the current list of listeners--the newly added listener
      // will be available for the next emit() but not the one in progress.  This is to match behavior with removeListener
      this.defendCallbacks();

      this.listeners.push( listener );
    },

    /**
     * Removes a listener
     * @param {function} listener
     * @public
     */
    removeListener: function( listener ) {

      var index = this.listeners.indexOf( listener );
      assert && assert( index >= 0, 'tried to removeListener on something that wasn\'t a listener' );

      // If callbacks are in progress, make a copy of the current list of listeners--the removed listener
      // will remain in the list and receive a callback for this emit call, see #72
      this.defendCallbacks();

      this.listeners.splice( index, 1 );
    },

    /**
     * Removes all the listeners
     * @public
     */
    removeAllListeners: function() {
      while ( this.listeners.length > 0 ) {
        this.removeListener( this.listeners[ 0 ] );
      }
    },

    /**
     * If processing callbacks during an emit() call and addListener/removeListener() is called,
     * make a defensive copy of the array of listener before changing the array, and use it for
     * the rest of the callbacks until the emit call has completed.
     * @private
     */
    defendCallbacks: function() {

      for ( var i = this.listenersToEmitTo.length - 1; i >= 0; i-- ) {

        // Once we meet a level that was already defended, we can stop, since all previous levels are also defended
        if ( this.listenersToEmitTo[ i ].defended ) {
          break;
        }
        else {
          var defendedListeners = this.listeners.slice();

          // Mark copies as 'defended' so that it will use the original listeners when emit started and not the modified list.
          defendedListeners.defended = true;
          this.listenersToEmitTo[ i ] = defendedListeners;
        }
      }
    },

    /**
     * Emits a single event.
     * This method is called many times in a simulation and must be well-optimized.
     * @public
     */
    emit: function() {
      this.startEvent( 'model', 'emitted' );
      this.listenersToEmitTo.push( this.listeners );
      var lastEntry = this.listenersToEmitTo.length - 1;

      for ( var i = 0; i < this.listenersToEmitTo[ lastEntry ].length; i++ ) {
        this.listenersToEmitTo[ lastEntry ][ i ]();
      }

      this.listenersToEmitTo.pop();
      this.endEvent();
    },

    /**
     * Emits a single event with one argument.  This is a copy-paste of emit() for performance reasons.
     * @param {*} arg0
     * @public
     */
    emit1: function( arg0 ) {

      // TODO: name the args for the data stream
      this.tandem.isSuppliedAndEnabled() && this.startEvent( 'model', 'emitted', {
        args: [ this.phetioType.parameterTypes[ 0 ].toStateObject( arg0 ) ]
      } );
      this.listenersToEmitTo.push( this.listeners );
      var lastEntry = this.listenersToEmitTo.length - 1;

      for ( var i = 0; i < this.listenersToEmitTo[ lastEntry ].length; i++ ) {
        this.listenersToEmitTo[ lastEntry ][ i ]( arg0 );
      }

      this.listenersToEmitTo.pop();
      this.tandem.isSuppliedAndEnabled() && this.endEvent();
    },

    /**
     * Emits a single event with two arguments.  This is a copy-paste of emit() for performance reasons.
     * @param {*} arg0
     * @param {*} arg1
     * @public
     */
    emit2: function( arg0, arg1 ) {
      this.tandem.isSuppliedAndEnabled() && this.startEvent( 'model', 'emitted', {
        args: [
          this.phetioType.parameterTypes[ 0 ].toStateObject( arg0 ),
          this.phetioType.parameterTypes[ 1 ].toStateObject( arg1 )
        ]
      } );
      this.listenersToEmitTo.push( this.listeners );
      var lastEntry = this.listenersToEmitTo.length - 1;

      for ( var i = 0; i < this.listenersToEmitTo[ lastEntry ].length; i++ ) {
        this.listenersToEmitTo[ lastEntry ][ i ]( arg0, arg1 );
      }

      this.listenersToEmitTo.pop();
      this.tandem.isSuppliedAndEnabled() && this.endEvent();
    },

    /**
     * Emits a single event with three arguments.  This is a copy-paste of emit() for performance reasons.
     * @param {*} arg0
     * @param {*} arg1
     * @param {*} arg2
     * @public
     */
    emit3: function( arg0, arg1, arg2 ) {
      this.tandem.isSuppliedAndEnabled() && this.startEvent( 'model', 'emitted', {
        args: [
          this.phetioType.parameterTypes[ 0 ].toStateObject( arg0 ),
          this.phetioType.parameterTypes[ 1 ].toStateObject( arg1 ),
          this.phetioType.parameterTypes[ 2 ].toStateObject( arg2 )
        ]
      } );
      this.listenersToEmitTo.push( this.listeners );
      var lastEntry = this.listenersToEmitTo.length - 1;

      for ( var i = 0; i < this.listenersToEmitTo[ lastEntry ].length; i++ ) {
        this.listenersToEmitTo[ lastEntry ][ i ]( arg0, arg1, arg2 );
      }

      this.listenersToEmitTo.pop();
      this.tandem.isSuppliedAndEnabled() && this.endEvent();
    },

    /**
     * Emits a single event with three arguments.  This is a copy-paste of emit() for performance reasons.
     * @param {*} arg0
     * @param {*} arg1
     * @param {*} arg2
     * @param {*} arg3
     * @public
     */
    emit4: function( arg0, arg1, arg2, arg3 ) {
      this.tandem.isSuppliedAndEnabled() && this.startEvent( 'model', 'emitted', {
        args: [
          this.phetioType.parameterTypes[ 0 ].toStateObject( arg0 ),
          this.phetioType.parameterTypes[ 1 ].toStateObject( arg1 ),
          this.phetioType.parameterTypes[ 2 ].toStateObject( arg2 ),
          this.phetioType.parameterTypes[ 3 ].toStateObject( arg3 )
        ]
      } );
      this.listenersToEmitTo.push( this.listeners );
      var lastEntry = this.listenersToEmitTo.length - 1;

      for ( var i = 0; i < this.listenersToEmitTo[ lastEntry ].length; i++ ) {
        this.listenersToEmitTo[ lastEntry ][ i ]( arg0, arg1, arg2, arg3 );
      }

      this.listenersToEmitTo.pop();
      this.tandem.isSuppliedAndEnabled() && this.endEvent();
    },

    /**
     * Checks whether a listener is registered with this Emitter
     * @param {function} listener
     * @returns {boolean}
     * @public
     */
    hasListener: function( listener ) {
      assert && assert( arguments.length === 1, 'Emitter.hasListener should be called with 1 argument' );
      return this.listeners.indexOf( listener ) >= 0;
    },

    /**
     * Returns true if there are any listeners.
     * @returns {boolean}
     * @public
     */
    hasListeners: function() {
      assert && assert( arguments.length === 0, 'Emitter.hasListeners should be called without arguments' );
      return this.listeners.length > 0;
    },

    /**
     * Returns the number of listeners.
     * @returns {number}
     * @public
     */
    getListenerCount: function() {
      return this.listeners.length;
    }
  } );
} );<|endoftext|>


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

/**
 * Horizontal slider.
 *
 * Moved from beers-law-lab/EvaporationSlider on 9/15/2013
 * see https://github.com/phetsims/sun/issues/9
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var Dimension2 = require( 'DOT/Dimension2' );
  var inherit = require( 'PHET_CORE/inherit' );
  var LinearFunction = require( 'DOT/LinearFunction' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Property = require( 'AXON/Property' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Shape = require( 'KITE/Shape' );
  var SimpleDragHandler = require( 'SCENERY/input/SimpleDragHandler' );
  var ButtonListener = require( 'SCENERY/input/ButtonListener' );
  var Input = require( 'SCENERY/input/Input' );
  var Util = require( 'DOT/Util' );

  /**
   * @param {Property.<number>} valueProperty
   * @param { {min:number, max:number} } range
   * @param {Object} [options]
   * @constructor
   */
  function HSlider( valueProperty, range, options ) {

    var thisSlider = this;
    Node.call( thisSlider );

    options = _.extend( {
      // track
      trackSize: new Dimension2( 100, 5 ),
      trackFill: 'white',
      trackStroke: 'black',
      trackLineWidth: 1,
      // {Node} optional thumb, replaces the default. Client is responsible for highlighting and disabling. Centered in the track.
      // If you are using the default thumb, see ThumbNode constructor for additional pass-through options.
      thumbNode: null,
      // ticks
      tickLabelSpacing: 6,
      majorTickLength: 25,
      majorTickStroke: 'black',
      majorTickLineWidth: 1,
      minorTickLength: 10,
      minorTickStroke: 'black',
      minorTickLineWidth: 1,
      // other
      cursor: 'pointer',
      enabledProperty: new Property( true ),
      startDrag: function() {}, // called when a drag sequence starts
      endDrag: function() {}, // called when a drag sequence ends
      constrainValue: function( value ) { return value; }, // called before valueProperty is set
      // tandem
      tandem: null
    }, options );
    this.options = options; // @private TODO save only the options that are needed by prototype functions
    this.enabledProperty = options.enabledProperty;

    // @private ticks are added to this parent, so they are behind the knob
    thisSlider.ticksParent = new Node();
    thisSlider.addChild( thisSlider.ticksParent );

    // @private mapping between value and track position
    thisSlider.valueToPosition = new LinearFunction( range.min, range.max, 0, options.trackSize.width, true /* clamp */ );

    // @private track
    thisSlider.track = new Rectangle( 0, 0, options.trackSize.width, options.trackSize.height,
      { fill: options.trackFill, stroke: options.trackStroke, lineWidth: options.trackLineWidth } );
    thisSlider.addChild( thisSlider.track );

    // click in the track to change the value, continue dragging if desired
    var trackHandler = new SimpleDragHandler( {

      handleTrackEvent: function( event, trail ) {
        if ( thisSlider.enabledProperty.get() ) {
          var transform = trail.subtrailTo( thisSlider ).getTransform();
          var x = transform.inversePosition2( event.pointer.point ).x;
          var value = thisSlider.valueToPosition.inverse( x );
          valueProperty.set( options.constrainValue( value ) );
        }
      },

      start: function( event, trail ) {
        if ( thisSlider.enabledProperty.get() ) {
          thisSlider.trigger1( 'startedCallbacksForTrackDragStarted', valueProperty.get() );
          options.startDrag();
          thisSlider.trigger1( 'endedCallbacksForTrackDragStarted', valueProperty.get() );
        }
        this.handleTrackEvent( event, trail );
      },

      drag: function( event, trail ) {
        thisSlider.trigger1( 'startedCallbacksForTrackDrag', valueProperty.get() );
        this.handleTrackEvent( event, trail );
        thisSlider.trigger1( 'endedCallbacksForTrackDrag', valueProperty.get() );
      },

      end: function() {
        if ( thisSlider.enabledProperty.get() ) {
          thisSlider.trigger1( 'startedCallbacksForTrackDragEnded', valueProperty.get() );
          options.endDrag();
          thisSlider.trigger1( 'endedCallbacksForTrackDragEnded', valueProperty.get() );
        }
      }
    } );
    thisSlider.track.addInputListener( trackHandler );

    // thumb
    var thumbNode = options.thumbNode || new ThumbNode( this.enabledProperty, options );

    // Make the thumb focusable for keyboard accessibility
    thumbNode.focusable = true;

    thumbNode.centerY = thisSlider.track.centerY;
    thisSlider.addChild( thumbNode );

    // Keyboard accessibility
    thumbNode.addInputListener( {
      keydown: function( event, trail ) {
        var keyCode = event.domEvent.keyCode;
        var delta = keyCode === Input.KEY_LEFT_ARROW || keyCode === Input.KEY_DOWN_ARROW ? -1 :
                    keyCode === Input.KEY_RIGHT_ARROW || keyCode === Input.KEY_UP_ARROW ? +1 :
                    0;
        var clampedValue = Util.clamp( valueProperty.get() + (range.max - range.min) * 0.1 * delta, range.min, range.max );
        valueProperty.set( options.constrainValue( clampedValue ) );
      }
    } );

    // thumb touch area
    if ( !options.thumbNode ) {
      var dx = 0.5 * thumbNode.width;
      var dy = 0.25 * thumbNode.height;
      thumbNode.touchArea = Shape.rectangle( ( -thumbNode.width / 2 ) - dx, ( -thumbNode.height / 2 ) - dy, thumbNode.width + dx + dx, thumbNode.height + dy + dy );
    }

    // update value when thumb is dragged
    var thumbHandler = new SimpleDragHandler( {

      // x-offset between initial click and thumb's origin
      clickXOffset: 0,

      allowTouchSnag: true,

      start: function( event, trail ) {
        if ( thisSlider.enabledProperty.get() ) {
          thisSlider.trigger1( 'startedCallbacksForDragStarted', valueProperty.get() );
          options.startDrag();

          var transform = trail.subtrailTo( thisSlider ).getTransform();
          this.clickXOffset = transform.inversePosition2( event.pointer.point ).x - thumbNode.x;

          thisSlider.trigger1( 'endedCallbacksForDragStarted', valueProperty.get() );
        }
      },

      drag: function( event, trail ) {
        if ( thisSlider.enabledProperty.get() ) {
          thisSlider.trigger1( 'startedCallbacksForDragged', valueProperty.get() );
          var transform = trail.subtrailTo( thisSlider ).getTransform(); // we only want the transform to our parent
          var x = transform.inversePosition2( event.pointer.point ).x - this.clickXOffset;
          var newValue = thisSlider.valueToPosition.inverse( x );

          valueProperty.set( options.constrainValue( newValue ) );

          thisSlider.trigger1( 'endedCallbacksForDragged', valueProperty.get() );
        }
      },

      end: function() {
        if ( thisSlider.enabledProperty.get() ) {
          thisSlider.trigger1( 'startedCallbacksForDragEnded', valueProperty.get() );
          options.endDrag();
          thisSlider.trigger1( 'endedCallbacksForDragEnded', valueProperty.get() );
        }
      }
    } );
    thumbNode.addInputListener( thumbHandler );

    // @private enable/disable
    var enabledObserver = function( enabled ) {
      thisSlider.cursor = thisSlider.enabledProperty.get() ? options.cursor : 'default';
      if ( !enabled ) {
        if ( thumbHandler.dragging ) { thumbHandler.endDrag(); }
        if ( trackHandler.dragging ) { trackHandler.endDrag(); }
      }
    };
    thisSlider.enabledProperty.link( enabledObserver ); // must be unlinked in disposeHSlider

    // @private update thumb location when value changes
    var valueObserver = function( value ) {
      thumbNode.centerX = thisSlider.valueToPosition( value );
    };
    valueProperty.link( valueObserver ); // must be unlinked in disposeHSlider

    // @private Called by dispose
    this.disposeHSlider = function() {
      thumbNode.dispose && thumbNode.dispose(); // in case a custom thumb is provided via options.thumbNode that doesn't implement dispose
      valueProperty.unlink( valueObserver );
      thisSlider.enabledProperty.unlink( enabledObserver );
      options.tandem && options.tandem.removeInstance( thisSlider );
    };

    thisSlider.mutate( options );

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

  inherit( Node, HSlider, {

    // Ensures that this object is eligible for GC.
    dispose: function() {
      this.disposeHSlider();
    },

    /**
     * Adds a major tick mark.
     * @param {number} value
     * @param {Node} [label] optional
     */
    addMajorTick: function( value, label ) {
      this.addTick( value, label, this.options.majorTickLength, this.options.majorTickStroke, this.options.majorTickLineWidth );
    },

    /**
     * Adds a minor tick mark.
     * @param {number} value
     * @param {Node} [label] optional
     */
    addMinorTick: function( value, label ) {
      this.addTick( value, label, this.options.minorTickLength, this.options.minorTickStroke, this.options.minorTickLineWidth );
    },

    /*
     * Adds a tick mark above the track.
     * @param {number} value
     * @param {Node} [label] optional
     * @param {number} length
     * @param {number} stroke
     * @param {number} lineWidth
     * @private
     */
    addTick: function( value, label, length, stroke, lineWidth ) {
      var labelX = this.valueToPosition( value );
      // ticks
      var tick = new Path( new Shape()
          .moveTo( labelX, this.track.top )
          .lineTo( labelX, this.track.top - length ),
        { stroke: stroke, lineWidth: lineWidth } );
      this.ticksParent.addChild( tick );
      // label
      if ( label ) {
        this.ticksParent.addChild( label );
        label.centerX = tick.centerX;
        label.bottom = tick.top - this.options.tickLabelSpacing;
      }
    },

    setEnabled: function( enabled ) { this.enabledProperty.value = enabled; },
    set enabled( value ) { this. setEnabled( value ); },

    getEnabled: function() { return this.enabledProperty.value; },
    get enabled() { return this. getEnabled(); }
  } );

  /**
   * Default thumb, a rectangle with a vertical white line down the center
   * @param {Property.<boolean>} enabledProperty
   * @param {Object} [options] see HSlider constructor
   * @constructor
   */
  function ThumbNode( enabledProperty, options ) {

    options = _.extend( {
      // default thumb (ignored if thumbNode is provided)
      thumbSize: new Dimension2( 22, 45 ),
      thumbFillEnabled: 'rgb(50,145,184)',
      thumbFillHighlighted: 'rgb(71,207,255)',
      thumbFillDisabled: '#F0F0F0',
      thumbStroke: 'black',
      thumbLineWidth: 1,
      thumbCenterLineStroke: 'white'
    }, options );

    var thisNode = this;

    // rectangle
    var arcWidth = 0.25 * options.thumbSize.width;
    Rectangle.call( thisNode,
      -options.thumbSize.width / 2, -options.thumbSize.height / 2,
      options.thumbSize.width, options.thumbSize.height,
      arcWidth, arcWidth,
      {
        fill: enabledProperty.get() ? options.thumbFillEnabled : options.thumbFillDisabled,
        stroke: options.thumbStroke,
        lineWidth: options.thumbLineWidth,
        cachedPaints: [
          options.thumbFillHighlighted, options.thumbFillEnabled, options.thumbFillDisabled
        ]
      } );

    // vertical line down the center
    var centerLineYMargin = 3;
    thisNode.addChild( new Path( Shape.lineSegment(
        0, -( options.thumbSize.height / 2 ) + centerLineYMargin,
        0, ( options.thumbSize.height / 2 ) - centerLineYMargin ),
      { stroke: options.thumbCenterLineStroke } ) );

    // highlight thumb on pointer over
    thisNode.addInputListener( new ButtonListener( {
      over: function( event ) {
        console.log( 'ThumbNode.over' );//XXX
        if ( enabledProperty.get() ) { thisNode.fill = options.thumbFillHighlighted; }
      },
      up: function( event ) {
        console.log( 'ThumbNode.up' );//XXX
        if ( enabledProperty.get() ) { thisNode.fill = options.thumbFillEnabled; }
      }
    } ) );

    // @private enable/disable the look of the thumb
    var enabledObserver = function( enabled ) {
      thisNode.fill = enabled ? options.thumbFillEnabled : options.thumbFillDisabled;
    };
    enabledProperty.link( enabledObserver ); // must be unlinked in disposeThumbNode

    // @private Called by dispose
    this.disposeThumbNode = function() {
      enabledProperty.unlink( enabledObserver );
    };
  }

  inherit( Rectangle, ThumbNode, {

    // Ensures that this object is eligible for GC.
    dispose: function() {
      this.disposeThumbNode();
    }
  } );

  return HSlider;
} );<commit_msg>fixed an issue with where the handleTrackEvent method was being called<commit_after>// Copyright 2002-2013, University of Colorado Boulder

/**
 * Horizontal slider.
 *
 * Moved from beers-law-lab/EvaporationSlider on 9/15/2013
 * see https://github.com/phetsims/sun/issues/9
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var Dimension2 = require( 'DOT/Dimension2' );
  var inherit = require( 'PHET_CORE/inherit' );
  var LinearFunction = require( 'DOT/LinearFunction' );
  var Node = require( 'SCENERY/nodes/Node' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Property = require( 'AXON/Property' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var Shape = require( 'KITE/Shape' );
  var SimpleDragHandler = require( 'SCENERY/input/SimpleDragHandler' );
  var ButtonListener = require( 'SCENERY/input/ButtonListener' );
  var Input = require( 'SCENERY/input/Input' );
  var Util = require( 'DOT/Util' );

  /**
   * @param {Property.<number>} valueProperty
   * @param { {min:number, max:number} } range
   * @param {Object} [options]
   * @constructor
   */
  function HSlider( valueProperty, range, options ) {

    var thisSlider = this;
    Node.call( thisSlider );

    options = _.extend( {
      // track
      trackSize: new Dimension2( 100, 5 ),
      trackFill: 'white',
      trackStroke: 'black',
      trackLineWidth: 1,
      // {Node} optional thumb, replaces the default. Client is responsible for highlighting and disabling. Centered in the track.
      // If you are using the default thumb, see ThumbNode constructor for additional pass-through options.
      thumbNode: null,
      // ticks
      tickLabelSpacing: 6,
      majorTickLength: 25,
      majorTickStroke: 'black',
      majorTickLineWidth: 1,
      minorTickLength: 10,
      minorTickStroke: 'black',
      minorTickLineWidth: 1,
      // other
      cursor: 'pointer',
      enabledProperty: new Property( true ),
      startDrag: function() {}, // called when a drag sequence starts
      endDrag: function() {}, // called when a drag sequence ends
      constrainValue: function( value ) { return value; }, // called before valueProperty is set
      // tandem
      tandem: null
    }, options );
    this.options = options; // @private TODO save only the options that are needed by prototype functions
    this.enabledProperty = options.enabledProperty;

    // @private ticks are added to this parent, so they are behind the knob
    thisSlider.ticksParent = new Node();
    thisSlider.addChild( thisSlider.ticksParent );

    // @private mapping between value and track position
    thisSlider.valueToPosition = new LinearFunction( range.min, range.max, 0, options.trackSize.width, true /* clamp */ );

    // @private track
    thisSlider.track = new Rectangle( 0, 0, options.trackSize.width, options.trackSize.height,
      { fill: options.trackFill, stroke: options.trackStroke, lineWidth: options.trackLineWidth } );
    thisSlider.addChild( thisSlider.track );

    // click in the track to change the value, continue dragging if desired
    var trackHandler = new SimpleDragHandler( {

      handleTrackEvent: function( event, trail ) {
        if ( thisSlider.enabledProperty.get() ) {
          var transform = trail.subtrailTo( thisSlider ).getTransform();
          var x = transform.inversePosition2( event.pointer.point ).x;
          var value = thisSlider.valueToPosition.inverse( x );
          valueProperty.set( options.constrainValue( value ) );
        }
      },

      start: function( event, trail ) {
        if ( thisSlider.enabledProperty.get() ) {
          thisSlider.trigger1( 'startedCallbacksForTrackDragStarted', valueProperty.get() );
          options.startDrag();
          this.handleTrackEvent( event, trail );
          thisSlider.trigger1( 'endedCallbacksForTrackDragStarted', valueProperty.get() );
        }
      },

      drag: function( event, trail ) {
        thisSlider.trigger1( 'startedCallbacksForTrackDrag', valueProperty.get() );
        this.handleTrackEvent( event, trail );
        thisSlider.trigger1( 'endedCallbacksForTrackDrag', valueProperty.get() );
      },

      end: function() {
        if ( thisSlider.enabledProperty.get() ) {
          thisSlider.trigger1( 'startedCallbacksForTrackDragEnded', valueProperty.get() );
          options.endDrag();
          thisSlider.trigger1( 'endedCallbacksForTrackDragEnded', valueProperty.get() );
        }
      }
    } );
    thisSlider.track.addInputListener( trackHandler );

    // thumb
    var thumbNode = options.thumbNode || new ThumbNode( this.enabledProperty, options );

    // Make the thumb focusable for keyboard accessibility
    thumbNode.focusable = true;

    thumbNode.centerY = thisSlider.track.centerY;
    thisSlider.addChild( thumbNode );

    // Keyboard accessibility
    thumbNode.addInputListener( {
      keydown: function( event, trail ) {
        var keyCode = event.domEvent.keyCode;
        var delta = keyCode === Input.KEY_LEFT_ARROW || keyCode === Input.KEY_DOWN_ARROW ? -1 :
                    keyCode === Input.KEY_RIGHT_ARROW || keyCode === Input.KEY_UP_ARROW ? +1 :
                    0;
        var clampedValue = Util.clamp( valueProperty.get() + (range.max - range.min) * 0.1 * delta, range.min, range.max );
        valueProperty.set( options.constrainValue( clampedValue ) );
      }
    } );

    // thumb touch area
    if ( !options.thumbNode ) {
      var dx = 0.5 * thumbNode.width;
      var dy = 0.25 * thumbNode.height;
      thumbNode.touchArea = Shape.rectangle( ( -thumbNode.width / 2 ) - dx, ( -thumbNode.height / 2 ) - dy, thumbNode.width + dx + dx, thumbNode.height + dy + dy );
    }

    // update value when thumb is dragged
    var thumbHandler = new SimpleDragHandler( {

      // x-offset between initial click and thumb's origin
      clickXOffset: 0,

      allowTouchSnag: true,

      start: function( event, trail ) {
        if ( thisSlider.enabledProperty.get() ) {
          thisSlider.trigger1( 'startedCallbacksForDragStarted', valueProperty.get() );
          options.startDrag();

          var transform = trail.subtrailTo( thisSlider ).getTransform();
          this.clickXOffset = transform.inversePosition2( event.pointer.point ).x - thumbNode.x;

          thisSlider.trigger1( 'endedCallbacksForDragStarted', valueProperty.get() );
        }
      },

      drag: function( event, trail ) {
        if ( thisSlider.enabledProperty.get() ) {
          thisSlider.trigger1( 'startedCallbacksForDragged', valueProperty.get() );
          var transform = trail.subtrailTo( thisSlider ).getTransform(); // we only want the transform to our parent
          var x = transform.inversePosition2( event.pointer.point ).x - this.clickXOffset;
          var newValue = thisSlider.valueToPosition.inverse( x );

          valueProperty.set( options.constrainValue( newValue ) );

          thisSlider.trigger1( 'endedCallbacksForDragged', valueProperty.get() );
        }
      },

      end: function() {
        if ( thisSlider.enabledProperty.get() ) {
          thisSlider.trigger1( 'startedCallbacksForDragEnded', valueProperty.get() );
          options.endDrag();
          thisSlider.trigger1( 'endedCallbacksForDragEnded', valueProperty.get() );
        }
      }
    } );
    thumbNode.addInputListener( thumbHandler );

    // @private enable/disable
    var enabledObserver = function( enabled ) {
      thisSlider.cursor = thisSlider.enabledProperty.get() ? options.cursor : 'default';
      if ( !enabled ) {
        if ( thumbHandler.dragging ) { thumbHandler.endDrag(); }
        if ( trackHandler.dragging ) { trackHandler.endDrag(); }
      }
    };
    thisSlider.enabledProperty.link( enabledObserver ); // must be unlinked in disposeHSlider

    // @private update thumb location when value changes
    var valueObserver = function( value ) {
      thumbNode.centerX = thisSlider.valueToPosition( value );
    };
    valueProperty.link( valueObserver ); // must be unlinked in disposeHSlider

    // @private Called by dispose
    this.disposeHSlider = function() {
      thumbNode.dispose && thumbNode.dispose(); // in case a custom thumb is provided via options.thumbNode that doesn't implement dispose
      valueProperty.unlink( valueObserver );
      thisSlider.enabledProperty.unlink( enabledObserver );
      options.tandem && options.tandem.removeInstance( thisSlider );
    };

    thisSlider.mutate( options );

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

  inherit( Node, HSlider, {

    // Ensures that this object is eligible for GC.
    dispose: function() {
      this.disposeHSlider();
    },

    /**
     * Adds a major tick mark.
     * @param {number} value
     * @param {Node} [label] optional
     */
    addMajorTick: function( value, label ) {
      this.addTick( value, label, this.options.majorTickLength, this.options.majorTickStroke, this.options.majorTickLineWidth );
    },

    /**
     * Adds a minor tick mark.
     * @param {number} value
     * @param {Node} [label] optional
     */
    addMinorTick: function( value, label ) {
      this.addTick( value, label, this.options.minorTickLength, this.options.minorTickStroke, this.options.minorTickLineWidth );
    },

    /*
     * Adds a tick mark above the track.
     * @param {number} value
     * @param {Node} [label] optional
     * @param {number} length
     * @param {number} stroke
     * @param {number} lineWidth
     * @private
     */
    addTick: function( value, label, length, stroke, lineWidth ) {
      var labelX = this.valueToPosition( value );
      // ticks
      var tick = new Path( new Shape()
          .moveTo( labelX, this.track.top )
          .lineTo( labelX, this.track.top - length ),
        { stroke: stroke, lineWidth: lineWidth } );
      this.ticksParent.addChild( tick );
      // label
      if ( label ) {
        this.ticksParent.addChild( label );
        label.centerX = tick.centerX;
        label.bottom = tick.top - this.options.tickLabelSpacing;
      }
    },

    setEnabled: function( enabled ) { this.enabledProperty.value = enabled; },
    set enabled( value ) { this. setEnabled( value ); },

    getEnabled: function() { return this.enabledProperty.value; },
    get enabled() { return this. getEnabled(); }
  } );

  /**
   * Default thumb, a rectangle with a vertical white line down the center
   * @param {Property.<boolean>} enabledProperty
   * @param {Object} [options] see HSlider constructor
   * @constructor
   */
  function ThumbNode( enabledProperty, options ) {

    options = _.extend( {
      // default thumb (ignored if thumbNode is provided)
      thumbSize: new Dimension2( 22, 45 ),
      thumbFillEnabled: 'rgb(50,145,184)',
      thumbFillHighlighted: 'rgb(71,207,255)',
      thumbFillDisabled: '#F0F0F0',
      thumbStroke: 'black',
      thumbLineWidth: 1,
      thumbCenterLineStroke: 'white'
    }, options );

    var thisNode = this;

    // rectangle
    var arcWidth = 0.25 * options.thumbSize.width;
    Rectangle.call( thisNode,
      -options.thumbSize.width / 2, -options.thumbSize.height / 2,
      options.thumbSize.width, options.thumbSize.height,
      arcWidth, arcWidth,
      {
        fill: enabledProperty.get() ? options.thumbFillEnabled : options.thumbFillDisabled,
        stroke: options.thumbStroke,
        lineWidth: options.thumbLineWidth,
        cachedPaints: [
          options.thumbFillHighlighted, options.thumbFillEnabled, options.thumbFillDisabled
        ]
      } );

    // vertical line down the center
    var centerLineYMargin = 3;
    thisNode.addChild( new Path( Shape.lineSegment(
        0, -( options.thumbSize.height / 2 ) + centerLineYMargin,
        0, ( options.thumbSize.height / 2 ) - centerLineYMargin ),
      { stroke: options.thumbCenterLineStroke } ) );

    // highlight thumb on pointer over
    thisNode.addInputListener( new ButtonListener( {
      over: function( event ) {
        console.log( 'ThumbNode.over' );//XXX
        if ( enabledProperty.get() ) { thisNode.fill = options.thumbFillHighlighted; }
      },
      up: function( event ) {
        console.log( 'ThumbNode.up' );//XXX
        if ( enabledProperty.get() ) { thisNode.fill = options.thumbFillEnabled; }
      }
    } ) );

    // @private enable/disable the look of the thumb
    var enabledObserver = function( enabled ) {
      thisNode.fill = enabled ? options.thumbFillEnabled : options.thumbFillDisabled;
    };
    enabledProperty.link( enabledObserver ); // must be unlinked in disposeThumbNode

    // @private Called by dispose
    this.disposeThumbNode = function() {
      enabledProperty.unlink( enabledObserver );
    };
  }

  inherit( Rectangle, ThumbNode, {

    // Ensures that this object is eligible for GC.
    dispose: function() {
      this.disposeThumbNode();
    }
  } );

  return HSlider;
} );<|endoftext|>


<commit_before>function Network() {
    // To server functions and constants
    function sendGameRequest(numPlayers) {
        var message = {"NumPlayers": numPlayers};
        sendMessage(NEW_GAME_CMD, message);
    }

    function sendClientInfo(token) {
        var message = {"Token": token};
        sendMessage(CLIENT_INFO_CMD, message);
    }

    function sendViewWorld() {
        sendMessage(VIEW_WORLD_CMD, {});
    }

    function sendUnitMove(unit, move) {
        var message = {move: []};
        for (var i = 0; i < move.length; i++) {
            message.move.push(move[i].position);
        }

        sendMessage(MOVE_UNIT_CMD, message);
    }

    CLIENT_INFO_CMD = "clientInfo";
    NEW_GAME_CMD = "newGame";
    VIEW_WORLD_CMD = "view";
    MOVE_UNIT_CMD = "move";

    host = window.location.host.split(":")[0];
    port = getPortNum();
    ws_addr = "ws://" + host + ":" + port + "/ws";
    conn = new WebSocket(ws_addr);
    DELIM = ":";

    // This function expects the message as a javascript object
    function sendMessage(command, messageObj) {
        var jsonified = JSON.stringify(messageObj);
        packet = command + DELIM + jsonified;
        conn.send(packet);
    }

    // From server functions and constants
    this.packetHandlers = {
        "view": parseViewWorld.bind(this),
        "clientinfo": parseClientInfo.bind(this),
        "new": parseGameRequestSuccess.bind(this),
        "move": parseMoveResponse.bind(this)
    };

    function logTemplateComp(name, realResponse) {
        if (!verifyStructure(responseTemplates[name], realResponse)) {
            console.log("The", name, "template does not match. Proceed with caution");
        }
    }

    function parseViewWorld(status, viewWorld) {
        this.logTemplateComp("viewWorld", viewWorld);
        var terrain = viewWorld.terrain;
        if (game.world === undefined) {
            // TODO check if the terrain actually exists first
            game.world = new World(terrain.length, terrain[0].length);
            game.currentState = "CAMERA_CONTROL";
        }
        for (var x = 0; x < terrain.length; x++) {
            for (var y = 0; y < terrain[x].length; y++) {
                // TODO terrain cells may someday be the actual JSON
                terrain[x][y] = terrainLookup(terrain[x][y], x, y);
            }
        }
        game.world.initialize(terrain);

        var players = viewWorld.players;
        for (var i = players.length - 1; i >= 0; i--) {
            var player = players[i];
            game.world.addOrUpdatePlayer(player.id, player.nation,
                                         player.team);
        }
        game.currentPlayerId = viewWorld.turnOwner;

        var units = viewWorld.units;
        for (i = units.length - 1; i >= 0; i--) {
            // this should be tank, but I've only got wizards right now
            // not sent movementType right now
            var unit = units[i];
            var position = unit.position;
            var movement = unit.movement;
            var unitnation = parseInt(unit.nation) + unit.name;
            var spriteName = UnitNationSprite[unitnation];
            if (!game.world.findUnit(position.x, position.y)) {
                game.world.addUnit(unit.nation, spriteName, position,
                                   movement.distance, movement.type,
                                   movement.speeds, unit.health,
                                   unit.nation, unit.name, unit.canMove);
            } else {
                console.log("Verifing the unit could be useful here");
            }
        }
    }

    function parseClientInfo(status) {
        console.log("Your client info has been received, sending game request");
        this.sendGameRequest(parseInt(desiredPlayers));
    }

    function parseGameRequestSuccess(status) {
        console.log("Congratulations, you've been matched to a game!");
        this.sendViewWorld();
    }

    function parseMoveResponse(status) {
        if (status === 0) {
            var unit = unitControlState.unit;
            var path = unitControlState.path;
            if (path) {
                // tell the renderer about the move
                var unitSprite = assets.spriteManager.getSprite(unit.spriteName);
                // unitSprite.movements = unitSprite.movements.concat(translatePathToMoves(path));
                var goal = path[path.length - 1];
                unit.pos.x = goal.position.x;
                unit.pos.y = goal.position.y;
                // shim, while the mac tab crash bug is still happening
                unitSprite.drawPos = goal.position;
                unit.canMove = false;
                unitControlState.reset();
            } else {
                console.log("Your path is null, man");
            }
        } else {
            console.log("Go a bad status from server:", status);
        }
    }

    conn.onopen = function () {
        this.sendClientInfo(getPlayerId());
    }.bind(this);

    // Log errors
    conn.onerror = function (error) {
        console.log('WebSocket Error ' + error);
    };

    // Log messages from the server
    conn.onmessage = function (e) {
        var cmds = trimSocketChars(e.data.actualSplit(DELIM, 2));
        if (cmds.length == 2) {
            var pkt_type = cmds[0];
            var response = JSON.parse(cmds[1]);
            var dataObj = response.payload;
            var status = response.status;
            // It's likely the individual handlers would be interested in the
            // failure condition
            if (status >= 0) {
                var handler = this.packetHandlers[pkt_type];
                if (handler !== undefined) {
                    handler(status, dataObj);
                } else {
                    console.log("Recieved unknown command:", pkt_type);
                }
            } else {
                console.log("Received failure from server:", cmds);
            }
        } else {
            console.log("Received malformed command:", cmds);
        }
    }.bind(this);

    this.sendGameRequest = function(numPlayers) {
        sendGameRequest(numPlayers);
    };

    this.sendClientInfo = function(playerId) {
        var token = $.cookie("token");
        if (token) {
            sendClientInfo(token);
        } else {
            alert("You do not have a token, can't start game");
        }
    };

    this.sendViewWorld = function() {
        sendViewWorld();
    };

    this.sendUnitMove = function(unit, move) {
        sendUnitMove(unit, move);
    };

    this.logTemplateComp = function(name, realResponse) {
        logTemplateComp(name, realResponse);
    };
}

function trimSocketChars(cmds) {
    cmds[cmds.length - 1] = cmds[cmds.length - 1].replace(/\x00/gi, "");
    return cmds;
}

String.prototype.actualSplit = function(sep, maxsplit) {
    var split = this.split(sep);
    var excess = split.slice(maxsplit - 1, split.length).join(":");
    if (excess.length > 0) {
        return maxsplit ? split.slice(0, maxsplit - 1).concat(excess) : split;
    } else {
        return maxsplit ? split.slice(0, maxsplit - 1) : split;
    }
};
<commit_msg>Add initial support for chat<commit_after>function Network() {
    // To server functions and constants
    function sendGameRequest(numPlayers) {
        var message = {"NumPlayers": numPlayers};
        sendMessage(NEW_GAME_CMD, message);
    }

    function sendClientInfo(token) {
        var message = {"Token": token};
        sendMessage(CLIENT_INFO_CMD, message);
    }

    function sendViewWorld() {
        sendMessage(VIEW_WORLD_CMD, {});
    }

    function sendUnitMove(unit, move) {
        var message = {move: []};
        for (var i = 0; i < move.length; i++) {
            message.move.push(move[i].position);
        }

        sendMessage(MOVE_UNIT_CMD, message);
    }

    function sendChatMessage(message) {
        sendMessage(CHAT_CMD, {"message": message})
    }

    CLIENT_INFO_CMD = "clientInfo";
    NEW_GAME_CMD = "newGame";
    VIEW_WORLD_CMD = "view";
    MOVE_UNIT_CMD = "move";
    CHAT_CMD = "chat";

    host = window.location.host.split(":")[0];
    port = getPortNum();
    ws_addr = "ws://" + host + ":" + port + "/ws";
    conn = new WebSocket(ws_addr);
    DELIM = ":";

    // This function expects the message as a javascript object
    function sendMessage(command, messageObj) {
        var jsonified = JSON.stringify(messageObj);
        packet = command + DELIM + jsonified;
        conn.send(packet);
    }

    // From server functions and constants
    this.packetHandlers = {
        "view": parseViewWorld.bind(this),
        "clientinfo": parseClientInfo.bind(this),
        "new": parseGameRequestSuccess.bind(this),
        "move": parseMoveResponse.bind(this),
        "chat": parseChatResponse.bind(this),
    };

    function logTemplateComp(name, realResponse) {
        if (!verifyStructure(responseTemplates[name], realResponse)) {
            console.log("The", name, "template does not match. Proceed with caution");
        }
    }

    function parseViewWorld(status, viewWorld) {
        this.logTemplateComp("viewWorld", viewWorld);
        var terrain = viewWorld.terrain;
        if (game.world === undefined) {
            // TODO check if the terrain actually exists first
            game.world = new World(terrain.length, terrain[0].length);
            game.currentState = "CAMERA_CONTROL";
        }
        for (var x = 0; x < terrain.length; x++) {
            for (var y = 0; y < terrain[x].length; y++) {
                // TODO terrain cells may someday be the actual JSON
                terrain[x][y] = terrainLookup(terrain[x][y], x, y);
            }
        }
        game.world.initialize(terrain);

        var players = viewWorld.players;
        for (var i = players.length - 1; i >= 0; i--) {
            var player = players[i];
            game.world.addOrUpdatePlayer(player.id, player.nation,
                                         player.team);
        }
        game.currentPlayerId = viewWorld.turnOwner;

        var units = viewWorld.units;
        for (i = units.length - 1; i >= 0; i--) {
            // this should be tank, but I've only got wizards right now
            // not sent movementType right now
            var unit = units[i];
            var position = unit.position;
            var movement = unit.movement;
            var unitnation = parseInt(unit.nation) + unit.name;
            var spriteName = UnitNationSprite[unitnation];
            if (!game.world.findUnit(position.x, position.y)) {
                game.world.addUnit(unit.nation, spriteName, position,
                                   movement.distance, movement.type,
                                   movement.speeds, unit.health,
                                   unit.nation, unit.name, unit.canMove);
            } else {
                console.log("Verifing the unit could be useful here");
            }
        }
    }

    function parseClientInfo(status) {
        console.log("Your client info has been received, sending game request");
        this.sendGameRequest(parseInt(desiredPlayers));
    }

    function parseGameRequestSuccess(status) {
        console.log("Congratulations, you've been matched to a game!");
        this.sendViewWorld();
    }

    function parseMoveResponse(status) {
        if (status === 0) {
            var unit = unitControlState.unit;
            var path = unitControlState.path;
            if (path) {
                // tell the renderer about the move
                var unitSprite = assets.spriteManager.getSprite(unit.spriteName);
                // unitSprite.movements = unitSprite.movements.concat(translatePathToMoves(path));
                var goal = path[path.length - 1];
                unit.pos.x = goal.position.x;
                unit.pos.y = goal.position.y;
                // shim, while the mac tab crash bug is still happening
                unitSprite.drawPos = goal.position;
                unit.canMove = false;
                unitControlState.reset();
            } else {
                console.log("Your path is null, man");
            }
        } else {
            console.log("Go a bad status from server:", status);
        }
    }

    function parseChatResponse(status, data) {
        console.log("Chat: " + data.message);
    }

    conn.onopen = function () {
        this.sendClientInfo(getPlayerId());
    }.bind(this);

    // Log errors
    conn.onerror = function (error) {
        console.log('WebSocket Error ' + error);
    };

    // Log messages from the server
    conn.onmessage = function (e) {
        var cmds = trimSocketChars(e.data.actualSplit(DELIM, 2));
        if (cmds.length == 2) {
            var pkt_type = cmds[0];
            var response = JSON.parse(cmds[1]);
            var dataObj = response.payload;
            var status = response.status;
            // It's likely the individual handlers would be interested in the
            // failure condition
            if (status >= 0) {
                var handler = this.packetHandlers[pkt_type];
                if (handler !== undefined) {
                    handler(status, dataObj);
                } else {
                    console.log("Recieved unknown command:", pkt_type);
                }
            } else {
                console.log("Received failure from server:", cmds);
            }
        } else {
            console.log("Received malformed command:", cmds);
        }
    }.bind(this);

    this.sendGameRequest = function(numPlayers) {
        sendGameRequest(numPlayers);
    };

    this.sendClientInfo = function(playerId) {
        var token = $.cookie("token");
        if (token) {
            sendClientInfo(token);
        } else {
            alert("You do not have a token, can't start game");
        }
    };

    this.sendViewWorld = function() {
        sendViewWorld();
    };

    this.sendUnitMove = function(unit, move) {
        sendUnitMove(unit, move);
    };

    this.logTemplateComp = function(name, realResponse) {
        logTemplateComp(name, realResponse);
    };

    this.sendChatMessage = sendChatMessage;
}

function trimSocketChars(cmds) {
    cmds[cmds.length - 1] = cmds[cmds.length - 1].replace(/\x00/gi, "");
    return cmds;
}

String.prototype.actualSplit = function(sep, maxsplit) {
    var split = this.split(sep);
    var excess = split.slice(maxsplit - 1, split.length).join(":");
    if (excess.length > 0) {
        return maxsplit ? split.slice(0, maxsplit - 1).concat(excess) : split;
    } else {
        return maxsplit ? split.slice(0, maxsplit - 1) : split;
    }
};
<|endoftext|>


<commit_before>var Elemental = Elemental || {};

Elemental.Preload = function(){};

Elemental.Preload.prototype = {
    preload: function() {

        //load game assets
        this.load.tilemap('level', 'Assets/maps/level.json', null, Phaser.Tilemap.TILED_JSON);
        this.load.image('tiles', 'Assets/maps/tiles.png');
        this.load.image('player', 'Assets/peasant.png');
        this.load.image('chest_opened', 'Assets/openchest.png');
        this.load.image('chest_closed', 'Assets/chest.png');
        this.load.spritesheet('player', 'Assets/sprite.png', 96, 128);

    },
    create: function() {
        this.state.start('Game');
    }
};<commit_msg>removed peasant<commit_after>var Elemental = Elemental || {};

Elemental.Preload = function(){};

Elemental.Preload.prototype = {
    preload: function() {

        //load game assets
        this.load.tilemap('level', 'Assets/maps/level.json', null, Phaser.Tilemap.TILED_JSON);
        this.load.image('tiles', 'Assets/maps/tiles.png');
        this.load.image('chest_opened', 'Assets/openchest.png');
        this.load.image('chest_closed', 'Assets/chest.png');
        this.load.spritesheet('player', 'Assets/sprite.png', 96, 128);

    },
    create: function() {
        this.state.start('Game');
    }
};<|endoftext|>