<commit_before>/**
 * Jquery plugin to render like contribution graph on Github.
 *
 * @see       {@link https://github.com/bachvtuan/Github-Contribution-Graph}
 * @author    bachvtuan@gmail.com
 * @license   MIT License
 * @since     0.1.0
 */

//Format string
if (!String.prototype.formatString) {
  String.prototype.formatString = function() {
    var args = arguments;
    return this.replace(/{(\d+)}/g, function(match, number) {
      return typeof args[number] != 'undefined'
        ? args[number]
        : match
      ;
    });
  };
}

(function ( $ ) {

    $.fn.github_graph = function( options ) {

        //If the number less than 10, add Zero before it
        var prettyNumber = function( number ){
          return  number < 10 ? '0' + number.toString() : number = number.toString();
        };

        /*
        Count the number on each day and store the object
        */
        var processListTimeStamp = function(list_timestamp){

          //The result will store into this varriable
          obj_timestamp = {};
          for (var i=0; i < list_timestamp.length; i++){
            var _type = typeof(list_timestamp[i]);
            var _d = _type == "number" ? new Date( list_timestamp[i] ) : new Date( list_timestamp[i].timestamp )

            var display_date = getDisplayDate( _d );
            var increase = _type == "number" ? 1 : list_timestamp[i].count ;
            if ( !obj_timestamp[ display_date ] ){
              obj_timestamp[ display_date ] = increase;
            }
            else{
              obj_timestamp[ display_date ] += increase;
            }
          }
        }

        var getDisplayDate = function( date_obj ){
          var pretty_month = prettyNumber( date_obj.getMonth() + 1);
          var pretty_date = prettyNumber(date_obj.getDate());
          return "{0}-{1}-{2}".formatString( date_obj.getFullYear(), pretty_month , pretty_date  );
        }

        var getCount = function( display_date ){
          if ( obj_timestamp[ display_date ] ){
            return obj_timestamp[ display_date];
          }
          return 0;
        }

        var getColor = function(count){
          if ( count >= settings.colors.length ){
            return settings.colors[  settings.colors.length - 1 ];
          }
          return settings.colors[ count ];
        }

        var start = function(){
          processListTimeStamp( settings.data );
          var wrap_chart = _this;

          settings.colors_length = settings.colors.length;
          var radius = settings.radius;
          var clickCallback = settings.click;

          var start_date;
          if (settings.start_date == null){
            // if set null, will get from 365 day from now
            start_date= new Date();
            start_date.setMonth( start_date.getMonth() - 12  );
          }else{
            start_date = settings.start_date;
          }

          for ( var i=0; i < 7; i++ ){
            var day = start_date.getDay();
            if (day == 0){
              //sunday
              break;
            }
            else{
              //Loop until get Sunday
              start_date.setDate( start_date.getDate() + 1 );
            }
          }

          var loop_html = "";

          //One year has 52 weeks
          var step = 13;

          var month_position = [];
          // var current_date = new Date();
          month_position.push({month_index: start_date.getMonth(), x: 0 });
          var using_month = start_date.getMonth();
          for ( var i =0; i < 52; i++ ){
            var g_x =i * step;
            var item_html = '<g transform="translate(' + g_x.toString() + ',0)">';

            for ( var j = 0; j < 7; j++ ){

              // if ( start_date > current_date ){
              //   //Break the loop
              //   break;
              // }
              var y = j * step;

              var month_in_day = start_date.getMonth();
              var data_date = getDisplayDate( start_date );
              //Check first day in week
              if ( j == 0 && month_in_day != using_month ){
                  using_month = month_in_day;
                  month_position.push({month_index: using_month, x: g_x });
              }
              //move on to next day
              start_date.setDate( start_date.getDate() + 1 );
              var count = getCount( data_date );
              var color = getColor( count );

                  item_html += '<rect class="day" width="11" height="11" y="'+ y +'" fill="'+ color + '" data-count="'+ count +'" data-date="'+ data_date +'" rx="'+radius+'" ry="'+radius+'"/>';
              }

              item_html += "</g>";
              loop_html += item_html;

          }

          //trick
          if ( month_position[1].x - month_position[0].x < 40 ){
            //Fix ugly graph by remove first item
            month_position.shift(0);
          }

          for (  var i =0; i < month_position.length; i++){
            var item = month_position[i];
            var month_name =  settings.month_names[ item.month_index ];
            loop_html += '<text x="'+ item.x +'" y="-5" class="month">'+ month_name +'</text>';
          }

          //Add Monday, Wenesday, Friday label
          loop_html +=  '<text text-anchor="middle" class="wday" dx="-10" dy="22">{0}</text>'.formatString( settings.h_days[0] )+
                        '<text text-anchor="middle" class="wday" dx="-10" dy="48">{0}</text>'.formatString( settings.h_days[1] )+
                        '<text text-anchor="middle" class="wday" dx="-10" dy="74">{0}</text>'.formatString( settings.h_days[2] );

          //Fixed size for now with width= 721 and height = 110
          var wire_html =
            '<svg width="721" height="110" viewBox="0 0 721 110"  class="js-calendar-graph-svg">'+
              '<g transform="translate(20, 20)">'+
                loop_html +
              '</g>'+
            '</svg>';

          wrap_chart.html(wire_html);

          $(_this).find(".day").on('click',function () {

            if (clickCallback){
              clickCallback($(this).attr("data-date"), parseInt($(this).attr("data-count")));
            }

          });

          $(_this).find(".day").hover(function () {
              $(this).attr("style", "stroke-width:1;stroke:rgb(1,1,1)");
            }, function() {
              $( this ).attr( "style", "stroke-width:0" );
          });

          //Mare sure off previous event
          /*$(document).off('mouseenter', _this.find('rect'), mouseEnter );
          $(document).off('mouseleave', _this.find('rect'), mouseLeave );
          $(document).on('mouseenter', _this.find('rect'), mouseEnter );
          $(document).on('mouseleave', _this.find('rect'), mouseLeave );
*/
          _this.find('rect').on("mouseenter", mouseEnter );
          _this.find('rect').on("mouseleave",mouseLeave );
          appendTooltip();

        }

        var mouseLeave =function(evt){
          $('.svg-tip').hide();
        }

        //handle event mouseenter when enter into rect element
        var mouseEnter = function(evt){

          var target_offset = $(evt.target).offset();
          var count = $(evt.target).attr('data-count');
          var date = $(evt.target).attr('data-date');

          var count_text = ( count > 1 ) ? settings.texts[1]: settings.texts[0];
          var text = "{0} {1} on {2}".formatString( count, count_text , date );

          var svg_tip = $('.svg-tip').show();
          svg_tip.html( text );
          var svg_width = Math.round( svg_tip.width()/2 + 5 )  ;
          var svg_height =  svg_tip.height() *2 + 10 ;

          svg_tip.css({top:target_offset.top - svg_height - 5});
          svg_tip.css({left:target_offset.left -svg_width});
        }
        //Append tooltip to display when mouse enter the rect element
        //Default is display:none
        var appendTooltip = function(){
          if ( $('.svg-tip').length == 0 ){
            $('body').append('

');
          }
        }

        var settings = $.extend({
          //Default init settings.colors, user can override
          colors: ['#eeeeee','#d6e685','#8cc665','#44a340','#44a340'],
          radius: 2,
          click: null,
          start_date: null,
          //List of name months
          month_names: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
          h_days : ['M','W','F'],
          //Default is empty, it can be overrided
          data:[],
        }, options );

        var _this = $(this);

        start();

    };

}( jQuery ));<commit_msg>improve display; add levels, border_color parameters<commit_after>/**
 * Jquery plugin to render like contribution graph on Github.
 *
 * @see       {@link https://github.com/bachvtuan/Github-Contribution-Graph}
 * @author    bachvtuan@gmail.com
 * @license   MIT License
 * @since     0.1.0
 */

//Format string
if (!String.prototype.formatString) {
  String.prototype.formatString = function() {
    var args = arguments;
    return this.replace(/{(\d+)}/g, function(match, number) {
      return typeof args[number] != 'undefined'
        ? args[number]
        : match
      ;
    });
  };
}

(function ( $ ) {

    $.fn.github_graph = function( options ) {

        //If the number less than 10, add Zero before it
        var prettyNumber = function( number ){
          return  number < 10 ? '0' + number.toString() : number = number.toString();
        };

        /*
        Count the number on each day and store the object
        */
        var processListTimeStamp = function(list_timestamp){

          //The result will store into this varriable
          obj_timestamp = {};
          for (var i=0; i < list_timestamp.length; i++){
            var _type = typeof(list_timestamp[i]);
            var _d = _type == "number" ? new Date( list_timestamp[i] ) : new Date( list_timestamp[i].timestamp )

            var display_date = getDisplayDate( _d );
            var increase = _type == "number" ? 1 : list_timestamp[i].count ;
            if ( !obj_timestamp[ display_date ] ){
              obj_timestamp[ display_date ] = increase;
            }
            else{
              obj_timestamp[ display_date ] += increase;
            }
          }
        }

        var getDisplayDate = function( date_obj ){
          var pretty_month = prettyNumber( date_obj.getMonth() + 1);
          var pretty_date = prettyNumber(date_obj.getDate());
          return "{0}-{1}-{2}".formatString( date_obj.getFullYear(), pretty_month , pretty_date  );
        }

        var getCount = function( display_date ){
          if ( obj_timestamp[ display_date ] ){
            return obj_timestamp[ display_date];
          }
          return 0;
        }

        var getColor = function(count) {
          const isLargeNumber = (element) => element > count;
          i = settings.levels.findIndex(isLargeNumber);
          return settings.colors[ i - 1 ];
        }

        var start = function(){
          processListTimeStamp( settings.data );
          var wrap_chart = _this;

          settings.colors_length = settings.colors.length;
          var radius = settings.radius;
          var clickCallback = settings.click;

          var start_date;
          if (settings.start_date == null) {
              // if set null, will get from 365 days from now
              start_date= new Date();
              start_date.setMonth( start_date.getMonth() - 12  );
              start_date.setDate(start_date.getDate() + 1)
           } else {
              // formats:
              // - YYYY-MM-DD
              // - YYYY/MM/DD
              start_date = new Date(settings.start_date);
          }

          end_date = new Date(start_date);
          end_date.setMonth(end_date.getMonth() + 12);
          end_date.setDate(end_date.getDate() - 1);

          var loop_html = "";
          var step = 13;

          var month_position = [];
          month_position.push({month_index: start_date.getMonth(), x: 0 });
          var using_month = start_date.getMonth();

          var week = 0;
          var g_x = week * step;
          var item_html = '<g transform="translate(' + g_x.toString() + ', 0)">';

          for(; start_date.getTime() <= end_date.getTime(); start_date.setDate(start_date.getDate() + 1)) {

              if(start_date.getDay() == 0) {
                  var item_html = '<g transform="translate(' + g_x.toString() + ', 0)">';
              }

              var month_in_day = start_date.getMonth();
              var data_date = getDisplayDate( start_date );

              if ( start_date.getDay() == 0 && month_in_day != using_month ){
                  using_month = month_in_day;
                  month_position.push({month_index: using_month, x: g_x });
              }
              var count = getCount( data_date );
              var color = getColor( count );

              var y = start_date.getDay() * step;
              item_html += '<rect class="day" width="11" height="11" y="'+ y +'" fill="'+ color + '" data-count="'+ count +'" data-date="'+ data_date +'" rx="'+radius+'" ry="'+radius+'"/>';

              if(start_date.getDay() == 6) {
                  item_html += "</g>";
                  loop_html += item_html;

                  item_html = null;

                  week ++;
                  g_x = week * step;
              }
          }

          if(item_html != null) {
              item_html += "</g>";
              loop_html += item_html;
          }

          //trick
          if ( month_position[1].x - month_position[0].x < 40 ){
            //Fix ugly graph by remove first item
            month_position.shift(0);
          }

          for (  var i =0; i < month_position.length; i++){
            var item = month_position[i];
            var month_name =  settings.month_names[ item.month_index ];
            loop_html += '<text x="'+ item.x +'" y="-5" class="month">'+ month_name +'</text>';
          }

          //Add Monday, Wenesday, Friday label
          loop_html +=  '<text text-anchor="middle" class="wday" dx="-10" dy="22">{0}</text>'.formatString( settings.h_days[0] )+
                        '<text text-anchor="middle" class="wday" dx="-10" dy="48">{0}</text>'.formatString( settings.h_days[1] )+
                        '<text text-anchor="middle" class="wday" dx="-10" dy="74">{0}</text>'.formatString( settings.h_days[2] );

          //Fixed size for now with width= 721 and height = 110
          var wire_html =
            '<svg width="721" height="110" viewBox="0 0 721 110"  class="js-calendar-graph-svg">'+
              '<g transform="translate(20, 20)">'+
                loop_html +
              '</g>'+
            '</svg>';

          wrap_chart.html(wire_html);

          $(_this).find(".day").on('click',function () {

            if (clickCallback){
              clickCallback($(this).attr("data-date"), parseInt($(this).attr("data-count")));
            }

          });

          $(_this).find(".day").hover(function () {
              $(this).attr("style", "stroke-width: 1; stroke: "+ settings.hover_border_color);
            }, function() {
              $( this ).attr( "style", "stroke-width:0" );
          });

          //Mare sure off previous event
          /*$(document).off('mouseenter', _this.find('rect'), mouseEnter );
          $(document).off('mouseleave', _this.find('rect'), mouseLeave );
          $(document).on('mouseenter', _this.find('rect'), mouseEnter );
          $(document).on('mouseleave', _this.find('rect'), mouseLeave );
           */
          _this.find('rect').on("mouseenter", mouseEnter );
          _this.find('rect').on("mouseleave",mouseLeave );
          appendTooltip();

        }

        var mouseLeave =function(evt){
          $('.svg-tip').hide();
        }

        //handle event mouseenter when enter into rect element
        var mouseEnter = function(evt){

          var target_offset = $(evt.target).offset();
          var count = $(evt.target).attr('data-count');
          var date = $(evt.target).attr('data-date');

          var count_text = ( count > 1 ) ? settings.texts[1]: settings.texts[0];
          var text = "{0} {1} on {2}".formatString( count, count_text , date );

          var svg_tip = $('.svg-tip').show();
          svg_tip.html( text );
          var svg_width = Math.round( svg_tip.width()/2 + 5 )  ;
          var svg_height =  svg_tip.height() *2 + 10 ;

          svg_tip.css({top:target_offset.top - svg_height - 5});
          svg_tip.css({left:target_offset.left -svg_width});
        }
        //Append tooltip to display when mouse enter the rect element
        //Default is display:none
        var appendTooltip = function(){
          if ( $('.svg-tip').length == 0 ){
            $('body').append('

');
          }
        }

        var settings = $.extend({
          //Default init settings.colors, user can override
          levels: [0, 1, 10, 20, 30],
          colors: ['#eeeeee','#d6e685','#8cc665','#44a340','#44a340'],
          radius: 2,
          hover_border_color: "#999",
          click: null,
          start_date: null,
          //List of name months
          month_names: ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'],
          h_days : ['M','W','F'],
          //Default is empty, it can be overrided
          data:[],
        }, options );

        var _this = $(this);

        start();

    };

}( jQuery ));<|endoftext|>


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

/**
 *
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var Emitter = require( 'AXON/Emitter' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Matrix = require( 'DOT/Matrix' );
  var waveInterference = require( 'WAVE_INTERFERENCE/waveInterference' );

  // constants
  var c = 0.5;
  var cSquared = c * c;

  /**
   * @constructor
   */
  function Lattice( width, height, dampX, dampY, getPotential ) {
    var numMatrices = 3;
    this.dampX = dampX;
    this.dampY = dampY;
    this.matrices = [];
    for ( var i = 0; i < numMatrices; i++ ) {
      this.matrices.push( new Matrix( width, height ) );
    }
    this.currentMatrixIndex = 0;
    this.getPotential = getPotential || function() {return 0;};
    this.changedEmitter = new Emitter();
  }

  waveInterference.register( 'Lattice', Lattice );

  return inherit( Object, Lattice, {
    getCurrentValue: function( i, j ) {
      return this.matrices[ this.currentMatrixIndex ].get( i, j );
    },
    setCurrentValue: function( i, j, value ) {
      this.matrices[ this.currentMatrixIndex ].set( i, j, value );
    },

    getLastValue: function( i, j ) {
      return this.matrices[ (this.currentMatrixIndex + 1) % this.matrices.length ].get( i, j );
    },
    setLastValue: function( i, j, value ) {
      this.matrices[ (this.currentMatrixIndex + 1) % this.matrices.length ].set( i, j, value );
    },

    // Sets values for all history times
    setInitialValue: function( i, j, value ) {
      for ( var index = 0; index < this.matrices.length; index++ ) {
        this.matrices[ index ].set( i, j, value );
      }
    },
    get width() {
      return this.matrices[ 0 ].getRowDimension();
    },
    get height() {
      return this.matrices[ 0 ].getColumnDimension();
    },

    // Do we need this and dampScale?
    dampHorizontalROOT: function( j, dj, matrix0, matrix2 ) {
      for ( var i = 0; i < this.width; i++ ) {
        matrix0.set( i, j, matrix2.get( i, j + dj ) );
      }
    },

    dampVerticalROOT: function( i, di, matrix0, matrix2 ) {
      for ( var j = 0; j < this.height; j++ ) {
        matrix0.set( i, j, matrix2.get( i + di, j ) );
      }
    },

    dampScale: function() {
      this.dampVerticalSUB( 0, +1, this.dampY / 2 );
      this.dampVerticalSUB( this.width - 1, -1, this.dampY / 2 );
      this.dampHorizontalSUB( 0, +1, this.dampX / 2 );
      this.dampHorizontalSUB( this.height - 1, -1, this.dampX / 2 );
    },

    dampVerticalSUB: function( origin, sign, numDampPts ) {
      for ( var j = 0; j < this.height; j++ ) {
        for ( var step = 0; step < numDampPts; step++ ) {
          var distFromDampBoundary = numDampPts - step;
          var damp = this.getDamp( distFromDampBoundary );
          var i = origin + step * sign;
          this.setCurrentValue( i, j, this.getCurrentValue( i, j ) * damp );
          this.setLastValue( i, j, this.getLastValue( i, j ) * damp );
        }
      }
    },

    dampHorizontalSUB: function( origin, sign, numDampPts ) {
      for ( var i = 0; i < this.width; i++ ) {
        for ( var step = 0; step < numDampPts; step++ ) {
          var distFromDampBoundary = numDampPts - step;
          var damp = this.getDamp( distFromDampBoundary );
          var j = origin + step * sign;
          this.setCurrentValue( i, j, this.getCurrentValue( i, j ) * damp );
          this.setLastValue( i, j, this.getLastValue( i, j ) * damp );
        }
      }
    },

    getDamp: function( depthInDampRegion ) {
      return (1 - depthInDampRegion * 0.0001);
    },

    step: function( dt ) {
      this.currentMatrixIndex = (this.currentMatrixIndex - 1 + this.matrices.length) % this.matrices.length;

      var matrix0 = this.matrices[ (this.currentMatrixIndex + 0) % this.matrices.length ];
      var matrix1 = this.matrices[ (this.currentMatrixIndex + 1) % this.matrices.length ];
      var matrix2 = this.matrices[ (this.currentMatrixIndex + 2) % this.matrices.length ];
      var width = matrix0.getRowDimension();
      var height = matrix0.getColumnDimension();
      for ( var i = 1; i < width - 1; i++ ) {
        for ( var j = 1; j < height - 1; j++ ) {
          if ( this.getPotential( i, j ) !== 0 ) {
            matrix0.set( i, j, 0 );
          }
          else {
            var neighborSum = matrix1.get( i + 1, j ) + matrix1.get( i - 1, j ) + matrix1.get( i, j + 1 ) + matrix1.get( i, j - 1 );
            var m1ij = matrix1.get( i, j );
            var term = cSquared * (neighborSum + m1ij * -4);
            matrix0.set( i, j, m1ij * 2 - matrix2.get( i, j ) + term );
          }
        }
      }

      this.dampHorizontalROOT( this.dampY, 1, matrix0, matrix2 );
      this.dampHorizontalROOT( this.height - 1 - this.dampY, -1, matrix0, matrix2 );
      this.dampVerticalROOT( this.dampX, +1, matrix0, matrix2 );
      this.dampVerticalROOT( this.width - 1 - this.dampX, -1, matrix0, matrix2 );

      this.dampScale();

      this.changedEmitter.emit();
    }
  } );
} );<commit_msg>Cleanup in Lattice<commit_after>// Copyright 2017, University of Colorado Boulder

