<commit_before>// Generated on 2014-01-22 using generator-angular 0.7.1
'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    yeoman: {
      // configurable paths
      app: require('./bower.json').appPath || 'app',
      dist: 'dist'
    },

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      js: {
        files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: true
        }
      },
      jsTest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['newer:jshint:test', 'karma']
      },
      styles: {
        files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
        tasks: ['newer:copy:styles', 'autoprefixer']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/{,*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: 'localhost',
        livereload: 35729
      },
      livereload: {
        options: {
          open: true,
          base: [
            '.tmp',
            '<%= yeoman.app %>'
          ]
        }
      },
      test: {
        options: {
          port: 9001,
          base: [
            '.tmp',
            'test',
            '<%= yeoman.app %>'
          ]
        }
      },
      dist: {
        options: {
          base: '<%= yeoman.dist %>'
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: [
        'Gruntfile.js',
        '<%= yeoman.app %>/scripts/{,*/}*.js'
      ],
      test: {
        options: {
          jshintrc: 'test/.jshintrc'
        },
        src: ['test/spec/{,*/}*.js']
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '.tmp',
            '<%= yeoman.dist %>/*',
            '!<%= yeoman.dist %>/.git*'
          ]
        }]
      },
      server: '.tmp'
    },

    // Add vendor prefixed styles
    autoprefixer: {
      options: {
        browsers: ['last 1 version']
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the app
    'bower-install': {
      app: {
        html: '<%= yeoman.app %>/index.html',
        ignorePath: '<%= yeoman.app %>/'
      }
    },

    // Renames files for browser caching purposes
    rev: {
      dist: {
        files: {
          src: [
            '<%= yeoman.dist %>/scripts/{,*/}*.js',
            '<%= yeoman.dist %>/styles/{,*/}*.css',
            '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
            '<%= yeoman.dist %>/styles/fonts/*'
          ]
        }
      }
    },

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      html: '<%= yeoman.app %>/index.html',
      options: {
        dest: '<%= yeoman.dist %>'
      }
    },

    // Performs rewrites based on rev and the useminPrepare configuration
    usemin: {
      html: ['<%= yeoman.dist %>/{,*/}*.html'],
      css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
      options: {
        assetsDirs: ['<%= yeoman.dist %>']
      }
    },

    // The following *-min tasks produce minified files in the dist folder
    imagemin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.{png,jpg,jpeg,gif}',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },
    svgmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.svg',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },
    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: '<%= yeoman.dist %>',
          src: ['*.html', 'views/{,*/}*.html'],
          dest: '<%= yeoman.dist %>'
        }]
      }
    },

    // Allow the use of non-minsafe AngularJS files. Automatically makes it
    // minsafe compatible so Uglify does not destroy the ng references
    ngmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/concat/scripts',
          src: '*.js',
          dest: '.tmp/concat/scripts'
        }]
      }
    },

    // Replace Google CDN references
    cdnify: {
      dist: {
        html: ['<%= yeoman.dist %>/*.html']
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.app %>',
          dest: '<%= yeoman.dist %>',
          src: [
            '*.{ico,png,txt}',
            '.htaccess',
            '*.html',
            'views/{,*/}*.html',
            'bower_components/**/*',
            'images/{,*/}*.{webp}',
            'fonts/*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: '<%= yeoman.dist %>/images',
          src: ['generated/*']
        }]
      },
      styles: {
        expand: true,
        cwd: '<%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    // Run some tasks in parallel to speed up the build process
    concurrent: {
      server: [
        'copy:styles'
      ],
      test: [
        'copy:styles'
      ],
      dist: [
        'copy:styles',
        'imagemin',
        'svgmin'
      ]
    },

    // By default, your `index.html`'s <!-- Usemin block --> will take care of
    // minification. These next options are pre-configured if you do not wish
    // to use the Usemin blocks.
    // cssmin: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/styles/main.css': [
    //         '.tmp/styles/{,*/}*.css',
    //         '<%= yeoman.app %>/styles/{,*/}*.css'
    //       ]
    //     }
    //   }
    // },
    // uglify: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/scripts/scripts.js': [
    //         '<%= yeoman.dist %>/scripts/scripts.js'
    //       ]
    //     }
    //   }
    // },
    // concat: {
    //   dist: {}
    // },

    // Test settings
    karma: {
      unit: {
        configFile: 'karma.conf.js',
        singleRun: true
      }
    }
  });

  grunt.registerTask('serve', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'bower-install',
      'concurrent:server',
      'autoprefixer',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('server', function () {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run(['serve']);
  });

  grunt.registerTask('test', [
    'clean:server',
    'concurrent:test',
    'autoprefixer',
    'connect:test',
    'karma'
  ]);

  grunt.registerTask('build', [
    'clean:dist',
    'bower-install',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngmin',
    'copy:dist',
    'cdnify',
    'cssmin',
    'uglify',
    'rev',
    'usemin',
    'htmlmin'
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);
};
<commit_msg>make test server avialable to all<commit_after>// Generated on 2014-01-22 using generator-angular 0.7.1
'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {

  // Load grunt tasks automatically
  require('load-grunt-tasks')(grunt);

  // Time how long tasks take. Can help when optimizing build times
  require('time-grunt')(grunt);

  // Define the configuration for all the tasks
  grunt.initConfig({

    // Project settings
    yeoman: {
      // configurable paths
      app: require('./bower.json').appPath || 'app',
      dist: 'dist'
    },

    // Watches files for changes and runs tasks based on the changed files
    watch: {
      js: {
        files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
        tasks: ['newer:jshint:all'],
        options: {
          livereload: true
        }
      },
      jsTest: {
        files: ['test/spec/{,*/}*.js'],
        tasks: ['newer:jshint:test', 'karma']
      },
      styles: {
        files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
        tasks: ['newer:copy:styles', 'autoprefixer']
      },
      gruntfile: {
        files: ['Gruntfile.js']
      },
      livereload: {
        options: {
          livereload: '<%= connect.options.livereload %>'
        },
        files: [
          '<%= yeoman.app %>/{,*/}*.html',
          '.tmp/styles/{,*/}*.css',
          '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
        ]
      }
    },

    // The actual grunt server settings
    connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: '0.0.0.0',
        livereload: 35729
      },
      livereload: {
        options: {
          open: false,
          base: [
            '.tmp',
            '<%= yeoman.app %>'
          ]
        }
      },
      test: {
        options: {
          port: 9001,
          base: [
            '.tmp',
            'test',
            '<%= yeoman.app %>'
          ]
        }
      },
      dist: {
        options: {
          base: '<%= yeoman.dist %>'
        }
      }
    },

    // Make sure code styles are up to par and there are no obvious mistakes
    jshint: {
      options: {
        jshintrc: '.jshintrc',
        reporter: require('jshint-stylish')
      },
      all: [
        'Gruntfile.js',
        '<%= yeoman.app %>/scripts/{,*/}*.js'
      ],
      test: {
        options: {
          jshintrc: 'test/.jshintrc'
        },
        src: ['test/spec/{,*/}*.js']
      }
    },

    // Empties folders to start fresh
    clean: {
      dist: {
        files: [{
          dot: true,
          src: [
            '.tmp',
            '<%= yeoman.dist %>/*',
            '!<%= yeoman.dist %>/.git*'
          ]
        }]
      },
      server: '.tmp'
    },

    // Add vendor prefixed styles
    autoprefixer: {
      options: {
        browsers: ['last 1 version']
      },
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/styles/',
          src: '{,*/}*.css',
          dest: '.tmp/styles/'
        }]
      }
    },

    // Automatically inject Bower components into the app
    'bower-install': {
      app: {
        html: '<%= yeoman.app %>/index.html',
        ignorePath: '<%= yeoman.app %>/'
      }
    },

    // Renames files for browser caching purposes
    rev: {
      dist: {
        files: {
          src: [
            '<%= yeoman.dist %>/scripts/{,*/}*.js',
            '<%= yeoman.dist %>/styles/{,*/}*.css',
            '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
            '<%= yeoman.dist %>/styles/fonts/*'
          ]
        }
      }
    },

    // Reads HTML for usemin blocks to enable smart builds that automatically
    // concat, minify and revision files. Creates configurations in memory so
    // additional tasks can operate on them
    useminPrepare: {
      html: '<%= yeoman.app %>/index.html',
      options: {
        dest: '<%= yeoman.dist %>'
      }
    },

    // Performs rewrites based on rev and the useminPrepare configuration
    usemin: {
      html: ['<%= yeoman.dist %>/{,*/}*.html'],
      css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
      options: {
        assetsDirs: ['<%= yeoman.dist %>']
      }
    },

    // The following *-min tasks produce minified files in the dist folder
    imagemin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.{png,jpg,jpeg,gif}',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },
    svgmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/images',
          src: '{,*/}*.svg',
          dest: '<%= yeoman.dist %>/images'
        }]
      }
    },
    htmlmin: {
      dist: {
        options: {
          collapseWhitespace: true,
          collapseBooleanAttributes: true,
          removeCommentsFromCDATA: true,
          removeOptionalTags: true
        },
        files: [{
          expand: true,
          cwd: '<%= yeoman.dist %>',
          src: ['*.html', 'views/{,*/}*.html'],
          dest: '<%= yeoman.dist %>'
        }]
      }
    },

    // Allow the use of non-minsafe AngularJS files. Automatically makes it
    // minsafe compatible so Uglify does not destroy the ng references
    ngmin: {
      dist: {
        files: [{
          expand: true,
          cwd: '.tmp/concat/scripts',
          src: '*.js',
          dest: '.tmp/concat/scripts'
        }]
      }
    },

    // Replace Google CDN references
    cdnify: {
      dist: {
        html: ['<%= yeoman.dist %>/*.html']
      }
    },

    // Copies remaining files to places other tasks can use
    copy: {
      dist: {
        files: [{
          expand: true,
          dot: true,
          cwd: '<%= yeoman.app %>',
          dest: '<%= yeoman.dist %>',
          src: [
            '*.{ico,png,txt}',
            '.htaccess',
            '*.html',
            'views/{,*/}*.html',
            'bower_components/**/*',
            'images/{,*/}*.{webp}',
            'fonts/*'
          ]
        }, {
          expand: true,
          cwd: '.tmp/images',
          dest: '<%= yeoman.dist %>/images',
          src: ['generated/*']
        }]
      },
      styles: {
        expand: true,
        cwd: '<%= yeoman.app %>/styles',
        dest: '.tmp/styles/',
        src: '{,*/}*.css'
      }
    },

    // Run some tasks in parallel to speed up the build process
    concurrent: {
      server: [
        'copy:styles'
      ],
      test: [
        'copy:styles'
      ],
      dist: [
        'copy:styles',
        'imagemin',
        'svgmin'
      ]
    },

    // By default, your `index.html`'s <!-- Usemin block --> will take care of
    // minification. These next options are pre-configured if you do not wish
    // to use the Usemin blocks.
    // cssmin: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/styles/main.css': [
    //         '.tmp/styles/{,*/}*.css',
    //         '<%= yeoman.app %>/styles/{,*/}*.css'
    //       ]
    //     }
    //   }
    // },
    // uglify: {
    //   dist: {
    //     files: {
    //       '<%= yeoman.dist %>/scripts/scripts.js': [
    //         '<%= yeoman.dist %>/scripts/scripts.js'
    //       ]
    //     }
    //   }
    // },
    // concat: {
    //   dist: {}
    // },

    // Test settings
    karma: {
      unit: {
        configFile: 'karma.conf.js',
        singleRun: true
      }
    }
  });

  grunt.registerTask('serve', function (target) {
    if (target === 'dist') {
      return grunt.task.run(['build', 'connect:dist:keepalive']);
    }

    grunt.task.run([
      'clean:server',
      'bower-install',
      'concurrent:server',
      'autoprefixer',
      'connect:livereload',
      'watch'
    ]);
  });

  grunt.registerTask('server', function () {
    grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
    grunt.task.run(['serve']);
  });

  grunt.registerTask('test', [
    'clean:server',
    'concurrent:test',
    'autoprefixer',
    'connect:test',
    'karma'
  ]);

  grunt.registerTask('build', [
    'clean:dist',
    'bower-install',
    'useminPrepare',
    'concurrent:dist',
    'autoprefixer',
    'concat',
    'ngmin',
    'copy:dist',
    'cdnify',
    'cssmin',
    'uglify',
    'rev',
    'usemin',
    'htmlmin'
  ]);

  grunt.registerTask('default', [
    'newer:jshint',
    'test',
    'build'
  ]);
};
<|endoftext|>


