<commit_before>/* global jest, before, beforeEach, describe, it, expect */
import { assert, expect } from 'chai';
import * as RulesService from '../../../modules/services/rules-service';

jest.unmock('../../../modules/services/rules-service');

const toExplicitRule = Value => ({
  Matcher: {},
  Value,
  Type: 'SingleVariant',

});

describe('rules-service', () => {
  describe('addPartition', () => {
    const PARTITION_NAME = 'somePartition';

    const rulesToCheck = [
      { expected: { '*': [] }, PARTITION_NAME, rules: [], depth: 0 },
      {
        expected: {
          someValue1: [{ Matcher: {} }],
          someValue2: [
            { Matcher: {} },
            { Matcher: { property: 'someValue' } },
          ],
          '*': [{ Matcher: {} }],
        },
        PARTITION_NAME,
        rules: [
          { Matcher: { [PARTITION_NAME]: 'someValue1' } },
          { Matcher: { [PARTITION_NAME]: 'someValue2' } },
          { Matcher: { [PARTITION_NAME]: 'someValue2', property: 'someValue' } },
          { Matcher: {} },
        ],
        depth: 0,
      },
      {
        expected: {
          partition1: {
            someValue: [
              { Matcher: {} },
              { Matcher: { property: 'someValue2' } },
            ],
            '*': [{ Matcher: {} }],
          },
          partition2: { '*': [] },
        },
        PARTITION_NAME,
        rules: {
          partition1: [
            { Matcher: { [PARTITION_NAME]: 'someValue' } },
            { Matcher: { [PARTITION_NAME]: 'someValue', property: 'someValue2' } },
            { Matcher: {} },
          ],
          partition2: [],
        },
        depth: 1,
      },
    ];

    rulesToCheck.forEach(({ expected, partition, rules, depth }) => {
      it('should add partition', () => {
        const result = RulesService.addPartition(partition, rules, depth);
        assert.deepEqual(result, expected);
      });
    });
  });

  describe('convertToExplicitRules', () => {
    const rulesToCheck = [
      {
        expected: [toExplicitRule('someValue')],
        rule: 'someValue',
        depth: 0,
      },
      {
        expected: [toExplicitRule('someValue')],
        rule: [toExplicitRule('someValue')],
        depth: 0,
      },
      {
        expected: { '*': [toExplicitRule('someValue')] },
        rule: 'someValue',
        depth: 1,
      },
      {
        expected: { '*': [toExplicitRule('someValue')] },
        rule: [toExplicitRule('someValue')],
        depth: 1,
      },
      {
        expected: {
          somePartition1: [toExplicitRule('someValue1')],
          somePartition2: [toExplicitRule('someValue2')],
        },
        rule: {
          somePartition1: 'someValue1',
          somePartition2: [toExplicitRule('someValue2')],
        },
        depth: 1,
      },
      {
        expected: { '*': { '*': [toExplicitRule('someValue')] } },
        rule: 'someValue',
        depth: 2,
      },
      {
        expected: { '*': { '*': [toExplicitRule('someValue')] } },
        rule: [toExplicitRule('someValue')],
        depth: 2,
      },
      {
        expected: {
          somePartition1: { '*': [toExplicitRule('someValue1')] },
          somePartition2: { '*': [toExplicitRule('someValue2')] },
          somePartition3: {
            innerPartition1: [toExplicitRule('someValue3')],
            innerPartition2: [toExplicitRule('someValue4')],
          },
        },
        rule: {
          somePartition1: 'someValue1',
          somePartition2: [toExplicitRule('someValue2')],
          somePartition3: {
            innerPartition1: 'someValue3',
            innerPartition2: [toExplicitRule('someValue4')],
          },
        },
        depth: 2,
      },
    ];

    rulesToCheck.forEach(({ expected, rule, depth }) => {
      it('should convert to explicit', () => {
        const result = RulesService.convertToExplicitRules(rule, depth);
        assert.deepEqual(result, expected);
      });
    });
  });

  describe('convertToExplicitKey', () => {
    it('should convert to explicit key', () => {
      const key = {
        partitions: ['p1', 'p2'],
        rules: {
          somePartition1: 'someValue1',
          somePartition2: [toExplicitRule('someValue2')],
          somePartition3: { innerPartition: 'someValue3' },
          somePartition4: { innerPartition: [toExplicitRule('someValue4')] },
        },
        valueType: 'someType',
      };
      const expected = {
        partitions: ['p1', 'p2'],
        rules: {
          somePartition1: { '*': [toExplicitRule('someValue1')] },
          somePartition2: { '*': [toExplicitRule('someValue2')] },
          somePartition3: { innerPartition: [toExplicitRule('someValue3')] },
          somePartition4: { innerPartition: [toExplicitRule('someValue4')] },
        },
        valueType: 'someType',
      };
      const result = RulesService.convertToExplicitKey(key);
      assert.deepEqual(result, expected);
    });
  });
});
<commit_msg>fix tests<commit_after>/* global jest, before, beforeEach, describe, it, expect */
import { assert, expect } from 'chai';
import * as RulesService from '../../../modules/services/rules-service';

jest.unmock('../../../modules/services/rules-service');

const toExplicitRule = Value => ({
  Matcher: {},
  Value,
  Type: 'SingleVariant',

});

describe('rules-service', () => {
  describe('addPartition', () => {
    const partition = 'somePartition';

    const rulesToCheck = [
      { expected: { '*': [] }, partition, rules: [], depth: 0 },
      {
        expected: {
          someValue1: [{ Matcher: {} }],
          someValue2: [
            { Matcher: {} },
            { Matcher: { property: 'someValue' } },
          ],
          '*': [{ Matcher: {} }],
        },
        partition,
        rules: [
          { Matcher: { [partition]: 'someValue1' } },
          { Matcher: { [partition]: 'someValue2' } },
          { Matcher: { [partition]: 'someValue2', property: 'someValue' } },
          { Matcher: {} },
        ],
        depth: 0,
      },
      {
        expected: {
          partition1: {
            someValue: [
              { Matcher: {} },
              { Matcher: { property: 'someValue2' } },
            ],
            '*': [{ Matcher: {} }],
          },
          partition2: { '*': [] },
        },
        partition,
        rules: {
          partition1: [
            { Matcher: { [partition]: 'someValue' } },
            { Matcher: { [partition]: 'someValue', property: 'someValue2' } },
            { Matcher: {} },
          ],
          partition2: [],
        },
        depth: 1,
      },
    ];

    rulesToCheck.forEach(({ expected, partition, rules, depth }) => {
      it('should add partition', () => {
        const result = RulesService.addPartition(partition, rules, depth);
        assert.deepEqual(result, expected);
      });
    });
  });

  describe('convertToExplicitRules', () => {
    const rulesToCheck = [
      {
        expected: [toExplicitRule('someValue')],
        rule: 'someValue',
        depth: 0,
      },
      {
        expected: [toExplicitRule('someValue')],
        rule: [toExplicitRule('someValue')],
        depth: 0,
      },
      {
        expected: { '*': [toExplicitRule('someValue')] },
        rule: 'someValue',
        depth: 1,
      },
      {
        expected: { '*': [toExplicitRule('someValue')] },
        rule: [toExplicitRule('someValue')],
        depth: 1,
      },
      {
        expected: {
          somePartition1: [toExplicitRule('someValue1')],
          somePartition2: [toExplicitRule('someValue2')],
        },
        rule: {
          somePartition1: 'someValue1',
          somePartition2: [toExplicitRule('someValue2')],
        },
        depth: 1,
      },
      {
        expected: { '*': { '*': [toExplicitRule('someValue')] } },
        rule: 'someValue',
        depth: 2,
      },
      {
        expected: { '*': { '*': [toExplicitRule('someValue')] } },
        rule: [toExplicitRule('someValue')],
        depth: 2,
      },
      {
        expected: {
          somePartition1: { '*': [toExplicitRule('someValue1')] },
          somePartition2: { '*': [toExplicitRule('someValue2')] },
          somePartition3: {
            innerPartition1: [toExplicitRule('someValue3')],
            innerPartition2: [toExplicitRule('someValue4')],
          },
        },
        rule: {
          somePartition1: 'someValue1',
          somePartition2: [toExplicitRule('someValue2')],
          somePartition3: {
            innerPartition1: 'someValue3',
            innerPartition2: [toExplicitRule('someValue4')],
          },
        },
        depth: 2,
      },
    ];

    rulesToCheck.forEach(({ expected, rule, depth }) => {
      it('should convert to explicit', () => {
        const result = RulesService.convertToExplicitRules(rule, depth);
        assert.deepEqual(result, expected);
      });
    });
  });

  describe('convertToExplicitKey', () => {
    it('should convert to explicit key', () => {
      const key = {
        partitions: ['p1', 'p2'],
        rules: {
          somePartition1: 'someValue1',
          somePartition2: [toExplicitRule('someValue2')],
          somePartition3: { innerPartition: 'someValue3' },
          somePartition4: { innerPartition: [toExplicitRule('someValue4')] },
        },
        valueType: 'someType',
      };
      const expected = {
        partitions: ['p1', 'p2'],
        rules: {
          somePartition1: { '*': [toExplicitRule('someValue1')] },
          somePartition2: { '*': [toExplicitRule('someValue2')] },
          somePartition3: { innerPartition: [toExplicitRule('someValue3')] },
          somePartition4: { innerPartition: [toExplicitRule('someValue4')] },
        },
        valueType: 'someType',
      };
      const result = RulesService.convertToExplicitKey(key);
      assert.deepEqual(result, expected);
    });
  });
});
<|endoftext|>


<commit_before>/* jshint camelcase:false */
var gulp = require('gulp');
var del = require('del');
var merge = require('merge-stream');
var paths = require('./gulp.config.json');
var plug = require('gulp-load-plugins')();

var log = plug.util.log;

/**
 * List the available gulp tasks
 */
gulp.task('help', plug.taskListing);

/**
 * Lint the code
 * @return {Stream}
 */
gulp.task('analyze', function () {
    log('Analyzing source with JSHint, JSCS');

    var jshint = analyzejshint(paths.js);
    var jscs = analyzejscs(paths.js);

    return merge(jshint, jscs);
});

/**
 * Create $templateCache from the html templates
 * @return {Stream}
 */
gulp.task('templatecache', function () {
    log('Creating an AngularJS $templateCache');

    return gulp.src(paths.htmltemplates)
               .pipe(plug.minifyHtml({
                   empty: true
               }))
               .pipe(plug.angularTemplatecache('templates.js', {
                   module: 'poker',
                   standalone: false,
                   root: '/components'
               }))
               .pipe(gulp.dest(paths.build));
});

/**
 * Minify and bundle the app's JavaScript
 * @return {Stream}
 */
gulp.task('js', ['analyze', 'templatecache'], function () {
    log('Bundling, minifying, and copying the app\'s JavaScript');

    var source = [].concat(paths.js, paths.build + 'templates.js');

    return gulp.src(source)
               .pipe(plug.concat('pokerApp.min.js'))
               .pipe(plug.ngAnnotate({
                   add: true,
                   single_quotes: true
               }))
               .pipe(plug.bytediff.start())
               .pipe(plug.uglify({
                   mangle: true
               }))
               .pipe(plug.bytediff.stop(bytediffFormatter))
               .pipe(gulp.dest(paths.build));
});

/**
 * Copy the Vendor JavaScript
 * @return {Stream}
 */
gulp.task('vendorjs', function () {
    log('Bundling, minifying, and copying the Vendor JavaScript');

    return gulp.src(paths.vendorjs)
               .pipe(plug.concat('vendor.min.js'))
               .pipe(plug.bytediff.start())
               .pipe(plug.uglify())
               .pipe(plug.bytediff.stop(bytediffFormatter))
               .pipe(gulp.dest(paths.build));
});

/**
 * Minify and bundle the CSS
 * @return {Stream}
 */