/**
 *
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var Emitter = require( 'AXON/Emitter' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Matrix = require( 'DOT/Matrix' );
  var waveInterference = require( 'WAVE_INTERFERENCE/waveInterference' );

  // constants
  var c = 0.5;
  var cSquared = c * c;

  /**
   * @constructor
   */
  function Lattice( width, height, dampX, dampY, getPotential ) {
    var numMatrices = 3;
    this.dampX = dampX;
    this.dampY = dampY;
    this.matrices = [];
    for ( var i = 0; i < numMatrices; i++ ) {
      this.matrices.push( new Matrix( width, height ) );
    }
    this.currentMatrixIndex = 0;
    this.getPotential = getPotential || function() {return 0;};
    this.changedEmitter = new Emitter();
  }

  waveInterference.register( 'Lattice', Lattice );

  return inherit( Object, Lattice, {
    getCurrentValue: function( i, j ) {
      return this.matrices[ this.currentMatrixIndex ].get( i, j );
    },
    setCurrentValue: function( i, j, value ) {
      this.matrices[ this.currentMatrixIndex ].set( i, j, value );
    },

    getLastValue: function( i, j ) {
      return this.matrices[ (this.currentMatrixIndex + 1) % this.matrices.length ].get( i, j );
    },
    setLastValue: function( i, j, value ) {
      this.matrices[ (this.currentMatrixIndex + 1) % this.matrices.length ].set( i, j, value );
    },
    get width() {
      return this.matrices[ 0 ].getRowDimension();
    },
    get height() {
      return this.matrices[ 0 ].getColumnDimension();
    },

    // Do we need this and dampScale?
    dampHorizontalROOT: function( j, dj, matrix0, matrix2 ) {
      for ( var i = 0; i < this.width; i++ ) {
        matrix0.set( i, j, matrix2.get( i, j + dj ) );
      }
    },

    dampVerticalROOT: function( i, di, matrix0, matrix2 ) {
      for ( var j = 0; j < this.height; j++ ) {
        matrix0.set( i, j, matrix2.get( i + di, j ) );
      }
    },

    dampVerticalSUB: function( origin, sign, numDampPts ) {
      for ( var j = 0; j < this.height; j++ ) {
        for ( var step = 0; step < numDampPts; step++ ) {
          var distFromDampBoundary = numDampPts - step;
          var damp = this.getDamp( distFromDampBoundary );
          var i = origin + step * sign;
          this.setCurrentValue( i, j, this.getCurrentValue( i, j ) * damp );
          this.setLastValue( i, j, this.getLastValue( i, j ) * damp );
        }
      }
    },

    dampHorizontalSUB: function( origin, sign, numDampPts ) {
      for ( var i = 0; i < this.width; i++ ) {
        for ( var step = 0; step < numDampPts; step++ ) {
          var distFromDampBoundary = numDampPts - step;
          var damp = this.getDamp( distFromDampBoundary );
          var j = origin + step * sign;
          this.setCurrentValue( i, j, this.getCurrentValue( i, j ) * damp );
          this.setLastValue( i, j, this.getLastValue( i, j ) * damp );
        }
      }
    },

    getDamp: function( depthInDampRegion ) {
      return (1 - depthInDampRegion * 0.0001);
    },

    step: function( dt ) {
      this.currentMatrixIndex = (this.currentMatrixIndex - 1 + this.matrices.length) % this.matrices.length;

      var matrix0 = this.matrices[ (this.currentMatrixIndex + 0) % this.matrices.length ];
      var matrix1 = this.matrices[ (this.currentMatrixIndex + 1) % this.matrices.length ];
      var matrix2 = this.matrices[ (this.currentMatrixIndex + 2) % this.matrices.length ];
      var width = matrix0.getRowDimension();
      var height = matrix0.getColumnDimension();
      for ( var i = 0; i < width; i++ ) {
        for ( var j = 0; j < height; j++ ) {
          if ( this.getPotential( i, j ) !== 0 ) {
            matrix0.set( i, j, 0 );
          }
          else {
            var neighborSum = matrix1.get( i + 1, j ) + matrix1.get( i - 1, j ) + matrix1.get( i, j + 1 ) + matrix1.get( i, j - 1 );
            var m1ij = matrix1.get( i, j );
            var value = m1ij * 2 - matrix2.get( i, j ) + cSquared * (neighborSum + m1ij * -4);
            matrix0.set( i, j, value );
          }
        }
      }

      // temporal dampen on the visible region
      this.dampHorizontalROOT( this.dampY, 1, matrix0, matrix2 );
      this.dampHorizontalROOT( this.height - 1 - this.dampY, -1, matrix0, matrix2 );
      this.dampVerticalROOT( this.dampX, +1, matrix0, matrix2 );
      this.dampVerticalROOT( this.width - 1 - this.dampX, -1, matrix0, matrix2 );

      // dampen the absorption barriers
      this.dampVerticalSUB( 0, +1, this.dampY / 2 );
      this.dampVerticalSUB( this.width - 1, -1, this.dampY / 2 );
      this.dampHorizontalSUB( 0, +1, this.dampX / 2 );
      this.dampHorizontalSUB( this.height - 1, -1, this.dampX / 2 );

      this.changedEmitter.emit();
    }
  } );
} );<|endoftext|>