<commit_before>module.exports = {
    "com": {
        "ability": {
            "conflict": "Ability not found. Possible conflicts: %s",
            "nothing": "Couldn't find anything."
        },
        "admin": {
            "main": {
                "cspcd": "Channel-specific cooldowns: `%s`",
                "mspcd": "Member-specific cooldowns: `%s`",
                "cuspre": "Custom prefix: `%s`",
                "tricha": "Trivia channel: %s",
                "notricha": "none",
                "disa": "Disabled commands here: %s",
                "nodisa": "No disabled commands in this channel."
            },
            "cooldowns": {
                "confirm": ":ok_hand: Set %1$s limit to %2$s seconds.", // Set [channel] limit to [3] seconds.
                "channel": "channel",
                "member": "member"
            },
            "disable": {
                "confirmsome": ":ok_hand: All disabled commands in this channel: %s",
                "confirmnone": ":ok_hand: No disabled commands in this channel.",
                "error": "Disable some commands first!"
            },
            "locale": {
                "confirm": ":ok_hand: Language set to `%s`.",
                "error": "Can't set to this language! Available langauges: `%s`"
            },
            "prefix": {
                "confirm": ":ok_hand: Prefix set to `%s`."
            },
            "trivia": {
                "enable": ":ok_hand: Trivia enabled in <#%s>.",
                "disable": ":ok_hand: Trivia channel disabled."
            },
            "botspam": {
                "confirm": ":ok_hand: Botspam channel set to %s."
            }
        },
        "help": {
            "notfound": "Help topic not found: `%s`\n",
            "list": "List of commands:"
        },
        "help_topics": { // for help, ignore everything but usage, text, and summary
            "static": [{
                "name": "hero",
                "usage": "hero <hero name>",
                "summary": "returns hero's basic stats and ability names.",
                "aliases": ["abilities", "skills", "spells"],
                "example": "hero sf"
            }, {
                "name": "patch",
                "usage": "patch <hero name>",
                "summary": "returns the latest changes to a hero in dota from the latest balance patch.",
                "aliases": ["patchnotes", "notes"],
                "example": "patch monkey king"
            }, {
                "name": "talents",
                "usage": "talents <hero>",
                "summary": "returns the current talents of a requested hero.",
                "aliases": ["talent"],
                "example": "talents spirit breaker"
            }, {
                "name": "ability",
                "usage": "ability [<hero>] <ability>",
                "summary": "returns info about a hero's ability.",
                "text": [
                    "there are multiple aliases for abilites, you can discover them all",
                    "simply by using commands! supplying a hero is not required."
                ],
                "aliases": ["skill", "spell"],
                "example": "ability earthbind"
            }, {
                "name": "item",
                "usage": "item <item name>",
                "summary": "search for an item name and display it.",
                "example": "item skadi"
            }, {
                "name": "wiki",
                "usage": "wiki <hero name>",
                "summary": "returns the dota 2 wiki link for a specified hero.",
                "example": "wiki techies"
            }, {
                "name": "guide",
                "usage": "guide <hero name>",
                "summary": "returns a torte de lini guide for a hero. still in beta",
                "example": "guide phoenix"
            }],
            "personal": [{
                "name": "register",
                "usage": "register <steam profile, dotabuff link or opendota link>",
                "summary": "register your steam profile with the bot.",
                "text": [
                    "accepts a steam profile link or a dotabuff/opendota link.",
                    "the bot account will send you a friend request on steam",
                    "along with a direct message on discord with instructions.",
                    "**make sure your steam account is public and you have discord set",
                    "to accept direct messages from other users before trying this!**"
                ],
                "example": "register https://www.dotabuff.com/players/103637655"
            }, {
                "name": "unregister",
                "summary": "removes your account with the bot."
            }, {
                "name": "playerinfo",
                "usage": "playerinfo <member>",
                "summary": "returns some basic player info for a specified person.",
                "text": [
                    "accepts a dotabuff or opendota link, as well as a username mention (if that user",
                    "has their profile linked.)"
                ],
                "example": "playerinfo https://www.opendota.com/players/103637655",
                "aliases": ["profile"]
            }, {
                "name": "matchinfo",
                "usage": "matchinfo <link or match id>",
                "summary": "returns information about a specific dota match.",
                "example": "matchinfo https://www.dotabuff.com/matches/2992668596",
                "aliases": ["gameinfo"]
            }, {
                "name": "lastmatch",
                "summary": "looks up the last match someone played.",
                "text": [
                    "\n`with <player>`: adds you to the search of players",
                    "\n`of <player>`: only looks for the last match between these players",
                    "\n`as <hero>`: adds a hero filter to the first player in the search is"
                ],
                "example": "lastmatch of Moo",
                "aliases": ["lastgame"]
            }, {
                "name": "history",
                "usage": "history [with <person>] OR [of <person>] [as <hero>]",
                "summary": "looks up the history someone has on a hero, or history between two players. ",
                "text": [
                    "\n`with <player>`: shows winrate with and against you and someone else",
                    "\n`of <player> as <hero>`: shows someone's winrate on a specific hero",
                    "\n`as <hero>`: shows your winrate on a hero"
                ],
                "example": "history with frosty"
            }, {
                "name": "matches",
                "usage": "matches [as <hero>] and/or with <person>] and/or [of <person>]",
                "summary": "returns the last 12 games of someone.",
                "text": [
                    "filter by hero or with a person."
                ],
                "example": "matches of alexa as ember",
                "aliases": ["matchhistory", "games"]
            }, {
                "name": "mmr",
                "summary": "returns mmr of a member or of the server.",
                "text": [
                    "\n`%smmr`: returns your mmr.",
                    "\n`%smmr of [member]`: returns mmr of a registered member.",
                    "\n`%smmr all`: returns a leaderboard of the whole server."
                ]
            }],
            "esports": [{
                "name": "gosu",
                "summary": "some commands for pro games",
                "text": [
                    "\n`%sgosu list`: lists 8 upcoming pro games"
                ]
            }, {
                "name": "prommr",
                "summary": "lists highest mmr players on valve's leaderboards",
                "example": "prommr europe",
                "usage": "prommr <region>"
            }, {
                "name": "twitch",
                "summary": "commands for twitch.tv",
                "text": [
                    "\n`%stwitch clip`: returns a random twitch clip from trending clips.",
                    "\nlist updated every hour.",
                    "\n`%stwitch streams [<locale>]`: returns top 5 streams. optional: 2 char language code."
                ]
            }, {
                "name": "sub",
                "summary": "subscribes and unsubscribes from match feeds for players/teams/leagues/news feeds",
                "text": [
                    "\n`%ssub player <username mention>`: if a player is registered, their matches will post to this channel",
                    "\n`%ssub team <team id>`: subscribe to a team ID and have all their new games posted to this channel",
                    "\n`%ssub league <league id>`: subscribe to a league ID"
                ]
            }, {
                "name": "unsub",
                "summary": "interactively remove your subscriptions from this channel"
            }],
            "fun": [{
                "name": "courage",
                "summary": "returns an item build challenge a la divine courage.",
            }, {
                "name": "trivia",
                "summary": "set of commands for the trivia game. ",
                "text": [
                    "\n`%strivia start`: starts the game.",
                    "\n`%strivia stop`: stops the game.",
                    "\n`%strivia top`: leaderboard of users in this server.",
                    "\n`%strivia top all`: leaderboard of all users.",
                    "\n`%strivia points`: your scores.",
                    "\n`%strivia points [member]`: scores of someone.",
                    "\n`%strivia stats`: stats about the trivia game."
                ],
                "aliases": ["t", "challenge"]
            }, {
                "name": "mike",
                "summary": "<:ixmikeW:256896118380691466>",
                "aliases": ["ixmike", "mikeism", "mikeisms"]
            }, {
                "name": "arteezy",
                "summary": "<:rtzW:298509964203393024>"
            }],
            "meta": [{
                "name": "info",
                "summary": "returns some info about the bot.",
                "aliases": ["about"]
            }, {
                "name": "invite",
                "summary": "invite link for the bot"
            }, {
                "name": "botstats",
                "summary": "returns some stats about the bot"
            }, {
                "name": "help",
                "usage": "help [<topic>]",
                "summary": "return list of help topics, or specific help about a topic.",
                "example": "help patch"
            }, {
                "name": "suggest",
                "summary": "creates a suggestion in the bot's help server.",
                "usage": "suggest <suggestion>"
            }],
            "admin": [{
                "name": "admin",
                "summary": "displays the current config for the server."
            }, {
                "name": "admin prefix",
                "usage": "admin prefix <new prefix>",
                "summary": "change the server specific prefix (note: will still respond to `--`)",
                "example": "admin prefix &&"
            }, {
                "name": "admin cooldowns",
                "usage": "admin cooldowns <channel or member> <time in seconds>",
                "summary": "sets the cooldowns for the entire guild for members or channels.",
                "text": [
                    "for example: if the channel cooldown is 10, 10 seconds must pass before a new command is processed.",
                    "all commands in between the time of the first command and any new commands are ignored.",
                    "\n\nrecommended cooldowns: 10 seconds for member and 5 seconds for channel. \ndefault cooldowns: 0."
                ],
                "example": "admin cooldowns member 5"
            }, {
                "name": "admin disable",
                "usage": "admin disable [commands]",
                "summary": "disables a list of commands per channel.",
                "text": [
                    "note: you can not disable commands from the meta or admin categories.",
                    "use the admin command by itself to see disabled commands for a channel."
                ],
                "example": "admin disable lastmatch matchinfo"
            }, {
                "name": "admin enable",
                "usage": "admin enable [commands]",
                "summary": "re enables a list of commands for a channel.",
                "text": [
                    "use the admin command by itself to see a list of disabled commands."
                ],
                "example": "admin enable patch list wiki"
            }, {
                "name": "admin trivia",
                "summary": "configures the channel in which trivia takes place.",
                "text": [
                    "\n`%sadmin trivia channel [#channel]`: sets the trivia channel to #channel.",
                    "\n`%sadmin trivia channel here`: sets the trivia channel to the current channel.",
                    "\n`%sadmin trivia channel none`: disables trivia entirely."
                ]
            }, {
                "name": "admin locale",
                "summary": "changes language of bot on this server"
            }, {
                "name": "admin botspam",
                "usage": "admin prefix <botspam channel>",
                "summary": "change the designated botspam channel (this will be immune to cooldowns)",
                "example": "admin botspam #use-the-bots"
            }]
        },
        "history": {
            "main": {
                "error": "Invalid syntax."
            },
            "as": {
                "embed": {
                    "title": "%s as %s",
                    "wins": "**Wins:** %s",
                    "games": "**Games:** %s",
                    "wr": "**Winrate:** %s%%"
                }
            },
            "with": {
                "spelling": "Something went wrong. Check the spelling of the players and try again.",
                "nodata": "I don't have enough data for this command!",
                "embed": {
                    "desc": "History between <@%s> and <@%s>",
                    "same": "Same Team",
                    "wl": "**Win/Loss:** %s/%s (%s games)",
                    "wr": "**Winrate:** %s%%",
                    "diff": "Different Teams",
                    "wins": "**<@%s>'s wins:** %s"
                }
            }
        },
        "info": {
            "me": "A Dota 2 related bot. Contact %s for support and questions!",
            "onlinehelp": "Online Help",
            "invitelink": "Invite Link",
            "helpserver": "Help Server", // say like (EN) here or something
            "specialthanks": "Special Thanks",
            "alphatest": "Alpha Testers",
            "translators": "Translators",
            "githublog": "Github Log",
            "links": "Links"
        },
        "item": {
            "noitem": "Couldn't find that item.",
            "conflicts": "Possible conflicts: %s"
        },
        "matches": {
            "header": "Use `%smatchinfo` to get more about a specific match."
        },
        "matchinfo": {
            "error": "I couldn't find a match ID in your message!"
        },
        "mmr": {
            "loading": "Hold tight while I grab the latest data.",
            "nomember": "Please specify a member!",
            "cantmember": "Couldn't find a member.",
            "notregistered": "%s has not registered with me yet! Try `%shelp register`."
        },
        "patch": {
            "noversion": "Cant find that version! Here's the latest:"
        },
        "playerinfo": {
            "nouser": "That user has not registered with me yet! Try `%shelp register`.",
            "noid": "I couldn't find an account ID in your message!"
        },
        "restart": {
            "message": "The bot is going down for an update. Wait a few seconds and type `--trivia start` again. Sorry for the inconvenience!"
        }
    },
    "generic": {
        "noheroerror": "Couldn't find that hero.",
        "privateaccount": "This user's account is private.",
        "generic": "Something went wrong."
    },
    "resolve_dota_id": {
        "notregistered": "%s has not registered with me yet! Try `%shelp register`.",
        "badselect": "Something went wrong selecting this user from the database.",
        "noid": "I couldn't find an account ID in your message!"
    }
};<commit_msg>document newsfeed<commit_after>module.exports = {
    "com": {
        "ability": {
            "conflict": "Ability not found. Possible conflicts: %s",
            "nothing": "Couldn't find anything."
        },
        "admin": {
            "main": {
                "cspcd": "Channel-specific cooldowns: `%s`",
                "mspcd": "Member-specific cooldowns: `%s`",
                "cuspre": "Custom prefix: `%s`",
                "tricha": "Trivia channel: %s",
                "notricha": "none",
                "disa": "Disabled commands here: %s",
                "nodisa": "No disabled commands in this channel."
            },
            "cooldowns": {
                "confirm": ":ok_hand: Set %1$s limit to %2$s seconds.", // Set [channel] limit to [3] seconds.
                "channel": "channel",
                "member": "member"
            },
            "disable": {
                "confirmsome": ":ok_hand: All disabled commands in this channel: %s",
                "confirmnone": ":ok_hand: No disabled commands in this channel.",
                "error": "Disable some commands first!"
            },
            "locale": {
                "confirm": ":ok_hand: Language set to `%s`.",
                "error": "Can't set to this language! Available langauges: `%s`"
            },
            "prefix": {
                "confirm": ":ok_hand: Prefix set to `%s`."
            },
            "trivia": {
                "enable": ":ok_hand: Trivia enabled in <#%s>.",
                "disable": ":ok_hand: Trivia channel disabled."
            },
            "botspam": {
                "confirm": ":ok_hand: Botspam channel set to %s."
            }
        },
        "help": {
            "notfound": "Help topic not found: `%s`\n",
            "list": "List of commands:"
        },
        "help_topics": { // for help, ignore everything but usage, text, and summary
            "static": [{
                "name": "hero",
                "usage": "hero <hero name>",
                "summary": "returns hero's basic stats and ability names.",
                "aliases": ["abilities", "skills", "spells"],
                "example": "hero sf"
            }, {
                "name": "patch",
                "usage": "patch <hero name>",
                "summary": "returns the latest changes to a hero in dota from the latest balance patch.",
                "aliases": ["patchnotes", "notes"],
                "example": "patch monkey king"
            }, {
                "name": "talents",
                "usage": "talents <hero>",
                "summary": "returns the current talents of a requested hero.",
                "aliases": ["talent"],
                "example": "talents spirit breaker"
            }, {
                "name": "ability",
                "usage": "ability [<hero>] <ability>",
                "summary": "returns info about a hero's ability.",
                "text": [
                    "there are multiple aliases for abilites, you can discover them all",
                    "simply by using commands! supplying a hero is not required."
                ],
                "aliases": ["skill", "spell"],
                "example": "ability earthbind"
            }, {
                "name": "item",
                "usage": "item <item name>",
                "summary": "search for an item name and display it.",
                "example": "item skadi"
            }, {
                "name": "wiki",
                "usage": "wiki <hero name>",
                "summary": "returns the dota 2 wiki link for a specified hero.",
                "example": "wiki techies"
            }, {
                "name": "guide",
                "usage": "guide <hero name>",
                "summary": "returns a torte de lini guide for a hero. still in beta",
                "example": "guide phoenix"
            }],
            "personal": [{
                "name": "register",
                "usage": "register <steam profile, dotabuff link or opendota link>",
                "summary": "register your steam profile with the bot.",
                "text": [
                    "accepts a steam profile link or a dotabuff/opendota link.",
                    "the bot account will send you a friend request on steam",
                    "along with a direct message on discord with instructions.",
                    "**make sure your steam account is public and you have discord set",
                    "to accept direct messages from other users before trying this!**"
                ],
                "example": "register https://www.dotabuff.com/players/103637655"
            }, {
                "name": "unregister",
                "summary": "removes your account with the bot."
            }, {
                "name": "playerinfo",
                "usage": "playerinfo <member>",
                "summary": "returns some basic player info for a specified person.",
                "text": [
                    "accepts a dotabuff or opendota link, as well as a username mention (if that user",
                    "has their profile linked.)"
                ],
                "example": "playerinfo https://www.opendota.com/players/103637655",
                "aliases": ["profile"]
            }, {
                "name": "matchinfo",
                "usage": "matchinfo <link or match id>",
                "summary": "returns information about a specific dota match.",
                "example": "matchinfo https://www.dotabuff.com/matches/2992668596",
                "aliases": ["gameinfo"]
            }, {
                "name": "lastmatch",
                "summary": "looks up the last match someone played.",
                "text": [
                    "\n`with <player>`: adds you to the search of players",
                    "\n`of <player>`: only looks for the last match between these players",
                    "\n`as <hero>`: adds a hero filter to the first player in the search is"
                ],
                "example": "lastmatch of Moo",
                "aliases": ["lastgame"]
            }, {
                "name": "history",
                "usage": "history [with <person>] OR [of <person>] [as <hero>]",
                "summary": "looks up the history someone has on a hero, or history between two players. ",
                "text": [
                    "\n`with <player>`: shows winrate with and against you and someone else",
                    "\n`of <player> as <hero>`: shows someone's winrate on a specific hero",
                    "\n`as <hero>`: shows your winrate on a hero"
                ],
                "example": "history with frosty"
            }, {
                "name": "matches",
                "usage": "matches [as <hero>] and/or with <person>] and/or [of <person>]",
                "summary": "returns the last 12 games of someone.",
                "text": [
                    "filter by hero or with a person."
                ],
                "example": "matches of alexa as ember",
                "aliases": ["matchhistory", "games"]
            }, {
                "name": "mmr",
                "summary": "returns mmr of a member or of the server.",
                "text": [
                    "\n`%smmr`: returns your mmr.",
                    "\n`%smmr of [member]`: returns mmr of a registered member.",
                    "\n`%smmr all`: returns a leaderboard of the whole server."
                ]
            }],
            "esports": [{
                "name": "gosu",
                "summary": "some commands for pro games",
                "text": [
                    "\n`%sgosu list`: lists 8 upcoming pro games"
                ]
            }, {
                "name": "prommr",
                "summary": "lists highest mmr players on valve's leaderboards",
                "example": "prommr europe",
                "usage": "prommr <region>"
            }, {
                "name": "twitch",
                "summary": "commands for twitch.tv",
                "text": [
                    "\n`%stwitch clip`: returns a random twitch clip from trending clips.",
                    "\nlist updated every hour.",
                    "\n`%stwitch streams [<locale>]`: returns top 5 streams. optional: 2 char language code."
                ]
            }, {
                "name": "sub",
                "summary": "subscribes and unsubscribes from match feeds for players/teams/leagues/news feeds",
                "text": [
                    "\n`%ssub player <username mention>`: if a player is registered, their matches will post to this channel",
                    "\n`%ssub team <team id>`: subscribe to a team ID and have all their new games posted to this channel",
                    "\n`%ssub league <league id>`: subscribe to a league ID",
                    "\n`%ssub newsfeed <feed name>`: subscribe to news feeds from various sources. use `%ssub newsfeed` to see what feeds are available."
                ]
            }, {
                "name": "unsub",
                "summary": "interactively remove your subscriptions from this channel"
            }],
            "fun": [{
                "name": "courage",
                "summary": "returns an item build challenge a la divine courage.",
            }, {
                "name": "trivia",
                "summary": "set of commands for the trivia game. ",
                "text": [
                    "\n`%strivia start`: starts the game.",
                    "\n`%strivia stop`: stops the game.",
                    "\n`%strivia top`: leaderboard of users in this server.",
                    "\n`%strivia top all`: leaderboard of all users.",
                    "\n`%strivia points`: your scores.",
                    "\n`%strivia points [member]`: scores of someone.",
                    "\n`%strivia stats`: stats about the trivia game."
                ],
                "aliases": ["t", "challenge"]
            }, {
                "name": "mike",
                "summary": "<:ixmikeW:256896118380691466>",
                "aliases": ["ixmike", "mikeism", "mikeisms"]
            }, {
                "name": "arteezy",
                "summary": "<:rtzW:298509964203393024>"
            }],
            "meta": [{
                "name": "info",
                "summary": "returns some info about the bot.",
                "aliases": ["about"]
            }, {
                "name": "invite",
                "summary": "invite link for the bot"
            }, {
                "name": "botstats",
                "summary": "returns some stats about the bot"
            }, {
                "name": "help",
                "usage": "help [<topic>]",
                "summary": "return list of help topics, or specific help about a topic.",
                "example": "help patch"
            }, {
                "name": "suggest",
                "summary": "creates a suggestion in the bot's help server.",
                "usage": "suggest <suggestion>"
            }],
            "admin": [{
                "name": "admin",
                "summary": "displays the current config for the server."
            }, {
                "name": "admin prefix",
                "usage": "admin prefix <new prefix>",
                "summary": "change the server specific prefix (note: will still respond to `--`)",
                "example": "admin prefix &&"
            }, {
                "name": "admin cooldowns",
                "usage": "admin cooldowns <channel or member> <time in seconds>",
                "summary": "sets the cooldowns for the entire guild for members or channels.",
                "text": [
                    "for example: if the channel cooldown is 10, 10 seconds must pass before a new command is processed.",
                    "all commands in between the time of the first command and any new commands are ignored.",
                    "\n\nrecommended cooldowns: 10 seconds for member and 5 seconds for channel. \ndefault cooldowns: 0."
                ],
                "example": "admin cooldowns member 5"
            }, {
                "name": "admin disable",
                "usage": "admin disable [commands]",
                "summary": "disables a list of commands per channel.",
                "text": [
                    "note: you can not disable commands from the meta or admin categories.",
                    "use the admin command by itself to see disabled commands for a channel."
                ],
                "example": "admin disable lastmatch matchinfo"
            }, {
                "name": "admin enable",
                "usage": "admin enable [commands]",
                "summary": "re enables a list of commands for a channel.",
                "text": [
                    "use the admin command by itself to see a list of disabled commands."
                ],
                "example": "admin enable patch list wiki"
            }, {
                "name": "admin trivia",
                "summary": "configures the channel in which trivia takes place.",
                "text": [
                    "\n`%sadmin trivia channel [#channel]`: sets the trivia channel to #channel.",
                    "\n`%sadmin trivia channel here`: sets the trivia channel to the current channel.",
                    "\n`%sadmin trivia channel none`: disables trivia entirely."
                ]
            }, {
                "name": "admin locale",
                "summary": "changes language of bot on this server"
            }, {
                "name": "admin botspam",
                "usage": "admin prefix <botspam channel>",
                "summary": "change the designated botspam channel (this will be immune to cooldowns)",
                "example": "admin botspam #use-the-bots"
            }]
        },
        "history": {
            "main": {
                "error": "Invalid syntax."
            },
            "as": {
                "embed": {
                    "title": "%s as %s",
                    "wins": "**Wins:** %s",
                    "games": "**Games:** %s",
                    "wr": "**Winrate:** %s%%"
                }
            },
            "with": {
                "spelling": "Something went wrong. Check the spelling of the players and try again.",
                "nodata": "I don't have enough data for this command!",
                "embed": {
                    "desc": "History between <@%s> and <@%s>",
                    "same": "Same Team",
                    "wl": "**Win/Loss:** %s/%s (%s games)",
                    "wr": "**Winrate:** %s%%",
                    "diff": "Different Teams",
                    "wins": "**<@%s>'s wins:** %s"
                }
            }
        },
        "info": {
            "me": "A Dota 2 related bot. Contact %s for support and questions!",
            "onlinehelp": "Online Help",
            "invitelink": "Invite Link",
            "helpserver": "Help Server", // say like (EN) here or something
            "specialthanks": "Special Thanks",
            "alphatest": "Alpha Testers",
            "translators": "Translators",
            "githublog": "Github Log",
            "links": "Links"
        },
        "item": {
            "noitem": "Couldn't find that item.",
            "conflicts": "Possible conflicts: %s"
        },
        "matches": {
            "header": "Use `%smatchinfo` to get more about a specific match."
        },
        "matchinfo": {
            "error": "I couldn't find a match ID in your message!"
        },
        "mmr": {
            "loading": "Hold tight while I grab the latest data.",
            "nomember": "Please specify a member!",
            "cantmember": "Couldn't find a member.",
            "notregistered": "%s has not registered with me yet! Try `%shelp register`."
        },
        "patch": {
            "noversion": "Cant find that version! Here's the latest:"
        },
        "playerinfo": {
            "nouser": "That user has not registered with me yet! Try `%shelp register`.",
            "noid": "I couldn't find an account ID in your message!"
        },
        "restart": {
            "message": "The bot is going down for an update. Wait a few seconds and type `--trivia start` again. Sorry for the inconvenience!"
        }
    },
    "generic": {
        "noheroerror": "Couldn't find that hero.",
        "privateaccount": "This user's account is private.",
        "generic": "Something went wrong."
    },
    "resolve_dota_id": {
        "notregistered": "%s has not registered with me yet! Try `%shelp register`.",
        "badselect": "Something went wrong selecting this user from the database.",
        "noid": "I couldn't find an account ID in your message!"
    }
};<|endoftext|>


