<commit_before>const EventEmitter = require('events');
const icepick = require('icepick');

const {
  PAD_OPERATIONS,
  PARAMETERS,
  PUYOPUYO_COLOR_TYPES,
  PUYOPUYO_CONTROLLABLE_COLORS,
  PUYOPUYO_FIELD_SIZE,
} = require('./constants');

const DIRECTIONS = {
  UP: 'UP',
  RIGHT: 'RIGHT',
  DOWN: 'DOWN',
  LEFT: 'LEFT',
};
const DIRECTIONS_TO_COORDINATE_DELTAS = {
  UP: {
    rowIndex: -1,
    columnIndex: 0,
  },
  RIGHT: {
    rowIndex: 0,
    columnIndex: 1,
  },
  DOWN: {
    rowIndex: 1,
    columnIndex: 0,
  },
  LEFT: {
    rowIndex: 0,
    columnIndex: -1,
  },
};
const FALLING_PUYO_FREE_FALL_INTERVAL = 300;
const FALLING_PUYO_ROTATION = ['UP', 'RIGHT', 'DOWN', 'LEFT'];

class Store {
  static _generatePuyopuyoFieldSquareMatrix(rowLength, columnLength) {
    const matrix = [];

    for (let rowIndex = 0; rowIndex < PUYOPUYO_FIELD_SIZE.rowLength; rowIndex += 1) {
      const line = [];
      matrix.push(line);

      for (let columnIndex = 0; columnIndex < PUYOPUYO_FIELD_SIZE.columnLength; columnIndex += 1) {
        line.push({
          rowIndex,
          columnIndex,
          colorType: PUYOPUYO_COLOR_TYPES.NONE,
        });
      }
    }

    return matrix;
  }

  static _getSquare(puyopuyoFieldSquareMatrix, rowIndex, columnIndex) {
    const line = puyopuyoFieldSquareMatrix[rowIndex];
    if (!line) {
      return null;
    }
    return line[columnIndex] || null;
  }

  static _canPlacePuyo(puyopuyoFieldSquareMatrix, rowIndex, columnIndex) {
    const square = Store._getSquare(puyopuyoFieldSquareMatrix, rowIndex, columnIndex);
    if (!square) {
      return false;
    }
    return square.colorType === PUYOPUYO_COLOR_TYPES.NONE;
  }

  static _convertFallingPuyoToSquares(coordinate, colorTypes, direction) {
    const baseSquare = Object.assign({}, coordinate, {
      colorType: colorTypes[0],
    });
    const delta = DIRECTIONS_TO_COORDINATE_DELTAS[direction];
    const followingSquare = {
      rowIndex: coordinate.rowIndex + delta.rowIndex,
      columnIndex: coordinate.columnIndex + delta.columnIndex,
      colorType: colorTypes[1],
    };

    return [baseSquare, followingSquare];
  }

  static _canPlaceFallingPuyoOnSquares(coordinate, colorTypes, direction, puyopuyoFieldSquareMatrix) {
    const squares = Store._convertFallingPuyoToSquares(coordinate, colorTypes, direction);

    return squares.every(square => {
      return Store._canPlacePuyo(
        puyopuyoFieldSquareMatrix,
        square.rowIndex,
        square.columnIndex
      );
    });
  }

  constructor() {
    // It has a "change" event only.
    this._emitter = new EventEmitter();

    this._state = {
      fallingPuyo: Store._createFallingPuyoState(),
      padOperation: PAD_OPERATIONS.NONE,
      puyopuyoFieldSquareMatrix: Store._generatePuyopuyoFieldSquareMatrix(
        PUYOPUYO_FIELD_SIZE.rowLength,
        PUYOPUYO_FIELD_SIZE.columnLength
      ),
      gameLoopId: 0,
      gameTime: 0,
    };

    this._gameLoopTimerId = null;

    // Tmp
    //setTimeout(() => {
    //  this._state.puyopuyoFieldSquareMatrix[0][0].colorType = 'RED';
    //  this._emitChange();
    //}, 1000);
  }

  get emitter() {
    return this._emitter;
  }

  _emitChange() {
    this._emitter.emit('change');
  }

  getState() {
    return this._state;
  }

  static generateFallingPuyoSquares(state) {
    return state.fallingPuyo.isFalling
      ? Store._convertFallingPuyoToSquares(
        state.fallingPuyo.coordinate,
        state.fallingPuyo.colorTypes,
        state.fallingPuyo.direction
      )
      : []
    ;
  }

  static _createFallingPuyoState() {
    return {
      coordinate: {
        rowIndex: 0,
        columnIndex: 2,
      },
      isFalling: true,
      lastMovedAt: 0,
      colorTypes: [1, 2].map(() => PUYOPUYO_CONTROLLABLE_COLORS[Math.floor(Math.random() * 4)]),
      direction: DIRECTIONS.UP,
    };
  }

  static _nextGameLoopState(state) {
    let newState = state;

    // Falling Puyo
    if (state.fallingPuyo.isFalling) {
      // Apply pad operation
      if (state.padOperation !== PAD_OPERATIONS.NONE) {
        const nextCoordinate = {
          rowIndex: state.fallingPuyo.coordinate.rowIndex,
          columnIndex: state.fallingPuyo.coordinate.columnIndex,
        };
        if (state.padOperation === PAD_OPERATIONS.RIGHT) {
          nextCoordinate.columnIndex += 1;
        } else if (state.padOperation === PAD_OPERATIONS.LEFT) {
          nextCoordinate.columnIndex -= 1;
        } else if (state.padOperation === PAD_OPERATIONS.DOWN) {
          nextCoordinate.rowIndex += 1;
        }
        if (
          Store._canPlaceFallingPuyoOnSquares(
            nextCoordinate,
            newState.fallingPuyo.colorTypes,
            newState.fallingPuyo.direction,
            newState.puyopuyoFieldSquareMatrix
          )
        ) {
          newState = icepick.assocIn(newState, ['fallingPuyo', 'coordinate'], nextCoordinate);
        }
      }
      newState = icepick.assocIn(newState, ['padOperation'], PAD_OPERATIONS.NONE);

      // Apply free fall
      if (state.fallingPuyo.lastMovedAt + FALLING_PUYO_FREE_FALL_INTERVAL <= newState.gameTime) {
        const nextCoordinate = {
          rowIndex: state.fallingPuyo.coordinate.rowIndex + 1,
          columnIndex: state.fallingPuyo.coordinate.columnIndex,
        };

        // Touch the ground/other-puyos or not
        if (
          !Store._canPlaceFallingPuyoOnSquares(
            nextCoordinate,
            newState.fallingPuyo.colorTypes,
            newState.fallingPuyo.direction,
            newState.puyopuyoFieldSquareMatrix
          )
        ) {
          Store.generateFallingPuyoSquares(newState).forEach(square => {
            newState = icepick.assocIn(
              newState,
              ['puyopuyoFieldSquareMatrix', square.rowIndex, square.columnIndex, 'colorType'],
              square.colorType
            );
          });
          newState = icepick.assocIn(newState, ['fallingPuyo'], Store._createFallingPuyoState());
        } else {
          newState = icepick.assocIn(newState, ['fallingPuyo', 'coordinate'], nextCoordinate);
          newState = icepick.assocIn(newState, ['fallingPuyo', 'lastMovedAt'], newState.gameTime);
        }
      }
    } else {
      // TODO: Reserve new falling puyo
    }

    // Game time
    newState = icepick.assocIn(newState, ['gameLoopId'], newState.gameLoopId + 1);
    newState = icepick.assocIn(newState, ['gameTime'], newState.gameTime + PARAMETERS.GAME_LOOP_INTERVAL);

    return newState;
  }

  startGame() {
    const _handleGameLoop = () => {
      this._state = Store._nextGameLoopState(this._state);
      this._emitChange();
    };
    this._gameLoopTimerId = setInterval(_handleGameLoop, PARAMETERS.GAME_LOOP_INTERVAL);
  }

  receivePadOperation(padOperation) {
    if (this._state.padOperation === PAD_OPERATIONS.NONE) {
      this._state = icepick.assocIn(this._state, ['padOperation'], padOperation);
      this._emitChange();
    }
  }
}

module.exports = Store;
<commit_msg>Make to rotate falling puyos<commit_after>const EventEmitter = require('events');
const icepick = require('icepick');

const {
  PAD_OPERATIONS,
  PARAMETERS,
  PUYOPUYO_COLOR_TYPES,
  PUYOPUYO_CONTROLLABLE_COLORS,
  PUYOPUYO_FIELD_SIZE,
} = require('./constants');

const DIRECTIONS = {
  UP: 'UP',
  RIGHT: 'RIGHT',
  DOWN: 'DOWN',
  LEFT: 'LEFT',
};
const DIRECTION_ROTATION_ORDER = [
  DIRECTIONS.UP,
  DIRECTIONS.RIGHT,
  DIRECTIONS.DOWN,
  DIRECTIONS.LEFT,
];
const DIRECTIONS_TO_COORDINATE_DELTAS = {
  UP: {
    rowIndex: -1,
    columnIndex: 0,
  },
  RIGHT: {
    rowIndex: 0,
    columnIndex: 1,
  },
  DOWN: {
    rowIndex: 1,
    columnIndex: 0,
  },
  LEFT: {
    rowIndex: 0,
    columnIndex: -1,
  },
};
const FALLING_PUYO_FREE_FALL_INTERVAL = 300;
const FALLING_PUYO_ROTATION = ['UP', 'RIGHT', 'DOWN', 'LEFT'];