gulp.task('css', function () {
    log('Bundling, minifying, and copying the app\'s CSS');

    return gulp.src(paths.css)
               .pipe(plug.concat('pokerApp.min.css')) // Before bytediff or after
               .pipe(plug.autoprefixer('last 2 version', '> 5%'))
               .pipe(plug.bytediff.start())
               .pipe(plug.minifyCss({}))
               .pipe(plug.bytediff.stop(bytediffFormatter))
               .pipe(gulp.dest(paths.build));
});

/**
 * Minify and bundle the Vendor CSS
 * @return {Stream}
 */
gulp.task('vendorcss', function () {
    log('Compressing, bundling, copying vendor CSS');

    return gulp.src(paths.vendorcss)
               .pipe(plug.concat('vendor.min.css'))
               .pipe(plug.bytediff.start())
               .pipe(plug.minifyCss({}))
               .pipe(plug.bytediff.stop(bytediffFormatter))
               .pipe(gulp.dest(paths.build));
});

/**
 * Inject all the files into the new index.html
 * rev, but no map
 * @return {Stream}
 */
gulp.task('rev-and-inject', ['js', 'vendorjs', 'css', 'vendorcss'], function () {
    log('Rev\'ing files and building index.html');

    var minified = paths.build + '**/*.min.*';
    var index = paths.html;
    var minFilter = plug.filter(['**/*.min.*', '!**/*.map']);
    var indexFilter = plug.filter(['index.html']);

    return gulp.src([].concat(minified, index)) // add all built min files and index.html
               .pipe(minFilter) // filter the stream to minified css and js
               .pipe(plug.rev()) // create files with rev's
               .pipe(plug.revDeleteOriginal())
               .pipe(gulp.dest(paths.build)) // write the rev files
               .pipe(minFilter.restore()) // remove filter, back to original stream

               // inject the files into index.html
               .pipe(indexFilter) // filter to index.html
               .pipe(inject('vendor.min.css', 'inject-vendor'))
               .pipe(inject('pokerApp.min.css'))
               .pipe(inject('vendor.min.js', 'inject-vendor'))
               .pipe(inject('pokerApp.min.js'))
               .pipe(gulp.dest(paths.build)) // write the rev files
               .pipe(indexFilter.restore()) // remove filter, back to original stream

               // replace the files referenced in index.html with the rev'd files
               .pipe(plug.revReplace()) // Substitute in new filenames
               .pipe(gulp.dest(paths.build)) // write the index.html file changes
               .pipe(plug.rev.manifest()) // create the manifest (must happen last or we screw up the injection)
               .pipe(gulp.dest(paths.build)); // write the manifest

    function inject(path, name) {
        var pathGlob = paths.build + path;
        var options = {
            ignorePath: paths.build.substring(1),
            read: false
        };
        if (name) {
            options.name = name;
        }
        return plug.inject(gulp.src(pathGlob), options);
    }
});

/**
 * Build the optimized app
 * @return {Stream}
 */
gulp.task('build', ['rev-and-inject'], function () {
    log('Building the optimized app');

    return gulp.src('').pipe(plug.notify({
        onLast: true,
        message: 'Deployed code!'
    }));
});

gulp.task('cleanIntermediates', function (cb) {
    del(paths.intermediateFiles, cb);
})

/**
 * Build the optimized app
 * @return {Stream}
 */
gulp.task('default', plug.sequence('clean', 'build', 'cleanIntermediates'))

/**
 * Remove all files from the build folder
 * One way to run clean before all tasks is to run
 * from the cmd line: gulp clean && gulp build
 * @return {Stream}
 */
gulp.task('clean', function (cb) {
    log('Cleaning: ' + plug.util.colors.blue(paths.build));

    del(paths.build, cb);
});

/**
 * Remove all files from the build folder
 * One way to run clean before all tasks is to run
 * from the cmd line: gulp clean && gulp build
 * @return {Stream}
 */
gulp.task('cleanNodeModules', function (cb) {
    log('Cleaning: ' + plug.util.colors.blue('./node_modules'));

    del('./node_modules', cb);
});

/**
 * Watch files and build
 */
gulp.task('watch', function () {
    log('Watching all files');

    var css = ['gulpfile.js'].concat(paths.css, paths.vendorcss);
    var js = ['gulpfile.js'].concat(paths.js);

    gulp.watch(js, ['js', 'vendorjs'])
        .on('change', logWatch);

    gulp.watch(css, ['css', 'vendorcss'])
        .on('change', logWatch);

    // TODO: Need to watch templates

    function logWatch(event) {
        log('*** File ' + event.path + ' was ' + event.type + ', running tasks...');
    }
});

////////////////

/**
 * Execute JSHint on given source files
 * @param  {Array} sources
 * @param  {String} overrideRcFile
 * @return {Stream}
 */
function analyzejshint(sources, overrideRcFile) {
    var jshintrcFile = overrideRcFile || './.jshintrc';
    log('Running JSHint');
    log(sources);
    return gulp.src(sources)
               .pipe(plug.jshint(jshintrcFile))
               .pipe(plug.jshint.reporter('jshint-stylish'));
}

/**
 * Execute JSCS on given source files
 * @param  {Array} sources
 * @return {Stream}
 */
function analyzejscs(sources) {
    log('Running JSCS');
    return gulp.src(sources)
               .pipe(plug.jscs('./.jscsrc'))
               .pipe(plug.jscsStylish());
}

/**
 * Formatter for bytediff to display the size changes after processing
 * @param  {Object} data - byte data
 * @return {String}      Difference in bytes, formatted
 */
function bytediffFormatter(data) {
    var difference = (data.savings > 0) ? ' smaller.' : ' larger.';
    return data.fileName + ' went from ' +
        (data.startSize / 1000).toFixed(2) + ' kB to ' + (data.endSize / 1000).toFixed(2) + ' kB' +
        ' and is ' + formatPercent(1 - data.percent, 2) + '%' + difference;
}

/**
 * Format a number as a percentage
 * @param  {Number} num       Number to format as a percent
 * @param  {Number} precision Precision of the decimal
 * @return {String}           Formatted percentage
 */
function formatPercent(num, precision) {
    return (num * 100).toFixed(precision);
}<commit_msg>removed cleanNodeModules task from gulpfile<commit_after>/* jshint camelcase:false */
var gulp = require('gulp');
var del = require('del');
var merge = require('merge-stream');
var paths = require('./gulp.config.json');
var plug = require('gulp-load-plugins')();

var log = plug.util.log;

/**
 * List the available gulp tasks
 */
gulp.task('help', plug.taskListing);

/**
 * Lint the code
 * @return {Stream}
 */
gulp.task('analyze', function () {
    log('Analyzing source with JSHint, JSCS');

    var jshint = analyzejshint(paths.js);
    var jscs = analyzejscs(paths.js);

    return merge(jshint, jscs);
});

/**
 * Create $templateCache from the html templates
 * @return {Stream}
 */
gulp.task('templatecache', function () {
    log('Creating an AngularJS $templateCache');

    return gulp.src(paths.htmltemplates)
               .pipe(plug.minifyHtml({
                   empty: true
               }))
               .pipe(plug.angularTemplatecache('templates.js', {
                   module: 'poker',
                   standalone: false,
                   root: '/components'
               }))
               .pipe(gulp.dest(paths.build));
});

/**
 * Minify and bundle the app's JavaScript
 * @return {Stream}
 */
gulp.task('js', ['analyze', 'templatecache'], function () {
    log('Bundling, minifying, and copying the app\'s JavaScript');

    var source = [].concat(paths.js, paths.build + 'templates.js');

    return gulp.src(source)
               .pipe(plug.concat('pokerApp.min.js'))
               .pipe(plug.ngAnnotate({
                   add: true,
                   single_quotes: true
               }))
               .pipe(plug.bytediff.start())
               .pipe(plug.uglify({
                   mangle: true
               }))
               .pipe(plug.bytediff.stop(bytediffFormatter))
               .pipe(gulp.dest(paths.build));
});

/**
 * Copy the Vendor JavaScript
 * @return {Stream}
 */
gulp.task('vendorjs', function () {
    log('Bundling, minifying, and copying the Vendor JavaScript');

    return gulp.src(paths.vendorjs)
               .pipe(plug.concat('vendor.min.js'))
               .pipe(plug.bytediff.start())
               .pipe(plug.uglify())
               .pipe(plug.bytediff.stop(bytediffFormatter))
               .pipe(gulp.dest(paths.build));
});

/**
 * Minify and bundle the CSS
 * @return {Stream}
 */
gulp.task('css', function () {
    log('Bundling, minifying, and copying the app\'s CSS');

    return gulp.src(paths.css)
               .pipe(plug.concat('pokerApp.min.css')) // Before bytediff or after
               .pipe(plug.autoprefixer('last 2 version', '> 5%'))
               .pipe(plug.bytediff.start())
               .pipe(plug.minifyCss({}))
               .pipe(plug.bytediff.stop(bytediffFormatter))
               .pipe(gulp.dest(paths.build));
});

/**
 * Minify and bundle the Vendor CSS
 * @return {Stream}
 */
gulp.task('vendorcss', function () {
    log('Compressing, bundling, copying vendor CSS');

    return gulp.src(paths.vendorcss)
               .pipe(plug.concat('vendor.min.css'))
               .pipe(plug.bytediff.start())
               .pipe(plug.minifyCss({}))
               .pipe(plug.bytediff.stop(bytediffFormatter))
               .pipe(gulp.dest(paths.build));
});

/**
 * Inject all the files into the new index.html
 * rev, but no map
 * @return {Stream}
 */
gulp.task('rev-and-inject', ['js', 'vendorjs', 'css', 'vendorcss'], function () {
    log('Rev\'ing files and building index.html');

    var minified = paths.build + '**/*.min.*';
    var index = paths.html;
    var minFilter = plug.filter(['**/*.min.*', '!**/*.map']);
    var indexFilter = plug.filter(['index.html']);

    return gulp.src([].concat(minified, index)) // add all built min files and index.html
               .pipe(minFilter) // filter the stream to minified css and js
               .pipe(plug.rev()) // create files with rev's
               .pipe(plug.revDeleteOriginal())
               .pipe(gulp.dest(paths.build)) // write the rev files
               .pipe(minFilter.restore()) // remove filter, back to original stream

               // inject the files into index.html
               .pipe(indexFilter) // filter to index.html
               .pipe(inject('vendor.min.css', 'inject-vendor'))
               .pipe(inject('pokerApp.min.css'))
               .pipe(inject('vendor.min.js', 'inject-vendor'))
               .pipe(inject('pokerApp.min.js'))
               .pipe(gulp.dest(paths.build)) // write the rev files
               .pipe(indexFilter.restore()) // remove filter, back to original stream

               // replace the files referenced in index.html with the rev'd files
               .pipe(plug.revReplace()) // Substitute in new filenames
               .pipe(gulp.dest(paths.build)) // write the index.html file changes
               .pipe(plug.rev.manifest()) // create the manifest (must happen last or we screw up the injection)
               .pipe(gulp.dest(paths.build)); // write the manifest

    function inject(path, name) {
        var pathGlob = paths.build + path;
        var options = {
            ignorePath: paths.build.substring(1),
            read: false
        };
        if (name) {
            options.name = name;
        }
        return plug.inject(gulp.src(pathGlob), options);
    }
});

/**
 * Build the optimized app
 * @return {Stream}
 */
gulp.task('build', ['rev-and-inject'], function () {
    log('Building the optimized app');

    return gulp.src('').pipe(plug.notify({
        onLast: true,
        message: 'Deployed code!'
    }));
});

gulp.task('cleanIntermediates', function (cb) {
    del(paths.intermediateFiles, cb);
})

/**
 * Build the optimized app
 * @return {Stream}
 */
gulp.task('default', plug.sequence('clean', 'build', 'cleanIntermediates'))

/**
 * Remove all files from the build folder
 * One way to run clean before all tasks is to run
 * from the cmd line: gulp clean && gulp build
 * @return {Stream}
 */
gulp.task('clean', function (cb) {
    log('Cleaning: ' + plug.util.colors.blue(paths.build));

    del(paths.build, cb);
});