<commit_before>app.controller('contactsCtrl', function ($scope, $http) {
  $scope.message = "hello world";
  $scope.bottom = 0;
  $scope.top = 100;
  $scope.contacts = [];
  $http.get('/api/contacts').success(function (data) {
    $scope.contacts = data;
  });

});
<commit_msg>added age filter<commit_after>app.controller('contactsCtrl', function ($scope, $http) {
  $scope.message = "hello world";
  $scope.bottom = 0;
  $scope.top = 100;
  $scope.contacts = [];
  $http.get('/api/contacts').success(function (data) {
    $scope.contacts = data;
  });
});
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { StyleSheet, Text, View, Animated, Easing, TouchableOpacity, PixelRatio } from 'react-native';
import ActionButtonItem from './ActionButtonItem';

const alignItemsMap = {
  "center" : "center",
  "left"  : "flex-start",
  "right" : "flex-end"
}

export default class ActionButton extends Component {

  constructor(props) {
    super(props);

    this.state = {
      active: props.active,
      btnOutRange: props.btnOutRange || props.buttonColor || 'rgba(0,0,0,1)',
      btnOutRangeTxt: props.btnOutRangeTxt || props.buttonTextColor || 'rgba(255,255,255,1)',
      anim: new Animated.Value(props.active ? 1 : 0),
    }

    this.timeout = null;
    this.setPositionAndSizeByType();
  }