class Store {
  static _generatePuyopuyoFieldSquareMatrix(rowLength, columnLength) {
    const matrix = [];

    for (let rowIndex = 0; rowIndex < PUYOPUYO_FIELD_SIZE.rowLength; rowIndex += 1) {
      const line = [];
      matrix.push(line);

      for (let columnIndex = 0; columnIndex < PUYOPUYO_FIELD_SIZE.columnLength; columnIndex += 1) {
        line.push({
          rowIndex,
          columnIndex,
          colorType: PUYOPUYO_COLOR_TYPES.NONE,
        });
      }
    }

    return matrix;
  }

  static _getSquare(puyopuyoFieldSquareMatrix, rowIndex, columnIndex) {
    const line = puyopuyoFieldSquareMatrix[rowIndex];
    if (!line) {
      return null;
    }
    return line[columnIndex] || null;
  }

  static _canPlacePuyo(puyopuyoFieldSquareMatrix, rowIndex, columnIndex) {
    const square = Store._getSquare(puyopuyoFieldSquareMatrix, rowIndex, columnIndex);
    if (!square) {
      return false;
    }
    return square.colorType === PUYOPUYO_COLOR_TYPES.NONE;
  }

  static _convertFallingPuyoToSquares(coordinate, colorTypes, direction) {
    const baseSquare = Object.assign({}, coordinate, {
      colorType: colorTypes[0],
    });
    const delta = DIRECTIONS_TO_COORDINATE_DELTAS[direction];
    const followingSquare = {
      rowIndex: coordinate.rowIndex + delta.rowIndex,
      columnIndex: coordinate.columnIndex + delta.columnIndex,
      colorType: colorTypes[1],
    };

    return [baseSquare, followingSquare];
  }

  static _canPlaceFallingPuyoOnSquares(coordinate, colorTypes, direction, puyopuyoFieldSquareMatrix) {
    const squares = Store._convertFallingPuyoToSquares(coordinate, colorTypes, direction);

    return squares.every(square => {
      return Store._canPlacePuyo(
        puyopuyoFieldSquareMatrix,
        square.rowIndex,
        square.columnIndex
      );
    });
  }

  static _rotateDirection(direction, isClockwise) {
    const currentIndex = DIRECTION_ROTATION_ORDER.indexOf(direction);
    if (currentIndex === -1) {
      throw new Error('Improbable direction');
    }

    const delta = isClockwise ? 1 : -1;
    const nextIndex = (currentIndex + delta + DIRECTION_ROTATION_ORDER.length) % DIRECTION_ROTATION_ORDER.length;

    return DIRECTION_ROTATION_ORDER[nextIndex];
  }

  constructor() {
    // It has a "change" event only.
    this._emitter = new EventEmitter();

    this._state = {
      fallingPuyo: Store._createFallingPuyoState(),
      padOperation: PAD_OPERATIONS.NONE,
      puyopuyoFieldSquareMatrix: Store._generatePuyopuyoFieldSquareMatrix(
        PUYOPUYO_FIELD_SIZE.rowLength,
        PUYOPUYO_FIELD_SIZE.columnLength
      ),
      gameLoopId: 0,
      gameTime: 0,
    };

    this._gameLoopTimerId = null;

    // Tmp
    //setTimeout(() => {
    //  this._state.puyopuyoFieldSquareMatrix[0][0].colorType = 'RED';
    //  this._emitChange();
    //}, 1000);
  }

  get emitter() {
    return this._emitter;
  }

  _emitChange() {
    this._emitter.emit('change');
  }

  getState() {
    return this._state;
  }

  static generateFallingPuyoSquares(state) {
    return state.fallingPuyo.isFalling
      ? Store._convertFallingPuyoToSquares(
        state.fallingPuyo.coordinate,
        state.fallingPuyo.colorTypes,
        state.fallingPuyo.direction
      )
      : []
    ;
  }

  static _createFallingPuyoState() {
    return {
      coordinate: {
        rowIndex: 0,
        columnIndex: 2,
      },
      isFalling: true,
      lastMovedAt: 0,
      colorTypes: [1, 2].map(() => PUYOPUYO_CONTROLLABLE_COLORS[Math.floor(Math.random() * 4)]),
      direction: DIRECTIONS.UP,
    };
  }

  static _nextGameLoopState(state) {
    let newState = state;

    // Falling Puyo
    if (state.fallingPuyo.isFalling) {
      // Apply pad operation
      if (state.padOperation !== PAD_OPERATIONS.NONE) {
        const nextCoordinate = {
          rowIndex: state.fallingPuyo.coordinate.rowIndex,
          columnIndex: state.fallingPuyo.coordinate.columnIndex,
        };
        let nextDirection = state.fallingPuyo.direction;

        if (state.padOperation === PAD_OPERATIONS.RIGHT) {
          nextCoordinate.columnIndex += 1;
        } else if (state.padOperation === PAD_OPERATIONS.LEFT) {
          nextCoordinate.columnIndex -= 1;
        } else if (state.padOperation === PAD_OPERATIONS.DOWN) {
          nextCoordinate.rowIndex += 1;
        } else if (state.padOperation === PAD_OPERATIONS.OK) {
          nextDirection = Store._rotateDirection(nextDirection, false);
        } else if (state.padOperation === PAD_OPERATIONS.CANCEL) {
          nextDirection = Store._rotateDirection(nextDirection, true);
        }

        if (
          Store._canPlaceFallingPuyoOnSquares(
            nextCoordinate,
            newState.fallingPuyo.colorTypes,
            nextDirection,
            newState.puyopuyoFieldSquareMatrix
          )
        ) {
          newState = icepick.assocIn(newState, ['fallingPuyo', 'coordinate'], nextCoordinate);
          newState = icepick.assocIn(newState, ['fallingPuyo', 'direction'], nextDirection);
        }
      }
      newState = icepick.assocIn(newState, ['padOperation'], PAD_OPERATIONS.NONE);

      // Apply free fall
      if (state.fallingPuyo.lastMovedAt + FALLING_PUYO_FREE_FALL_INTERVAL <= newState.gameTime) {
        const nextCoordinate = {
          rowIndex: state.fallingPuyo.coordinate.rowIndex + 1,
          columnIndex: state.fallingPuyo.coordinate.columnIndex,
        };

        // Touch the ground/other-puyos or not
        if (
          !Store._canPlaceFallingPuyoOnSquares(
            nextCoordinate,
            newState.fallingPuyo.colorTypes,
            newState.fallingPuyo.direction,
            newState.puyopuyoFieldSquareMatrix
          )
        ) {
          Store.generateFallingPuyoSquares(newState).forEach(square => {
            newState = icepick.assocIn(
              newState,
              ['puyopuyoFieldSquareMatrix', square.rowIndex, square.columnIndex, 'colorType'],
              square.colorType
            );
          });
          newState = icepick.assocIn(newState, ['fallingPuyo'], Store._createFallingPuyoState());
        } else {
          newState = icepick.assocIn(newState, ['fallingPuyo', 'coordinate'], nextCoordinate);
          newState = icepick.assocIn(newState, ['fallingPuyo', 'lastMovedAt'], newState.gameTime);
        }
      }
    } else {
      // TODO: Reserve new falling puyo
    }

    // Game time
    newState = icepick.assocIn(newState, ['gameLoopId'], newState.gameLoopId + 1);
    newState = icepick.assocIn(newState, ['gameTime'], newState.gameTime + PARAMETERS.GAME_LOOP_INTERVAL);

    return newState;
  }

  startGame() {
    const _handleGameLoop = () => {
      this._state = Store._nextGameLoopState(this._state);
      this._emitChange();
    };
    this._gameLoopTimerId = setInterval(_handleGameLoop, PARAMETERS.GAME_LOOP_INTERVAL);
  }

  receivePadOperation(padOperation) {
    if (this._state.padOperation === PAD_OPERATIONS.NONE) {
      this._state = icepick.assocIn(this._state, ['padOperation'], padOperation);
      this._emitChange();
    }
  }
}

module.exports = Store;
<|endoftext|>