/**
 * Watch files and build
 */
gulp.task('watch', function () {
    log('Watching all files');

    var css = ['gulpfile.js'].concat(paths.css, paths.vendorcss);
    var js = ['gulpfile.js'].concat(paths.js);

    gulp.watch(js, ['js', 'vendorjs'])
        .on('change', logWatch);

    gulp.watch(css, ['css', 'vendorcss'])
        .on('change', logWatch);

    // TODO: Need to watch templates

    function logWatch(event) {
        log('*** File ' + event.path + ' was ' + event.type + ', running tasks...');
    }
});

////////////////

/**
 * Execute JSHint on given source files
 * @param  {Array} sources
 * @param  {String} overrideRcFile
 * @return {Stream}
 */
function analyzejshint(sources, overrideRcFile) {
    var jshintrcFile = overrideRcFile || './.jshintrc';
    log('Running JSHint');
    log(sources);
    return gulp.src(sources)
               .pipe(plug.jshint(jshintrcFile))
               .pipe(plug.jshint.reporter('jshint-stylish'));
}

/**
 * Execute JSCS on given source files
 * @param  {Array} sources
 * @return {Stream}
 */
function analyzejscs(sources) {
    log('Running JSCS');
    return gulp.src(sources)
               .pipe(plug.jscs('./.jscsrc'))
               .pipe(plug.jscsStylish());
}

/**
 * Formatter for bytediff to display the size changes after processing
 * @param  {Object} data - byte data
 * @return {String}      Difference in bytes, formatted
 */
function bytediffFormatter(data) {
    var difference = (data.savings > 0) ? ' smaller.' : ' larger.';
    return data.fileName + ' went from ' +
        (data.startSize / 1000).toFixed(2) + ' kB to ' + (data.endSize / 1000).toFixed(2) + ' kB' +
        ' and is ' + formatPercent(1 - data.percent, 2) + '%' + difference;
}

/**
 * Format a number as a percentage
 * @param  {Number} num       Number to format as a percent
 * @param  {Number} precision Precision of the decimal
 * @return {String}           Formatted percentage
 */
function formatPercent(num, precision) {
    return (num * 100).toFixed(precision);
}<|endoftext|>


<commit_before>import fs from 'fs';
import path from 'path';

function findRecursive(directory) {
  if (directory === '/') {
    throw new Error('No project root found');
  }
  const pathToPackageJson = path.join(directory, 'package.json');
  const pathToNodeModulesFolder = path.join(directory, 'node_modules');
  const isPackageDependency = /\/node_modules$/.test(path.dirname(directory));
  if (fs.existsSync(pathToPackageJson) &&
    (fs.existsSync(pathToNodeModulesFolder) || isPackageDependency)) {
    return directory;
  }
  return findRecursive(path.dirname(directory));
}

function makeAbsolute(pathToFile) {
  if (pathToFile.startsWith('/')) {
    return pathToFile;
  }
  return path.join(process.cwd(), pathToFile);
}

export default function findProjectRoot(pathToFile) {
  return findRecursive(path.dirname(makeAbsolute(pathToFile)));
}
<commit_msg>Move a regex to a constant<commit_after>import fs from 'fs';
import path from 'path';

const NODE_MODULES_PATTERN = /\/node_modules$/;

function findRecursive(directory) {
  if (directory === '/') {
    throw new Error('No project root found');
  }
  const pathToPackageJson = path.join(directory, 'package.json');
  const pathToNodeModulesFolder = path.join(directory, 'node_modules');
  const isPackageDependency = NODE_MODULES_PATTERN.test(path.dirname(directory));
  if (fs.existsSync(pathToPackageJson) &&
    (fs.existsSync(pathToNodeModulesFolder) || isPackageDependency)) {
    return directory;
  }
  return findRecursive(path.dirname(directory));
}

function makeAbsolute(pathToFile) {
  if (pathToFile.startsWith('/')) {
    return pathToFile;
  }
  return path.join(process.cwd(), pathToFile);
}

export default function findProjectRoot(pathToFile) {
  return findRecursive(path.dirname(makeAbsolute(pathToFile)));
}
<|endoftext|>


<commit_before>import firebase from 'firebase'
import db from 'firebase/database'
import invariant from 'invariant'

const createTypes = (prefix) => {
  const c = (str) => `${prefix.toUpperCase()}_${str.toUpperCase()}`
  return {
    ACTION_LISTEN: c('listen'),
    ACTION_UNLISTEN: c('unlisten'),
    ACTION_REMOVE: c('remove'),
    ACTION_UPDATE: c('update'),
    ACTION_SET: c('set'),
    ACTION_GET: c('get'),
    ITEM_VALUE: c('value'),
    ITEM_ADDED: c('added'),
    ITEM_REMOVED: c('remove'),
    ITEM_CHANGED: c('changed'),
    UPDATED: c('updated')
  }
}

const defaultToObject = child => ({_key: child.key, ...child.val()})
const identity = (i) => i
const defaultSortFn = (a, b) => a.timestamp < b.timestamp
const defaultInitialState = {
  items: [],
}

export class FirestackModule {
  constructor(refName, opts={}) {
    invariant(refName && typeof refName !== 'undefined', 'No ref name passed');

    this._refName = refName;
    this._makeRef = opts.makeRef || identity;
    let firestack = this._firestack = opts.firestack;

    const initialState = Object.assign({}, opts.initialState || defaultInitialState, {
      listening: false,
      items: []
    })

    this._localState = initialState;

    this._types = createTypes(this._refName);
    this._toObject = opts.toObject || defaultToObject
    this._sortFn = opts.sortFn || defaultSortFn
    this._onChange = opts.onChange || identity;

    this.setStore(opts.store);
  }

  makeRef(path) {
    const refName = [this._refName, path].join('/');
    return this._makeRef(this._firestack.database.ref(refName));
  }

  setStore(store) {
    if (store) {
      this._store = store;
    }
  }

  /*
   * Actions
   */
  listen(cb) {
    let store = this._getStore();
    invariant(store, 'Please set the store');

    const T = this._types;
    const listenRef = this.makeRef();
    const toObject = this._toObject;

    const _itemAdded = (snapshot, prevKey) => {
      const state = this._getState(); // local state
      const newItem = toObject(snapshot, state);
      let list = state.items || [];
      list.push(newItem)
      list = list.sort(this._sortFn)
      return this._handleUpdate(T.ITEM_ADDED, {items: list}, cb);
    }
    const _itemRemoved = (snapshot, prevKey) => {
      const state = this._getState(); // local state
      const itemKeys = state.items.map(i => i._key);
      const itemIndex = itemKeys.indexOf(snapshot.key);
      let newItems = [].concat(state.items);
      newItems.splice(itemIndex, 1);
      let list = newItems.sort(this._sortFn)
      return this._handleUpdate(T.ITEM_REMOVED, {items: list}, cb);
    }
    const _itemChanged = (snapshot, prevKey) => {
      const state = this._getState()
      const existingItem = toObject(snapshot, state);

      let list = state.items;
      let listIds = state.items.map(i => i.uid);
      const itemIdx = listIds.indexOf(existingItem.uid);
      list.splice(itemIdx, 1, existingItem);
      return this._handleUpdate(T.ITEM_CHANGED, {items: list}, cb);
    }

    return new Promise((resolve, reject) => {
      listenRef.on('child_added', _itemAdded);
      listenRef.on('child_removed', _itemRemoved);
      listenRef.on('child_changed', _itemChanged);

      this._handleUpdate(T.ACTION_LISTEN, null, (state) => {
        resolve(state)
      })
    })
  }

  unlisten() {
    const T = this._types;
    return new Promise((resolve, reject) => {
      this.firestack.off(this._ref);
      this._handleUpdate(T.ACTION_UNLISTEN, null, (state) => {
        resolve(state)
      })
    })
    return (dispatch, getState) => {
      const {firestack} = getState();
      firestack.off(this._ref);
      dispatch({type: this._types.ACTION_UNLISTEN})
    }
  }

  // TODO: Untested
  getAt(path, cb) {
    const T = this._types;
    const ref = this.makeRef(path);
    const toObject = this._toObject;

    return new Promise((resolve, reject) => {
      ref.once('value', snapshot => {
        this._handleUpdate(T.ACTION_GET, null, (state) => {
          if (cb) {
            cb(toObject(snapshot, state));
          }
          resolve(state)
        })
      }, reject);
    });
  }

  setAt(path, value, cb) {
    const T = this._types;
    const ref = this.makeRef(path);
    const toObject = this._toObject;

    return new Promise((resolve, reject) => {
      ref.set(value, error => {
        this._handleUpdate(T.ACTION_SET, null, (state) => {
          if (cb) {
            cb(toObject(snapshot, state));
          }
          return error ? reject(error) : resolve(value)
        });
      });
    });
  }

  updateAt(path, value, cb) {
    const T = this._types;
    const ref = this.makeRef(path);
    const toObject = this._toObject;

    return new Promise((resolve, reject) => {
      ref.update(value, error => {
        this._handleUpdate(T.ACTION_UPDATE, null, (state) => {
          if (cb) {
            cb(toObject(snapshot, state));
          }
          return error ? reject(error) : resolve(value)
        });
      });
    });
  }

  removeAt(path, cb) {
    const T = this._types;
    const ref = this.makeRef(path);
    const toObject = this._toObject;

    return new Promise((resolve, reject) => {
      ref.remove(value, error => {
        this._handleUpdate(T.ACTION_SET, null, (state) => {
          if (cb) {
            cb(toObject(snapshot, state));
          }
          return error ? reject(error) : resolve(value)
        });
      });
    });
  }

  // hackish, for now
  get actions() {
    return [
      'listen', 'unlisten',
      'get', 'set', 'update', 'remove'
    ].reduce((sum, name) => {
      return {
        ...sum,
        [name]: this[name].bind(this)
      }
    }, {})
  }

  get initialState() {
    return this._initialState;
  }

  get types() {
    return this._types
  }

  get reducer() {
    const T = this._types;
    return (state = this._localState, {type, payload, meta}) => {
      if (meta && meta.module && meta.module === this._refName) {
        switch (type) {
          case T.ACTION_LISTEN:
            return ({...state, listening: true});
          case T.ACTION_UNLISTEN:
            return ({...state, listening: false});
          default:
            return {...state, ...payload};
        }
      }
      return state;
    }
  }

  /**
   * Helpers
   **/

  _handleUpdate(type, newState = {}, cb = identity) {
    const store = this._getStore();
    if (store && store.dispatch && typeof store.dispatch === 'function') {
      store.dispatch({type, payload: newState, meta: { module: this._refName }})
    }
    return cb(newState);
  }

  _getStore() {
    return this._store ?
            this._store :
            (this._firestack ? this._firestack.store : null);
  }

  _getState() {
    const store = this._getStore();
    return store.getState()[this._refName];
  }

  _runCallback(dispatch, getState) {
    return (action) => {
      if (this._onChange && typeof this._onChange === 'function') {
        try {
          dispatch(action);
          return this._onChange(dispatch, getState)(action)
        } catch (e) {
          console.log('Error in callback', e);
        }
      }
      return newState;
    }
  }

}

export default FirestackModule
<commit_msg>fixed firestackModule binding<commit_after>import firebase from 'firebase'
import db from 'firebase/database'
import invariant from 'invariant'

const createTypes = (prefix) => {
  const c = (str) => `${prefix.toUpperCase()}_${str.toUpperCase()}`
  return {
    ACTION_LISTEN: c('listen'),
    ACTION_UNLISTEN: c('unlisten'),
    ACTION_REMOVE: c('remove'),
    ACTION_UPDATE: c('update'),
    ACTION_SET: c('set'),
    ACTION_GET: c('get'),
    ITEM_VALUE: c('value'),
    ITEM_ADDED: c('added'),
    ITEM_REMOVED: c('remove'),
    ITEM_CHANGED: c('changed'),
    UPDATED: c('updated')
  }
}