  componentWillUnmount() {
    clearTimeout(this.timeout);
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      ...nextProps
    });
  }

  setPositionAndSizeByType() {
    let position, offsetX, offsetY, size;

    if (this.props.type == 'tab') {
      position = 'center',
      offsetX  = 10,
      offsetY  = 4,
      size     = 42;
    } else {
      position = 'right',
      offsetX  = 30,
      offsetY  = 30,
      size     = 56;
    }

    this.state.position = this.props.position || position;
    this.state.offsetX  = this.props.offsetX  || offsetX ;
    this.state.offsetY  = this.props.offsetY  || offsetY;
    this.state.size     = this.props.size     || size;
  }

  //////////////////////
  // STYLESHEET GETTERS
  //////////////////////

  getContainerStyles() {
    return [styles.overlay, this.getOrientation(), this.getOffsetXY()];
  }

  getActionButtonStyles() {
    return [styles.actionBarItem, this.getButtonSize()];
  }

  getOrientation() {
    return { alignItems: alignItemsMap[this.state.position] };
  }

  getButtonSize() {
    return {
      width: this.state.size,
      height: this.state.size,
    }
  }

  getOffsetXY() {
    return {
      paddingHorizontal: this.state.offsetX,
      paddingBottom: this.state.offsetY
    };
  }

  getActionsStyle() {
    return [ styles.actionsVertical, this.getOrientation() ];
  }

  //////////////////////
  // RENDER METHODS
  //////////////////////

  render() {
    return (
      <View pointerEvents="box-none" style={styles.overlay}>
        <Animated.View style={[styles.overlay, {
          backgroundColor: this.props.bgColor,
          opacity: this.state.anim
        }]}>
          {this.props.backdrop}
        </Animated.View>
        <View pointerEvents="box-none" style={this.getContainerStyles()}>
          {this.props.children && this._renderActions()}
          {this._renderButton()}
        </View>
      </View>
    );
  }

  _renderButton() {
    return (
      <View style={this.getActionButtonStyles()}>
        <TouchableOpacity
          activeOpacity={0.85}
          onLongPress={this.props.onLongPress}
          onPress={() => {
            this.props.onPress()
            if (this.props.children) this.animateButton()
          }}>
          <Animated.View
            style={[styles.btn, {
              width: this.state.size,
              height: this.state.size,
              borderRadius: this.state.size / 2,
              backgroundColor: this.state.anim.interpolate({
                inputRange: [0, 1],
                outputRange: [this.props.buttonColor, this.state.btnOutRange]
              }),
              transform: [{
                  scale: this.state.anim.interpolate({
                    inputRange: [0, 1],
                    outputRange: [1, this.props.outRangeScale]
                  }),
                }, {
                  rotate: this.state.anim.interpolate({
                    inputRange: [0, 1],
                    outputRange: ['0deg', this.props.degrees + 'deg']
                  })
                }],
            }]}>
            {this._renderButtonIcon()}
          </Animated.View>
        </TouchableOpacity>
      </View>
    );
  }

  _renderButtonIcon() {
    if (this.props.icon) return this.props.icon

    return (
      <Animated.Text style={[styles.btnText, {
        color: this.state.anim.interpolate({
          inputRange: [0, 1],
          outputRange: [this.props.buttonTextColor, this.state.btnOutRangeTxt]
        })
      }]}>
        +
      </Animated.Text>
    )
  }

  _renderActions() {
    if (!this.state.active) return null;

    let actionButtons = this.props.children

    if (!Array.isArray(this.props.children)) {
      actionButtons = [this.props.children]
    }

    return (
        <TouchableOpacity
          style={this.getActionsStyle()}
          activeOpacity={1}
          onPress={() => { this.reset() }}>
          {actionButtons.map((ActionButton, index) => {
            return (
              <ActionButtonItem
                key={index}
                position={this.state.position}
                spacing={this.props.spacing}
                anim={this.state.anim}
                size={this.state.size}
                btnColor={this.state.btnOutRange}
                {...ActionButton.props}
                onPress={() => {
                  if (this.props.autoInactive){
                    this.timeout = setTimeout(() => {
                      this.reset();
                    }, 200);
                  }
                  ActionButton.props.onPress();
                }}
              />
            )
          })}
        </TouchableOpacity>
    );
  }

  //////////////////////
  // Animation Methods
  //////////////////////

  animateButton() {
    if (this.state.active) return this.reset();

    Animated.spring(this.state.anim, {
       toValue: 1,
       duration: 250,
    }).start();

    this.setState({ active: true });
  }

  reset() {
    Animated.spring(this.state.anim, {
      toValue: 0,
      duration: 250,
    }).start();

    setTimeout(() => {
      this.setState({ active: false });
    }, 250);
  }
}