<commit_before>// Karma configuration
// Generated on Mon Jun 22 2015 10:34:42 GMT+0200 (CEST)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['mocha','chai', 'sinon-chai'],

    // list of files / patterns to load in the browser
    files: [
      'spendb.ui/vendor/moment/moment.js',
      'spendb.ui/vendor/d3/d3.js',
      'spendb.ui/vendor/speakingurl/speakingurl.min.js',
      'spendb.ui/vendor/angular/angular.js',
      'spendb.ui/vendor/angular-route/angular-route.js',
      'spendb.ui/vendor/angular-moment/angular-moment.js',
      'spendb.ui/vendor/angular-scroll/angular-scroll.js',
      'spendb.ui/vendor/angular-cookies/angular-cookies.js',
      'spendb.ui/vendor/angular-ui-select/dist/select.js',
      'spendb.ui/vendor/angular-filter/dist/angular-filter.js',
      'spendb.ui/vendor/ng-file-upload/ng-file-upload-shim.js',
      'spendb.ui/vendor/ng-file-upload/ng-file-upload.js',
      'spendb.ui/vendor/angular-bootstrap/ui-bootstrap-tpls.min.js',
      'spendb.ui/vendor/angular-cubes/build/angular-cubes.js',
      'node_modules/angular-mocks/angular-mocks.js',
      'spendb.ui/tests/js/helper.js',
      'spendb.ui/js/app.js',
      'spendb.ui/js/**/*.js',
      'spendb.ui/tests/js/**/*Spec.js'
    ],

    // list of files to exclude
    exclude: [
      '**/*.swp'
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false
  });
};
<commit_msg>Remove dependency from karma! <commit_after>// Karma configuration
// Generated on Mon Jun 22 2015 10:34:42 GMT+0200 (CEST)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['mocha','chai', 'sinon-chai'],

    // list of files / patterns to load in the browser
    files: [
      'spendb.ui/vendor/moment/moment.js',
      'spendb.ui/vendor/d3/d3.js',
      'spendb.ui/vendor/angular/angular.js',
      'spendb.ui/vendor/angular-route/angular-route.js',
      'spendb.ui/vendor/angular-moment/angular-moment.js',
      'spendb.ui/vendor/angular-scroll/angular-scroll.js',
      'spendb.ui/vendor/angular-cookies/angular-cookies.js',
      'spendb.ui/vendor/angular-ui-select/dist/select.js',
      'spendb.ui/vendor/angular-filter/dist/angular-filter.js',
      'spendb.ui/vendor/ng-file-upload/ng-file-upload-shim.js',
      'spendb.ui/vendor/ng-file-upload/ng-file-upload.js',
      'spendb.ui/vendor/angular-bootstrap/ui-bootstrap-tpls.min.js',
      'spendb.ui/vendor/angular-cubes/build/angular-cubes.js',
      'node_modules/angular-mocks/angular-mocks.js',
      'spendb.ui/tests/js/helper.js',
      'spendb.ui/js/app.js',
      'spendb.ui/js/**/*.js',
      'spendb.ui/tests/js/**/*Spec.js'
    ],

    // list of files to exclude
    exclude: [
      '**/*.swp'
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false
  });
};
<|endoftext|>


<commit_before>(function(window, document, $) {

  var colors = {};
  var $canvas = $('canvas');
  var context = $canvas[0].getContext('2d');
  var $textContent = $('#text-content');

  // Parent elements
  var elements = [$('html'), $('body'), $canvas];

  // Indexes for canvas position of painting
  var i = 0;
  var j = 0;

  // Size of the square to be painted
  var size = 40;

  // Width and height of the initial window
  var width = $(window).width();
  var height = $(window).height();

  var COLORS = 0;
  var TEXT = 1;
  var $toggleButton = $('#toggle');
  var $textButton = $('#change-to-text');
  var enabledMode = COLORS;

  // First row
  colors['81'] = ['#ff6b00', '#ffea00'];
  colors['87'] = ['#ffea00', '#bbff00'];
  colors['69'] = ['#bbff00', '#39ff00'];
  colors['82'] = ['#39ff00', '#00ff1a'];
  colors['84'] = ['#00ff1a', '#00ff8e'];
  colors['89'] = ['#00ff8e', '#00fff6'];
  colors['85'] = ['#00fff6', '#00a4ff'];
  colors['73'] = ['#00a4ff', '#001fff'];
  colors['79'] = ['#001fff', '#3400ff'];
  colors['80'] = ['#3400ff', '#b100ff'];

  // Second row
  colors['65'] = ['#ff8a00', '#fffa00'];
  colors['83'] = ['#fffa00', '#a2ff00'];
  colors['68'] = ['#a2ff00', '#24ff00'];
  colors['70'] = ['#24ff00', '#00ff2d'];
  colors['71'] = ['#00ff2d', '#00ffa3'];
  colors['72'] = ['#00ffa3', '#00fbff'];
  colors['74'] = ['#00fbff', '#0086ff'];
  colors['75'] = ['#0086ff', '#000cff'];
  colors['76'] = ['#000cff', '#4b00ff'];

  // Third row
  colors['90'] = ['#ffd000', '#dcff00'];
  colors['88'] = ['#dcff00', '#5fff00'];
  colors['67'] = ['#5fff00', '#00ff05'];
  colors['86'] = ['#00ff05', '#00ff69'];
  colors['66'] = ['#00ff69', '#00ffe1'];
  colors['78'] = ['#00ffe1', '#00c7ff'];
  colors['77'] = ['#00c7ff', '#003fff'];
  colors['188'] = ['#003fff', '#1800ff'];
  colors['190'] = ['#1800ff', '#8d00ff'];

  // Enter
  colors['32'] = ['#5fff00', '#003fff'];

  // Initialize parent elements size
  $canvas[0].width = width;
  $canvas[0].height = height;
  elements.forEach(function($element) {
     $element.css('width', width + 'px');
     $element.css('height', height + 'px');
  });

  $toggleButton.on('click', function(e) {
    e.preventDefault();
    e.stopPropagation();
    if (enabledMode === TEXT) {
      setMode(COLORS);
    }
    else {
      setMode(TEXT);
    }
  });

  function setMode(mode) {
    if (mode === TEXT) {
      $textButton.removeClass('invisible');
      $canvas.addClass('invisible');
      $textContent.removeClass('invisible');
      $textContent.focus();
      enabledMode = TEXT;
    }
    else {
      $textButton.addClass('invisible');
      $canvas.removeClass('invisible');
      $textContent.addClass('invisible');
      enabledMode = COLORS;
    }
  }

  // Paint a squared gradient
  function paint(keyCode) {
    context.beginPath();
    var grd = context.createLinearGradient(i, 0, i+size, 0);
    grd.addColorStop(0, colors[keyCode][0]);
    grd.addColorStop(1, colors[keyCode][1]);
    context.fillStyle = grd;
    context.fillRect(i, size*j, size, size);
  }

  window.addEventListener('keydown', function(e) {

    // Don't move scroll when enter key is pressed
    if (e.keyCode === 32) {
      e.preventDefault();
      e.stopPropagation();
      return;
    }

    // Paint a color
    paint(e.keyCode + '');
    i += size;

    if (i >= $canvas[0].width) {
      j++;
      i = 0;
    }
  });

}(window, document, jQuery));
<commit_msg>Update the text content div when keypress events<commit_after>(function(window, document, $) {

  var colors = {};
  var $canvas = $('canvas');
  var context = $canvas[0].getContext('2d');
  var $textContent = $('#text-content');

  // Parent elements
  var elements = [$('html'), $('body'), $canvas];

  // Indexes for canvas position of painting
  var i = 0;
  var j = 0;

  // Size of the square to be painted
  var size = 40;

  // Width and height of the initial window
  var width = $(window).width();
  var height = $(window).height();

  var COLORS = 0;
  var TEXT = 1;
  var $toggleButton = $('#toggle');
  var $textButton = $('#change-to-text');
  var enabledMode = COLORS;

  // First row
  colors['81'] = ['#ff6b00', '#ffea00'];
  colors['87'] = ['#ffea00', '#bbff00'];
  colors['69'] = ['#bbff00', '#39ff00'];
  colors['82'] = ['#39ff00', '#00ff1a'];
  colors['84'] = ['#00ff1a', '#00ff8e'];
  colors['89'] = ['#00ff8e', '#00fff6'];
  colors['85'] = ['#00fff6', '#00a4ff'];
  colors['73'] = ['#00a4ff', '#001fff'];
  colors['79'] = ['#001fff', '#3400ff'];
  colors['80'] = ['#3400ff', '#b100ff'];

  // Second row
  colors['65'] = ['#ff8a00', '#fffa00'];
  colors['83'] = ['#fffa00', '#a2ff00'];
  colors['68'] = ['#a2ff00', '#24ff00'];
  colors['70'] = ['#24ff00', '#00ff2d'];
  colors['71'] = ['#00ff2d', '#00ffa3'];
  colors['72'] = ['#00ffa3', '#00fbff'];
  colors['74'] = ['#00fbff', '#0086ff'];
  colors['75'] = ['#0086ff', '#000cff'];
  colors['76'] = ['#000cff', '#4b00ff'];

  // Third row
  colors['90'] = ['#ffd000', '#dcff00'];
  colors['88'] = ['#dcff00', '#5fff00'];
  colors['67'] = ['#5fff00', '#00ff05'];
  colors['86'] = ['#00ff05', '#00ff69'];
  colors['66'] = ['#00ff69', '#00ffe1'];
  colors['78'] = ['#00ffe1', '#00c7ff'];
  colors['77'] = ['#00c7ff', '#003fff'];
  colors['188'] = ['#003fff', '#1800ff'];
  colors['190'] = ['#1800ff', '#8d00ff'];

  // Enter
  colors['32'] = ['#5fff00', '#003fff'];

  // Initialize parent elements size
  $canvas[0].width = width;
  $canvas[0].height = height;
  elements.forEach(function($element) {
     $element.css('width', width + 'px');
     $element.css('height', height + 'px');
  });

  $toggleButton.on('click', function(e) {
    e.preventDefault();
    e.stopPropagation();
    if (enabledMode === TEXT) {
      setMode(COLORS);
    }
    else {
      setMode(TEXT);
    }
  });

  function setMode(mode) {
    if (mode === TEXT) {
      $textButton.removeClass('invisible');
      $canvas.addClass('invisible');
      $textContent.removeClass('invisible');
      $textContent.focus();
      enabledMode = TEXT;
    }
    else {
      $textButton.addClass('invisible');
      $canvas.removeClass('invisible');
      $textContent.addClass('invisible');
      enabledMode = COLORS;
    }
  }

  // Paint a squared gradient
  function paintGradient(keyCode) {
    context.beginPath();
    var grd = context.createLinearGradient(i, 0, i+size, 0);
    grd.addColorStop(0, colors[keyCode][0]);
    grd.addColorStop(1, colors[keyCode][1]);
    context.fillStyle = grd;
    context.fillRect(i, size*j, size, size);
  }

  // Add a new character to the text content div
  function addChar(charCode) {
    $textContent.get(0).innerText += String.fromCharCode(charCode);
  }

  // Handle keydown events for colors mode
  $(window).on('keydown', function(e) {

    // Don't move scroll when enter key is pressed
    if (e.keyCode === 32) {
      e.preventDefault();
      e.stopPropagation();
      return;
    }

    // Paint a color
    paintGradient(e.keyCode + '');
    i += size;

    if (i >= $canvas[0].width) {
      j++;
      i = 0;
    }
  });

  // The correct lowercase or uppercase value is returned for keypress events
  $(window).on('keypress', function(e) {
    if (enabledMode === COLORS) {
      addChar(e.which);
    }
  });

}(window, document, jQuery));
<|endoftext|>