const defaultToObject = child => ({_key: child.key, ...child.val()})
const identity = (i) => i
const defaultSortFn = (a, b) => a.timestamp < b.timestamp
const defaultInitialState = {
  items: [],
}

export class FirestackModule {
  constructor(refName, opts={}) {
    invariant(refName && typeof refName !== 'undefined', 'No ref name passed');

    this._refName = refName;
    this._makeRef = opts.makeRef || identity;
    let firestack = this._firestack = opts.firestack;

    const initialState = Object.assign({}, opts.initialState || defaultInitialState, {
      listening: false,
      items: []
    })

    this._localState = initialState;

    this._types = createTypes(this._refName);
    this._toObject = opts.toObject || defaultToObject
    this._sortFn = opts.sortFn || defaultSortFn
    this._onChange = opts.onChange || identity;

    this.setStore(opts.store);
  }

  makeRef(path) {
    const refName = '/' + [this._refName, path].join('/');
    const ref = this._firestack.database.ref(refName);
    return this._makeRef(ref);
  }

  setFirestack(firestack) {
    if (firestack) {
      this._firestack = firestack;
    }
  }

  setStore(store) {
    if (store) {
      this._store = store;
    }
  }

  /*
   * Actions
   */
  listen(cb) {
    let store = this._getStore();
    invariant(store, 'Please set the store');

    const T = this._types;
    const listenRef = this.makeRef();
    const toObject = this._toObject;

    const _itemAdded = (snapshot, prevKey) => {
      const state = this._getState(); // local state
      const newItem = toObject(snapshot, state);
      let list = state.items || [];
      list.push(newItem)
      list = list.sort(this._sortFn)
      return this._handleUpdate(T.ITEM_ADDED, {items: list}, cb);
    }
    const _itemRemoved = (snapshot, prevKey) => {
      const state = this._getState(); // local state
      const itemKeys = state.items.map(i => i._key);
      const itemIndex = itemKeys.indexOf(snapshot.key);
      let newItems = [].concat(state.items);
      newItems.splice(itemIndex, 1);
      let list = newItems.sort(this._sortFn)
      return this._handleUpdate(T.ITEM_REMOVED, {items: list}, cb);
    }
    const _itemChanged = (snapshot, prevKey) => {
      const state = this._getState()
      const existingItem = toObject(snapshot, state);

      let list = state.items;
      let listIds = state.items.map(i => i.uid);
      const itemIdx = listIds.indexOf(existingItem.uid);
      list.splice(itemIdx, 1, existingItem);
      return this._handleUpdate(T.ITEM_CHANGED, {items: list}, cb);
    }

    return new Promise((resolve, reject) => {
      listenRef.on('child_added', _itemAdded);
      listenRef.on('child_removed', _itemRemoved);
      listenRef.on('child_changed', _itemChanged);

      this._handleUpdate(T.ACTION_LISTEN, null, (state) => {
        resolve(state)
      })
    })
  }

  unlisten() {
    const T = this._types;
    return new Promise((resolve, reject) => {
      this.firestack.off(this._ref);
      this._handleUpdate(T.ACTION_UNLISTEN, null, (state) => {
        resolve(state)
      })
    })
    return (dispatch, getState) => {
      const {firestack} = getState();
      firestack.off(this._ref);
      dispatch({type: this._types.ACTION_UNLISTEN})
    }
  }

  // TODO: Untested
  getAt(path, cb) {
    const T = this._types;
    const ref = this.makeRef(path);
    const toObject = this._toObject;

    return new Promise((resolve, reject) => {
      ref.once('value', snapshot => {
        this._handleUpdate(T.ACTION_GET, null, (state) => {
          if (cb) {
            cb(toObject(snapshot, state));
          }
          resolve(state)
        })
      }, reject);
    });
  }

  setAt(path, value, cb) {
    const T = this._types;
    const ref = this.makeRef(path);
    const toObject = this._toObject;

    return new Promise((resolve, reject) => {
      ref.set(value, error => {
        this._handleUpdate(T.ACTION_SET, null, (state) => {
          if (cb) {
            cb(toObject(snapshot, state));
          }
          return error ? reject(error) : resolve(value)
        });
      })
    });
  }

  updateAt(path, value, cb) {
    const T = this._types;
    const ref = this.makeRef(path);
    const toObject = this._toObject;

    return new Promise((resolve, reject) => {
      ref.update(value, error => {
        this._handleUpdate(T.ACTION_UPDATE, null, (state) => {
          if (cb) {
            cb(toObject(snapshot, state));
          }
          return error ? reject(error) : resolve(value)
        });
      });
    });
  }

  removeAt(path, cb) {
    const T = this._types;
    const ref = this.makeRef(path);
    const toObject = this._toObject;

    return new Promise((resolve, reject) => {
      ref.remove(value, error => {
        this._handleUpdate(T.ACTION_SET, null, (state) => {
          if (cb) {
            cb(toObject(snapshot, state));
          }
          return error ? reject(error) : resolve(value)
        });
      });
    });
  }

  // hackish, for now
  get actions() {
    return [
      'listen', 'unlisten',
      'getAt', 'setAt', 'updateAt', 'removeAt'
    ].reduce((sum, name) => {
      return {
        ...sum,
        [name]: this[name].bind(this)
      }
    }, {})
  }

  get initialState() {
    return this._initialState;
  }

  get types() {
    return this._types
  }

  get reducer() {
    const T = this._types;
    return (state = this._localState, {type, payload, meta}) => {
      if (meta && meta.module && meta.module === this._refName) {
        switch (type) {
          case T.ACTION_LISTEN:
            return ({...state, listening: true});
          case T.ACTION_UNLISTEN:
            return ({...state, listening: false});
          default:
            return {...state, ...payload};
        }
      }
      return state;
    }
  }

  /**
   * Helpers
   **/

  _handleUpdate(type, newState = {}, cb = identity) {
    const store = this._getStore();
    if (store && store.dispatch && typeof store.dispatch === 'function') {
      store.dispatch({type, payload: newState, meta: { module: this._refName }})
    }
    return cb(newState);
  }

  _getStore() {
    return this._store ?
            this._store :
            (this._firestack ? this._firestack.store : null);
  }

  _getState() {
    const store = this._getStore();
    return store.getState()[this._refName];
  }

  _runCallback(dispatch, getState) {
    return (action) => {
      if (this._onChange && typeof this._onChange === 'function') {
        try {
          dispatch(action);
          return this._onChange(dispatch, getState)(action)
        } catch (e) {
          console.log('Error in callback', e);
        }
      }
      return newState;
    }
  }

}

export default FirestackModule
<|endoftext|>


<commit_before>function stream() {
  // if we have cached issues, the stream will simply reuse them
  var str = new this._streamType(this._config, this._cache);

  // when the stream is done grab the cached issues
  str.on('end', function() {
    this._cache = stream.cached();
  }.bind(this));

  // TODO sort this out
  str.on('error', function( err ) {
    throw err;
  });

  return str;
}

// clear cached issues to force a refetch
function reset(){
  this._cache = undefined;
}

function extend( constr, streamType ) {
  constr.prototype._reset = reset;
  constr.prototype._stream = stream;
  constr.prototype._streamType = streamType;
}

module.exports.Streamed = { extend: extend };
<commit_msg>broken merge<commit_after>function stream() {
  // if we have cached issues, the stream will simply reuse them
  var stream = new this._streamType(this._config, this._cache);

  // when the stream is done grab the cached issues
  stream.on('end', function() {
    this._cache = stream.cached();
  }.bind(this));

  // TODO sort this out
  stream.on('error', function( err ) {
    throw err;
  });

  return stream;
}

// clear cached issues to force a refetch
function reset(){
  this._cache = undefined;
}

function extend( constr, streamType ) {
  constr.prototype._reset = reset;
  constr.prototype._stream = stream;
  constr.prototype._streamType = streamType;
}

module.exports.Streamed = { extend: extend };
<|endoftext|>


<commit_before>import path from 'path'
import webpack from 'webpack'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import config from '../config'
import _debug from 'debug'
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin'
import LodashModuleReplacementPlugin from 'lodash-webpack-plugin'

const debug = _debug('app:webpack:config')
const paths = config.utils_paths
const {__DEV__, __PROD__, __STANDALONE__, __TEST__, __PACKAGE__} = config.globals

const packageDir = `packages/${__PACKAGE__}`
const absolutePackagePath = paths.client(`${packageDir}/`)

const outputDir = absolutePackagePath + '/dist'

debug('Create configuration.')
const webpackConfig = {
  name: 'client',
  target: 'web',
  devtool: __PROD__ ? 'source-map' : 'eval',
  resolve: {
    modules: [
      path.resolve(paths.client(), packageDir, 'src'),
      'node_modules',
      path.resolve(paths.client(), 'node_modules')
    ],
    alias: {
      'ReactDOM': `${__dirname}/../node_modules/react-dom/index.js`,
      'React': `${__dirname}/../node_modules/react/react.js`
    },
    extensions: ['.js', '.jsx', '.json']
  },
  performance: {
    hints: __PROD__ ? 'warning' : false
  },
  module: {}
}

if (!__TEST__) {
  webpackConfig.externals = {
    'react': 'React',
    'React': 'React',
    'react-dom': 'ReactDOM',
    'ReactDOM': 'ReactDOM'
  }
}

// ------------------------------------
// Entry Points
// ------------------------------------
const APP_ENTRY_PATH = paths.client(`${packageDir}/src/main.js`)

webpackConfig.entry = {
  app: __DEV__
    ? [APP_ENTRY_PATH, `webpack-hot-middleware/client?path=/__webpack_hmr`]
    : [APP_ENTRY_PATH]
}

// ------------------------------------
// Bundle Output
// ------------------------------------
webpackConfig.output = {
  filename: 'index.js',
  path: outputDir,
  libraryTarget: 'umd',
  publicPath: ''
}

// ------------------------------------
// Plugins
// ------------------------------------
webpackConfig.plugins = [
  new webpack.DefinePlugin(config.globals)
]

if (__DEV__) {
  webpackConfig.plugins.push(
    new HtmlWebpackPlugin({
      template: paths.client('server/index.html'),
      hash: false,
      filename: 'index.html',
      inject: 'body',
      minify: {
        collapseWhitespace: true
      }
    })
  )
}

if (__STANDALONE__) {
  webpackConfig.plugins.push(
    new HtmlWebpackPlugin({
      template: paths.client('server/standalone.html'),
      hash: false,
      filename: 'index.html',
      inject: 'body',
      minify: {
        collapseWhitespace: true
      }
    })
  )
}

if (__DEV__) {
  debug('Enable plugin for case-sensitive path check')
  webpackConfig.plugins.push(new CaseSensitivePathsPlugin())

  debug('Enable plugins for live development (HMR, NoErrors).')
  webpackConfig.plugins.push(
    new webpack.HotModuleReplacementPlugin()
  )
} else if (__PROD__) {
  webpackConfig.plugins.push(
      new webpack.LoaderOptionsPlugin({
        minimize: true,
        debug: false
      }),
      new LodashModuleReplacementPlugin(),
      new webpack.optimize.OccurrenceOrderPlugin(),
      new webpack.optimize.UglifyJsPlugin({
        compress: {
          warnings: false,
          screw_ie8: true,
          conditionals: true,
          unused: true,
          comparisons: true,
          sequences: true,
          dead_code: true,
          evaluate: true,
          if_return: true,
          join_vars: true
        },
        output: {
          comments: false
        }
      })
    )
}

const testPlugins = []

// see: https://github.com/karma-runner/karma-sauce-launcher/issues/95
if (!process || !process.env || !process.env.DISABLE_ISTANBUL_COVERAGE) {
  debug('Enable instanbul test plugin.')
  testPlugins.push(['istanbul', {
    'exclude': [
      '**/dev/**',
      '**/*/*.spec.js',
      '**/tocco-ui/**/example.js',
      '**/tocco-ui/dist'
    ]
  }])
}