ActionButton.Item = ActionButtonItem;

ActionButton.propTypes = {
  active: PropTypes.bool,

  type: PropTypes.oneOf(['float', 'tab']),
  position: PropTypes.string,

  bgColor: PropTypes.string,
  buttonColor: PropTypes.string,
  buttonTextColor: PropTypes.string,

  offsetX : PropTypes.number,
  offsetY: PropTypes.number,
  spacing: PropTypes.number,
  size: PropTypes.number,
  autoInactive: PropTypes.bool,
  onPress: PropTypes.func,
  backdrop: PropTypes.oneOfType([
    PropTypes.bool,
    PropTypes.object
]),
  degrees: PropTypes.number
};

ActionButton.defaultProps = {
  active: false,
  type: 'float',
  bgColor: 'transparent',
  buttonColor: 'rgba(0,0,0,1)',
  buttonTextColor: 'rgba(255,255,255,1)',
  spacing: 20,
  outRangeScale: 1,
  autoInactive: true,
  onPress: () => {},
  backdrop: false,
  degrees: 135
};

const styles = StyleSheet.create({
  overlay: {
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
    top: 0,
    backgroundColor: 'transparent',
    justifyContent: 'flex-end',
  },
  actionBarItem: {
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'transparent',
  },
  btn: {
    justifyContent: 'center',
    alignItems: 'center',
    shadowOpacity: 0.3,
    shadowOffset: {
      width: 0, height: 1,
    },
    shadowColor: '#444',
    shadowRadius: 1,
  },
  btnText: {
    marginTop: -4,
    fontSize: 24,
    backgroundColor: 'transparent',
    position: 'relative',
  },
  actionsVertical: {
    flex: 1,
    justifyContent: 'flex-end',
    flexDirection: 'column',
  },
});
<commit_msg>Updace ActionButton.js<commit_after>import React, { Component, PropTypes } from 'react';
import { StyleSheet, Text, View, Animated, Easing, TouchableOpacity, PixelRatio } from 'react-native';
import ActionButtonItem from './ActionButtonItem';

const alignItemsMap = {
  "center" : "center",
  "left"  : "flex-start",
  "right" : "flex-end"
}

export default class ActionButton extends Component {

  constructor(props) {
    super(props);

    this.state = {
      active: props.active,
      btnOutRange: props.btnOutRange || props.buttonColor || 'rgba(0,0,0,1)',
      btnOutRangeTxt: props.btnOutRangeTxt || props.buttonTextColor || 'rgba(255,255,255,1)',
      anim: new Animated.Value(props.active ? 1 : 0),
    }

    this.timeout = null;
    this.setPositionAndSizeByType();
  }

  componentWillUnmount() {
    clearTimeout(this.timeout);
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      ...nextProps
    });
  }

  setPositionAndSizeByType() {
    let position, offsetX, offsetY, size;

    if (this.props.type == 'tab') {
      position = 'center',
      offsetX  = 10,
      offsetY  = 4,
      size     = 42;
    } else {
      position = 'right',
      offsetX  = 30,
      offsetY  = 30,
      size     = 56;
    }

    this.state.position = this.props.position || position;
    this.state.offsetX  = this.props.offsetX  || offsetX ;
    this.state.offsetY  = this.props.offsetY  || offsetY;
    this.state.size     = this.props.size     || size;
  }

  //////////////////////
  // STYLESHEET GETTERS
  //////////////////////

  getContainerStyles() {
    return [styles.overlay, this.getOrientation(), this.getOffsetXY()];
  }

  getActionButtonStyles() {
    return [styles.actionBarItem, this.getButtonSize()];
  }

  getOrientation() {
    return { alignItems: alignItemsMap[this.state.position] };
  }

  getButtonSize() {
    return {
      width: this.state.size,
      height: this.state.size,
    }
  }

  getOffsetXY() {
    return {
      paddingHorizontal: this.state.offsetX,
      paddingBottom: this.state.offsetY
    };
  }

  getActionsStyle() {
    return [ styles.actionsVertical, this.getOrientation() ];
  }

  //////////////////////
  // RENDER METHODS
  //////////////////////

  render() {
    return (
      <View pointerEvents="box-none" style={styles.overlay}>
        <Animated.View style={[styles.overlay, {
          backgroundColor: this.props.bgColor,
          opacity: this.state.anim
        }]}>
          {this.props.backdrop}
        </Animated.View>
        <View pointerEvents="box-none" style={this.getContainerStyles()}>
          {this.props.children && this._renderActions()}
          {this._renderButton()}
        </View>
      </View>
    );
  }

  _renderButton() {
    return (
      <View style={this.getActionButtonStyles()}>
        <TouchableOpacity
          activeOpacity={0.85}
          onLongPress={this.props.onLongPress}
          onPress={() => {
            this.props.onPress()
            if (this.props.children) this.animateButton()
          }}>
          <Animated.View
            style={[styles.btn, {
              width: this.state.size,
              height: this.state.size,
              borderRadius: this.state.size / 2,
              backgroundColor: this.state.anim.interpolate({
                inputRange: [0, 1],
                outputRange: [this.props.buttonColor, this.state.btnOutRange]
              }),
              transform: [{
                  scale: this.state.anim.interpolate({
                    inputRange: [0, 1],
                    outputRange: [1, this.props.outRangeScale]
                  }),
                }, {
                  rotate: this.state.anim.interpolate({
                    inputRange: [0, 1],
                    outputRange: ['0deg', this.props.degrees + 'deg']
                  })
                }],
            }]}>
            {this._renderButtonIcon()}
          </Animated.View>
        </TouchableOpacity>
      </View>
    );
  }

  _renderButtonIcon() {
    if (this.props.icon) return this.props.icon

    return (
      <Animated.Text style={[styles.btnText, {
        color: this.state.anim.interpolate({
          inputRange: [0, 1],
          outputRange: [this.props.buttonTextColor, this.state.btnOutRangeTxt]
        })
      }]}>
        +
      </Animated.Text>
    )
  }

  _renderActions() {
    if (!this.state.active) return null;

    let actionButtons = this.props.children

    if (!Array.isArray(this.props.children)) {
      actionButtons = [this.props.children]
    }

    return (
        <TouchableOpacity
          style={this.getActionsStyle()}
          activeOpacity={1}
          onPress={() => { this.reset() }}>
          {actionButtons.map((ActionButton, index) => {
            return (
              <ActionButtonItem
                key={index}
                position={this.state.position}
                spacing={this.props.spacing}
                anim={this.state.anim}
                size={this.state.size}
                btnColor={this.state.btnOutRange}
                {...ActionButton.props}
                onPress={() => {
                  if (this.props.autoInactive){
                    this.timeout = setTimeout(() => {
                      this.reset();
                    }, 200);
                  }
                  ActionButton.props.onPress();
                }}
              />
            )
          })}
        </TouchableOpacity>
    );
  }

  //////////////////////
  // Animation Methods
  //////////////////////

  animateButton() {
    if (this.state.active) return this.reset();

    Animated.spring(this.state.anim, {
       toValue: 1,
       duration: 250,
    }).start();

    this.setState({ active: true });
  }

  reset() {
    if(this.props.onReset){
      this.props.onReset();
    }

    Animated.spring(this.state.anim, {
      toValue: 0,
      duration: 250,
    }).start();

    setTimeout(() => {
      this.setState({ active: false });
    }, 250);
  }
}