<commit_before>var Botkit = require(__dirname + '/CoreBot.js');
var express = require('express');
var bodyParser = require('body-parser');
var querystring = require('querystring');
var request = require('requestretry');
var clone = require('clone');
var TeamsAPI = require(__dirname + '/TeamsAPI.js');

function TeamsBot(configuration) {
    var controller = Botkit(configuration || {});

    controller.api = TeamsAPI(configuration || {});

    controller.api.getToken(function(err) {
        if (err) {
            // this is a fatal error - could not create a Teams API client
            throw new Error(err);
        }
    });

    controller.defineBot(function(botkit, config) {
        var bot = {
            type: 'teams',
            botkit: botkit,
            config: config || {},
            utterances: botkit.utterances
        };

        bot.startConversation = function(message, cb) {
            botkit.startConversation(this, message, cb);
        };

        bot.createConversation = function(message, cb) {
            botkit.createConversation(this, message, cb);
        };

        bot.channelLink = function(channel_info) {
            return '' + channel_info.name + '';
        };

        bot.startPrivateConversation = function(message, cb) {

            bot.createPrivateConversation(message, function(err, new_convo) {

                if (err) {
                    cb(err);
                } else {
                    new_convo.activate();
                    cb(null, new_convo);
                }

            });

        };

        bot.createPrivateConversation = function(message, cb) {

            bot.openPrivateConvo(message, function(err, new_convo) {
                if (err) {
                    cb(err);
                } else {
                    message.raw_message.conversation = new_convo;
                    bot.createConversation(message, cb);
                }
            });

        };

        bot.openPrivateConvo = function(src, cb) {

            var data = {
                bot: src.recipient,
                members: [src.raw_message.from],
                channelData: src.channelData,
            };

            bot.api.createConversation(data, cb);

        };

        bot.openConvo = function(src, members, cb) {

            var data = {
                isGroup: true,
                bot: src.recipient,
                members: members,
                channelData: src.channelData,
            };

            bot.api.createConveration(data, cb);
        };

        bot.send = function(message, cb) {
            bot.api.addMessageToConversation(message.conversation.id, message, cb);
        };

        bot.replyWithActivity = function(src, message, cb) {

            var data = {
                type: 'message',
                recipient: src.raw_message.from,
                from: src.raw_message.recipient,
                conversation: src.conversation,
                channelData: {
                    notification: {
                      alert: true
                    }
                },
                text: message.text,
                summary: message.summary,
                attachments: message.attachments || null,
                attachmentLayout: message.attachmentLayout || 'list',
            };

            bot.api.addMessageToConversation(src.conversation.id, data, cb);
        };

        bot.replyToComposeExtension = function(src, attachments, cb) {

            // attachments will be an array of attachments
            // need to wrap it in necessary stuff
             var resp = {
              composeExtension:{
                type:"result",
                attachmentLayout:"list",
                attachments: attachments,
              }
            }

            src.http_res.send(resp);
            if (cb) {
                cb();
            }
        };

        bot.replyInThread = function(src, resp, cb) {

            // can't clone theis, not needed for this type of messages.
            delete(src.http_res);
            var copy = clone(src);

            // make sure this does NOT include the activity id
            copy.raw_message.conversation = src.raw_message.channelData.channel;

            bot.reply(copy, resp, cb);

        };

        bot.reply = function(src, resp, cb) {
            if (src.type === 'composeExtension') {
                bot.replyToComposeExtension(src, resp, cb);
            }
            if (typeof resp == 'string') {
                resp = {
                    text: resp
                };
            }

            resp.serviceUrl = src.raw_message.serviceUrl;
            resp.from = src.raw_message.recipient;
            resp.recipient = src.raw_message.from;
            resp.to = src.user;
            resp.channel = src.channel;
            resp.conversation = src.raw_message.conversation;

            bot.say(resp, cb);
        };

        bot.findConversation = function(message, cb) {
            botkit.debug('CUSTOM FIND CONVO', message.user, message.channel);
            for (var t = 0; t < botkit.tasks.length; t++) {
                for (var c = 0; c < botkit.tasks[t].convos.length; c++) {
                    if (
                      botkit.tasks[t].convos[c].isActive() &&
                      botkit.tasks[t].convos[c].source_message.user == message.user
                    ) {
                      botkit.debug('FOUND EXISTING CONVO!');
                      cb(botkit.tasks[t].convos[c]);
                      return;
                    }
                }
            }

            cb();
        };

        /* helper functions for creating attachments */
        bot.createAttachment = function(type, title, subtitle, text, images, buttons, tap) {

            var obj = {
                content: (typeof(title) === 'object') ? title : {
                    title: title || null,
                    subtitle: subtitle || null,
                    text: text || null,
                    buttons: buttons || [],
                    images: images || [],
                    tap: tap || null,
                },
                contentType: 'application/vnd.microsoft.card.' + type,
                title: function(v) {
                    this.content.title = v;
                    return this;
                },
                subtitle: function(v) {
                    this.content.subtitle = v;
                    return this;
                },
                text: function(v) {
                    this.content.text = v;
                    return this;
                },
                button: function(type, title, payload) {
                    if (!this.content.buttons) {
                        this.content.buttons = [];
                    }

                    var button_obj = (typeof(type) === 'object') ? type : {
                        type: type,
                        title: title,
                        payload: payload,
                    };

                    this.content.buttons.push(button_obj);
                    return this;
                },
                image: function(url, alt) {
                    if (!this.content.images) {
                        this.content.images = [];
                    }

                    var img_obj = (typeof(url) === 'object') ? type : {
                        url: url,
                        alt: alt || null
                    };

                    this.content.images.push(img_obj);
                    return this;
                },
                tap: function(type, title, payload) {
                  var tap_action = (typeof(type) === 'object') ? type : {
                      type: type,
                      title: title,
                      payload: payload,
                  };

                  this.content.tap = tap_action;

                  return this;
                },
                asString: function() {
                    return JSON.stringify(this, null, 2);
                }
            }

            return obj;

        }

        bot.createHero = function(title, subtitle, text, buttons, images, tap) {
          return bot.createAttachment('hero',title, subtitle, text, buttons, images, tap);
        }

        bot.createThumbnail = function(title, subtitle, text, buttons, images, tap) {
          return bot.createAttachment('thumbnail', title, subtitle, text, buttons, images, tap);
        }

        return bot;
    });

    controller.createWebhookEndpoints = function() {
        controller.webserver.post('/teams/receive', function(req, res) {

            var bot = controller.spawn({
                serviceUrl: message.serviceUrl,
            });

            var message = req.body;

            controller.ingest(bot, message, res);

        });
    };

    controller.middleware.spawn.use(function(bot, next) {

        if (!bot.config.serviceUrl) {
            throw new Error('Cannot spawn a bot without a serviceUrl in the configuration');
        }

        // set up the teams api client
        bot.api = TeamsAPI({
            clientId: controller.config.clientId,
            clientSecret: controller.config.clientSecret,
            token: controller.config.token,
            serviceUrl: bot.config.serviceUrl,
            team: bot.config.team,
        });

        next();

    });

    controller.middleware.ingest.use(function(bot, message, res, next) {

        res.status(200);
        if (message.name != 'composeExtension/query') {
            // send a result back immediately
            res.send('');
        }

        message.http_res = res;
        next();

    });

    controller.middleware.normalize.use(function(bot, message, next) {

        message.user = message.raw_message.from.id;
        message.channel = message.raw_message.conversation.id;

        next();

    });

    controller.middleware.categorize.use(function(bot, message, next) {

        if (message.type === 'invoke' && message.name === 'composeExtension/query') {
            message.type = 'composeExtension';

            // teams only supports a single parameter, it either exists or doesn't!
            message.text = message.value.parameters[0].value;

        }

        next();

    });

    controller.middleware.categorize.use(function(bot, message, next) {

        if (message.type == 'conversationUpdate') {

            console.log('got a conversation update event', JSON.stringify(message.channelData));
            if (message.raw_message.membersAdded) {
                // replies to these end up in the right place
                for (var m = 0; m < message.raw_message.membersAdded.length; m++) {

                    // clone the message
                    // and copy this member into the from list
                    delete(message.http_res); // <-- that can't be cloned safely
                    var copy = clone(message);
                    copy.from = message.raw_message.membersAdded[m];
                    copy.user = copy.from.id;

                    if (copy.user == message.raw_message.recipient.id) {
                        copy.type = 'bot_channel_join';
                    } else {
                        copy.type = 'user_channel_join';
                    }

                    // restart the categorize process for the newly cloned messages
                    controller.categorize(bot, copy);

                }

            } else if (message.raw_message.membersRemoved) {

                // replies to these end up in the right place
                for (var m = 0; m < message.raw_message.membersRemoved.length; m++) {

                    // clone the message
                    // and copy this member into the from list
                    delete(message.http_res); // <-- that can't be cloned safely
                    var copy = clone(message);
                    copy.from = message.raw_message.membersRemoved[m];
                    copy.user = copy.from.id;

                    if (copy.user == message.raw_message.recipient.id) {
                        copy.type = 'bot_channel_leave';
                    } else {
                        copy.type = 'user_channel_leave';
                    }

                    // restart the categorize process for the newly cloned messages
                    controller.categorize(bot, copy);
                }

                next();
            } else if (message.raw_message.channelData && message.raw_message.channelData.eventType) {
                // channelCreated
                // channelDeleted
                // channelRenamed
                // teamRenamed
                message.type = message.raw_message.channelData.eventType;

                // replies to these end up in general
                next();
            }

        } else {
            next();
        }
    });

    controller.middleware.categorize.use(function(bot, message, next) {

        if (message.type == 'message') message.type = 'message_received';

        if (!message.conversation.isGroup && message.type == 'message_received') {
            message.type = 'direct_message';
        } else if (message.conversation.isGroup && message.type == 'message_received') {

            // start by setting this to a mention, meaning that the bot's name was _somewhere_ in the string
            message.type = 'mention';

            // check to see if this is a direct mention ,meaning bot was mentioned at start of string
            for (var e = 0; e < message.entities.length; e++) {
                var entity = message.entities[0];
                if (entity.type == 'mention' && message.text) {
                    var pattern = new RegExp(message.recipient.id);
                    if (entity.mentioned.id.match(pattern)) {
                        var clean = new RegExp('^' + entity.text + '\\s+');

                        if (message.text.match(clean)) {

                          message.text = message.text.replace(clean, '');
                          message.type = 'direct_mention';
                        }
                    }
                }
            }
        }

        next();
    });

      // This middleware looks for Slack-style user mentions in a message
      // <@USERID> and translates them into Microsoft Teams style mentions
      // which look like <at>@User Name</at> and have a matching row in the
      // message.entities field.
      controller.middleware.send.use(function(bot, message, next) {

        var matches;
        var uniques = [];

        // extract all the <@USERID> patterns
        if (matches = message.text.match(/\<\@(.*?)\>/igm)) {

          // get a set of UNIQUE mentions - since the lookup of profile data is expensive
          for (var m = 0; m < matches.length; m++) {
            if (uniques.indexOf(matches[m]) == -1) {
              uniques.push(matches[m]);
            }
          }

          // loop over each mention
          async.each(uniques, function(match, next_match) {

            var uid = match.replace(/^\<\@/,'').replace(/\>$/,'');

            // use the teams API to load the latest profile information for the user
            bot.api.getUserById(message.channel,  uid, function(err, user_profile) {

              // if user is valid, replace the Slack-style mention and append to entities list
              if (user_profile) {
                var pattern = new RegExp('<@' + uid + '>','g');
                message.text = message.text.replace(pattern, '<at>@' + user_profile.name + '</at>');

                if (!message.entities) {
                  message.entities = [];
                }

                message.entities.push({
                  type: 'mention',
                  mentioned: {
                    id: uid,
                    name: user_profile.name,
                  },
                  text: '<at>@' + user_profile.name + '</at>',
                });
              }

              next_match();

            });

          }, function() {

            // we've processed all the matches, continue
            next();

          })

        } else {

          // if there were no matches, continue
          next();

        }

      });

    controller.middleware.format.use(function(bot, message, platform_message, next) {

      platform_message.type = 'message';
      platform_message.recipient =  message.recipient;
      platform_message.from =  message.from;
      platform_message.text =  message.text;
      platform_message.textFormat =  'markdown';
      platform_message.entities =  message.entities;
      platform_message.attachments =  message.attachments || null;
      platform_message.attachmentLayout =  message.attachmentLayout || 'list';
      platform_message.conversation =  message.conversation;

        next();
    });

    controller.startTicking();
    return controller;
}