<commit_before>/*
* jQuery Mobile Framework : events
* Copyright (c) jQuery Project
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*/
(function( $, window, undefined ) {

// add new event shortcuts
$.each( ( "touchstart touchmove touchend orientationchange throttledresize " +
					"tap taphold swipe swipeleft swiperight scrollstart scrollstop" ).split( " " ), function( i, name ) {

	$.fn[ name ] = function( fn ) {
		return fn ? this.bind( name, fn ) : this.trigger( name );
	};

	$.attrFn[ name ] = true;
});

var supportTouch = $.support.touch,
	scrollEvent = "touchmove scroll",
	touchStartEvent = supportTouch ? "touchstart" : "mousedown",
	touchStopEvent = supportTouch ? "touchend" : "mouseup",
	touchMoveEvent = supportTouch ? "touchmove" : "mousemove";

function triggerCustomEvent( obj, eventType, event ) {
	var originalType = event.type;
	event.type = eventType;
	$.event.handle.call( obj, event );
	event.type = originalType;
}

// also handles scrollstop
$.event.special.scrollstart = {

	enabled: true,

	setup: function() {

		var thisObject = this,
			$this = $( thisObject ),
			scrolling,
			timer;

		function trigger( event, state ) {
			scrolling = state;
			triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
		}

		// iPhone triggers scroll after a small delay; use touchmove instead
		$this.bind( scrollEvent, function( event ) {

			if ( !$.event.special.scrollstart.enabled ) {
				return;
			}

			if ( !scrolling ) {
				trigger( event, true );
			}

			clearTimeout( timer );
			timer = setTimeout(function() {
				trigger( event, false );
			}, 50 );
		});
	}
};

// also handles taphold
$.event.special.tap = {
	setup: function() {
		var thisObject = this,
			$this = $( thisObject );

		$this.bind( "vmousedown", function( event ) {

			if ( event.which && event.which !== 1 ) {
				return false;
			}

			var origTarget = event.target,
				origEvent = event.originalEvent,
				timer;

			function clearTapTimer() {
				clearTimeout( timer );
			}

			function clearTapHandlers() {
				clearTapTimer();

				$this.unbind( "vclick", clickHandler )
					.unbind( "vmouseup", clearTapTimer )
					.unbind( "vmousecancel", clearTapHandlers );
			}

			function clickHandler(event) {
				clearTapHandlers();

				// ONLY trigger a 'tap' event if the start target is
				// the same as the stop target.
				if ( origTarget == event.target ) {
					triggerCustomEvent( thisObject, "tap", event );
				}
			}

			$this.bind( "vmousecancel", clearTapHandlers )
				.bind( "vmouseup", clearTapTimer )
				.bind( "vclick", clickHandler );

			timer = setTimeout(function() {
					triggerCustomEvent( thisObject, "taphold", $.Event( "taphold" ) );
			}, 750 );
		});
	}
};

// also handles swipeleft, swiperight
$.event.special.swipe = {
	scrollSupressionThreshold: 10, // More than this horizontal displacement, and we will suppress scrolling.

	durationThreshold: 1000, // More time than this, and it isn't a swipe.

	horizontalDistanceThreshold: 30,  // Swipe horizontal displacement must be more than this.

	verticalDistanceThreshold: 75,  // Swipe vertical displacement must be less than this.

	setup: function() {
		var thisObject = this,
			$this = $( thisObject );

		$this.bind( touchStartEvent, function( event ) {
			var data = event.originalEvent.touches ?
								event.originalEvent.touches[ 0 ] : event,
				start = {
					time: ( new Date() ).getTime(),
					coords: [ data.pageX, data.pageY ],
					origin: $( event.target )
				},
				stop;

			function moveHandler( event ) {

				if ( !start ) {
					return;
				}

				var data = event.originalEvent.touches ?
						event.originalEvent.touches[ 0 ] : event;

				stop = {
					time: ( new Date() ).getTime(),
					coords: [ data.pageX, data.pageY ]
				};

				// prevent scrolling
				if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
					event.preventDefault();
				}
			}

			$this.bind( touchMoveEvent, moveHandler )
				.one( touchStopEvent, function( event ) {
					$this.unbind( touchMoveEvent, moveHandler );

					if ( start && stop ) {
						if ( stop.time - start.time < $.event.special.swipe.durationThreshold &&
								Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold &&
								Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) {

							start.origin.trigger( "swipe" )
								.trigger( start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight" );
						}
					}
					start = stop = undefined;
				});
		});
	}
};

(function( $, window ) {
	// "Cowboy" Ben Alman

	var win = $( window ),
		special_event,
		get_orientation,
		last_orientation;

	$.event.special.orientationchange = special_event = {
		setup: function() {
			// If the event is supported natively, return false so that jQuery
			// will bind to the event using DOM methods.
			if ( $.support.orientation ) {
				return false;
			}

			// Get the current orientation to avoid initial double-triggering.
			last_orientation = get_orientation();

			// Because the orientationchange event doesn't exist, simulate the
			// event by testing window dimensions on resize.
			win.bind( "throttledresize", handler );
		},
		teardown: function(){
			// If the event is not supported natively, return false so that
			// jQuery will unbind the event using DOM methods.
			if ( $.support.orientation ) {
				return false;
			}

			// Because the orientationchange event doesn't exist, unbind the
			// resize event handler.
			win.unbind( "throttledresize", handler );
		},
		add: function( handleObj ) {
			// Save a reference to the bound event handler.
			var old_handler = handleObj.handler;

			handleObj.handler = function( event ) {
				// Modify event object, adding the .orientation property.
				event.orientation = get_orientation();

				// Call the originally-bound event handler and return its result.
				return old_handler.apply( this, arguments );
			};
		}
	};

	// If the event is not supported natively, this handler will be bound to
	// the window resize event to simulate the orientationchange event.
	function handler() {
		// Get the current orientation.
		var orientation = get_orientation();

		if ( orientation !== last_orientation ) {
			// The orientation has changed, so trigger the orientationchange event.
			last_orientation = orientation;
			win.trigger( "orientationchange" );
		}
	};

	// Get the current page orientation. This method is exposed publicly, should it
	// be needed, as jQuery.event.special.orientationchange.orientation()
	$.event.special.orientationchange.orientation = get_orientation = function() {
		var elem = document.documentElement;
		return elem && elem.clientWidth / elem.clientHeight < 1.1 ? "portrait" : "landscape";
	};

})( jQuery, window );

// throttled resize event
(function() {

	$.event.special.throttledresize = {
		setup: function() {
			$( this ).bind( "resize", handler );
		},
		teardown: function(){
			$( this ).unbind( "resize", handler );
		}
	};

	var throttle = 250,
		handler = function() {
			curr = ( new Date() ).getTime();
			diff = curr - lastCall;

			if ( diff >= throttle ) {

				lastCall = curr;
				$( this ).trigger( "throttledresize" );

			} else {

				if ( heldCall ) {
					clearTimeout( heldCall );
				}

				// Promise a held call will still execute
				heldCall = setTimeout( handler, throttle - diff );
			}
		},
		lastCall = 0,
		heldCall,
		curr,
		diff;
})();

$.each({
	scrollstop: "scrollstart",
	taphold: "tap",
	swipeleft: "swipe",
	swiperight: "swipe"
}, function( event, sourceEvent ) {

	$.event.special[ event ] = {
		setup: function() {
			$( this ).bind( sourceEvent, $.noop );
		}
	};
});

})( jQuery, this );
<commit_msg>whitespace in events<commit_after>/*
* jQuery Mobile Framework : events
* Copyright (c) jQuery Project
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*/
(function( $, window, undefined ) {

// add new event shortcuts
$.each( ( "touchstart touchmove touchend orientationchange throttledresize " +
					"tap taphold swipe swipeleft swiperight scrollstart scrollstop" ).split( " " ), function( i, name ) {

	$.fn[ name ] = function( fn ) {
		return fn ? this.bind( name, fn ) : this.trigger( name );
	};

	$.attrFn[ name ] = true;
});

var supportTouch = $.support.touch,
	scrollEvent = "touchmove scroll",
	touchStartEvent = supportTouch ? "touchstart" : "mousedown",
	touchStopEvent = supportTouch ? "touchend" : "mouseup",
	touchMoveEvent = supportTouch ? "touchmove" : "mousemove";

function triggerCustomEvent( obj, eventType, event ) {
	var originalType = event.type;
	event.type = eventType;
	$.event.handle.call( obj, event );
	event.type = originalType;
}

// also handles scrollstop
$.event.special.scrollstart = {

	enabled: true,

	setup: function() {

		var thisObject = this,
			$this = $( thisObject ),
			scrolling,
			timer;

		function trigger( event, state ) {
			scrolling = state;
			triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
		}

		// iPhone triggers scroll after a small delay; use touchmove instead
		$this.bind( scrollEvent, function( event ) {

			if ( !$.event.special.scrollstart.enabled ) {
				return;
			}

			if ( !scrolling ) {
				trigger( event, true );
			}

			clearTimeout( timer );
			timer = setTimeout(function() {
				trigger( event, false );
			}, 50 );
		});
	}
};

// also handles taphold
$.event.special.tap = {
	setup: function() {
		var thisObject = this,
			$this = $( thisObject );

		$this.bind( "vmousedown", function( event ) {

			if ( event.which && event.which !== 1 ) {
				return false;
			}

			var origTarget = event.target,
				origEvent = event.originalEvent,
				timer;

			function clearTapTimer() {
				clearTimeout( timer );
			}

			function clearTapHandlers() {
				clearTapTimer();

				$this.unbind( "vclick", clickHandler )
					.unbind( "vmouseup", clearTapTimer )
					.unbind( "vmousecancel", clearTapHandlers );
			}

			function clickHandler(event) {
				clearTapHandlers();

				// ONLY trigger a 'tap' event if the start target is
				// the same as the stop target.
				if ( origTarget == event.target ) {
					triggerCustomEvent( thisObject, "tap", event );
				}
			}

			$this.bind( "vmousecancel", clearTapHandlers )
				.bind( "vmouseup", clearTapTimer )
				.bind( "vclick", clickHandler );

			timer = setTimeout(function() {
					triggerCustomEvent( thisObject, "taphold", $.Event( "taphold" ) );
			}, 750 );
		});
	}
};

// also handles swipeleft, swiperight
$.event.special.swipe = {
	scrollSupressionThreshold: 10, // More than this horizontal displacement, and we will suppress scrolling.

	durationThreshold: 1000, // More time than this, and it isn't a swipe.

	horizontalDistanceThreshold: 30,  // Swipe horizontal displacement must be more than this.

	verticalDistanceThreshold: 75,  // Swipe vertical displacement must be less than this.

	setup: function() {
		var thisObject = this,
			$this = $( thisObject );

		$this.bind( touchStartEvent, function( event ) {
			var data = event.originalEvent.touches ?
								event.originalEvent.touches[ 0 ] : event,
				start = {
					time: ( new Date() ).getTime(),
					coords: [ data.pageX, data.pageY ],
					origin: $( event.target )
				},
				stop;

			function moveHandler( event ) {

				if ( !start ) {
					return;
				}

				var data = event.originalEvent.touches ?
						event.originalEvent.touches[ 0 ] : event;

				stop = {
					time: ( new Date() ).getTime(),
					coords: [ data.pageX, data.pageY ]
				};

				// prevent scrolling
				if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
					event.preventDefault();
				}
			}

			$this.bind( touchMoveEvent, moveHandler )
				.one( touchStopEvent, function( event ) {
					$this.unbind( touchMoveEvent, moveHandler );

					if ( start && stop ) {
						if ( stop.time - start.time < $.event.special.swipe.durationThreshold &&
								Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold &&
								Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) {

							start.origin.trigger( "swipe" )
								.trigger( start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight" );
						}
					}
					start = stop = undefined;
				});
		});
	}
};

(function( $, window ) {
	// "Cowboy" Ben Alman

	var win = $( window ),
		special_event,
		get_orientation,
		last_orientation;

	$.event.special.orientationchange = special_event = {
		setup: function() {
			// If the event is supported natively, return false so that jQuery
			// will bind to the event using DOM methods.
			if ( $.support.orientation ) {
				return false;
			}

			// Get the current orientation to avoid initial double-triggering.
			last_orientation = get_orientation();

			// Because the orientationchange event doesn't exist, simulate the
			// event by testing window dimensions on resize.
			win.bind( "throttledresize", handler );
		},
		teardown: function(){
			// If the event is not supported natively, return false so that
			// jQuery will unbind the event using DOM methods.
			if ( $.support.orientation ) {
				return false;
			}

			// Because the orientationchange event doesn't exist, unbind the
			// resize event handler.
			win.unbind( "throttledresize", handler );
		},
		add: function( handleObj ) {
			// Save a reference to the bound event handler.
			var old_handler = handleObj.handler;

			handleObj.handler = function( event ) {
				// Modify event object, adding the .orientation property.
				event.orientation = get_orientation();

				// Call the originally-bound event handler and return its result.
				return old_handler.apply( this, arguments );
			};
		}
	};

	// If the event is not supported natively, this handler will be bound to
	// the window resize event to simulate the orientationchange event.
	function handler() {
		// Get the current orientation.
		var orientation = get_orientation();

		if ( orientation !== last_orientation ) {
			// The orientation has changed, so trigger the orientationchange event.
			last_orientation = orientation;
			win.trigger( "orientationchange" );
		}
	};

	// Get the current page orientation. This method is exposed publicly, should it
	// be needed, as jQuery.event.special.orientationchange.orientation()
	$.event.special.orientationchange.orientation = get_orientation = function() {
		var elem = document.documentElement;
		return elem && elem.clientWidth / elem.clientHeight < 1.1 ? "portrait" : "landscape";
	};

})( jQuery, window );

// throttled resize event
(function() {

	$.event.special.throttledresize = {
		setup: function() {
			$( this ).bind( "resize", handler );
		},
		teardown: function(){
			$( this ).unbind( "resize", handler );
		}
	};

	var throttle = 250,
		handler = function() {
			curr = ( new Date() ).getTime();
			diff = curr - lastCall;

			if ( diff >= throttle ) {

				lastCall = curr;
				$( this ).trigger( "throttledresize" );

			} else {

				if ( heldCall ) {
					clearTimeout( heldCall );
				}

				// Promise a held call will still execute
				heldCall = setTimeout( handler, throttle - diff );
			}
		},
		lastCall = 0,
		heldCall,
		curr,
		diff;
})();

$.each({
	scrollstop: "scrollstart",
	taphold: "tap",
	swipeleft: "swipe",
	swiperight: "swipe"
}, function( event, sourceEvent ) {

	$.event.special[ event ] = {
		setup: function() {
			$( this ).bind( sourceEvent, $.noop );
		}
	};
});

})( jQuery, this );
<|endoftext|>