ActionButton.Item = ActionButtonItem;

ActionButton.propTypes = {
  active: PropTypes.bool,

  type: PropTypes.oneOf(['float', 'tab']),
  position: PropTypes.string,

  bgColor: PropTypes.string,
  buttonColor: PropTypes.string,
  buttonTextColor: PropTypes.string,

  offsetX : PropTypes.number,
  offsetY: PropTypes.number,
  spacing: PropTypes.number,
  size: PropTypes.number,
  autoInactive: PropTypes.bool,
  onPress: PropTypes.func,
  backdrop: PropTypes.oneOfType([
    PropTypes.bool,
    PropTypes.object
]),
  degrees: PropTypes.number
};

ActionButton.defaultProps = {
  active: false,
  type: 'float',
  bgColor: 'transparent',
  buttonColor: 'rgba(0,0,0,1)',
  buttonTextColor: 'rgba(255,255,255,1)',
  spacing: 20,
  outRangeScale: 1,
  autoInactive: true,
  onPress: () => {},
  backdrop: false,
  degrees: 135
};

const styles = StyleSheet.create({
  overlay: {
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
    top: 0,
    backgroundColor: 'transparent',
    justifyContent: 'flex-end',
  },
  actionBarItem: {
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: 'transparent',
  },
  btn: {
    justifyContent: 'center',
    alignItems: 'center',
    shadowOpacity: 0.3,
    shadowOffset: {
      width: 0, height: 1,
    },
    shadowColor: '#444',
    shadowRadius: 1,
  },
  btnText: {
    marginTop: -4,
    fontSize: 24,
    backgroundColor: 'transparent',
    position: 'relative',
  },
  actionsVertical: {
    flex: 1,
    justifyContent: 'flex-end',
    flexDirection: 'column',
  },
});
<|endoftext|>


<commit_before>const { connectionOpts, sslConnectionOpts } = require('../../testHelpers')
const sleep = require('../utils/sleep')
const { requests } = require('../protocol/requests')
const Decoder = require('../protocol/decoder')
const { KafkaJSRequestTimeoutError } = require('../errors')
const Connection = require('./connection')

describe('Network > Connection', () => {
  // According to RFC 5737:
  // The blocks 192.0.2.0/24 (TEST-NET-1), 198.51.100.0/24 (TEST-NET-2),
  // and 203.0.113.0/24 (TEST-NET-3) are provided for use in documentation.
  const invalidIP = '203.0.113.1'
  const invalidHost = 'kafkajs.test'
  let connection

  afterEach(async () => {
    connection && (await connection.disconnect())
  })

  describe('#connect', () => {
    describe('PLAINTEXT', () => {
      beforeEach(() => {
        connection = new Connection(connectionOpts())
      })

      test('resolves the Promise when connected', async () => {
        await expect(connection.connect()).resolves.toEqual(true)
        expect(connection.connected).toEqual(true)
      })

      test('rejects the Promise in case of errors', async () => {
        connection.host = invalidHost
        const messagePattern = /Connection error: getaddrinfo ENOTFOUND kafkajs.test/
        await expect(connection.connect()).rejects.toThrow(messagePattern)
        expect(connection.connected).toEqual(false)
      })
    })

    describe('SSL', () => {
      beforeEach(() => {
        connection = new Connection(sslConnectionOpts())
      })

      test('resolves the Promise when connected', async () => {
        await expect(connection.connect()).resolves.toEqual(true)
        expect(connection.connected).toEqual(true)
      })

      test('rejects the Promise in case of timeouts', async () => {
        connection = new Connection(sslConnectionOpts({ connectionTimeout: 1 }))
        connection.host = invalidIP

        await expect(connection.connect()).rejects.toHaveProperty('message', 'Connection timeout')
        expect(connection.connected).toEqual(false)
      })

      test('rejects the Promise in case of errors', async () => {
        connection.ssl.cert = 'invalid'
        const messagePattern = /Failed to connect/
        await expect(connection.connect()).rejects.toThrow(messagePattern)
        expect(connection.connected).toEqual(false)
      })
    })
  })

  describe('#disconnect', () => {
    beforeEach(() => {
      connection = new Connection(connectionOpts())
    })

    test('disconnects an active connection', async () => {
      await connection.connect()
      expect(connection.connected).toEqual(true)
      await expect(connection.disconnect()).resolves.toEqual(true)
      expect(connection.connected).toEqual(false)
    })
  })

  describe('#send', () => {
    let apiVersions

    beforeEach(() => {
      connection = new Connection(connectionOpts())
      apiVersions = requests.ApiVersions.protocol({ version: 0 })
    })

    test('resolves the Promise with the response', async () => {
      await connection.connect()
      await expect(connection.send(apiVersions())).resolves.toBeTruthy()
    })

    test('rejects the Promise if it is not connected', async () => {
      expect(connection.connected).toEqual(false)
      await expect(connection.send(apiVersions())).rejects.toEqual(new Error('Not connected'))
    })

    test('rejects the Promise in case of a non-retriable error', async () => {
      const protocol = {
        ...apiVersions(),
        response: {
          ...apiVersions().response,
          parse: () => {
            throw new Error('non-retriable')
          },
        },
      }

      await connection.connect()
      await expect(connection.send(protocol)).rejects.toEqual(new Error('non-retriable'))
    })

    test('respect the maxInFlightRequests', async () => {
      const protocol = apiVersions()
      connection = new Connection(connectionOpts({ maxInFlightRequests: 2 }))
      const originalProcessData = connection.processData

      connection.processData = async data => {
        await sleep(100)
        originalProcessData.apply(connection, [data])
      }

      await connection.connect()

      const requests = [
        connection.send(protocol),
        connection.send(protocol),
        connection.send(protocol),
      ]

      await sleep(50)

      const inFlightRequestsSize = connection.requestQueue.inflight.size
      const pendingRequestsSize = connection.requestQueue.pending.length

      await Promise.all(requests)

      expect(inFlightRequestsSize).toEqual(2)
      expect(pendingRequestsSize).toEqual(1)
    })

    test('respect the requestTimeout', async () => {
      const protocol = apiVersions()
      connection = new Connection(
        connectionOpts({
          requestTimeout: 50,
          enforceRequestTimeout: true,
          requestTimeoutCheckInterval: 1,
        })
      )
      const originalProcessData = connection.processData

      connection.processData = async data => {
        await sleep(100)
        originalProcessData.apply(connection, [data])
      }

      await connection.connect()
      await expect(connection.send(protocol)).rejects.toThrowError(KafkaJSRequestTimeoutError)
    })

    describe('Debug logging', () => {
      let initialValue, connection

      beforeAll(() => {
        initialValue = process.env.KAFKAJS_DEBUG_PROTOCOL_BUFFERS
      })

      afterAll(() => {
        process.env['KAFKAJS_DEBUG_PROTOCOL_BUFFERS'] = initialValue
      })

      afterEach(async () => {
        if (connection) {
          await connection.disconnect()
        }
      })

      test('logs the full payload in case of non-retriable error when "KAFKAJS_DEBUG_PROTOCOL_BUFFERS" runtime flag is set', async () => {
        process.env['KAFKAJS_DEBUG_PROTOCOL_BUFFERS'] = '1'
        connection = new Connection(connectionOpts())
        const debugStub = jest.fn()
        connection.logger.debug = debugStub
        const protocol = apiVersions()
        protocol.response.parse = () => {
          throw new Error('non-retriable')
        }
        await connection.connect()
        await expect(connection.send(protocol)).rejects.toBeTruthy()

        const lastCall = debugStub.mock.calls[debugStub.mock.calls.length - 1]
        expect(lastCall[1].payload).toEqual(expect.any(Buffer))
      })

      test('filters payload in case of non-retriable error when "KAFKAJS_DEBUG_PROTOCOL_BUFFERS" runtime flag is not set', async () => {
        delete process.env['KAFKAJS_DEBUG_PROTOCOL_BUFFERS']
        connection = new Connection(connectionOpts())
        const debugStub = jest.fn()
        connection.logger.debug = debugStub
        const protocol = apiVersions()
        protocol.response.parse = () => {
          throw new Error('non-retriable')
        }
        await connection.connect()
        await expect(connection.send(protocol)).rejects.toBeTruthy()

        const lastCall = debugStub.mock.calls[debugStub.mock.calls.length - 1]
        expect(lastCall[1].payload).toEqual({
          type: 'Buffer',
          data: '[filtered]',
        })
      })
    })
  })

  describe('#nextCorrelationId', () => {
    beforeEach(() => {
      connection = new Connection(connectionOpts())
    })

    test('increments the current correlationId', () => {
      const id1 = connection.nextCorrelationId()
      const id2 = connection.nextCorrelationId()
      expect(id1).toEqual(0)
      expect(id2).toEqual(1)
      expect(connection.correlationId).toEqual(2)
    })

    test('resets to 0 when correlationId is equal to Number.MAX_VALUE', () => {
      expect(connection.correlationId).toEqual(0)

      connection.nextCorrelationId()
      connection.nextCorrelationId()
      expect(connection.correlationId).toEqual(2)

      connection.correlationId = Number.MAX_VALUE
      const id1 = connection.nextCorrelationId()
      expect(id1).toEqual(0)
      expect(connection.correlationId).toEqual(1)
    })
  })

  describe('#processData', () => {
    beforeEach(() => {
      connection = new Connection(connectionOpts())
    })

    test('buffer data while it is not complete', () => {
      const correlationId = 1
      const resolve = jest.fn()
      const entry = { correlationId, resolve }

      connection.requestQueue.push({
        entry,
        expectResponse: true,
        sendRequest: jest.fn(),
      })

      const payload = Buffer.from('ab')
      const size = Buffer.byteLength(payload) + Decoder.int32Size()
      // expected response size
      const sizePart1 = Buffer.from([0, 0])
      const sizePart2 = Buffer.from([0, 6])

      const correlationIdPart1 = Buffer.from([0, 0])
      const correlationIdPart2 = Buffer.from([0])
      const correlationIdPart3 = Buffer.from([1])

      // write half of the expected size and expect to keep buffering
      expect(connection.processData(sizePart1)).toBeUndefined()
      expect(resolve).not.toHaveBeenCalled()

      // Write the rest of the size, but without any response
      expect(connection.processData(sizePart2)).toBeUndefined()
      expect(resolve).not.toHaveBeenCalled()

      // Response consists of correlation id + payload
      // Writing 1/3 of the correlation id
      expect(connection.processData(correlationIdPart1)).toBeUndefined()
      expect(resolve).not.toHaveBeenCalled()

      // At this point, we will write N bytes, where N == size,
      // but we should keep buffering because the size field should
      // not be considered as part of the response payload
      expect(connection.processData(correlationIdPart2)).toBeUndefined()
      expect(resolve).not.toHaveBeenCalled()

      // write full payload size
      const buffer = Buffer.concat([correlationIdPart3, payload])
      connection.processData(buffer)

      expect(resolve).toHaveBeenCalledWith({
        correlationId,
        size,
        entry,
        payload,
      })
    })
  })
})
<commit_msg>Fix lint<commit_after>const { connectionOpts, sslConnectionOpts } = require('../../testHelpers')
const sleep = require('../utils/sleep')
const { requests } = require('../protocol/requests')
const Decoder = require('../protocol/decoder')
const { KafkaJSRequestTimeoutError } = require('../errors')
const Connection = require('./connection')