module.exports = TeamsBot;
<commit_msg>add async<commit_after>var Botkit = require(__dirname + '/CoreBot.js');
var express = require('express');
var bodyParser = require('body-parser');
var querystring = require('querystring');
var request = require('requestretry');
var clone = require('clone');
var async = require('async');
var TeamsAPI = require(__dirname + '/TeamsAPI.js');

function TeamsBot(configuration) {
    var controller = Botkit(configuration || {});

    controller.api = TeamsAPI(configuration || {});

    controller.api.getToken(function(err) {
        if (err) {
            // this is a fatal error - could not create a Teams API client
            throw new Error(err);
        }
    });

    controller.defineBot(function(botkit, config) {
        var bot = {
            type: 'teams',
            botkit: botkit,
            config: config || {},
            utterances: botkit.utterances
        };

        bot.startConversation = function(message, cb) {
            botkit.startConversation(this, message, cb);
        };

        bot.createConversation = function(message, cb) {
            botkit.createConversation(this, message, cb);
        };

        bot.channelLink = function(channel_info) {
            return '' + channel_info.name + '';
        };

        bot.startPrivateConversation = function(message, cb) {

            bot.createPrivateConversation(message, function(err, new_convo) {

                if (err) {
                    cb(err);
                } else {
                    new_convo.activate();
                    cb(null, new_convo);
                }

            });

        };

        bot.createPrivateConversation = function(message, cb) {

            bot.openPrivateConvo(message, function(err, new_convo) {
                if (err) {
                    cb(err);
                } else {
                    message.raw_message.conversation = new_convo;
                    bot.createConversation(message, cb);
                }
            });

        };

        bot.openPrivateConvo = function(src, cb) {

            var data = {
                bot: src.recipient,
                members: [src.raw_message.from],
                channelData: src.channelData,
            };

            bot.api.createConversation(data, cb);

        };

        bot.openConvo = function(src, members, cb) {

            var data = {
                isGroup: true,
                bot: src.recipient,
                members: members,
                channelData: src.channelData,
            };

            bot.api.createConveration(data, cb);
        };

        bot.send = function(message, cb) {
            bot.api.addMessageToConversation(message.conversation.id, message, cb);
        };

        bot.replyWithActivity = function(src, message, cb) {

            var data = {
                type: 'message',
                recipient: src.raw_message.from,
                from: src.raw_message.recipient,
                conversation: src.conversation,
                channelData: {
                    notification: {
                      alert: true
                    }
                },
                text: message.text,
                summary: message.summary,
                attachments: message.attachments || null,
                attachmentLayout: message.attachmentLayout || 'list',
            };

            bot.api.addMessageToConversation(src.conversation.id, data, cb);
        };

        bot.replyToComposeExtension = function(src, attachments, cb) {

            // attachments will be an array of attachments
            // need to wrap it in necessary stuff
             var resp = {
              composeExtension:{
                type:"result",
                attachmentLayout:"list",
                attachments: attachments,
              }
            }

            src.http_res.send(resp);
            if (cb) {
                cb();
            }
        };

        bot.replyInThread = function(src, resp, cb) {

            // can't clone theis, not needed for this type of messages.
            delete(src.http_res);
            var copy = clone(src);

            // make sure this does NOT include the activity id
            copy.raw_message.conversation = src.raw_message.channelData.channel;

            bot.reply(copy, resp, cb);

        };

        bot.reply = function(src, resp, cb) {
            if (src.type === 'composeExtension') {
                bot.replyToComposeExtension(src, resp, cb);
            }
            if (typeof resp == 'string') {
                resp = {
                    text: resp
                };
            }

            resp.serviceUrl = src.raw_message.serviceUrl;
            resp.from = src.raw_message.recipient;
            resp.recipient = src.raw_message.from;
            resp.to = src.user;
            resp.channel = src.channel;
            resp.conversation = src.raw_message.conversation;

            bot.say(resp, cb);
        };

        bot.findConversation = function(message, cb) {
            botkit.debug('CUSTOM FIND CONVO', message.user, message.channel);
            for (var t = 0; t < botkit.tasks.length; t++) {
                for (var c = 0; c < botkit.tasks[t].convos.length; c++) {
                    if (
                      botkit.tasks[t].convos[c].isActive() &&
                      botkit.tasks[t].convos[c].source_message.user == message.user
                    ) {
                      botkit.debug('FOUND EXISTING CONVO!');
                      cb(botkit.tasks[t].convos[c]);
                      return;
                    }
                }
            }

            cb();
        };

        /* helper functions for creating attachments */
        bot.createAttachment = function(type, title, subtitle, text, images, buttons, tap) {

            var obj = {
                content: (typeof(title) === 'object') ? title : {
                    title: title || null,
                    subtitle: subtitle || null,
                    text: text || null,
                    buttons: buttons || [],
                    images: images || [],
                    tap: tap || null,
                },
                contentType: 'application/vnd.microsoft.card.' + type,
                title: function(v) {
                    this.content.title = v;
                    return this;
                },
                subtitle: function(v) {
                    this.content.subtitle = v;
                    return this;
                },
                text: function(v) {
                    this.content.text = v;
                    return this;
                },
                button: function(type, title, payload) {
                    if (!this.content.buttons) {
                        this.content.buttons = [];
                    }

                    var button_obj = (typeof(type) === 'object') ? type : {
                        type: type,
                        title: title,
                        payload: payload,
                    };

                    this.content.buttons.push(button_obj);
                    return this;
                },
                image: function(url, alt) {
                    if (!this.content.images) {
                        this.content.images = [];
                    }

                    var img_obj = (typeof(url) === 'object') ? type : {
                        url: url,
                        alt: alt || null
                    };

                    this.content.images.push(img_obj);
                    return this;
                },
                tap: function(type, title, payload) {
                  var tap_action = (typeof(type) === 'object') ? type : {
                      type: type,
                      title: title,
                      payload: payload,
                  };

                  this.content.tap = tap_action;

                  return this;
                },
                asString: function() {
                    return JSON.stringify(this, null, 2);
                }
            }

            return obj;

        }

        bot.createHero = function(title, subtitle, text, buttons, images, tap) {
          return bot.createAttachment('hero',title, subtitle, text, buttons, images, tap);
        }

        bot.createThumbnail = function(title, subtitle, text, buttons, images, tap) {
          return bot.createAttachment('thumbnail', title, subtitle, text, buttons, images, tap);
        }

        return bot;
    });

    controller.createWebhookEndpoints = function() {
        controller.webserver.post('/teams/receive', function(req, res) {

            var bot = controller.spawn({
                serviceUrl: message.serviceUrl,
            });

            var message = req.body;

            controller.ingest(bot, message, res);

        });
    };

    controller.middleware.spawn.use(function(bot, next) {

        if (!bot.config.serviceUrl) {
            throw new Error('Cannot spawn a bot without a serviceUrl in the configuration');
        }

        // set up the teams api client
        bot.api = TeamsAPI({
            clientId: controller.config.clientId,
            clientSecret: controller.config.clientSecret,
            token: controller.config.token,
            serviceUrl: bot.config.serviceUrl,
            team: bot.config.team,
        });

        next();

    });

    controller.middleware.ingest.use(function(bot, message, res, next) {

        res.status(200);
        if (message.name != 'composeExtension/query') {
            // send a result back immediately
            res.send('');
        }

        message.http_res = res;
        next();

    });

    controller.middleware.normalize.use(function(bot, message, next) {

        message.user = message.raw_message.from.id;
        message.channel = message.raw_message.conversation.id;

        next();

    });

    controller.middleware.categorize.use(function(bot, message, next) {

        if (message.type === 'invoke' && message.name === 'composeExtension/query') {
            message.type = 'composeExtension';

            // teams only supports a single parameter, it either exists or doesn't!
            message.text = message.value.parameters[0].value;

        }

        next();

    });

    controller.middleware.categorize.use(function(bot, message, next) {

        if (message.type == 'conversationUpdate') {

            console.log('got a conversation update event', JSON.stringify(message.channelData));
            if (message.raw_message.membersAdded) {
                // replies to these end up in the right place
                for (var m = 0; m < message.raw_message.membersAdded.length; m++) {

                    // clone the message
                    // and copy this member into the from list
                    delete(message.http_res); // <-- that can't be cloned safely
                    var copy = clone(message);
                    copy.from = message.raw_message.membersAdded[m];
                    copy.user = copy.from.id;

                    if (copy.user == message.raw_message.recipient.id) {
                        copy.type = 'bot_channel_join';
                    } else {
                        copy.type = 'user_channel_join';
                    }

                    // restart the categorize process for the newly cloned messages
                    controller.categorize(bot, copy);

                }

            } else if (message.raw_message.membersRemoved) {

                // replies to these end up in the right place
                for (var m = 0; m < message.raw_message.membersRemoved.length; m++) {

                    // clone the message
                    // and copy this member into the from list
                    delete(message.http_res); // <-- that can't be cloned safely
                    var copy = clone(message);
                    copy.from = message.raw_message.membersRemoved[m];
                    copy.user = copy.from.id;

                    if (copy.user == message.raw_message.recipient.id) {
                        copy.type = 'bot_channel_leave';
                    } else {
                        copy.type = 'user_channel_leave';
                    }

                    // restart the categorize process for the newly cloned messages
                    controller.categorize(bot, copy);
                }

                next();
            } else if (message.raw_message.channelData && message.raw_message.channelData.eventType) {
                // channelCreated
                // channelDeleted
                // channelRenamed
                // teamRenamed
                message.type = message.raw_message.channelData.eventType;

                // replies to these end up in general
                next();
            }

        } else {
            next();
        }
    });

    controller.middleware.categorize.use(function(bot, message, next) {

        if (message.type == 'message') message.type = 'message_received';

        if (!message.conversation.isGroup && message.type == 'message_received') {
            message.type = 'direct_message';
        } else if (message.conversation.isGroup && message.type == 'message_received') {

            // start by setting this to a mention, meaning that the bot's name was _somewhere_ in the string
            message.type = 'mention';

            // check to see if this is a direct mention ,meaning bot was mentioned at start of string
            for (var e = 0; e < message.entities.length; e++) {
                var entity = message.entities[0];
                if (entity.type == 'mention' && message.text) {
                    var pattern = new RegExp(message.recipient.id);
                    if (entity.mentioned.id.match(pattern)) {
                        var clean = new RegExp('^' + entity.text + '\\s+');

                        if (message.text.match(clean)) {

                          message.text = message.text.replace(clean, '');
                          message.type = 'direct_mention';
                        }
                    }
                }
            }
        }

        next();
    });

      // This middleware looks for Slack-style user mentions in a message
      // <@USERID> and translates them into Microsoft Teams style mentions
      // which look like <at>@User Name</at> and have a matching row in the
      // message.entities field.
      controller.middleware.send.use(function(bot, message, next) {

        var matches;
        var uniques = [];

        // extract all the <@USERID> patterns
        if (matches = message.text.match(/\<\@(.*?)\>/igm)) {

          // get a set of UNIQUE mentions - since the lookup of profile data is expensive
          for (var m = 0; m < matches.length; m++) {
            if (uniques.indexOf(matches[m]) == -1) {
              uniques.push(matches[m]);
            }
          }

          // loop over each mention
          async.each(uniques, function(match, next_match) {

            var uid = match.replace(/^\<\@/,'').replace(/\>$/,'');

            // use the teams API to load the latest profile information for the user
            bot.api.getUserById(message.channel,  uid, function(err, user_profile) {

              // if user is valid, replace the Slack-style mention and append to entities list
              if (user_profile) {
                var pattern = new RegExp('<@' + uid + '>','g');
                message.text = message.text.replace(pattern, '<at>@' + user_profile.name + '</at>');

                if (!message.entities) {
                  message.entities = [];
                }

                message.entities.push({
                  type: 'mention',
                  mentioned: {
                    id: uid,
                    name: user_profile.name,
                  },
                  text: '<at>@' + user_profile.name + '</at>',
                });
              }

              next_match();

            });

          }, function() {

            // we've processed all the matches, continue
            next();

          })

        } else {

          // if there were no matches, continue
          next();

        }

      });

    controller.middleware.format.use(function(bot, message, platform_message, next) {

      platform_message.type = 'message';
      platform_message.recipient =  message.recipient;
      platform_message.from =  message.from;
      platform_message.text =  message.text;
      platform_message.textFormat =  'markdown';
      platform_message.entities =  message.entities;
      platform_message.attachments =  message.attachments || null;
      platform_message.attachmentLayout =  message.attachmentLayout || 'list';
      platform_message.conversation =  message.conversation;

        next();
    });

    controller.startTicking();
    return controller;
}