webpackConfig.module.rules = [
  {
    test: /\.(js|jsx)$/,
    exclude: /node_modules/,
    loader: 'babel-loader',
    options: {
      cacheDirectory: true,
      plugins: [
        ['transform-imports', {
          'tocco-ui': {
            'transform': 'tocco-ui/src/${member}', // eslint-disable-line no-template-curly-in-string
            'preventFullImport': true
          },
          'tocco-util': {
            'transform': 'tocco-util/src/${member}', // eslint-disable-line no-template-curly-in-string
            'preventFullImport': true
          },
          'tocco-test-util': {
            'transform': 'tocco-test-util/src/${member}', // eslint-disable-line no-template-curly-in-string
            'preventFullImport': true
          }
        }],
        'transform-runtime',
        'transform-flow-strip-types'
      ],
      presets: [
        ['es2015', { 'modules': false }],
        'react',
        'stage-0'
      ],
      env: {
        production: {
          plugins: [
            'transform-react-remove-prop-types',
            'transform-react-constant-elements'
          ]
        },
        development: {
          plugins: ['flow-react-proptypes'],
          presets: [
            'react-hmre'
          ]
        },
        test: {
          plugins: testPlugins
        }
      }
    }
  }
]

if (__DEV__) {
  // Run linting but only show errors as warning
  webpackConfig.module.rules.push(
    {
      test: /\.jsx?$/,
      enforce: 'pre',
      use: ['eslint-loader']
    }
  )

  webpackConfig.plugins.push(
    new webpack.LoaderOptionsPlugin({
      options: {
        eslint: {
          emitWarning: true
        }
      }
    })
  )
}

webpackConfig.module.rules.push(
  {
    test: /\.css$/,
    use: 'style-loader!css-loader'
  },
  {
    test: /\.scss$/,
    use: ['style-loader', 'css-loader', 'sass-loader']
  }
)

// File loaders
/* eslint-disable */
webpackConfig.module.rules.push(
  {
    test: /\.woff(\?.*)?$/,
    use: 'url-loader?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff'
  },
  {
    test: /\.woff2(\?.*)?$/,
    use: 'url-loader?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff2'
  },
  {
    test: /\.otf(\?.*)?$/,
    use: 'file-loader?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=font/opentype'
  },
  {
    test: /\.ttf(\?.*)?$/,
    use: 'url-loader?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/octet-stream'
  },
  {
    test: /\.eot(\?.*)?$/,
    use: 'file-loader?prefix=fonts/&name=[path][name].[ext]'
  },
  {
    test: /\.svg(\?.*)?$/,
    use: 'url-loader?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=image/svg+xml'
  },
  {
    test: /\.(png|jpg)$/,
    use: 'file-loader?limit=8192'
  }
)
/* eslint-enable */

export default webpackConfig
<commit_msg>chore: webpack config tunings<commit_after>import path from 'path'
import webpack from 'webpack'
import HtmlWebpackPlugin from 'html-webpack-plugin'
import config from '../config'
import _debug from 'debug'
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin'
import LodashModuleReplacementPlugin from 'lodash-webpack-plugin'

const debug = _debug('app:webpack:config')
const paths = config.utils_paths
const {__DEV__, __PROD__, __STANDALONE__, __TEST__, __PACKAGE__} = config.globals

const packageDir = `packages/${__PACKAGE__}`
const absolutePackagePath = paths.client(`${packageDir}/`)

const outputDir = absolutePackagePath + '/dist'

debug('Create configuration.')
const webpackConfig = {
  name: 'client',
  target: 'web',
  devtool: __PROD__ ? 'source-map' : 'eval',
  resolve: {
    modules: [
      path.resolve(paths.client(), packageDir, 'src'),
      'node_modules',
      path.resolve(paths.client(), 'node_modules')
    ],
    alias: {
      'ReactDOM': `${__dirname}/../node_modules/react-dom/index.js`,
      'React': `${__dirname}/../node_modules/react/react.js`
    },
    extensions: ['.js', '.jsx', '.json']
  },
  performance: {
    hints: __PROD__ ? 'warning' : false
  },
  module: {}
}

if (!__TEST__) {
  webpackConfig.externals = {
    'react': 'React',
    'React': 'React',
    'react-dom': 'ReactDOM',
    'ReactDOM': 'ReactDOM'
  }
}

// ------------------------------------
// Entry Points
// ------------------------------------
const APP_ENTRY_PATH = paths.client(`${packageDir}/src/main.js`)

webpackConfig.entry = {
  app: __DEV__
    ? [APP_ENTRY_PATH, `webpack-hot-middleware/client?path=/__webpack_hmr`]
    : [APP_ENTRY_PATH]
}

// ------------------------------------
// Bundle Output
// ------------------------------------
webpackConfig.output = {
  filename: 'index.js',
  path: outputDir,
  libraryTarget: 'umd',
  publicPath: ''
}

// ------------------------------------
// Plugins
// ------------------------------------
webpackConfig.plugins = [
  new webpack.DefinePlugin(config.globals)
]

if (__DEV__) {
  webpackConfig.plugins.push(
    new HtmlWebpackPlugin({
      template: paths.client('server/index.html'),
      hash: false,
      filename: 'index.html',
      inject: 'body',
      minify: {
        collapseWhitespace: true
      }
    })
  )
}

if (__STANDALONE__) {
  webpackConfig.plugins.push(
    new HtmlWebpackPlugin({
      template: paths.client('server/standalone.html'),
      hash: false,
      filename: 'index.html',
      inject: 'body',
      minify: {
        collapseWhitespace: true
      }
    })
  )
}

if (__DEV__) {
  debug('Enable plugin for case-sensitive path check')
  webpackConfig.plugins.push(new CaseSensitivePathsPlugin())

  debug('Enable plugins for live development (HMR, NoErrors).')
  webpackConfig.plugins.push(
    new webpack.HotModuleReplacementPlugin()
  )
} else if (__PROD__) {
  webpackConfig.plugins.push(
      new webpack.LoaderOptionsPlugin({
        minimize: true,
        debug: false
      }),
      new LodashModuleReplacementPlugin({
        'shorthands': true
      }),
      new webpack.optimize.OccurrenceOrderPlugin(),
      new webpack.optimize.UglifyJsPlugin({
        compress: {
          warnings: false,
          screw_ie8: true,
          conditionals: true,
          unused: true,
          comparisons: true,
          sequences: true,
          dead_code: true,
          evaluate: true,
          if_return: true,
          join_vars: true
        },
        output: {
          comments: false
        }
      })
    )
}

const testPlugins = []

// see: https://github.com/karma-runner/karma-sauce-launcher/issues/95
if (!process || !process.env || !process.env.DISABLE_ISTANBUL_COVERAGE) {
  debug('Enable instanbul test plugin.')
  testPlugins.push(['istanbul', {
    'exclude': [
      '**/dev/**',
      '**/*/*.spec.js',
      '**/tocco-ui/**/example.js',
      '**/tocco-ui/dist'
    ]
  }])
}

webpackConfig.module.rules = [
  {
    test: /\.(js|jsx)$/,
    exclude: /node_modules/,
    loader: 'babel-loader',
    options: {
      cacheDirectory: true,
      plugins: [
        ['transform-imports', {
          'tocco-ui': {
            'transform': 'tocco-ui/src/${member}', // eslint-disable-line no-template-curly-in-string
            'preventFullImport': true
          },
          'tocco-util': {
            'transform': 'tocco-util/src/${member}', // eslint-disable-line no-template-curly-in-string
            'preventFullImport': true
          },
          'tocco-test-util': {
            'transform': 'tocco-util/src/${member}', // eslint-disable-line no-template-curly-in-string
            'preventFullImport': true
          }
        }],
        'transform-runtime',
        'transform-flow-strip-types'
      ],
      presets: [
        ['es2015', { 'modules': false }],
        'react',
        'stage-0'
      ],
      env: {
        production: {
          plugins: [
            'lodash',
            'transform-react-remove-prop-types',
            'transform-react-constant-elements'
          ]
        },
        development: {
          plugins: ['flow-react-proptypes'],
          presets: [
            'react-hmre'
          ]
        },
        test: {
          plugins: testPlugins
        }
      }
    }
  }
]

if (__DEV__) {
  // Run linting but only show errors as warning
  webpackConfig.module.rules.push(
    {
      test: /\.jsx?$/,
      enforce: 'pre',
      use: ['eslint-loader']
    }
  )

  webpackConfig.plugins.push(
    new webpack.LoaderOptionsPlugin({
      options: {
        eslint: {
          emitWarning: true
        }
      }
    })
  )
}

webpackConfig.module.rules.push(
  {
    test: /\.css$/,
    use: 'style-loader!css-loader'
  },
  {
    test: /\.scss$/,
    use: ['style-loader', 'css-loader', 'sass-loader']
  }
)

// File loaders
/* eslint-disable */
webpackConfig.module.rules.push(
  {
    test: /\.woff(\?.*)?$/,
    use: 'url-loader?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff'
  },
  {
    test: /\.woff2(\?.*)?$/,
    use: 'url-loader?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/font-woff2'
  },
  {
    test: /\.otf(\?.*)?$/,
    use: 'file-loader?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=font/opentype'
  },
  {
    test: /\.ttf(\?.*)?$/,
    use: 'url-loader?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=application/octet-stream'
  },
  {
    test: /\.eot(\?.*)?$/,
    use: 'file-loader?prefix=fonts/&name=[path][name].[ext]'
  },
  {
    test: /\.svg(\?.*)?$/,
    use: 'url-loader?prefix=fonts/&name=[path][name].[ext]&limit=10000&mimetype=image/svg+xml'
  },
  {
    test: /\.(png|jpg)$/,
    use: 'file-loader?limit=8192'
  }
)
/* eslint-enable */

export default webpackConfig
<|endoftext|>


<commit_before>// @flow
import Container from '../forms/container'
import React, {Component} from 'react'
import type {Props} from './username-email-form.render'
import {UserCard, Input, Button} from '../../common-adapters'
import {globalColors, globalMargins} from '../../styles'

class Render extends Component<void, Props, void> {
  render () {
    return (
      <Container onBack={this.props.onBack} outerStyle={stylesOuter}>
        <UserCard>
          <Input autoFocus={true} hintText='Create a username' floatingLabelText='Create a username' value={this.props.username} errorText={this.props.usernameErrorText} onChangeText={username => this.props.usernameChange(username)} />
          <Input style={stylesInput2} hintText='Email address' floatingLabelText='Email address' value={this.props.email} errorText={this.props.emailErrorText} onEnterKeyDown={this.props.onSubmit} onChangeText={email => this.props.emailChange(email)} keyboardType='email-address' />
          <Button waiting={this.props.waiting} style={{marginTop: 40}} fullWidth={true} type='Primary' label='Continue' onClick={this.props.onSubmit} />
        </UserCard>
      </Container>
    )
  }
}

const stylesOuter = {
  backgroundColor: globalColors.black_10,
}

const stylesInput2 = {
  marginTop: globalMargins.medium,
}

export default Render
<commit_msg>Get continue button onscreen on Android Nexus 5x<commit_after>// @flow
import Container from '../forms/container'
import React, {Component} from 'react'
import type {Props} from './username-email-form.render'
import {UserCard, Input, Button} from '../../common-adapters'
import {globalColors, globalMargins} from '../../styles'

class Render extends Component<void, Props, void> {
  render () {
    return (
      <Container onBack={this.props.onBack} outerStyle={stylesOuter}>
        <UserCard>
          <Input autoFocus={true} hintText='Create a username' floatingLabelText='Create a username' value={this.props.username} errorText={this.props.usernameErrorText} onChangeText={username => this.props.usernameChange(username)} />
          <Input style={stylesInput2} hintText='Email address' floatingLabelText='Email address' value={this.props.email} errorText={this.props.emailErrorText} onEnterKeyDown={this.props.onSubmit} onChangeText={email => this.props.emailChange(email)} keyboardType='email-address' />
          <Button waiting={this.props.waiting} style={stylesButton} fullWidth={true} type='Primary' label='Continue' onClick={this.props.onSubmit} />
        </UserCard>
      </Container>
    )
  }
}