describe('Network > Connection', () => {
  // According to RFC 5737:
  // The blocks 192.0.2.0/24 (TEST-NET-1), 198.51.100.0/24 (TEST-NET-2),
  // and 203.0.113.0/24 (TEST-NET-3) are provided for use in documentation.
  const invalidIP = '203.0.113.1'
  const invalidHost = 'kafkajs.test'
  let connection

  afterEach(async () => {
    connection && (await connection.disconnect())
  })

  describe('#connect', () => {
    describe('PLAINTEXT', () => {
      beforeEach(() => {
        connection = new Connection(connectionOpts())
      })

      test('resolves the Promise when connected', async () => {
        await expect(connection.connect()).resolves.toEqual(true)
        expect(connection.connected).toEqual(true)
      })

      test('rejects the Promise in case of errors', async () => {
        connection.host = invalidHost
        const messagePattern = /Connection error: getaddrinfo ENOTFOUND kafkajs.test/
        await expect(connection.connect()).rejects.toThrow(messagePattern)
        expect(connection.connected).toEqual(false)
      })
    })

    describe('SSL', () => {
      beforeEach(() => {
        connection = new Connection(sslConnectionOpts())
      })

      test('resolves the Promise when connected', async () => {
        await expect(connection.connect()).resolves.toEqual(true)
        expect(connection.connected).toEqual(true)
      })

      test('rejects the Promise in case of timeouts', async () => {
        connection = new Connection(sslConnectionOpts({ connectionTimeout: 1 }))
        connection.host = invalidIP

        await expect(connection.connect()).rejects.toHaveProperty('message', 'Connection timeout')
        expect(connection.connected).toEqual(false)
      })

      test('rejects the Promise in case of errors', async () => {
        connection.ssl.cert = 'invalid'
        const messagePattern = /Failed to connect/
        await expect(connection.connect()).rejects.toThrow(messagePattern)
        expect(connection.connected).toEqual(false)
      })
    })
  })

  describe('#disconnect', () => {
    beforeEach(() => {
      connection = new Connection(connectionOpts())
    })

    test('disconnects an active connection', async () => {
      await connection.connect()
      expect(connection.connected).toEqual(true)
      await expect(connection.disconnect()).resolves.toEqual(true)
      expect(connection.connected).toEqual(false)
    })
  })

  describe('#send', () => {
    let apiVersions

    beforeEach(() => {
      connection = new Connection(connectionOpts())
      apiVersions = requests.ApiVersions.protocol({ version: 0 })
    })

    test('resolves the Promise with the response', async () => {
      await connection.connect()
      await expect(connection.send(apiVersions())).resolves.toBeTruthy()
    })

    test('rejects the Promise if it is not connected', async () => {
      expect(connection.connected).toEqual(false)
      await expect(connection.send(apiVersions())).rejects.toEqual(new Error('Not connected'))
    })

    test('rejects the Promise in case of a non-retriable error', async () => {
      const protocol = {
        ...apiVersions(),
        response: {
          ...apiVersions().response,
          parse: () => {
            throw new Error('non-retriable')
          },
        },
      }

      await connection.connect()
      await expect(connection.send(protocol)).rejects.toEqual(new Error('non-retriable'))
    })

    test('respect the maxInFlightRequests', async () => {
      const protocol = apiVersions()
      connection = new Connection(connectionOpts({ maxInFlightRequests: 2 }))
      const originalProcessData = connection.processData

      connection.processData = async data => {
        await sleep(100)
        originalProcessData.apply(connection, [data])
      }

      await connection.connect()

      const requests = [
        connection.send(protocol),
        connection.send(protocol),
        connection.send(protocol),
      ]

      await sleep(50)

      const inFlightRequestsSize = connection.requestQueue.inflight.size
      const pendingRequestsSize = connection.requestQueue.pending.length

      await Promise.all(requests)

      expect(inFlightRequestsSize).toEqual(2)
      expect(pendingRequestsSize).toEqual(1)
    })

    test('respect the requestTimeout', async () => {
      const protocol = apiVersions()
      connection = new Connection(
        connectionOpts({
          requestTimeout: 50,
          enforceRequestTimeout: true,
          requestTimeoutCheckInterval: 1,
        })
      )
      const originalProcessData = connection.processData

      connection.processData = async data => {
        await sleep(100)
        originalProcessData.apply(connection, [data])
      }

      await connection.connect()
      await expect(connection.send(protocol)).rejects.toThrowError(KafkaJSRequestTimeoutError)
    })

    describe('Debug logging', () => {
      let initialValue, connection

      beforeAll(() => {
        initialValue = process.env.KAFKAJS_DEBUG_PROTOCOL_BUFFERS
      })

      afterAll(() => {
        process.env['KAFKAJS_DEBUG_PROTOCOL_BUFFERS'] = initialValue
      })

      afterEach(async () => {
        if (connection) {
          await connection.disconnect()
        }
      })

      test('logs the full payload in case of non-retriable error when "KAFKAJS_DEBUG_PROTOCOL_BUFFERS" runtime flag is set', async () => {
        process.env['KAFKAJS_DEBUG_PROTOCOL_BUFFERS'] = '1'
        connection = new Connection(connectionOpts())
        const debugStub = jest.fn()
        connection.logger.debug = debugStub
        const protocol = apiVersions()
        protocol.response.parse = () => {
          throw new Error('non-retriable')
        }
        await connection.connect()
        await expect(connection.send(protocol)).rejects.toBeTruthy()

        const lastCall = debugStub.mock.calls[debugStub.mock.calls.length - 1]
        expect(lastCall[1].payload).toEqual(expect.any(Buffer))
      })

      test('filters payload in case of non-retriable error when "KAFKAJS_DEBUG_PROTOCOL_BUFFERS" runtime flag is not set', async () => {
        delete process.env['KAFKAJS_DEBUG_PROTOCOL_BUFFERS']
        connection = new Connection(connectionOpts())
        const debugStub = jest.fn()
        connection.logger.debug = debugStub
        const protocol = apiVersions()
        protocol.response.parse = () => {
          throw new Error('non-retriable')
        }
        await connection.connect()
        await expect(connection.send(protocol)).rejects.toBeTruthy()

        const lastCall = debugStub.mock.calls[debugStub.mock.calls.length - 1]
        expect(lastCall[1].payload).toEqual({
          type: 'Buffer',
          data: '[filtered]',
        })
      })
    })
  })

  describe('#nextCorrelationId', () => {
    beforeEach(() => {
      connection = new Connection(connectionOpts())
    })

    test('increments the current correlationId', () => {
      const id1 = connection.nextCorrelationId()
      const id2 = connection.nextCorrelationId()
      expect(id1).toEqual(0)
      expect(id2).toEqual(1)
      expect(connection.correlationId).toEqual(2)
    })

    test('resets to 0 when correlationId is equal to Number.MAX_VALUE', () => {
      expect(connection.correlationId).toEqual(0)

      connection.nextCorrelationId()
      connection.nextCorrelationId()
      expect(connection.correlationId).toEqual(2)

      connection.correlationId = Number.MAX_VALUE
      const id1 = connection.nextCorrelationId()
      expect(id1).toEqual(0)
      expect(connection.correlationId).toEqual(1)
    })
  })

  describe('#processData', () => {
    beforeEach(() => {
      connection = new Connection(connectionOpts())
    })

    test('buffer data while it is not complete', () => {
      const correlationId = 1
      const resolve = jest.fn()
      const entry = { correlationId, resolve }

      connection.requestQueue.push({
        entry,
        expectResponse: true,
        sendRequest: jest.fn(),
      })

      const payload = Buffer.from('ab')
      const size = Buffer.byteLength(payload) + Decoder.int32Size()
      // expected response size
      const sizePart1 = Buffer.from([0, 0])
      const sizePart2 = Buffer.from([0, 6])

      const correlationIdPart1 = Buffer.from([0, 0])
      const correlationIdPart2 = Buffer.from([0])
      const correlationIdPart3 = Buffer.from([1])

      // write half of the expected size and expect to keep buffering
      expect(connection.processData(sizePart1)).toBeUndefined()
      expect(resolve).not.toHaveBeenCalled()

      // Write the rest of the size, but without any response
      expect(connection.processData(sizePart2)).toBeUndefined()
      expect(resolve).not.toHaveBeenCalled()

      // Response consists of correlation id + payload
      // Writing 1/3 of the correlation id
      expect(connection.processData(correlationIdPart1)).toBeUndefined()
      expect(resolve).not.toHaveBeenCalled()

      // At this point, we will write N bytes, where N == size,
      // but we should keep buffering because the size field should
      // not be considered as part of the response payload
      expect(connection.processData(correlationIdPart2)).toBeUndefined()
      expect(resolve).not.toHaveBeenCalled()

      // write full payload size
      const buffer = Buffer.concat([correlationIdPart3, payload])
      connection.processData(buffer)

      expect(resolve).toHaveBeenCalledWith({
        correlationId,
        size,
        entry,
        payload,
      })
    })
  })
})
<|endoftext|>