module.exports = TeamsBot;
<|endoftext|>


<commit_before>var should = require('should'),
    gd = require('../index.js'),
    _ = require('underscore'),
    fs = require('fs'),
    stream = require('stream'),
    samples = require('./samples.js'),
    h = require('./helpers.js')

describe('gd', function () {
    it('should .resize() images piped in', function (done) {
        testTransform(
            gd.resize({width:50, height:40}),
            function (image) {
                image.width.should.be.equal(40)
                image.height.should.be.equal(40)
                done()
            }
        )
    })

    it('should .crop() images piped in', function (done) {
        testTransform(
            gd.crop({width:20, height:30}),
            function (image) {
                image.width.should.be.equal(20)
                image.height.should.be.equal(30)
                done()
            }
        )
    })

    it('should .watermark() images piped in')
    it('should set format setting for piped in images with .format()')
    it('should set quality setting for piped in images with .quality()')
    it('should set compression setting for piped in images with .compression()')
    it('should set saving options for piped in images with .options()')
})

function testTransform(transform, callback) {
    var dst = h.WritableStream()
    dst.on('finish', function () {
        var image = gd.open(this.written)
        callback(image)
    })
    var imageData = h.createImage().save({format: 'png'})
    var src = new h.ReadableStream(imageData)
    src.pipe(transform).pipe(dst)
}
<commit_msg>Transform interface tests<commit_after>var should = require('should'),
    gd = require('../index.js'),
    _ = require('underscore'),
    async = require('async'),
    fs = require('fs'),
    stream = require('stream'),
    samples = require('./samples.js'),
    h = require('./helpers.js')