const stylesOuter = {
  backgroundColor: globalColors.black_10,
}

const stylesInput2 = {
  marginTop: globalMargins.tiny,
}

const stylesButton = {
  marginTop: globalMargins.small,
}

export default Render
<|endoftext|>


<commit_before>import React from 'react'
import { createDevTools } from 'redux-devtools'
import LogMonitor from 'redux-devtools-log-monitor'

export default createDevTools(
  <LogMonitor />
)
<commit_msg>removed file<commit_after><|endoftext|>


<commit_before>import React, { Component } from 'react';
import { View, Image, StatusBar, StyleSheet, AsyncStorage, Text } from 'react-native';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import RNAccountKit from 'react-native-facebook-account-kit';
import { Button } from 'react-native-elements';
import i18n from 'react-native-i18n';
import Hr from 'react-native-hr';

import { replaceRoute } from '../boundActionCreators/navigation';
import { createNewPouchDB } from '../boundActionCreators/pouchdb';
import { getLinkCode } from '../boundActionCreators/ebudgie';
import { initialLoad } from '../actionCreators/ebudgie';
import theme from '../themes/ApplicationStyles';
import colors from '../themes/Colors';
import metrics from '../themes/Metrics';
import categories from '../constants/InitialCategories';
import items from '../constants/InitialItems';
import { showSpinner, hideSpinner } from '../actionCreators/spinner';

const styles = StyleSheet.create({
  container: {
    backgroundColor: colors.snow,
    paddingTop: StatusBar.currentHeight,
  },
  header: {
    flex: 3,
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    position: 'relative',
  },
  icon: {
    resizeMode: 'contain',
    width: 200,
    height: 200,
  },
  title: {
    textAlign: 'center',
    color: colors.main,
    fontSize: 40,
  },
  background: {
    position: 'absolute',
    left: 0,
    right: 0,
    bottom: 0,
    zIndex: -1,
  },
  backgroundImage: {
    alignSelf: 'center',
    resizeMode: 'cover',
  },
  buttonContainer: {
    flex: 2,
    justifyContent: 'space-around',
    backgroundColor: colors.main,
  },
  buttonStyle: {
    marginVertical: 20,
    marginLeft: 50,
    marginRight: 50,
    padding: 10,
  },
});

const buttonProps = {
  borderRadius: 100,
  buttonStyle: styles.buttonStyle,
  fontWeight: 'bold',
  large: true,
  iconRight: true,
  raised: true,
  elevation: metrics.elevation,
};

const emailButton = {
  backgroundColor: colors.positive,
  color: colors.snow,
};

const phoneButton = {
  backgroundColor: colors.positive,
  color: colors.snow,
};

const withoutAccountButton = {
  color: colors.snow,
};

class LoginContainer extends Component {
  loginWithPhone = async () => {
    try {
      const token = await RNAccountKit.loginWithPhone();
      if (token) {
        const account = await RNAccountKit.getCurrentAccount();
        await this.goToHome(token.accountId, account.email, `+${account.phoneNumber.countryCode}${account.phoneNumber.number}`);
      }
    } catch (e) { }
  }

  loginWithEmail = async () => {
    try {
      const token = await RNAccountKit.loginWithEmail();
      if (token) {
        const account = await RNAccountKit.getCurrentAccount();
        await this.goToHome(token.accountId, account.email, account.phoneNumber);
      }
    } catch (e) { }
  }

  goToHome = async (dbName = 'unauthorized', email, phone) => {
    const {
      createPouchDB,
      replace,
      load,
      createLinkCode,
      hideLoader,
      showLoader
     } = this.props;

    try {
      const ebudgie = await createPouchDB(dbName);

      showLoader('LOADING');
      if (!ebudgie || !ebudgie.didInitialLoad) {
        load(categories, items);
      }

      if (!ebudgie || !ebudgie.link_code) {
        await createLinkCode(dbName, email, phone);
      }

      await AsyncStorage.setItem('isLogged', 'true');
      replace({ key: 'home' });
      hideLoader();
    } catch (e) {
      console.log(e);
      hideLoader();
      alert('Something went wrong'); // eslint-disable-line
    }
  }

  skip = async () => {
    await this.goToHome();
  }

  render() {
    return (
      <View style={[theme.container, styles.container]}>
        <View style={styles.header}>
          <Image
            source={require('../images/budgie-icon.png')}
            style={styles.icon} />
          <Text style={styles.title}>EBudgie</Text>
          <View style={styles.background}>
            <Image
              source={require('../images/stripes.png')}
              style={styles.backgroundImage} />
          </View>
        </View>
        <View style={styles.buttonContainer}>
          <Button
            {...buttonProps}
            {...emailButton}
            icon={{ name: 'email' }}
            onPress={this.loginWithEmail}
            title={i18n.t('LOGIN_WITH_EMAIL')} />
          <Button
            {...buttonProps}
            {...phoneButton}
            icon={{ name: 'phone' }}
            onPress={this.loginWithPhone}
            title={i18n.t('LOGIN_WITH_PHONE')} />
          <View style={{
            marginHorizontal: 20,
          }}>
            <Hr
              lineColor={colors.snow}
              text={i18n.t('OR')}
              textColor={colors.snow} />
          </View>
          <Button
            {...buttonProps}
            {...withoutAccountButton}
            icon={{ name: 'account-circle' }}
            onPress={this.skip}
            title={i18n.t('LOGIN_WITHOUT_ACCOUNT')} />
        </View>
      </View>
    );
  }
}

LoginContainer.propTypes = {
  replace: React.PropTypes.func.isRequired,
  createPouchDB: React.PropTypes.func.isRequired,
  load: React.PropTypes.func.isRequired,
  showLoader: React.PropTypes.func.isRequired,
  hideLoader: React.PropTypes.func.isRequired,
};