<commit_before>let tokens = require("./setupenv");
let app = require("../app");
let chai = require("chai");
let chaiHttp = require("chai-http");
chai.should();
chai.use(chaiHttp);

let location = {
  label: "Cleveland, OH, United States",
  placeId: "ChIJLWto4y7vMIgRQhhi91XLBO0",
  isFixture: false,
  gmaps: {
    address_components: [
      {
        long_name: "Cleveland",
        short_name: "Cleveland",
        types: ["locality", "political"]
      },
      {
        long_name: "Cuyahoga County",
        short_name: "Cuyahoga County",
        types: ["administrative_area_level_2", "political"]
      },
      {
        long_name: "Ohio",
        short_name: "OH",
        types: ["administrative_area_level_1", "political"]
      },
      {
        long_name: "United States",
        short_name: "US",
        types: ["country", "political"]
      }
    ],
    formatted_address: "Cleveland, OH, USA",
    geometry: {
      bounds: {
        south: 41.390628,
        west: -81.87897599999997,
        north: 41.604436,
        east: -81.53274390000001
      },
      location: {
        lat: 41.49932,
        lng: -81.69436050000002
      },
      location_type: "APPROXIMATE",
      viewport: {
        south: 41.390628,
        west: -81.87897599999997,
        north: 41.5992571,
        east: -81.53274390000001
      }
    },
    place_id: "ChIJLWto4y7vMIgRQhhi91XLBO0",
    types: ["locality", "political"]
  },
  location: {
    lat: 41.49932,
    lng: -81.69436050000002
  }
};

describe("Cases", () => {
  describe("Lookup", () => {
    it("finds case 100", done => {
      chai
        .request(app)
        .get("/case/100")
        .set("Content-Type", "application/json")
        .set("Accept", "application/json")
        .send({})
        .end((err, res) => {
          res.should.have.status(200);
          done();
        });
    });
  });
  describe("Adding", () => {
    it("fails without authentication", done => {
      chai
        .request(app)
        .post("/case/new")
        .set("Content-Type", "application/json")
        .set("Accept", "application/json")
        .send({})
        .end((err, res) => {
          res.should.have.status(401);
          done();
        });
    });
    it("fails without content", done => {
      chai
        .request(app)
        .post("/case/new")
        .set("Content-Type", "application/json")
        .set("Accept", "application/json")
        .set("Authorization", "Bearer " + tokens.user_token)
        .send({})
        .end((err, res) => {
          res.should.have.status(400);
          done();
        });
    });
    it("works with authentication", done => {
      chai
        .request(app)
        .post("/case/new")
        .set("Content-Type", "application/json")
        .set("Accept", "application/json")
        .set("Authorization", "Bearer " + tokens.user_token)
        .send({
          // mandatory
          title: "This is the first title of the rest of your post",
          summary: "Eat this, it is my body",
          // optional
          lead_image: "https://cdn.thinglink.me/api/image/756598547733807104/", // not sure what the client wants to send here
          vidURL: "https://www.youtube.com/watch?v=QF7g3rCnD-w",
          location: location,
          relatedCases: ["1", "2", "3", "4"],
          relatedMethods: ["145", "146", "147"],
          relatedOrganizations: ["199", "200", "201"]
        })
        .end((err, res) => {
          res.should.have.status(201);
          done();
        });
    });
  });
  // let userID = tokens.user_payload.user_id;
  describe("Counting by country", () => {
    it("returns stuff", done => {
      chai
        .request(app)
        .get("/case/countsByCountry")
        .set("Content-Type", "application/json")
        .set("Accept", "application/json")
        .set("Authorization", "Bearer " + tokens.user_token)
        .end((err, res) => {
          let countryCounts = res.body.data.countryCounts;
          countryCounts.should.have.property("france");
          res.should.have.status(200);
          done();
        });
    });
  });
  describe("Get case with tags", () => {
    it("should have 3 tags", done => {
      chai
        .request(app)
        .get("/case/39")
        .set("Content-Type", "application/json")
        .set("Accept", "application/json")
        .end((err, res) => {
          res.body.OK.should.equal(true);
          res.should.have.status(200);
          let the_case = res.body.data;
          the_case.tags.should.have.lengthOf(3);
          done();
        });
    });
  });
});
<commit_msg>update test for lead_image<commit_after>let tokens = require("./setupenv");
let app = require("../app");
let chai = require("chai");
let chaiHttp = require("chai-http");
chai.should();
chai.use(chaiHttp);

let location = {
  label: "Cleveland, OH, United States",
  placeId: "ChIJLWto4y7vMIgRQhhi91XLBO0",
  isFixture: false,
  gmaps: {
    address_components: [
      {
        long_name: "Cleveland",
        short_name: "Cleveland",
        types: ["locality", "political"]
      },
      {
        long_name: "Cuyahoga County",
        short_name: "Cuyahoga County",
        types: ["administrative_area_level_2", "political"]
      },
      {
        long_name: "Ohio",
        short_name: "OH",
        types: ["administrative_area_level_1", "political"]
      },
      {
        long_name: "United States",
        short_name: "US",
        types: ["country", "political"]
      }
    ],
    formatted_address: "Cleveland, OH, USA",
    geometry: {
      bounds: {
        south: 41.390628,
        west: -81.87897599999997,
        north: 41.604436,
        east: -81.53274390000001
      },
      location: {
        lat: 41.49932,
        lng: -81.69436050000002
      },
      location_type: "APPROXIMATE",
      viewport: {
        south: 41.390628,
        west: -81.87897599999997,
        north: 41.5992571,
        east: -81.53274390000001
      }
    },
    place_id: "ChIJLWto4y7vMIgRQhhi91XLBO0",
    types: ["locality", "political"]
  },
  location: {
    lat: 41.49932,
    lng: -81.69436050000002
  }
};

describe("Cases", () => {
  describe("Lookup", () => {
    it("finds case 100", done => {
      chai
        .request(app)
        .get("/case/100")
        .set("Content-Type", "application/json")
        .set("Accept", "application/json")
        .send({})
        .end((err, res) => {
          res.should.have.status(200);
          done();
        });
    });
  });
  describe("Adding", () => {
    it("fails without authentication", done => {
      chai
        .request(app)
        .post("/case/new")
        .set("Content-Type", "application/json")
        .set("Accept", "application/json")
        .send({})
        .end((err, res) => {
          res.should.have.status(401);
          done();
        });
    });
    it("fails without content", done => {
      chai
        .request(app)
        .post("/case/new")
        .set("Content-Type", "application/json")
        .set("Accept", "application/json")
        .set("Authorization", "Bearer " + tokens.user_token)
        .send({})
        .end((err, res) => {
          res.should.have.status(400);
          done();
        });
    });
    it("works with authentication", done => {
      chai
        .request(app)
        .post("/case/new")
        .set("Content-Type", "application/json")
        .set("Accept", "application/json")
        .set("Authorization", "Bearer " + tokens.user_token)
        .send({
          // mandatory
          title: "This is the first title of the rest of your post",
          summary: "Eat this, it is my body",
          // optional
          lead_image: "CitizensAssembly_2.jpg", // key into S3 bucket
          vidURL: "https://www.youtube.com/watch?v=QF7g3rCnD-w",
          location: location,
          relatedCases: ["1", "2", "3", "4"],
          relatedMethods: ["145", "146", "147"],
          relatedOrganizations: ["199", "200", "201"]
        })
        .end((err, res) => {
          res.should.have.status(201);
          done();
        });
    });
  });
  // let userID = tokens.user_payload.user_id;
  describe("Counting by country", () => {
    it("returns stuff", done => {
      chai
        .request(app)
        .get("/case/countsByCountry")
        .set("Content-Type", "application/json")
        .set("Accept", "application/json")
        .set("Authorization", "Bearer " + tokens.user_token)
        .end((err, res) => {
          let countryCounts = res.body.data.countryCounts;
          countryCounts.should.have.property("france");
          res.should.have.status(200);
          done();
        });
    });
  });
  describe("Get case with tags", () => {
    it("should have 3 tags", done => {
      chai
        .request(app)
        .get("/case/39")
        .set("Content-Type", "application/json")
        .set("Accept", "application/json")
        .end((err, res) => {
          res.body.OK.should.equal(true);
          res.should.have.status(200);
          let the_case = res.body.data;
          the_case.tags.should.have.lengthOf(3);
          done();
        });
    });
  });
});
<|endoftext|>