describe('gd', function () {
    it('should .resize() images piped in', function (done) {
        testTransform(
            gd.resize({width:50, height:40}),
            function (image) {
                image.width.should.be.equal(40)
                image.height.should.be.equal(40)
                done()
            }
        )
    })

    it('should .crop() images piped in', function (done) {
        testTransform(
            gd.crop({width:20, height:30}),
            function (image) {
                image.width.should.be.equal(20)
                image.height.should.be.equal(30)
                done()
            }
        )
    })

    it('should .watermark() images piped in', function (done) {
        testTransform(
            gd.watermark(samples.watermark, {x: 0, y: 0}),
            function (image) {
                image.getPixel(1, 1).should.equal(0xFFFFFF)
                done()
            }
        )
    })

    it('should set format setting for piped in images with .format()', function (done) {
        async.each(samples.types, function (type, doneType) {
            testTransform(
                gd.format(type),
                function (image) {
                    image.format.should.equal(type)
                    doneType()
                }
            )
        }, done)
    })

    it('should set quality setting for piped in images with .quality()', function (done) {
        async.parallel({
            highQualityImage: function (callback) {
                testTransform(
                    gd.format('jpeg').quality(100),
                    function (image, data) {callback(null, data)}
                )
            },
            lowQualityImage: function (callback) {
                testTransform(
                    gd.format('jpeg').quality(10),
                    function (image, data) {callback(null, data)}
                )
            }
        }, function (err, results) {
            results.highQualityImage.length.should.be.above(results.lowQualityImage.length)
            done()
        })
    })

    it('should set compression setting for piped in images with .compression()', function (done) {
        async.parallel({
            poorlyCompressedImage: function (callback) {
                testTransform(
                    gd.format('png').compression(1),
                    function (image, data) {callback(null, data)}
                )
            },
            heavilyCompressedImage: function (callback) {
                testTransform(
                    gd.format('png').compression(9),
                    function (image, data) {callback(null, data)}
                )
            }
        }, function (err, results) {
            results.poorlyCompressedImage.length.should.be.above(results.heavilyCompressedImage.length)
            done()
        })
    })

    it('should set saving options for piped in images with .options()', function (done) {
        async.each(samples.types, function (type, doneType) {
            testTransform(
                gd.options({format :type}),
                function (image) {
                    image.format.should.equal(type)
                    doneType()
                }
            )
        }, done)
    })
})

function testTransform(transform, callback) {
    var dst = h.WritableStream()
    dst.on('finish', function () {
        var image = gd.open(this.written)
        callback(image, this.written)
    })
    var imageData = h.createImage(100, 100).save({format: 'png'})
    var src = new h.ReadableStream(imageData)
    src.pipe(transform).pipe(dst)
}
<|endoftext|>


<commit_before>//jshint strict: false
module.exports = function(config) {
	config.set({

		basePath: './app',

		files: [
			'bower_components/angular/angular.js',
			'**/*.module.js',
			'**/*.js'
		],

		exclude: [
			'bower_components/**/!(angular.js|angular-route.js|angular-mocks.js)'
		],

		autoWatch: true,

		frameworks: ['jasmine'],

		browsers: [
			// 'Chrome',
			'PhantomJS'
		],

		reporters: ['coverage', 'spec'],

		plugins: [
			'karma-chrome-launcher',
			'karma-firefox-launcher',
			'karma-phantomjs-launcher',
			'karma-jasmine',
			'karma-junit-reporter',
			'karma-spec-reporter',
			'karma-coverage'
		],

		preprocessors: {
			'app.*.js': ['coverage'],
			'!(bower_components)/**/*.js': ['coverage']
		},

		junitReporter: {
			outputFile: '../test_out/unit.xml',
			suite: 'unit'
		},

		coverageReporter: {
			dir: '../test_out/',
			type: 'html'
		}

	});
};
<commit_msg>Disabled Karma coverage plugin by default to make debugging easier<commit_after>//jshint strict: false
module.exports = function(config) {
	config.set({

		basePath: './app',

		files: [
			'bower_components/angular/angular.js',
			'**/*.module.js',
			'**/*.js'
		],

		exclude: [
			'bower_components/**/!(angular.js|angular-route.js|angular-mocks.js)'
		],

		autoWatch: true,

		frameworks: ['jasmine'],

		browsers: [
			// 'Chrome',
			'PhantomJS'
		],

		reporters: [
			// 'coverage',
			'spec'
		],

		plugins: [
			'karma-chrome-launcher',
			'karma-firefox-launcher',
			'karma-phantomjs-launcher',
			'karma-jasmine',
			'karma-junit-reporter',
			'karma-spec-reporter',
			'karma-coverage'
		],

		preprocessors: {
			'app.*.js': ['coverage'],
			'!(bower_components)/**/*.js': ['coverage']
		},

		junitReporter: {
			outputFile: '../test_out/unit.xml',
			suite: 'unit'
		},

		coverageReporter: {
			dir: '../test_out/',
			type: 'html'
		}

	});
};
<|endoftext|>


<commit_before>'use strict';
// (function(){
  var btnCrypt = $('.crypt')
  var btnSSL = $('.ssl')
  var btnDecrypt = $('.decrypt')
  var btnSend = $('.btn-send')
  var recipient = $('#recipient')
  var pgpMessage
  var mail = {
    topic: 'Cryptoparty Test-Email',
    sender: 'demo@cryptoparty-hamburg.de',
    receiver: 'frank@frank.de',
    message: '',
    timestamp: '',
    ssl: false,
    pgp: false
  }

  var socket = io('http://cryptoterrorist.net:4223')

  socket.on('message', function(data){
    console.log(data)
    if ( data.pgp ) {
      btnDecrypt.show()
    } else {
      btnDecrypt.hide()
    }
    $('.to').text(data.receiver)
    $('.date').text(data.timestamp)
    $('.topic').text(data.topic)
    $('.mail-content').text(data.message)
    if (endpoint) {
      if( data.ssl ) {
        $('.mail-content').text(window.atob(data.message))
      }
    }

    if (server) {
      if( data.ssl ) {
        $('.mail-content').text(window.atob(data.message))
      }
    }

    if (attacker) {
      if( data.ssl ) {
        $('.sender').text('Can\'t break SSL encryption')
        $('.to').text('Can\'t break SSL encryption')
        $('.date').text('Can\'t break SSL encryption')
        $('.topic').text('Can\'t break SSL encryption')
        $('.mail-content').text('Can\'t break SSL encryption')
      }
    }

  })

  btnDecrypt.hide()
  recipient.on('change', function() {
    mail.receiver = $(this).val()
  })

  btnSend.on('click', function(){
    mail.topic = $('#subject').val()
    mail.timestamp = moment()
    mail.timestamp = moment().format('MMMM Do YYYYY, h:mm:ss a')
    if( btnSSL.hasClass('btn-success') ) {
      mail.message = window.btoa(window.text.value)
    } else {
      mail.message = window.text.value
    }

    console.log(mail)
    socket.emit('message', mail)
  })

  btnDecrypt.on('click', function(){
    $('.mail-content').text( textDecrypt( $('.mail-content').text() ) )
  })

  btnSSL.on('click', function(){
    mail.ssl = true
    $(this).toggleClass('btn-danger')
    $(this).toggleClass('btn-success')
  })

  btnCrypt.on('click', function(){
    if( $(this).hasClass('btn-danger') ) {
      window.text.value = textEncrypt(window.text.value)
      $(this).text('Verschlüsselt')
      mail.pgp = true
    } else {
      window.text.value = textDecrypt(window.text.value)
      $(this).text('Verschlüsseln')
      mail.pgp = false
    }
    $(this).toggleClass('btn-danger')
    $(this).toggleClass('btn-success')
  })