function mapStateToProps(state) {
  return {

  };
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators({
    replace: replaceRoute,
    createPouchDB: createNewPouchDB,
    load: initialLoad,
    createLinkCode: getLinkCode,
    showLoader: showSpinner,
    hideLoader: hideSpinner
  }, dispatch);
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(LoginContainer);
<commit_msg>Don't make request for creating link code if a test account is used<commit_after>import React, { Component } from 'react';
import { View, Image, StatusBar, StyleSheet, AsyncStorage, Text } from 'react-native';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import RNAccountKit from 'react-native-facebook-account-kit';
import { Button } from 'react-native-elements';
import i18n from 'react-native-i18n';
import Hr from 'react-native-hr';

import { replaceRoute } from '../boundActionCreators/navigation';
import { createNewPouchDB } from '../boundActionCreators/pouchdb';
import { getLinkCode } from '../boundActionCreators/ebudgie';
import { initialLoad } from '../actionCreators/ebudgie';
import theme from '../themes/ApplicationStyles';
import colors from '../themes/Colors';
import metrics from '../themes/Metrics';
import categories from '../constants/InitialCategories';
import items from '../constants/InitialItems';
import { showSpinner, hideSpinner } from '../actionCreators/spinner';

const styles = StyleSheet.create({
  container: {
    backgroundColor: colors.snow,
    paddingTop: StatusBar.currentHeight,
  },
  header: {
    flex: 3,
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    position: 'relative',
  },
  icon: {
    resizeMode: 'contain',
    width: 200,
    height: 200,
  },
  title: {
    textAlign: 'center',
    color: colors.main,
    fontSize: 40,
  },
  background: {
    position: 'absolute',
    left: 0,
    right: 0,
    bottom: 0,
    zIndex: -1,
  },
  backgroundImage: {
    alignSelf: 'center',
    resizeMode: 'cover',
  },
  buttonContainer: {
    flex: 2,
    justifyContent: 'space-around',
    backgroundColor: colors.main,
  },
  buttonStyle: {
    marginVertical: 20,
    marginLeft: 50,
    marginRight: 50,
    padding: 10,
  },
});

const buttonProps = {
  borderRadius: 100,
  buttonStyle: styles.buttonStyle,
  fontWeight: 'bold',
  large: true,
  iconRight: true,
  raised: true,
  elevation: metrics.elevation,
};

const emailButton = {
  backgroundColor: colors.positive,
  color: colors.snow,
};

const phoneButton = {
  backgroundColor: colors.positive,
  color: colors.snow,
};

const withoutAccountButton = {
  color: colors.snow,
};

class LoginContainer extends Component {
  loginWithPhone = async () => {
    try {
      const token = await RNAccountKit.loginWithPhone();
      if (token) {
        const account = await RNAccountKit.getCurrentAccount();
        await this.goToHome(token.accountId, account.email, `+${account.phoneNumber.countryCode}${account.phoneNumber.number}`);
      }
    } catch (e) { }
  }

  loginWithEmail = async () => {
    try {
      const token = await RNAccountKit.loginWithEmail();
      if (token) {
        const account = await RNAccountKit.getCurrentAccount();
        await this.goToHome(token.accountId, account.email, account.phoneNumber);
      }
    } catch (e) { }
  }

  goToHome = async (dbName = 'unauthorized', email, phone) => {
    const {
      createPouchDB,
      replace,
      load,
      createLinkCode,
      hideLoader,
      showLoader
     } = this.props;

    try {
      const ebudgie = await createPouchDB(dbName);

      showLoader('LOADING');
      if (!ebudgie || !ebudgie.didInitialLoad) {
        load(categories, items);
      }

      if (dbName !== 'unauthorized') {
        if (!ebudgie || !ebudgie.link_code) {
          await createLinkCode(dbName, email, phone);
        }
      }

      await AsyncStorage.setItem('isLogged', 'true');
      replace({ key: 'home' });
      hideLoader();
    } catch (e) {
      hideLoader();
      alert('Something went wrong'); // eslint-disable-line
    }
  }

  skip = async () => {
    await this.goToHome();
  }

  render() {
    return (
      <View style={[theme.container, styles.container]}>
        <View style={styles.header}>
          <Image
            source={require('../images/budgie-icon.png')}
            style={styles.icon} />
          <Text style={styles.title}>EBudgie</Text>
          <View style={styles.background}>
            <Image
              source={require('../images/stripes.png')}
              style={styles.backgroundImage} />
          </View>
        </View>
        <View style={styles.buttonContainer}>
          <Button
            {...buttonProps}
            {...emailButton}
            icon={{ name: 'email' }}
            onPress={this.loginWithEmail}
            title={i18n.t('LOGIN_WITH_EMAIL')} />
          <Button
            {...buttonProps}
            {...phoneButton}
            icon={{ name: 'phone' }}
            onPress={this.loginWithPhone}
            title={i18n.t('LOGIN_WITH_PHONE')} />
          <View style={{
            marginHorizontal: 20,
          }}>
            <Hr
              lineColor={colors.snow}
              text={i18n.t('OR')}
              textColor={colors.snow} />
          </View>
          <Button
            {...buttonProps}
            {...withoutAccountButton}
            icon={{ name: 'account-circle' }}
            onPress={this.skip}
            title={i18n.t('LOGIN_WITHOUT_ACCOUNT')} />
        </View>
      </View>
    );
  }
}

LoginContainer.propTypes = {
  replace: React.PropTypes.func.isRequired,
  createPouchDB: React.PropTypes.func.isRequired,
  load: React.PropTypes.func.isRequired,
  showLoader: React.PropTypes.func.isRequired,
  hideLoader: React.PropTypes.func.isRequired,
};

function mapStateToProps(state) {
  return {

  };
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators({
    replace: replaceRoute,
    createPouchDB: createNewPouchDB,
    load: initialLoad,
    createLinkCode: getLinkCode,
    showLoader: showSpinner,
    hideLoader: hideSpinner
  }, dispatch);
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(LoginContainer);
<|endoftext|>


<commit_before>import {debounce} from 'lodash';
import React, {PropTypes} from 'react';

const DEFAULT_DELAY_MS = 200;

/**
 * HoC that encapsulates common behavior and functionality for doing
 * asynchronous searches, including:
 *
 *  - Debouncing user input
 *  - Query caching (optional)
 *  - Search prompt and empty results behaviors
 */
const AsyncContainer = Typeahead => {

  let _cache = {};

  return React.createClass({
    propTypes: {
      /**
       * Delay, in milliseconds, before performing search.
       */
      delay: PropTypes.number,
      /**
       * Callback to perform when the search is executed.
       */
      onSearch: PropTypes.func.isRequired,
      /**
       * Options to be passed to the typeahead. Will typically be the query
       * results, but can also be initial default options.
       */
      options: PropTypes.array,
      /**
       * Text to display in the menu while the request is pending.
       */
      searchText: PropTypes.string,
      /**
       * Whether or not the component should cache query results.
       */
      useCache: PropTypes.bool,
    },

    getDefaultProps() {
      return {
        delay: DEFAULT_DELAY_MS,
        minLength: 2,
        options: [],
        searchText: 'Searching...',
        useCache: true,
      };
    },

    getInitialState() {
      return {
        hasSelection: false,
        query: '',
        requestPending: false,
      };
    },

    componentWillMount() {
      this._handleSearchDebounced = debounce(
        this._handleSearch,
        this.props.delay
      );
    },

    componentWillReceiveProps(nextProps) {
      const {options, useCache} = nextProps;
      const {query, requestPending} = this.state;

      if (!requestPending) {
        return;
      }

      if (useCache) {
        _cache[query] = options;
      }

      this.setState({requestPending: false});
    },

    componentWillUnmount() {
      _cache = {};
    },

    render() {
      const {useCache, ...props} = this.props;
      const cachedQuery = _cache[this.state.query];

      return (
        <Typeahead
          {...props}
          emptyLabel={this._getEmptyLabel()}
          isLoading={this.state.requestPending}
          onChange={this._handleChange}
          onInputChange={this._handleInputChange}
          options={useCache && cachedQuery ? cachedQuery : this.props.options}
          ref={instance => this._instance = instance}
        />
      );
    },

    /**
     * Make the component instance available.
     */
    getInstance() {
      return this._instance.getInstance();
    },

    _getEmptyLabel() {
      const {emptyLabel, searchText, useCache} = this.props;
      const {query, requestPending} = this.state;

      if (!query.length) {
        return 'Type to search...';
      }

      if (requestPending || (useCache && !_cache[query])) {
        return searchText;
      }

      return emptyLabel;
    },

    _handleChange(selected) {
      this.props.onChange && this.props.onChange(selected);
      this.setState({hasSelection: !!selected.length});
    },

    _handleInputChange(query) {
      this.props.onInputChange && this.props.onInputChange(query);
      this._handleSearchDebounced(query);
    },

    _handleSearch(initialQuery) {
      const {caseSensitive, minLength, onSearch, useCache} = this.props;

      let query = initialQuery.trim();
      if (!caseSensitive) {
        query = query.toLowerCase();
      }

      this.setState({query});

      if (!query || (minLength && query.length < minLength)) {
        return;
      }

      // Use cached results, if available.
      if (useCache && _cache[query]) {
        return;
      }

      // Only perform a search on user input, not selection.
      if (this.state.hasSelection) {
        return;
      }

      // Perform the async search.
      this.setState({requestPending: true}, () => onSearch(query));
    },
  });
};

export default AsyncContainer;
<commit_msg>Update emptyLabel display logic in `asyncContainer`<commit_after>import {debounce} from 'lodash';
import React, {PropTypes} from 'react';

const DEFAULT_DELAY_MS = 200;

/**
 * HoC that encapsulates common behavior and functionality for doing
 * asynchronous searches, including:
 *
 *  - Debouncing user input
 *  - Query caching (optional)
 *  - Search prompt and empty results behaviors
 */
const asyncContainer = Typeahead => {

  let _cache = {};

  return React.createClass({
    propTypes: {
      /**
       * Delay, in milliseconds, before performing search.
       */
      delay: PropTypes.number,
      /**
       * Callback to perform when the search is executed.
       */
      onSearch: PropTypes.func.isRequired,
      /**
       * Options to be passed to the typeahead. Will typically be the query
       * results, but can also be initial default options.
       */
      options: PropTypes.array,
      /**
       * Text to display in the menu while the request is pending.
       */
      searchText: PropTypes.string,
      /**
       * Whether or not the component should cache query results.
       */
      useCache: PropTypes.bool,
    },

    getDefaultProps() {
      return {
        delay: DEFAULT_DELAY_MS,
        minLength: 2,
        options: [],
        promptText: 'Type to search...',
        searchText: 'Searching...',
        useCache: true,
      };
    },

    getInitialState() {
      return {
        hasSelection: false,
        query: '',
        requestPending: false,
      };
    },

    componentWillMount() {
      this._handleSearchDebounced = debounce(
        this._handleSearch,
        this.props.delay
      );
    },

    componentWillReceiveProps(nextProps) {
      const {options, useCache} = nextProps;
      const {query, requestPending} = this.state;

      if (!requestPending) {
        return;
      }

      if (useCache) {
        _cache[query] = options;
      }

      this.setState({requestPending: false});
    },

    componentWillUnmount() {
      _cache = {};
    },

    render() {
      const {useCache, ...props} = this.props;
      const cachedQuery = _cache[this.state.query];

      return (
        <Typeahead
          {...props}
          emptyLabel={this._getEmptyLabel()}
          isLoading={this.state.requestPending}
          onChange={this._handleChange}
          onInputChange={this._handleInputChange}
          options={useCache && cachedQuery ? cachedQuery : this.props.options}
          ref={instance => this._instance = instance}
        />
      );
    },

    /**
     * Make the component instance available.
     */
    getInstance() {
      return this._instance.getInstance();
    },

    _getEmptyLabel() {
      const {emptyLabel, promptText, searchText, useCache} = this.props;
      const {hasSelection, query, requestPending} = this.state;

      if (!query.length || hasSelection) {
        return promptText;
      }

      if (requestPending || (useCache && !_cache[query])) {
        return searchText;
      }

      return emptyLabel;
    },

    _handleChange(selected) {
      this.props.onChange && this.props.onChange(selected);
      this.setState({hasSelection: !!selected.length});
    },

    _handleInputChange(query) {
      this.props.onInputChange && this.props.onInputChange(query);
      this._handleSearchDebounced(query);
    },

    _handleSearch(initialQuery) {
      const {caseSensitive, minLength, onSearch, useCache} = this.props;

      let query = initialQuery.trim();
      if (!caseSensitive) {
        query = query.toLowerCase();
      }

      this.setState({query});

      if (!query || (minLength && query.length < minLength)) {
        return;
      }

      // Use cached results, if available.
      if (useCache && _cache[query]) {
        return;
      }

      // Only perform a search on user input, not selection.
      if (this.state.hasSelection) {
        return;
      }

      // Perform the async search.
      this.setState({requestPending: true}, () => onSearch(query));
    },
  });
};

export default asyncContainer;
<|endoftext|>


<commit_before>import express from 'express';
import r from 'rethinkdb'
import Promise from 'bluebird'
import API from '../../lib/API'
import Character from '../../models/character'
import rp from 'request-promise'

const router = express.Router()

API.registerCall(
  '/api/character/:characterName',
  'Gets the ranking information of a character',
  API.createParameter(':characterName', 'string', 'The name of the player to look up'),
  {
    'name':'SomePerson123',
    'job':'Magician',
    'ranking':1,
    'world':'Khaini',
    'level':255,
    'exp':4339186,
    'rankMovement':1,
    'rankDirection':'up',
    'avatar':'/api/character/SomePerson123/avatar',
    'got':'2016-10-05T04:48:21.537Z',
    'avatarData':'data:image/png;base64,'
  }
)

router.get('/:characterName', async (req, res, next) => {
  try{
    const ranking = 'overall'
    const characterName = req.params.characterName
    const character = await Character.GetCharacter(characterName, ranking)
    if (!character) return res.status(400).send({ error: 'Could not find character' })

    res.send(character)
  }catch(ex){
    res.status(500).send({error: ex.message || ex, trace: ex.trace || null})
    console.log(ex, ex.stack)
  }
})

API.registerCall(
  '/api/character/:characterName/fame',
  'Gets the fame ranking information of a character',
  API.createParameter(':characterName', 'string', 'The name of the player to look up'),
  {
    'name':'SomePerson123',
    'job':'Magician',
    'ranking':1,
    'world':'Khaini',
    'level':2377,
    'rankMovement':1,
    'rankDirection':'up',
    'avatar':'/api/character/SomePerson123/avatar',
    'got':'2016-10-05T04:48:21.537Z',
    'avatarData':'data:image/png;base64,'
  }
)

router.get('/:characterName/fame', async (req, res, next) => {
  try{
    const ranking = 'fame'
    const characterName = req.params.characterName
    const character = await Character.GetCharacter(characterName, ranking)

    res.send(character)
  }catch(ex){
    res.status(500).send({error: ex.message || ex, trace: ex.trace || null})
    console.log(ex, ex.stack)
  }
})

API.registerCall(
  '/api/character/:characterName/avatar',
  'Gets the avatar of a character',
  API.createParameter(':characterName', 'string', 'The name of the player to look up'),
  'Image/PNG'
)

router.get('/:characterName/avatar', async (req, res, next) => {
  const ranking = 'overall'
  const characterName = req.params.characterName
  const character = await Character.GetCharacter(characterName, ranking, true)

  const base64Location = character.avatarData.indexOf('base64,')
  const type = character.avatarData.substr(5, base64Location - 5)
  const avatarData = character.avatarData.substr(base64Location + 7)
  const characterAvatar = new Buffer(avatarData, 'base64')
  res.set('Content-Type', type)
  res.send(characterAvatar)
})

export default router<commit_msg>Cache all character API calls by 12 hours<commit_after>import express from 'express';
import r from 'rethinkdb'
import Promise from 'bluebird'
import API from '../../lib/API'
import Character from '../../models/character'
import rp from 'request-promise'
import apicache from 'apicache'
import redis from 'redis'
import { ENV, PORT, DATADOG_API_KEY, DATADOG_APP_KEY, REDIS_HOST, REDIS_PORT } from '../../environment'

const router = express.Router()

let redisClient
if (REDIS_HOST && REDIS_PORT) {
  redisClient = redis.createClient({
    host: REDIS_HOST,
    port: REDIS_PORT
  })
  console.warn('Redis caching enabled')
} else {
  console.warn('Redis not enabled')
}

const caching = apicache.options({
    debug: ENV.NODE_ENV == 'development',
    defaultDuration: 43200000,
    enabled: true,
    redisClient
  }).middleware

//Try to cache the results for at least 12 hours as CPU is also costly
router.use(caching())

API.registerCall(
  '/api/character/:characterName',
  'Gets the ranking information of a character',
  API.createParameter(':characterName', 'string', 'The name of the player to look up'),
  {
    'name':'SomePerson123',
    'job':'Magician',
    'ranking':1,
    'world':'Khaini',
    'level':255,
    'exp':4339186,
    'rankMovement':1,
    'rankDirection':'up',
    'avatar':'/api/character/SomePerson123/avatar',
    'got':'2016-10-05T04:48:21.537Z',
    'avatarData':'data:image/png;base64,'
  }
)

router.get('/:characterName', async (req, res, next) => {
  try{
    const ranking = 'overall'
    const characterName = req.params.characterName
    const character = await Character.GetCharacter(characterName, ranking)
    if (!character) return res.status(400).send({ error: 'Could not find character' })

    res.send(character)
  }catch(ex){
    res.status(500).send({error: ex.message || ex, trace: ex.trace || null})
    console.log(ex, ex.stack)
  }
})

API.registerCall(
  '/api/character/:characterName/fame',
  'Gets the fame ranking information of a character',
  API.createParameter(':characterName', 'string', 'The name of the player to look up'),
  {
    'name':'SomePerson123',
    'job':'Magician',
    'ranking':1,
    'world':'Khaini',
    'level':2377,
    'rankMovement':1,
    'rankDirection':'up',
    'avatar':'/api/character/SomePerson123/avatar',
    'got':'2016-10-05T04:48:21.537Z',
    'avatarData':'data:image/png;base64,'
  }
)

router.get('/:characterName/fame', async (req, res, next) => {
  try{
    const ranking = 'fame'
    const characterName = req.params.characterName
    const character = await Character.GetCharacter(characterName, ranking)

    res.send(character)
  }catch(ex){
    res.status(500).send({error: ex.message || ex, trace: ex.trace || null})
    console.log(ex, ex.stack)
  }
})

API.registerCall(
  '/api/character/:characterName/avatar',
  'Gets the avatar of a character',
  API.createParameter(':characterName', 'string', 'The name of the player to look up'),
  'Image/PNG'
)

router.get('/:characterName/avatar', async (req, res, next) => {
  const ranking = 'overall'
  const characterName = req.params.characterName
  const character = await Character.GetCharacter(characterName, ranking, true)

  const base64Location = character.avatarData.indexOf('base64,')
  const type = character.avatarData.substr(5, base64Location - 5)
  const avatarData = character.avatarData.substr(base64Location + 7)
  const characterAvatar = new Buffer(avatarData, 'base64')
  res.set('Content-Type', type)
  res.send(characterAvatar)
})

export default router<|endoftext|>


<commit_before>/**
 * Internal dependencies.
 */

var util = require('./util');

/**
 * Public interface for proxy methods.
 *
 * @param {Hydro} hydro
 * @constructor
 */

function Interface(hydro) {
  this.hydro = hydro;
}

/**
 * Delegate proxies methods to hydro.
 */

util.forEach(['addTest', 'addSuite', 'createTest', 'createSuite'], function(method) {
  Interface.prototype[method] = function() {
    return this.hydro[method].apply(this.hydro, arguments);
  };
});

/**
 * Primary export.
 */

module.exports = Interface;
<commit_msg>Coding style<commit_after>/**
 * Internal dependencies.
 */

var util = require('./util');

/**
 * Public interface for proxy methods.
 *
 * @param {Hydro} hydro
 * @constructor
 */

function Interface(hydro) {
  this.hydro = hydro;
}

/**
 * Delegate proxies methods to hydro.
 */

util.forEach(['addTest', 'addSuite', 'createTest', 'createSuite'], function(method) {
  Interface.prototype[method] = function() {
    return this.hydro[method].apply(this.hydro, arguments);
  };
});

/**
 * Primary export.
 */

module.exports = Interface;
<|endoftext|>


<commit_before>var mongodb = require('mongodb');
var MongoClient = mongodb.MongoClient;
var async = require('async');

function populateColumnNames(req) {
  var extractedColumnNames = [];

  for (index in req.query.columns) {
    extractedColumnNames.push(req.query.columns[index].name);
  }

  return extractedColumnNames;
}

function initializeMongoDataTables(connectionUri, collectionName) {
  function handleRequest(req, res, next) {
    var response = {
      draw: parseInt(req.query.draw, 10),
      recordsTotal: 0,
      recordsFiltered: 0,
      data: [],
      error: null
    };

    function initializeConnection(callback) {
      MongoClient.connect(connectionUri, callback);
    }

    function queryData(db, callback) {
      var extractedColumnNames = populateColumnNames(req);

      if (extractedColumnNames.length < 1) {
        callback(new Error('Columns data are not provided'), null);
        return;
      }

      var collection = db.collection(collectionName);
      var fields = {};
      var searchCriteria = { $or: [] };
      var fieldValuePair = {};
      var columnName;
      var searchValue = req.query.search.value.replace(/[\W\s]/g, '\\$&');

      for (index in extractedColumnNames) {
        columnName = extractedColumnNames[index];
        fields[columnName] = 1;
        fieldValuePair = {};

        if (!!searchValue) {
          fieldValuePair[columnName] = new RegExp(searchValue);
          searchCriteria['$or'].push(fieldValuePair);
        }
      }

      if (searchCriteria['$or'].length < 1) {
        searchCriteria = {};
      }

      collection = collection.find(searchCriteria, fields);
      callback(null, collection);
    }

    function sortData(cursor, callback) {
      var extractedColumnNames = populateColumnNames(req);
      var orders = req.query.order;
      var sortObject = {}
      var columnIndex;
      var orderedColumn;
      var orderDirection;

      for (index in orders) {
        columnIndex = parseInt(orders[index].column, 10);
        orderedColumn = extractedColumnNames[columnIndex];
        orderDirection = (orders[index].dir === 'asc')
          ? 1
          : -1;
        sortObject[orderedColumn] = orderDirection;
        cursor = cursor.sort(sortObject)
      }

      cursor.toArray(callback);
    }

    function paginateData(docs, callback) {
      if (docs.length < 1) {
        callback(null, []);
        return;
      }

      var start = parseInt(req.query.start, 10);
      var length = parseInt(req.query.length, 10);
      var newDocs = docs.slice(start, length + start);
      response.recordsTotal = docs.length;
      response.recordsFiltered = response.recordsTotal;

      callback(null, newDocs);
    }

    function sendData(error, result) {
      if (error) {
        console.error(error);
        response.error = error.message;
      }

      response.data = result;
      res.json(response);
    }

    async.waterfall(
      [initializeConnection, queryData, sortData, paginateData],
      sendData
    );
  }

  return handleRequest;
}

module.exports = initializeMongoDataTables;<commit_msg>populateColumnNames is renamed to extractColumnNames<commit_after>var mongodb = require('mongodb');
var MongoClient = mongodb.MongoClient;
var async = require('async');

function extractColumnNames(req) {
  var extractedColumnNames = [];

  for (index in req.query.columns) {
    extractedColumnNames.push(req.query.columns[index].name);
  }

  return extractedColumnNames;
}

function initializeMongoDataTables(connectionUri, collectionName) {
  function handleRequest(req, res, next) {
    var response = {
      draw: parseInt(req.query.draw, 10),
      recordsTotal: 0,
      recordsFiltered: 0,
      data: [],
      error: null
    };

    function initializeConnection(callback) {
      MongoClient.connect(connectionUri, callback);
    }

    function queryData(db, callback) {
      var extractedColumnNames = extractColumnNames(req);

      if (extractedColumnNames.length < 1) {
        callback(new Error('Columns data are not provided'), null);
        return;
      }

      var collection = db.collection(collectionName);
      var fields = {};
      var searchCriteria = { $or: [] };
      var fieldValuePair = {};
      var columnName;
      var searchValue = req.query.search.value.replace(/[\W\s]/g, '\\$&');

      for (index in extractedColumnNames) {
        columnName = extractedColumnNames[index];
        fields[columnName] = 1;
        fieldValuePair = {};

        if (!!searchValue) {
          fieldValuePair[columnName] = new RegExp(searchValue);
          searchCriteria['$or'].push(fieldValuePair);
        }
      }

      if (searchCriteria['$or'].length < 1) {
        searchCriteria = {};
      }

      collection = collection.find(searchCriteria, fields);
      callback(null, collection);
    }

    function sortData(cursor, callback) {
      var extractedColumnNames = extractColumnNames(req);
      var orders = req.query.order;
      var sortObject = {}
      var columnIndex;
      var orderedColumn;
      var orderDirection;

      for (index in orders) {
        columnIndex = parseInt(orders[index].column, 10);
        orderedColumn = extractedColumnNames[columnIndex];
        orderDirection = (orders[index].dir === 'asc')
          ? 1
          : -1;
        sortObject[orderedColumn] = orderDirection;
        cursor = cursor.sort(sortObject)
      }

      cursor.toArray(callback);
    }

    function paginateData(docs, callback) {
      if (docs.length < 1) {
        callback(null, []);
        return;
      }

      var start = parseInt(req.query.start, 10);
      var length = parseInt(req.query.length, 10);
      var newDocs = docs.slice(start, length + start);
      response.recordsTotal = docs.length;
      response.recordsFiltered = response.recordsTotal;

      callback(null, newDocs);
    }

    function sendData(error, result) {
      if (error) {
        console.error(error);
        response.error = error.message;
      }

      response.data = result;
      res.json(response);
    }

    async.waterfall(
      [initializeConnection, queryData, sortData, paginateData],
      sendData
    );
  }

  return handleRequest;
}

module.exports = initializeMongoDataTables;<|endoftext|>


<commit_before>/*
 * Copyright 2014-2015 Fabian Tollenaar <fabian@starting-point.nl>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

module.exports = function(app) {
  'use strict';

  var debug = require('debug')('signalk-server:interfaces:rest');
  var express = require('express');

  var pathPrefix = '/signalk';
  var versionPrefix = '/v1';
  var apiPathPrefix = pathPrefix + versionPrefix + '/api/';
  var streamPath = pathPrefix + versionPrefix + '/stream';

  return {
    start: function() {
      app.use('/', express.static(__dirname + '/../../public'));

      app.get(apiPathPrefix + '*', function(req, res) {
        var path = String(req.path).replace(apiPathPrefix, '');
        var data = app.signalk.retrieve();
        var last = data;

        path = path.length > 0 ? path.replace(/\/$/, '').replace(/self/, app.selfId).split('/') : [];

        for(var i in path) {
          var p = path[i];

          if(typeof last[p] !== 'undefined') {
            last = last[p];
          } else {
            res.status(404).send("Not found");
            return
          }
        }

        return res.json(last);
      });

      app.get(pathPrefix, function(req, res) {
        var host = app.config.getExternalHostname();
        var port = app.config.getExternalPort();
        port = (port === '' || port === 80) ? '' : ':' + port;

        var protocol = 'http://';
        if (app.config.settings.ssl) {
          protocol = 'https://';
        }

        res.json({
          'endpoints': {
            'v1': {
              'version': '1.alpha1',
              'signalk-http': protocol + host + port + apiPathPrefix,
              'signalk-ws': protocol + host + port + streamPath
            }
          }
        });
      })

    },

    mdns: {
      name: app.config.settings.ssl ? "_signalk-https" : "_signalk-http",
      type: "tcp",
      port: app.config.port
    }
  };
};
<commit_msg>fix websocket endpoint advertisement - fixes #99<commit_after>/*
 * Copyright 2014-2015 Fabian Tollenaar <fabian@starting-point.nl>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

module.exports = function(app) {
  'use strict';

  var debug = require('debug')('signalk-server:interfaces:rest');
  var express = require('express');

  var pathPrefix = '/signalk';
  var versionPrefix = '/v1';
  var apiPathPrefix = pathPrefix + versionPrefix + '/api/';
  var streamPath = pathPrefix + versionPrefix + '/stream';

  return {
    start: function() {
      app.use('/', express.static(__dirname + '/../../public'));

      app.get(apiPathPrefix + '*', function(req, res) {
        var path = String(req.path).replace(apiPathPrefix, '');
        var data = app.signalk.retrieve();
        var last = data;

        path = path.length > 0 ? path.replace(/\/$/, '').replace(/self/, app.selfId).split('/') : [];

        for(var i in path) {
          var p = path[i];

          if(typeof last[p] !== 'undefined') {
            last = last[p];
          } else {
            res.status(404).send("Not found");
            return
          }
        }

        return res.json(last);
      });

      app.get(pathPrefix, function(req, res) {
        var host = app.config.getExternalHostname();
        var port = app.config.getExternalPort();
        port = (port === '' || port === 80) ? '' : ':' + port;

        var httpProtocol = 'http://';
        var wsProtocol = 'ws://';
        if (app.config.settings.ssl) {
          httpProtocol = 'https://';
          wsProtocol = 'wss://';
        }

        res.json({
          'endpoints': {
            'v1': {
              'version': '1.alpha1',
              'signalk-http': httpProtocol + host + port + apiPathPrefix,
              'signalk-ws': wsProtocol + host + port + streamPath
            }
          }
        });
      })

    },

    mdns: {
      name: app.config.settings.ssl ? "_signalk-https" : "_signalk-http",
      type: "tcp",
      port: app.config.port
    }
  };
};
<|endoftext|>