<commit_before>/*
James Cryer / Huddle / 2014
https://github.com/Huddle/PhantomCSS
http://tldr.huddle.com/blog/css-testing/
*/

var fs = require('fs');

var _src = '.'+fs.separator+'screenshots';
var _results; // for backwards compatibility results and src are the same - but you can change it!
var _failures = '.'+fs.separator+'failures';

var _count = 0;
var _realPath;
var _diffsToProcess = [];
var _libraryRoot = '.';
var exitStatus;
var _hideElements;
var _addLabelToFailedImage = true;
var _test_match;
var _test_exclude;
var _mismatchTolerance = 0.05;
var _resembleOutputSettings;
var diffsCreated = [];

exports.screenshot = screenshot;
exports.compareAll = compareAll;
exports.compareMatched = compareMatched;
exports.compareExplicit = compareExplicit;
exports.compareSession = compareSession;
exports.compareFiles = compareFiles;
exports.waitForTests = waitForTests;
exports.init = init;
exports.update = update;
exports.turnOffAnimations = turnOffAnimations;
exports.getExitStatus = getExitStatus;
exports.getCreatedDiffFiles = getCreatedDiffFiles;

function update(options){

	function stripslash ( str ){
		return str.replace(/\/\//g,'/').replace(/\\/g,'\\');
	}

	options = options || {};

	casper = options.casper || casper;
	_libraryRoot = options.libraryRoot || _libraryRoot;

	_src = stripslash(options.screenshotRoot || _src);
	_results = stripslash(options.comparisonResultRoot || _results || _src);
	_failures = stripslash(options.failedComparisonsRoot || _failures);

	_fileNameGetter = options.fileNameGetter || _fileNameGetter;

	_onPass = options.onPass || _onPass;
	_onFail = options.onFail || _onFail;
	_onTimeout = options.onTimeout || _onTimeout;
	_onComplete = options.onComplete || options.report || _onComplete;

	_hideElements = options.hideElements;

	_mismatchTolerance = options.mismatchTolerance || _mismatchTolerance;

	_resembleOutputSettings = options.outputSettings || _resembleOutputSettings;

	if(options.addLabelToFailedImage !== undefined){
		_addLabelToFailedImage = options.addLabelToFailedImage;
	}
}

function init(options){
	update(options);
}

function turnOffAnimations(){
	console.log('[PhantomCSS] Turning off animations');
	casper.evaluate(function turnOffAnimations(){
		window.addEventListener('load', function(){

			var css = document.createElement("style");
			css.type = "text/css";
			css.innerHTML = "* { -webkit-transition: none !important; transition: none !important; }";
			document.body.appendChild(css);

			if(jQuery){
				jQuery.fx.off = true;
			}
		},false);
	});
}

function _fileNameGetter(root, fileName){
	var name;

	fileName = fileName || "screenshot";
	name = root + fs.separator + fileName + "_" + _count++;

	if(fs.isFile(name+'.png')){
		return name+'.diff.png';
	} else {
		return name+'.png';
	}
}

function screenshot(selector, timeToWait, hideSelector, fileName){

	if(isNaN(Number(timeToWait)) && typeof timeToWait === 'string'){
		fileName = timeToWait;
		timeToWait = void 0;
	}

	casper.captureBase64('png'); // force pre-render
	casper.wait(timeToWait || 250, function(){

		var srcPath = _fileNameGetter(_src, fileName);
		var resultPath =  srcPath.replace(_src, _results);

		if(hideSelector || _hideElements){
			casper.evaluate(function(s1, s2){
				if(s1){
					$(s1).css('visibility', 'hidden');
				}
				$(s2).css('visibility', 'hidden');
			}, {
				s1: _hideElements,
				s2: hideSelector
			});
		}

		capture(srcPath, resultPath, selector);

	}); // give a bit of time for all the images appear
}

function capture(srcPath, resultPath, selector){
	var originalForResult = resultPath.replace('.diff', '');
	var originalFromSource = srcPath.replace('.diff', '');

	try {

		if( isThisImageADiff(resultPath) ){

			casper.captureSelector( resultPath , selector );

			diffsCreated.push(resultPath);

			if(srcPath !== resultPath){
				// also copy the original over to the result directory
				copyAndReplaceFile(originalFromSource, originalForResult);
			}

		} else {

			casper.captureSelector( srcPath , selector );

			if(srcPath !== resultPath){
				copyAndReplaceFile(srcPath, resultPath);
			}
		}

	}
	catch(ex){
		console.log("[PhantomCSS] Screenshot capture failed: ", ex.message);
	}
}

function isThisImageADiff(path){
	return /\.diff\.png/.test(path);
}

function copyAndReplaceFile(src, dest){
	if(fs.isFile(dest)){
		fs.remove(dest);
	}
	fs.copy(src, dest);
}

function asyncCompare(one, two, func){

	if(!casper.evaluate(function(){ return window._imagediff_;})){
		initClient();
	}

	casper.fill('form#image-diff', {
		'one': one,
		'two': two
	});

	casper.evaluate(function(filename){
		window._imagediff_.run( filename );
	}, {
		label: _addLabelToFailedImage ? one : false
	});

	casper.waitFor(
		function check() {
			return this.evaluate(function(){
				return window._imagediff_.hasResult;
			});
		},
		function () {

			var mismatch = casper.evaluate(function(){
				return window._imagediff_.getResult();
			});

			if(Number(mismatch)){
				func(false, mismatch);
			} else {
				func(true);
			}

		}, function(){
			func(false);
		},
		10000
	);
}

function getDiffs (path){

	var filePath;

	if(({'..':1,'.':1})[path]){ return true; }

	if(_realPath){
		_realPath += fs.separator + path;
	} else {
		_realPath = path;
	}

	filePath = _realPath;

	if(fs.isDirectory(_realPath) ){
		fs.list(_realPath).forEach(getDiffs);
	} else {
		if( /\.diff\./.test(path.toLowerCase()) ){
			if(_test_match){
				if( _test_match.test(_realPath.toLowerCase()) ){
					if( !(_test_exclude && _test_exclude.test(_realPath.toLowerCase())) ){
						console.log('[PhantomCSS] Analysing', _realPath);
						_diffsToProcess.push(filePath);
					}
				}
			} else {
				if( !(_test_exclude && _test_exclude.test(_realPath.toLowerCase())) ){
					_diffsToProcess.push(filePath);
				}
			}
		}
	}

	_realPath = _realPath.replace(fs.separator + path, '');
}

function getCreatedDiffFiles(){
	var d = diffsCreated;
	diffsCreated = [];
	return d;
}

function compareMatched(match, exclude){
	// Search for diff images, but only compare matched filenames
	_test_match = typeof match === 'string' ? new RegExp(match) : match;
	compareAll(exclude);
}

function compareExplicit(list){
	// An explicit list of diff images to compare ['/dialog.diff.png', '/header.diff.png']
	compareAll(void 0, list);
}

function compareSession(list){
	// compare the diffs created in this session
	compareAll(void 0, getCreatedDiffFiles() );
}

function compareFiles(baseFile, file) {
	var html = _libraryRoot+fs.separator+"ResembleJs"+fs.separator+"resemblejscontainer.html";
	var test = {
		filename: baseFile
	};

	if(!fs.isFile(baseFile)) {
		test.error = true;
	} else {

		if( !fs.isFile(html) ){
			console.log('[PhantomCSS] Can\'t find Resemble container. Perhaps the library root is mis configured. ('+html+')');
			test.error = true;
			return;
		}

		casper.thenOpen ( html , function (){

			asyncCompare(baseFile, file, function(isSame, mismatch){

				if(!isSame){

					test.fail = true;

					casper.waitFor(
						function check() {
							return casper.evaluate(function(){
								return window._imagediff_.hasImage;
							});
						},
						function () {
							var failFile, safeFileName, increment;

							if(_failures){
								// flattened structure for failed diffs so that it is easier to preview
								failFile = _failures + fs.separator + file.split(fs.separator).pop().replace('.diff.png', '').replace('.png', '');
								safeFileName = failFile;
								increment = 0;

								while ( fs.isFile(safeFileName+'.fail.png') ){
									increment++;
									safeFileName = failFile+'.'+increment;
								}

								failFile = safeFileName + '.fail.png';

								casper.captureSelector(failFile, 'img');
								console.log('Failure! Saved to', failFile);
							}

							// Always create non-flattened failure images
							casper.captureSelector(file.replace('.diff.png', '.fail.png'), 'img');

							casper.evaluate(function(){
								window._imagediff_.hasImage = false;
							});

							if(mismatch){
								test.mismatch = mismatch;
								_onFail(test); // casper.test.fail throws and error, this function call is aborted
								return;  // Just to make it clear what is happening
							} else {
								_onTimeout(test);
							}

						}, function(){},
						10000
					);
				} else {
                    test.success = true;
					_onPass(test);
				}

			});
		});
	}
    return test;
}

function compareAll(exclude, list){
	var tests = [];

	_test_exclude = typeof exclude === 'string' ? new RegExp(exclude) : exclude;

	if (list){
		_diffsToProcess = list;
	} else {
		_realPath = undefined;
		getDiffs(_results);
	}

	_diffsToProcess.forEach(function(file){
		var baseFile = file.replace('.diff', '');
		tests.push(compareFiles(baseFile, file));
	});
	waitForTests(tests);
}

function waitForTests(tests){
	casper.then(function(){
		casper.waitFor(function(){
			return tests.length === tests.reduce(function(count, test){
				if (test.success || test.fail || test.error) {
					return count + 1;
				} else {
					return count;
				}
			}, 0);
		}, function(){
			var fails = 0, errors = 0;
			tests.forEach(function(test){
				if (test.fail){
					fails++;
				} else if (test.error){
					errors++;
				}
			});
			_onComplete(tests, fails, errors);
		}, function(){

		},
		10000);
	});
}

function initClient(){

	casper.page.injectJs(_libraryRoot+fs.separator+'ResembleJs'+fs.separator+'resemble.js');

	casper.evaluate(function(mismatchTolerance, resembleOutputSettings){

		var result;

		var div = document.createElement('div');

		// this is a bit of hack, need to get images into browser for analysis
		div.style = "display:block;position:absolute;border:0;top:-1px;left:-1px;height:1px;width:1px;overflow:hidden;";
		div.innerHTML = '<form id="image-diff">'+
			'<input type="file" id="image-diff-one" name="one"/>'+
			'<input type="file" id="image-diff-two" name="two"/>'+
		'</form>

';
		document.body.appendChild(div);

		if(resembleOutputSettings){
			resemble.outputSettings(resembleOutputSettings);
		}

		window._imagediff_ = {
			hasResult: false,
			hasImage: false,
			run: run,
			getResult: function(){
				window._imagediff_.hasResult = false;
				return result;
			}
		};

		function run(label){

			function render(data){
				var img = new Image();

				img.onload = function(){
					window._imagediff_.hasImage = true;
				};
				document.getElementById('image-diff').appendChild(img);
				img.src = data.getImageDataUrl(label);
			}

			resemble(document.getElementById('image-diff-one').files[0]).
				compareTo(document.getElementById('image-diff-two').files[0]).
				ignoreAntialiasing(). // <-- muy importante
				onComplete(function(data){
					var diffImage;

					if(Number(data.misMatchPercentage) > mismatchTolerance){
						result = data.misMatchPercentage;
					} else {
						result = false;
					}

					window._imagediff_.hasResult = true;

					if(Number(data.misMatchPercentage) > mismatchTolerance){
						render(data);
					}

				});
		}
	},
		_mismatchTolerance,
		_resembleOutputSettings
	);
}

function _onPass(test){
	console.log('\n');
	casper.test.pass('No changes found for screenshot ' + test.filename);
}
function _onFail(test){
	console.log('\n');
	casper.test.fail('Visual change found for screenshot ' + test.filename + ' (' + test.mismatch + '% mismatch)');
}
function _onTimeout(test){
	console.log('\n');
	casper.test.info('Could not complete image comparison for ' + test.filename);
}
function _onComplete(tests, noOfFails, noOfErrors){

	if( tests.length === 0){
		console.log("\nMust be your first time?");
		console.log("Some screenshots have been generated in the directory " + _results);
		console.log("This is your 'baseline', check the images manually. If they're wrong, delete the images.");
		console.log("The next time you run these tests, new screenshots will be taken.  These screenshots will be compared to the original.");
		console.log('If they are different, PhantomCSS will report a failure.');
	} else {

		if(noOfFails === 0){
			console.log("\nPhantomCSS found " + tests.length + " tests, None of them failed. Which is good right?");
			console.log("\nIf you want to make them fail, go change some CSS - weirdo.");
		} else {
			console.log("\nPhantomCSS found " + tests.length + " tests, " + noOfFails + ' of them failed.');
			console.log('\nPhantomCSS has created some images that try to show the difference (in the directory '+_failures+'). Fuchsia colored pixels indicate a difference betwen the new and old screenshots.');
		}

		if(noOfErrors !== 0){
			console.log("There were " + noOfErrors + "errors.  Is it possible that a baseline image was deleted but not the diff?");
		}

		exitStatus = noOfErrors+noOfFails;
	}
}

function getExitStatus() {
	return exitStatus;
}<commit_msg>disable css animation<commit_after>/*
James Cryer / Huddle / 2014
https://github.com/Huddle/PhantomCSS
http://tldr.huddle.com/blog/css-testing/
*/

var fs = require('fs');

var _src = '.'+fs.separator+'screenshots';
var _results; // for backwards compatibility results and src are the same - but you can change it!
var _failures = '.'+fs.separator+'failures';

var _count = 0;
var _realPath;
var _diffsToProcess = [];
var _libraryRoot = '.';
var exitStatus;
var _hideElements;
var _addLabelToFailedImage = true;
var _test_match;
var _test_exclude;
var _mismatchTolerance = 0.05;
var _resembleOutputSettings;
var diffsCreated = [];

exports.screenshot = screenshot;
exports.compareAll = compareAll;
exports.compareMatched = compareMatched;
exports.compareExplicit = compareExplicit;
exports.compareSession = compareSession;
exports.compareFiles = compareFiles;
exports.waitForTests = waitForTests;
exports.init = init;
exports.update = update;
exports.turnOffAnimations = turnOffAnimations;
exports.getExitStatus = getExitStatus;
exports.getCreatedDiffFiles = getCreatedDiffFiles;

function update(options){

	function stripslash ( str ){
		return str.replace(/\/\//g,'/').replace(/\\/g,'\\');
	}

	options = options || {};

	casper = options.casper || casper;
	_libraryRoot = options.libraryRoot || _libraryRoot;

	_src = stripslash(options.screenshotRoot || _src);
	_results = stripslash(options.comparisonResultRoot || _results || _src);
	_failures = stripslash(options.failedComparisonsRoot || _failures);

	_fileNameGetter = options.fileNameGetter || _fileNameGetter;

	_onPass = options.onPass || _onPass;
	_onFail = options.onFail || _onFail;
	_onTimeout = options.onTimeout || _onTimeout;
	_onComplete = options.onComplete || options.report || _onComplete;

	_hideElements = options.hideElements;

	_mismatchTolerance = options.mismatchTolerance || _mismatchTolerance;

	_resembleOutputSettings = options.outputSettings || _resembleOutputSettings;

	if(options.addLabelToFailedImage !== undefined){
		_addLabelToFailedImage = options.addLabelToFailedImage;
	}
}

function init(options){
	update(options);
}

function turnOffAnimations(){
	console.log('[PhantomCSS] Turning off animations');
	casper.evaluate(function turnOffAnimations(){
		window.addEventListener('load', function(){

			var css = document.createElement("style");
			css.type = "text/css";
			css.innerHTML = "* { -webkit-transition: none !important; transition: none !important; -webkit-animation: none !important; animation: none !important; }";
			document.body.appendChild(css);

			if(jQuery){
				jQuery.fx.off = true;
			}
		},false);
	});
}

function _fileNameGetter(root, fileName){
	var name;

	fileName = fileName || "screenshot";
	name = root + fs.separator + fileName + "_" + _count++;

	if(fs.isFile(name+'.png')){
		return name+'.diff.png';
	} else {
		return name+'.png';
	}
}

function screenshot(selector, timeToWait, hideSelector, fileName){

	if(isNaN(Number(timeToWait)) && typeof timeToWait === 'string'){
		fileName = timeToWait;
		timeToWait = void 0;
	}

	casper.captureBase64('png'); // force pre-render
	casper.wait(timeToWait || 250, function(){

		var srcPath = _fileNameGetter(_src, fileName);
		var resultPath =  srcPath.replace(_src, _results);

		if(hideSelector || _hideElements){
			casper.evaluate(function(s1, s2){
				if(s1){
					$(s1).css('visibility', 'hidden');
				}
				$(s2).css('visibility', 'hidden');
			}, {
				s1: _hideElements,
				s2: hideSelector
			});
		}

		capture(srcPath, resultPath, selector);

	}); // give a bit of time for all the images appear
}

function capture(srcPath, resultPath, selector){
	var originalForResult = resultPath.replace('.diff', '');
	var originalFromSource = srcPath.replace('.diff', '');

	try {

		if( isThisImageADiff(resultPath) ){

			casper.captureSelector( resultPath , selector );

			diffsCreated.push(resultPath);

			if(srcPath !== resultPath){
				// also copy the original over to the result directory
				copyAndReplaceFile(originalFromSource, originalForResult);
			}

		} else {

			casper.captureSelector( srcPath , selector );

			if(srcPath !== resultPath){
				copyAndReplaceFile(srcPath, resultPath);
			}
		}

	}
	catch(ex){
		console.log("[PhantomCSS] Screenshot capture failed: ", ex.message);
	}
}

function isThisImageADiff(path){
	return /\.diff\.png/.test(path);
}

function copyAndReplaceFile(src, dest){
	if(fs.isFile(dest)){
		fs.remove(dest);
	}
	fs.copy(src, dest);
}

function asyncCompare(one, two, func){

	if(!casper.evaluate(function(){ return window._imagediff_;})){
		initClient();
	}

	casper.fill('form#image-diff', {
		'one': one,
		'two': two
	});

	casper.evaluate(function(filename){
		window._imagediff_.run( filename );
	}, {
		label: _addLabelToFailedImage ? one : false
	});

	casper.waitFor(
		function check() {
			return this.evaluate(function(){
				return window._imagediff_.hasResult;
			});
		},
		function () {

			var mismatch = casper.evaluate(function(){
				return window._imagediff_.getResult();
			});

			if(Number(mismatch)){
				func(false, mismatch);
			} else {
				func(true);
			}

		}, function(){
			func(false);
		},
		10000
	);
}

function getDiffs (path){

	var filePath;

	if(({'..':1,'.':1})[path]){ return true; }

	if(_realPath){
		_realPath += fs.separator + path;
	} else {
		_realPath = path;
	}

	filePath = _realPath;

	if(fs.isDirectory(_realPath) ){
		fs.list(_realPath).forEach(getDiffs);
	} else {
		if( /\.diff\./.test(path.toLowerCase()) ){
			if(_test_match){
				if( _test_match.test(_realPath.toLowerCase()) ){
					if( !(_test_exclude && _test_exclude.test(_realPath.toLowerCase())) ){
						console.log('[PhantomCSS] Analysing', _realPath);
						_diffsToProcess.push(filePath);
					}
				}
			} else {
				if( !(_test_exclude && _test_exclude.test(_realPath.toLowerCase())) ){
					_diffsToProcess.push(filePath);
				}
			}
		}
	}

	_realPath = _realPath.replace(fs.separator + path, '');
}

function getCreatedDiffFiles(){
	var d = diffsCreated;
	diffsCreated = [];
	return d;
}

function compareMatched(match, exclude){
	// Search for diff images, but only compare matched filenames
	_test_match = typeof match === 'string' ? new RegExp(match) : match;
	compareAll(exclude);
}

function compareExplicit(list){
	// An explicit list of diff images to compare ['/dialog.diff.png', '/header.diff.png']
	compareAll(void 0, list);
}

function compareSession(list){
	// compare the diffs created in this session
	compareAll(void 0, getCreatedDiffFiles() );
}

function compareFiles(baseFile, file) {
	var html = _libraryRoot+fs.separator+"ResembleJs"+fs.separator+"resemblejscontainer.html";
	var test = {
		filename: baseFile
	};

	if(!fs.isFile(baseFile)) {
		test.error = true;
	} else {

		if( !fs.isFile(html) ){
			console.log('[PhantomCSS] Can\'t find Resemble container. Perhaps the library root is mis configured. ('+html+')');
			test.error = true;
			return;
		}

		casper.thenOpen ( html , function (){

			asyncCompare(baseFile, file, function(isSame, mismatch){

				if(!isSame){

					test.fail = true;

					casper.waitFor(
						function check() {
							return casper.evaluate(function(){
								return window._imagediff_.hasImage;
							});
						},
						function () {
							var failFile, safeFileName, increment;

							if(_failures){
								// flattened structure for failed diffs so that it is easier to preview
								failFile = _failures + fs.separator + file.split(fs.separator).pop().replace('.diff.png', '').replace('.png', '');
								safeFileName = failFile;
								increment = 0;

								while ( fs.isFile(safeFileName+'.fail.png') ){
									increment++;
									safeFileName = failFile+'.'+increment;
								}

								failFile = safeFileName + '.fail.png';

								casper.captureSelector(failFile, 'img');
								console.log('Failure! Saved to', failFile);
							}

							// Always create non-flattened failure images
							casper.captureSelector(file.replace('.diff.png', '.fail.png'), 'img');

							casper.evaluate(function(){
								window._imagediff_.hasImage = false;
							});

							if(mismatch){
								test.mismatch = mismatch;
								_onFail(test); // casper.test.fail throws and error, this function call is aborted
								return;  // Just to make it clear what is happening
							} else {
								_onTimeout(test);
							}

						}, function(){},
						10000
					);
				} else {
                    test.success = true;
					_onPass(test);
				}

			});
		});
	}
    return test;
}

function compareAll(exclude, list){
	var tests = [];

	_test_exclude = typeof exclude === 'string' ? new RegExp(exclude) : exclude;

	if (list){
		_diffsToProcess = list;
	} else {
		_realPath = undefined;
		getDiffs(_results);
	}

	_diffsToProcess.forEach(function(file){
		var baseFile = file.replace('.diff', '');
		tests.push(compareFiles(baseFile, file));
	});
	waitForTests(tests);
}

function waitForTests(tests){
	casper.then(function(){
		casper.waitFor(function(){
			return tests.length === tests.reduce(function(count, test){
				if (test.success || test.fail || test.error) {
					return count + 1;
				} else {
					return count;
				}
			}, 0);
		}, function(){
			var fails = 0, errors = 0;
			tests.forEach(function(test){
				if (test.fail){
					fails++;
				} else if (test.error){
					errors++;
				}
			});
			_onComplete(tests, fails, errors);
		}, function(){

		},
		10000);
	});
}

function initClient(){

	casper.page.injectJs(_libraryRoot+fs.separator+'ResembleJs'+fs.separator+'resemble.js');

	casper.evaluate(function(mismatchTolerance, resembleOutputSettings){

		var result;

		var div = document.createElement('div');

		// this is a bit of hack, need to get images into browser for analysis
		div.style = "display:block;position:absolute;border:0;top:-1px;left:-1px;height:1px;width:1px;overflow:hidden;";
		div.innerHTML = '<form id="image-diff">'+
			'<input type="file" id="image-diff-one" name="one"/>'+
			'<input type="file" id="image-diff-two" name="two"/>'+
		'</form>

';
		document.body.appendChild(div);

		if(resembleOutputSettings){
			resemble.outputSettings(resembleOutputSettings);
		}

		window._imagediff_ = {
			hasResult: false,
			hasImage: false,
			run: run,
			getResult: function(){
				window._imagediff_.hasResult = false;
				return result;
			}
		};

		function run(label){

			function render(data){
				var img = new Image();

				img.onload = function(){
					window._imagediff_.hasImage = true;
				};
				document.getElementById('image-diff').appendChild(img);
				img.src = data.getImageDataUrl(label);
			}

			resemble(document.getElementById('image-diff-one').files[0]).
				compareTo(document.getElementById('image-diff-two').files[0]).
				ignoreAntialiasing(). // <-- muy importante
				onComplete(function(data){
					var diffImage;

					if(Number(data.misMatchPercentage) > mismatchTolerance){
						result = data.misMatchPercentage;
					} else {
						result = false;
					}

					window._imagediff_.hasResult = true;

					if(Number(data.misMatchPercentage) > mismatchTolerance){
						render(data);
					}

				});
		}
	},
		_mismatchTolerance,
		_resembleOutputSettings
	);
}

function _onPass(test){
	console.log('\n');
	casper.test.pass('No changes found for screenshot ' + test.filename);
}
function _onFail(test){
	console.log('\n');
	casper.test.fail('Visual change found for screenshot ' + test.filename + ' (' + test.mismatch + '% mismatch)');
}
function _onTimeout(test){
	console.log('\n');
	casper.test.info('Could not complete image comparison for ' + test.filename);
}
function _onComplete(tests, noOfFails, noOfErrors){

	if( tests.length === 0){
		console.log("\nMust be your first time?");
		console.log("Some screenshots have been generated in the directory " + _results);
		console.log("This is your 'baseline', check the images manually. If they're wrong, delete the images.");
		console.log("The next time you run these tests, new screenshots will be taken.  These screenshots will be compared to the original.");
		console.log('If they are different, PhantomCSS will report a failure.');
	} else {

		if(noOfFails === 0){
			console.log("\nPhantomCSS found " + tests.length + " tests, None of them failed. Which is good right?");
			console.log("\nIf you want to make them fail, go change some CSS - weirdo.");
		} else {
			console.log("\nPhantomCSS found " + tests.length + " tests, " + noOfFails + ' of them failed.');
			console.log('\nPhantomCSS has created some images that try to show the difference (in the directory '+_failures+'). Fuchsia colored pixels indicate a difference betwen the new and old screenshots.');
		}

		if(noOfErrors !== 0){
			console.log("There were " + noOfErrors + "errors.  Is it possible that a baseline image was deleted but not the diff?");
		}

		exitStatus = noOfErrors+noOfFails;
	}
}

function getExitStatus() {
	return exitStatus;
}<|endoftext|>


<commit_before>function Sniper(resources, overrides) {
  Entity.call(this, resources, overrides);

  this.width  = 42;
  this.height = 42;
  this.speed  = 7 + _.random(8);
  this.shadow.on = true;

  this.target    = null;
  this.targetAge = 0;
  this.fireAge   = 0;
  this.variance  = 4;
  this.fireRate  = 80;
  this.firing    = false;
  this.moveTarget = { x: this.game.player.x, y: this.game.player.y };

  this.name = _.sample(['Athene', 'Bubo', 'Otus', 'Surnia', 'Asio', 'Nesasio', 'Strix', 'Ninox']);
  this.info.draw = true;

  this.xp = 0;
  this.kills = 0;

  this.sounds = {
    spawn: 'shartshooper',
    fire: ['shoot1', 'shoot4', 'shoot3']
  };

  this.game.audio.play(this.sounds.spawn);

  this.say(_.sample([
    'Shartest shooper in all the lands.',
    '100% stopping power.',
    'Grab the penetration upgrade, will ya?',
    'Precision.',
  ]));
}

Sniper.prototype = new Entity();

Sniper.prototype.constructor = Sniper;

Sniper.prototype.tick = function () {
  this.targetAge++;
  this.fireAge++;

  if (!isDefined(this.target) ||
      this.target.markedForDeletion) {
    this.target = _.sample(this.game.enemies);
    this.targetAge = 0;
  }

  if (isDefined(this.target)) {
    this.every(this.fireRate, function () {
      this.fireAt(this.target);
      this.fireAge = 0;

      // Set new moveTarget position after firing
      this.moveTarget = {
        x: this.game.player.x + randomNegation(_.random(64, 128)),
        y: this.game.player.y + randomNegation(_.random(64, 128))
      };
    });

    // Actually move to moveTarget after firing (0.25-0.5 of cooldown)
    if (this.fireAge > this.fireRate * 0.25 && this.fireAge < this.fireRate * 0.5) {
      this.moveToTarget(this.speed, this.distanceTo(this.moveTarget) / 150);
    }

    this.lookAt(this.target);

    this.every(15, function () {
      this.info.text = [
        this.name,
        this.xp + 'xp'
      ];
    }.bind(this));
  }
};

Sniper.prototype.fireAt = function (object) {
  this.fire(Math.atan2(this.y - object.y, this.x - object.x));
};

Sniper.prototype.fire = function (radians, offsetDegrees) {
  offsetDegrees = deg2rad(offsetDegrees) || 0;
  var variance = _.random(this.variance) * offsetDegrees;

  this.game.addEntity(
    new Bullet(this.resources, {
      x: this.x,
      y: this.y,
      direction: radians + variance,
      rotation: radians + variance,
      damage: 5,
      speed: 80,
      source: this
    })
  );

  this.game.audio.play(this.sounds.fire, 1);
  this.fireShake();
};

Sniper.prototype.fireShake = function () {
  var offsetDistance = 50;
  var normalized = normalize({ x: this.x - this.target.x, y: this.y - this.target.y });
  this.drawOffset.x += normalized.x * offsetDistance;
  this.drawOffset.y += normalized.y * offsetDistance;
};

Sniper.prototype.getImage = function () {
  return this.sprites.herosniper;
};

Sniper.prototype.draw = function (context) {
  this.drawOffset.x = Math.min(this.drawOffset.x * 0.9, 100);
  this.drawOffset.y = Math.min(this.drawOffset.y * 0.9, 100);

  if (this.fireAge <= 5)
    context.drawImage(this.sprites.flash1, -this.width, -this.height * 2);
  else if (this.fireAge <= 15)
    context.drawImage(this.sprites.flash2, -this.width, -this.height * 2);
};<commit_msg>Snipers now telegraph target<commit_after>function Sniper(resources, overrides) {
  Entity.call(this, resources, overrides);

  this.width  = 42;
  this.height = 42;
  this.speed  = 7 + _.random(8);
  this.shadow.on = true;

  this.target    = null;
  this.targetAge = 0;
  this.fireAge   = 0;
  this.variance  = 4;
  this.fireRate  = 80;
  this.firing    = false;
  this.moveTarget = { x: this.game.player.x, y: this.game.player.y };

  this.name = _.sample(['Athene', 'Bubo', 'Otus', 'Surnia', 'Asio', 'Nesasio', 'Strix', 'Ninox']);
  this.info.draw = true;

  this.xp = 0;
  this.kills = 0;

  this.sounds = {
    spawn: 'shartshooper',
    fire: ['shoot1', 'shoot4', 'shoot3']
  };

  this.game.audio.play(this.sounds.spawn);

  this.say(_.sample([
    'Shartest shooper in all the lands.',
    '100% stopping power.',
    'Grab the penetration upgrade, will ya?',
    'Precision.',
  ]));
}

Sniper.prototype = new Entity();

Sniper.prototype.constructor = Sniper;

Sniper.prototype.tick = function () {
  this.targetAge++;
  this.fireAge++;

  if (!isDefined(this.target) ||
      this.target.markedForDeletion) {
    this.target = _.sample(this.game.enemies);
    this.targetAge = 0;
  }

  if (isDefined(this.target)) {
    this.every(this.fireRate, function () {
      this.fireAt(this.target);
      this.fireAge = 0;

      // Set new moveTarget position after firing
      this.moveTarget = {
        x: this.game.player.x + randomNegation(_.random(64, 128)),
        y: this.game.player.y + randomNegation(_.random(64, 128))
      };
    });

    // Actually move to moveTarget after firing (0.25-0.5 of cooldown)
    if (this.fireAge > this.fireRate * 0.25 && this.fireAge < this.fireRate * 0.5) {
      this.moveToTarget(this.speed, this.distanceTo(this.moveTarget) / 150);
    }

    this.lookAt(this.target);

    this.every(15, function () {
      this.info.text = [
        this.name,
        this.xp + 'xp'
      ];
    }.bind(this));
  }
};

Sniper.prototype.fireAt = function (object) {
  this.fire(Math.atan2(this.y - object.y, this.x - object.x));
};

Sniper.prototype.fire = function (radians, offsetDegrees) {
  offsetDegrees = deg2rad(offsetDegrees) || 0;
  var variance = _.random(this.variance) * offsetDegrees;

  this.game.addEntity(
    new Bullet(this.resources, {
      x: this.x,
      y: this.y,
      direction: radians + variance,
      rotation: radians + variance,
      damage: 5,
      speed: 80,
      source: this
    })
  );

  this.game.audio.play(this.sounds.fire, 1);
  this.fireShake();
};

Sniper.prototype.fireShake = function () {
  var offsetDistance = 50;
  var normalized = normalize({ x: this.x - this.target.x, y: this.y - this.target.y });
  this.drawOffset.x += normalized.x * offsetDistance;
  this.drawOffset.y += normalized.y * offsetDistance;
};

Sniper.prototype.getImage = function () {
  return this.sprites.herosniper;
};

Sniper.prototype.draw = function (context) {
  this.drawOffset.x = Math.min(this.drawOffset.x * 0.9, 100);
  this.drawOffset.y = Math.min(this.drawOffset.y * 0.9, 100);

  if (this.fireAge <= 5)
    context.drawImage(this.sprites.flash1, -this.width, -this.height * 2);
  else if (this.fireAge <= 15)
    context.drawImage(this.sprites.flash2, -this.width, -this.height * 2);

  if (this.fireAge >= 50 && isDefined(this.target)) {
    context.beginPath();
    context.moveTo(0, 0);
    context.lineTo(0, -this.distanceTo(this.target));
    context.strokeStyle = 'red';
    context.strokeWidth = 2;
    context.stroke();
  }
};<|endoftext|>


<commit_before>var // Maths
    M = Math,
    phi = .618,
    PHI = 1 / phi,
    RGBMAX = 255,
    ceil = M.ceil,
    random = M.random,

    // Settings
    fps = 25,
    units = 10,
    multiplier = 0.1,
    maxDriftFactor = PHI / 100, // factor of canvas dimensions

    // Window & document
    win = this,
    doc = document,
    canvas = doc.getElementById('c'),
    ctx = canvas.getContext('2d'),
    width = canvas.width = win.innerWidth,
    height = canvas.height = win.innerHeight,

    // Modifiers
    factor = multiplier * phi,
    maxDriftX = width * maxDriftFactor,
    maxDriftY = height * maxDriftFactor,
    maxRadius = width * PHI / 100,

    // Timeline
    frequency = 1 / fps,
    intervalRef

    // String lookups
    //length = 'length',
    rgba = 'rgba(',
    stroke = 'stroke',
    strokeStyle = stroke + 'Style',
    beginPath = 'beginPath',
    closePath = 'closePath';

// **

function randomInt(length){
    return ceil((length || 2) * random()) - 1;
}

function drift(maxDrift){
    return ceil(random() * (maxDrift * 2 + 1) - maxDrift / 2 - 1);
}

function circle(x, y, intensity){
    var intensity = x / width * (y / height) / 2, // x, y position, in relation to the available width and height
        radius = ceil(intensity * maxRadius); // TODO, add randomness

    // create paths
    ctx[beginPath]();
    ctx.arc(x, y, radius, 0, M.PI * 2, 0);
    ctx[closePath]();

    // styles
    //rbgStr = rgba + r + ',' + g + ',' + b + ',';
    //ctx[strokeStyle] = rbgStr + phi * drift +')';
    //ctx.fillStyle = rbgStr + (phi * phi * phi * drift) +')';

    // draw
    ctx[stroke]();
    ctx.fill();
}

function unit(){
    var x = randomInt(width - maxDriftX + 1),
        y = randomInt(height - maxDriftY + 1),
        factorX, factorY,
        directionX, directionY,
        driftX, driftY;

    // Randomise
    driftX = drift(maxDriftX);
    driftY = drift(maxDriftY);

    // Calculate new position
    factorX = x * factor;
    factorY = y * factor;
    x = ceil(x + (directionX ? factorX : -factorX) + driftX);
    y = ceil(y + (directionY ? factorY : -factorY) + driftY);

    // Wrap around at boundaries to the canvas width and height
    if (x > width || x < 0){
        x = x > width ?
            2 * width - x :
            0 - x;
        directionX = ~directionX; // swich bits, to reverse the direction
    }
    if (y > height || y < 0){
        y = y > height ?
            2 * height - y :
            0 - y;
        directionY = ~directionY;
    }

    // draw
    circle(x, y);
}

function frame(){
    for (var i = units; i; i--){
        unit();
    }
}

//setInterval(frame, frequency);

// toggle animation on any mouse click or key press
(canvas.onclick = doc.onkeydown = function(){
    intervalRef = intervalRef ?
        clearInterval(intervalRef) :    // clearInterval and set intervalRef to undefined
        setInterval(frame, frequency);  // setInterval and create reference to it
})(); // start animation
<commit_msg>black and white<commit_after>var // Maths
    M = Math,
    phi = .618,
    PHI = 1 / phi,
    RGBMAX = 255,
    ceil = M.ceil,
    random = M.random,

    // Settings
    fps = 25,
    units = 10,
    multiplier = 0,
    maxDriftFactor = PHI / 100, // factor of canvas dimensions

    // Window & document
    win = this,
    doc = document,
    canvas = doc.getElementById('c'),
    ctx = canvas.getContext('2d'),
    width = canvas.width = win.innerWidth,
    height = canvas.height = win.innerHeight,

    // Modifiers
    factor = multiplier * phi,
    maxDriftX = width * maxDriftFactor,
    maxDriftY = height * maxDriftFactor,
    maxRadius = width * PHI / 100,

    // Timeline
    frequency = 1 / fps,
    intervalRef,

    // String lookups
    //length = 'length',
    rgba = 'rgba(',
    stroke = 'stroke',
    strokeStyle = stroke + 'Style',
    beginPath = 'beginPath',
    closePath = 'closePath';

// **

function randomInt(length){
    return ceil((length || 2) * random()) - 1;
}

function drift(maxDrift){
    return ceil(random() * (maxDrift * 2 + 1) - maxDrift / 2 - 1);
}

function circle(x, y, intensity){
    var intensity = x / width * (y / height) / 2, // x, y position, in relation to the available width and height
        radius = ceil(intensity * maxRadius),
        r = RGBMAX,
        g = RGBMAX,
        b = RGBMAX,
        rgbStr;

    // create paths
    ctx[beginPath]();
    ctx.arc(x, y, radius, 0, M.PI * 2, 0);
    ctx[closePath]();

    // styles
    //rbgStr = rgba + r + ',' + g + ',' + b + ',';
    //ctx[strokeStyle] = rbgStr + phi * drift +')';
    //ctx.fillStyle = rbgStr + (phi * phi * phi * drift) +')';
    rbgStr = rgba + r + ',' + g + ',' + b + ',';
    ctx[strokeStyle] = rbgStr + intensity +')';
    ctx.fillStyle = rbgStr + (phi * intensity) +')';

    // draw
    ctx[stroke]();
    ctx.fill();
}

function unit(){
    var x = randomInt(width - maxDriftX + 1),
        y = randomInt(height - maxDriftY + 1),
        factorX, factorY,
        directionX, directionY,
        driftX, driftY;

    // Randomise
    driftX = drift(maxDriftX);
    driftY = drift(maxDriftY);

    // Calculate new position
    factorX = x * factor;
    factorY = y * factor;
    x = ceil(x + (directionX ? factorX : -factorX) + driftX);
    y = ceil(y + (directionY ? factorY : -factorY) + driftY);

    // Wrap around at boundaries to the canvas width and height
    if (x > width || x < 0){
        x = x > width ?
            2 * width - x :
            0 - x;
        directionX = ~directionX; // swich bits, to reverse the direction
    }
    if (y > height || y < 0){
        y = y > height ?
            2 * height - y :
            0 - y;
        directionY = ~directionY;
    }

    // draw
    circle(x, y);
}

function frame(){
    for (var i = units; i; i--){
        unit();
    }
}

// Set body style
doc.body.style.cssText = 'margin:0;background-color:#000;overflow:hidden';

//setInterval(frame, frequency);

// toggle animation on any mouse click or key press
(canvas.onclick = doc.onkeydown = function(){
    intervalRef = intervalRef ?
        clearInterval(intervalRef) :    // clearInterval and set intervalRef to undefined
        setInterval(frame, frequency);  // setInterval and create reference to it
})(); // start animation
<|endoftext|>


<commit_before>"use strict";
(function($, ko) {
	window.GridViewModel = function(grid) {
		var self = this;

		self.answers = ko.observableArray();

		var firstLetter = {};
		var latestLetter = {};
		var direction = "";
		var canvas = document.getElementById("drawing");
		var context = canvas.getContext("2d");
		var currentLine = {};

		self.selectedWord = ko.observable();

		var canvasBackgroundColour = "#ffffff";
		var canvasAnswerColour = "#fe0011";
		var canvasCurrenctColour = "#00ff88";

		var gridOffset = $("#grid").offset();

		// setup the answers array
		(function() {
			var answers = [];
			$.each(grid.answers, function(i, answer) {
				answers.push( ko.observable({found:false, word:answer, line:{}}));
			});
			self.answers(answers);

			var rowsArrays = [];
			for (var i = 0, length=grid.vSize; i < length; i++) {
				var cells = ko.observableArray(grid.getRow(i));
				rowsArrays.push(cells);
			}
		   self.gridRowsArrays = ko.observableArray(rowsArrays);

		})();

		self.mouseUpInGrid = function(event) {
			currentLine = {};
			calculateLine(event);

			$.each(self.answers(), function(i,answer) {
				var answerObservable = answer();
				if (answerObservable.word.w.display() == self.selectedWord()) {

					var start = {x:firstLetter.cX, y:firstLetter.cY};
					var end = getCanvasPositionFromEvent(event);
					var answerLine = new Line(start, end, direction, canvasAnswerColour);
					answerObservable.line = answerLine;

					answerObservable.found = true;
					answer(answerObservable);

					clearWord();
					alert("FOUND " + self.selectedWord());
				}
			});
		};

		self.mouseDownInGrid = function(event) {
			var letter = letterCoord(event.target.id);
			var pos = getCanvasPositionFromEvent(event);
			letter.cX = pos.x;
			letter.cY = pos.y;

			firstLetter = letter;
			currentLine = {};
			console.log("firstLetter", firstLetter)
		};

		self.letterEnter = function(event) {
			fixWhich(event);

			if (event.which == 1) {
				var letter = letterCoord(event.target.id);

				var pos = getCanvasPositionFromEvent(event);
				letter.cX=pos.x;
				letter.cY=pos.y;
				latestLetter = letter;

				calculateLine();
			}
		};

		var getCanvasPositionFromEvent = function(event) {
			var target = $(event.target);

			var x = target[0].offsetLeft + 16;
			var y = target[0].offsetTop + 16;
			return {x:x, y:y};
		};

		var getCanvasPositionFromLetterPos = function(x,y) {
			var id = "cell_" + x + "_" + y;
			var letterElement = $("#" + id);
			var offset = letterElement.offset();
			var cX = offset.left - gridOffset.left;
			var cY = offset.top - gridOffset.top;

			return {x:cX + 16, y:cY + 16};
		};

		var fixWhich = function(e) {
		  if (!e.which && e.button) {
			if (e.button & 1) {
				e.which = 1; // Left
			} else if (e.button & 4) {
				e.which = 2; // Middle
			} else if (e.button & 2) {
				e.which = 3; // Right
			}
		  }
		};

		var letterCoord = function(id) {
			var bits = id.split("_");
			var letter = {x: parseInt(bits[1],10), y: parseInt(bits[2],10)};

			return letter;
		};

		var calculateLine = function(event) {
			if (!firstLetter) {
				return;
			}
			var x1 = firstLetter.x;
			var y1 = firstLetter.y;
			var x2,y2,i;

			if (event) {
				var pos = letterCoord(event.target.id);
				x2 = pos.x;
				y2 = pos.y;
			} else {
				x2 = (latestLetter && latestLetter.x) || x1 ;
				y2 = (latestLetter && latestLetter.y) || y1;
			}

			if (x1 == x2 && y1 == y2) {
				return;
			}

			var selected = "";
			// worry about e-w and n-s for the moment....
			if (y1 == y2) {
				// horizontal
				if (x1 < x2) {
					direction = "W";
					for (i =x1; i<=x2; i++) {
						selected += grid.getLetterAt(i,y1);
					}
				} else {
					direction = "E";
					for (i =x1; i>=x2; i--) {
						selected += grid.getLetterAt(i,y1);
					}
				}

			} else if(x1 == x2) {
				// vertical
				if (y1 < y2) {
					direction = "N";
					for (i=y1; i <= y2; i++) {
						selected += grid.getLetterAt(x1,i);
					}
				} else {
					direction = "S";
					for (i=y1; i >= y2; i--) {
						selected += grid.getLetterAt(x1,i);
					}
				}
			} else if (x2-x1 == y2-y1) {
				// diagonal
				var length = Math.abs(x2 - x1);
				var sign = Math.sign(x2 - x1);
				for (i=0; i<= length ; i++) {
					var x = x1 + (sign * i);
					var y = y1 + (sign * i);
					selected += grid.getLetterAt(x,y);
				}
				if (y1 < y2) {
					direction = "N";
				} else {
					direction = "S";
				}
				if (x1 < x2) {
					direction += "W";
				} else {
					direction += "E";
				}
			}

			self.selectedWord(selected);

			// draw a line....
			if (selected) {
				currentLine = new Line(
						getCanvasPositionFromLetterPos(x1, y1),
						getCanvasPositionFromLetterPos(x2, y2),
						direction,
						canvasCurrenctColour);
			}
		};

		var clearWord = function() {
			direction = "";
			firstLetter = undefined;
			latestLetter = undefined;
			self.selectedWord("");
			currentLine = {};
		};

		var drawCanvas = function() {
			// clear canvas
			context.fillStyle=canvasBackgroundColour;
			context.fillRect(0,0,canvas.width, canvas.height);

			// loop through all of the answered words and paint them
			$.each(self.answers(), function(i, answerObservable) {
				var answer = answerObservable();
				if (answer.found) {

console.log("answer found", answer.line);

// 					// draw answered lines
// 					context.lineWidth=10;
// 		 			context.strokeStyle = canvasAnswerColour;//"#ff00ff";
// // 		 			context.lineCap="round";
// 					context.beginPath();
// 					context.moveTo(answer.line.start.x, answer.line.start.y);
// 					context.lineTo(answer.line.end.x, answer.line.end.y);
// 					context.stroke();
					answer.line.simpleDraw(context);
				}

			});

			// draw the current line
			if (currentLine && currentLine.start && currentLine.end) {
				// draw current line
// 				context.lineWidth=10;
// 				context.strokeStyle = canvasCurrenctColour;
// 				context.beginPath();
// 				context.moveTo(currentLine.start.x, currentLine.start.y);
// 				context.lineTo(currentLine.end.x, currentLine.end.y);
// 				context.stroke();
				currentLine.simpleDraw(context);
			}
		};

		var canvasInterval = setInterval(drawCanvas, 1000.0 / 5);

		self.stopC = function() {
			clearInterval(canvasInterval);
		};

		return self;
	};
}(jQuery, ko));<commit_msg>clean up of some wierdies. N is now S etc...<commit_after>"use strict";
(function($, ko) {
	window.GridViewModel = function(grid) {
		var self = this;

		self.answers = ko.observableArray();

		var firstLetter = {};
		var latestLetter = {};
		var direction = "";
		var canvas = document.getElementById("drawing");
		var context = canvas.getContext("2d");
		var currentLine = {};

		var selectedWord;

		var canvasBackgroundColour = "#ffffff";
		var canvasAnswerColour = "#fe0011";
		var canvasCurrenctColour = "#00ff88";

		var gridOffset = $("#grid").offset();

		// setup the answers array
		(function() {
			var answers = [];
			$.each(grid.answers, function(i, answer) {
				answers.push( ko.observable({found:false, word:answer, line:{}}));
			});
			self.answers(answers);

			var rowsArrays = [];
			for (var i = 0, length=grid.vSize; i < length; i++) {
				var cells = ko.observableArray(grid.getRow(i));
				rowsArrays.push(cells);
			}
		   self.gridRowsArrays = ko.observableArray(rowsArrays);

		})();

		self.mouseUpInGrid = function(event) {
			currentLine = {};
			calculateLine(event);

			$.each(self.answers(), function(i,answer) {
				var answerObservable = answer();
				if (answerObservable.word.w.display() == selectedWord) {

					var start = {x:firstLetter.cX, y:firstLetter.cY};
					var end = getCanvasPositionFromEvent(event);
					var answerLine = new Line(start, end, direction, canvasAnswerColour);
					answerObservable.line = answerLine;

					answerObservable.found = true;
					answer(answerObservable);

					clearWord();
					alert("FOUND " + selectedWord);
				}
			});
		};

		self.mouseDownInGrid = function(event) {
			var letter = letterCoord(event.target.id);
			var pos = getCanvasPositionFromEvent(event);
			letter.cX = pos.x;
			letter.cY = pos.y;

			firstLetter = letter;
			currentLine = {};
			console.log("firstLetter", firstLetter);
		};

		self.letterEnter = function(event) {
			fixWhich(event);

			if (event.which == 1) {
				var letter = letterCoord(event.target.id);

				var pos = getCanvasPositionFromEvent(event);
				letter.cX=pos.x;
				letter.cY=pos.y;
				latestLetter = letter;

				calculateLine();
			}
		};

		var getCanvasPositionFromEvent = function(event) {
			var target = $(event.target);

			var x = target[0].offsetLeft + 16;
			var y = target[0].offsetTop + 16;
			return {x:x, y:y};
		};

		var getCanvasPositionFromLetterPos = function(x,y) {
			var id = "cell_" + x + "_" + y;
			var letterElement = $("#" + id);
			var offset = letterElement.offset();
			var cX = offset.left - gridOffset.left;
			var cY = offset.top - gridOffset.top;

			return {x:cX + 16, y:cY + 16};
		};

		var fixWhich = function(e) {
		  if (!e.which && e.button) {
			if (e.button & 1) {
				e.which = 1; // Left
			} else if (e.button & 4) {
				e.which = 2; // Middle
			} else if (e.button & 2) {
				e.which = 3; // Right
			}
		  }
		};

		var letterCoord = function(id) {
			var bits = id.split("_");
			var letter = {x: parseInt(bits[1],10), y: parseInt(bits[2],10)};

			return letter;
		};

		var calculateLine = function(event) {
			if (!firstLetter) {
				return;
			}
			var x1 = firstLetter.x;
			var y1 = firstLetter.y;
			var x2,y2,i;

			if (event) {
				var pos = letterCoord(event.target.id);
				x2 = pos.x;
				y2 = pos.y;
			} else {
				x2 = (latestLetter && latestLetter.x) || x1 ;
				y2 = (latestLetter && latestLetter.y) || y1;
			}

			if (x1 == x2 && y1 == y2) {
				return;
			}

			var selected = "";
			// worry about e-w and n-s for the moment....
			if (y1 == y2) {
				// horizontal
				if (x1 < x2) {
					direction = "E";
					for (i =x1; i<=x2; i++) {
						selected += grid.getLetterAt(i,y1);
					}
				} else {
					direction = "W";
					for (i =x1; i>=x2; i--) {
						selected += grid.getLetterAt(i,y1);
					}
				}

			} else if(x1 == x2) {
				// vertical
				if (y1 < y2) {
					direction = "S";
					for (i=y1; i <= y2; i++) {
						selected += grid.getLetterAt(x1,i);
					}
				} else {
					direction = "N";
					for (i=y1; i >= y2; i--) {
						selected += grid.getLetterAt(x1,i);
					}
				}
			} else if (Math.abs(x2-x1) == Math.abs(y2-y1)) {
				// diagonal
				var length = Math.abs(x2 - x1);
				var sign = Math.sign(x2 - x1);
				for (i=0; i<= length ; i++) {
					var x = x1 + (sign * i);
					var y = y1 + (sign * i);
					selected += grid.getLetterAt(x,y);
				}
				if (y1 < y2) {
					direction = "S";
				} else {
					direction = "N";
				}
				if (x1 < x2) {
					direction += "E";
				} else {
					direction += "W";
				}
			}

			// draw a line....
			if (selected) {
				selectedWord = selected;
				currentLine = new Line(
						getCanvasPositionFromLetterPos(x1, y1),
						getCanvasPositionFromLetterPos(x2, y2),
						direction,
						canvasCurrenctColour);
			}
		};

		var clearWord = function() {
			direction = "";
			firstLetter = undefined;
			latestLetter = undefined;
			selectedWord = undefined;
			currentLine = {};
		};

		var drawCanvas = function() {
			// clear canvas
			context.fillStyle=canvasBackgroundColour;
			context.fillRect(0,0,canvas.width, canvas.height);

			// loop through all of the answered words and paint them
			$.each(self.answers(), function(i, answerObservable) {
				var answer = answerObservable();
				if (answer.found) {

console.log("answer found", answer.line);

// 					// draw answered lines
// 					context.lineWidth=10;
// 		 			context.strokeStyle = canvasAnswerColour;//"#ff00ff";
// // 		 			context.lineCap="round";
// 					context.beginPath();
// 					context.moveTo(answer.line.start.x, answer.line.start.y);
// 					context.lineTo(answer.line.end.x, answer.line.end.y);
// 					context.stroke();
					answer.line.simpleDraw(context);
				}

			});

			// draw the current line
			if (currentLine && currentLine.start && currentLine.end) {
				// draw current line
// 				context.lineWidth=10;
// 				context.strokeStyle = canvasCurrenctColour;
// 				context.beginPath();
// 				context.moveTo(currentLine.start.x, currentLine.start.y);
// 				context.lineTo(currentLine.end.x, currentLine.end.y);
// 				context.stroke();
				currentLine.simpleDraw(context);
			}
		};

		var canvasInterval = setInterval(drawCanvas, 1000.0 / 5);

		self.stopC = function() {
			clearInterval(canvasInterval);
		};

		return self;
	};
}(jQuery, ko));<|endoftext|>


<commit_before>var Model = require("../model");

module.exports = Model.extend({
    name: "conversation",
    path: "conversations",
    idAttribute: "zid",
    urlRoot: "conversations",
    url: function() {
        return this.urlRoot + "/" + this.id;
    },
    defaults: {
      //topic: "", // an empty topic will be shown as a localized date string
      // topic: function() {
      //   return new Date(this.created).toString();
      // },
      description: "",
      created: 0,
      owner: undefined,
      participant_count: "",
      url_name: function(){
        var s = "/" + this.zid;
        if (this.zinvites) {
          s += "/" + this.zinvites[0]; // TODO deal with multiple?
        }
        return s;
      },
      url_name_with_hostname: function() {
        // build the URL for the user to copy & paste
        var s = "";
        if (/pol.is/.exec(document.location.hostname)) {
          // production
          s += "https://";
        }
        s += document.location.hostname;
        s += document.location.port ? (":" + document.location.port) : "";
        return s + this.url_name();
      },
      url_name_with_production_hostname: function() {
        return "https://pol.is" + this.url_name();
      },
      is_anon: false,
      is_draft: false,
      is_active: false,
      is_public: false, // if false, must validate and have account.
                       // if no domain specified, sms validation.
                       // if email address not empty string, email validation - ie., school email address.
      email_domain: undefined // @microsoft.com etc if require validation AND
    }

  });<commit_msg>only use zinvites when conversation is private<commit_after>var Model = require("../model");

module.exports = Model.extend({
    name: "conversation",
    path: "conversations",
    idAttribute: "zid",
    urlRoot: "conversations",
    url: function() {
        return this.urlRoot + "/" + this.id;
    },
    defaults: {
      //topic: "", // an empty topic will be shown as a localized date string
      // topic: function() {
      //   return new Date(this.created).toString();
      // },
      description: "",
      created: 0,
      owner: undefined,
      participant_count: "",
      url_name: function(){
        var s = "/" + this.zid;
        if (!this.is_public && this.zinvites) {
          s += "/" + this.zinvites[0]; // TODO deal with multiple?
        }
        return s;
      },
      url_name_with_hostname: function() {
        // build the URL for the user to copy & paste
        var s = "";
        if (/pol.is/.exec(document.location.hostname)) {
          // production
          s += "https://";
        }
        s += document.location.hostname;
        s += document.location.port ? (":" + document.location.port) : "";
        return s + this.url_name();
      },
      url_name_with_production_hostname: function() {
        return "https://pol.is" + this.url_name();
      },
      is_anon: false,
      is_draft: false,
      is_active: false,
      is_public: false, // if false, must validate and have account.
                       // if no domain specified, sms validation.
                       // if email address not empty string, email validation - ie., school email address.
      email_domain: undefined // @microsoft.com etc if require validation AND
    }

  });<|endoftext|>