// })()
<commit_msg>Fixed but in attacker view when SSL was enabled. Thanks guide!<commit_after>'use strict';
// (function(){
  var btnCrypt = $('.crypt')
  var btnSSL = $('.ssl')
  var btnDecrypt = $('.decrypt')
  var btnSend = $('.btn-send')
  var recipient = $('#recipient')
  var pgpMessage
  var mail = {
    topic: 'Cryptoparty Test-Email',
    sender: 'demo@cryptoparty-hamburg.de',
    receiver: 'frank@frank.de',
    message: '',
    timestamp: '',
    ssl: false,
    pgp: false
  }

  var socket = io('http://cryptoterrorist.net:4223')

  socket.on('message', function(data){
    console.log(data)
    if ( data.pgp ) {
      btnDecrypt.show()
    } else {
      btnDecrypt.hide()
    }
    $('.to').text(data.receiver)
    $('.date').text(data.timestamp)
    $('.topic').text(data.topic)
    $('.mail-content').text(data.message)
    if (endpoint) {
      if( data.ssl ) {
        $('.mail-content').text(window.atob(data.message))
      }
    }

    if (server) {
      if( data.ssl ) {
        $('.mail-content').text(window.atob(data.message))
      }
    }

    if (attacker) {
      if( data.ssl ) {
        $('.sender').text('Can\'t break SSL encryption')
        $('.to').text('Can\'t break SSL encryption')
        $('.date').text('Can\'t break SSL encryption')
        $('.topic').text('Can\'t break SSL encryption')
        $('.mail-content').text('Can\'t break SSL encryption')
      }
    }

  })

  btnDecrypt.hide()
  recipient.on('change', function() {
    mail.receiver = $(this).val()
  })

  btnSend.on('click', function(){
    mail.topic = $('#subject').val()
    mail.timestamp = moment()
    mail.timestamp = moment().format('MMMM Do YYYYY, h:mm:ss a')
    if( btnSSL.hasClass('btn-success') ) {
      mail.message = window.btoa(window.text.value)
    } else {
      mail.message = window.text.value
    }

    console.log(mail)
    socket.emit('message', mail)
  })

  btnDecrypt.on('click', function(){
    $('.mail-content').text( textDecrypt( $('.mail-content').text() ) )
  })

  btnSSL.on('click', function(){
    if(mail.ssl) {
      mail.ssl = false
    } else {
      mail.ssl = true
    }

    $(this).toggleClass('btn-danger')
    $(this).toggleClass('btn-success')
  })

  btnCrypt.on('click', function(){
    if( $(this).hasClass('btn-danger') ) {
      window.text.value = textEncrypt(window.text.value)
      $(this).text('Verschlüsselt')
      mail.pgp = true
    } else {
      window.text.value = textDecrypt(window.text.value)
      $(this).text('Verschlüsseln')
      mail.pgp = false
    }
    $(this).toggleClass('btn-danger')
    $(this).toggleClass('btn-success')
  })

// })()
<|endoftext|>


<commit_before>/**
 * @author Mat Groves http://matgroves.com/ @Doormat23
 */

/**
 * The sprite sheet loader is used to load in JSON sprite sheet data
 * To generate the data you can use http://www.codeandweb.com/texturepacker and publish the "JSON" format
 * There is a free version so thats nice, although the paid version is great value for money.
 * It is highly recommended to use Sprite sheets (also know as texture atlas') as it means sprite's can be batched and drawn together for highly increased rendering speed.
 * Once the data has been loaded the frames are stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrameId() and PIXI.Sprite.fromFromeId()
 * This loader will also load the image file that the Spritesheet points to as well as the data.
 * When loaded this class will dispatch a 'loaded' event
 * @class SpriteSheetLoader
 * @extends EventTarget
 * @constructor
 * @param url {String} the url of the sprite sheet JSON file
 */

PIXI.SpriteSheetLoader = function(url)
{
	/*
	 * i use texture packer to load the assets..
	 * http://www.codeandweb.com/texturepacker
	 * make sure to set the format as "JSON"
	 */
	PIXI.EventTarget.call( this );
	this.url = url;
	this.baseUrl = url.replace(/[^\/]*$/, '');
	this.texture;
	this.frames = {};
}

// constructor
PIXI.SpriteSheetLoader.constructor = PIXI.SpriteSheetLoader;

/**
 * This will begin loading the JSON file
 */
PIXI.SpriteSheetLoader.prototype.load = function()
{
	this.ajaxRequest = new AjaxRequest();
	var scope = this;
	this.ajaxRequest.onreadystatechange=function()
	{
		scope.onLoaded();
	}

	this.ajaxRequest.open("GET", this.url, true)
	this.ajaxRequest.send(null)
}

PIXI.SpriteSheetLoader.prototype.onLoaded = function()
{
	if (this.ajaxRequest.readyState==4)
	{
		 if (this.ajaxRequest.status==200 || window.location.href.indexOf("http")==-1)
	 	{
			var jsondata = eval("("+this.ajaxRequest.responseText+")");

			var textureUrl = this.baseUrl + jsondata.meta.image;

			this.texture = PIXI.Texture.fromImage(textureUrl).baseTexture;

		//	if(!this.texture)this.texture = new PIXI.Texture(textureUrl);

			var frameData = jsondata.frames;
			for (var i in frameData)
			{
				var rect = frameData[i].frame;
				PIXI.TextureCache[i] = new PIXI.Texture(this.texture, {x:rect.x, y:rect.y, width:rect.w, height:rect.h});

				if(frameData[i].trimmed)
				{
					//var realSize = frameData[i].spriteSourceSize;
					PIXI.TextureCache[i].realSize = frameData[i].spriteSourceSize;
					PIXI.TextureCache[i].trim.x = 0// (realSize.x / rect.w)
					// calculate the offset!
				}
//				this.frames[i] = ;
   			}

			if(this.texture.hasLoaded)
			{
				this.dispatchEvent( { type: 'loaded', content: this } );
			}
			else
			{
				var scope = this;
				// wait for the texture to load..
				this.texture.addEventListener('loaded', function(){

					scope.dispatchEvent( { type: 'loaded', content: scope } );

				});
			}
	 	}
	}

}

<commit_msg>Add mime type override to json request<commit_after>/**
 * @author Mat Groves http://matgroves.com/ @Doormat23
 */

/**
 * The sprite sheet loader is used to load in JSON sprite sheet data
 * To generate the data you can use http://www.codeandweb.com/texturepacker and publish the "JSON" format
 * There is a free version so thats nice, although the paid version is great value for money.
 * It is highly recommended to use Sprite sheets (also know as texture atlas') as it means sprite's can be batched and drawn together for highly increased rendering speed.
 * Once the data has been loaded the frames are stored in the PIXI texture cache and can be accessed though PIXI.Texture.fromFrameId() and PIXI.Sprite.fromFromeId()
 * This loader will also load the image file that the Spritesheet points to as well as the data.
 * When loaded this class will dispatch a 'loaded' event
 * @class SpriteSheetLoader
 * @extends EventTarget
 * @constructor
 * @param url {String} the url of the sprite sheet JSON file
 */

PIXI.SpriteSheetLoader = function(url)
{
	/*
	 * i use texture packer to load the assets..
	 * http://www.codeandweb.com/texturepacker
	 * make sure to set the format as "JSON"
	 */
	PIXI.EventTarget.call( this );
	this.url = url;
	this.baseUrl = url.replace(/[^\/]*$/, '');
	this.texture;
	this.frames = {};
}

// constructor
PIXI.SpriteSheetLoader.constructor = PIXI.SpriteSheetLoader;

/**
 * This will begin loading the JSON file
 */
PIXI.SpriteSheetLoader.prototype.load = function()
{
	this.ajaxRequest = new AjaxRequest();
	var scope = this;
	this.ajaxRequest.onreadystatechange=function()
	{
		scope.onLoaded();
	}

	this.ajaxRequest.open("GET", this.url, true)
	this.ajaxRequest.overrideMimeType("application/json");
	this.ajaxRequest.send(null)
}

PIXI.SpriteSheetLoader.prototype.onLoaded = function()
{
	if (this.ajaxRequest.readyState==4)
	{
		 if (this.ajaxRequest.status==200 || window.location.href.indexOf("http")==-1)
	 	{
			var jsondata = eval("("+this.ajaxRequest.responseText+")");

			var textureUrl = this.baseUrl + jsondata.meta.image;

			this.texture = PIXI.Texture.fromImage(textureUrl).baseTexture;

		//	if(!this.texture)this.texture = new PIXI.Texture(textureUrl);

			var frameData = jsondata.frames;
			for (var i in frameData)
			{
				var rect = frameData[i].frame;
				PIXI.TextureCache[i] = new PIXI.Texture(this.texture, {x:rect.x, y:rect.y, width:rect.w, height:rect.h});

				if(frameData[i].trimmed)
				{
					//var realSize = frameData[i].spriteSourceSize;
					PIXI.TextureCache[i].realSize = frameData[i].spriteSourceSize;
					PIXI.TextureCache[i].trim.x = 0// (realSize.x / rect.w)
					// calculate the offset!
				}
//				this.frames[i] = ;
   			}

			if(this.texture.hasLoaded)
			{
				this.dispatchEvent( { type: 'loaded', content: this } );
			}
			else
			{
				var scope = this;
				// wait for the texture to load..
				this.texture.addEventListener('loaded', function(){

					scope.dispatchEvent( { type: 'loaded', content: scope } );

				});
			}
	 	}
	}

}

<|endoftext|>