import React from 'react';
import { requireNativeComponent } from 'react-native';
import PropTypes from 'prop-types';

const RNPdfScanner = requireNativeComponent('RNPdfScanner', PdfScanner);

class PdfScanner extends React.Component {
  sendOnPictureTakenEvent(event) {
    return this.props.onPictureTaken(event.nativeEvent);
  }
  render() {
    return (
      <RNPdfScanner
        {...this.props}
        onPictureTaken={this.sendOnPictureTakenEvent.bind(this)}
      />
    );
  }
}

PdfScanner.propTypes = {
  onPictureTaken: PropTypes.func,
  overlayColor: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  enableTorch: PropTypes.bool,
  saturation: PropTypes.number,
  brightness: PropTypes.number,
  contrast: PropTypes.number,
};

export default PdfScanner;

import React from 'react';
import { requireNativeComponent } from 'react-native';
import PropTypes from 'prop-types';

const RNPdfScanner = requireNativeComponent('RNPdfScanner', PdfScanner);

class PdfScanner extends React.Component {
  sendOnPictureTakenEvent(event) {
    return this.props.onPictureTaken(event.nativeEvent);
  }
  render() {
    return (
      <RNPdfScanner
        {...this.props}
        onPictureTaken={this.sendOnPictureTakenEvent.bind(this)}
        brightness={this.props.brightness||0}
        saturation={this.props.saturation||1}
        contrast={this.props.contrast||1}
      />;
    );
  }
}

PdfScanner.propTypes = {
  onPictureTaken: PropTypes.func,
  overlayColor: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  enableTorch: PropTypes.bool,
  saturation: PropTypes.number,
  brightness: PropTypes.number,
  contrast: PropTypes.number,
};

export default PdfScanner;

Add default saturation, brightness, contrast value


#!/usr/bin/env node

const fs = require( 'fs' );
const path = require( 'path' );
const readline = require( 'readline' );
const eol = require( 'os' ).EOL;
const moment = require( 'moment' );
const now = moment();

if ( process.argv.length !== 3 ) printUsageAndExit();

const FILENAME = `${process.argv[2]}.txt`;
const FILEPATH = path.resolve( process.cwd(), './', FILENAME );

const timestamp = function() {
  return moment().format( 'hh:mm:ss A' );
}

const rli = readline.createInterface({
  input: process.stdin,
  output: fs.createWriteStream( FILEPATH, { flags: 'a' } )
});

rli.on( 'line', function ( line ) {
  rli.output.write( timestamp() + ' > ' + String( line ) + eol );
  process.stdout.write( '> ' );
});

process.on( 'SIGINT', function() {
  rli.output.write( eol );
  process.stdout.write( eol + 'Note saved!' + eol );
  process.exit( 0 );
});

process.stdout.write( '> ' );
rli.output.write( timestamp() + eol );

function printUsageAndExit () {
  console.log('Usage: timestamp <filename>');
  process.exit(1);
}

#!/usr/bin/env node

const fs = require( 'fs' );
const path = require( 'path' );
const readline = require( 'readline' );
const eol = require( 'os' ).EOL;

if ( process.argv.length !== 3 ) printUsageAndExit();

const FILENAME = `${process.argv[2]}.txt`;
const FILEPATH = path.resolve( process.cwd(), './', FILENAME );

const timestamp = function() {
  let seconds = process.hrtime(time)[0];
  return calculateTimestamp(seconds);
}

const calculateTimestamp = function(seconds) {
  const minutes = Math.floor(seconds / 60).toString();
  let remainingSeconds = (seconds % 60).toString();
  let formatedSeconds = remainingSeconds.length === 1 ? remainingSeconds + '0' : remainingSeconds;
  return `${minutes}:${formatedSeconds}`;
}

const rli = readline.createInterface({
  input: process.stdin,
  output: fs.createWriteStream( FILEPATH, { flags: 'a' } )
});

let time = process.hrtime();

rli.on( 'line', function ( line ) {
  rli.output.write( timestamp() + ' - ' + String( line ) + eol );
  process.stdout.write( '> ' );
});

process.on( 'SIGINT', function() {
  rli.output.write( eol );
  process.stdout.write( eol + 'Note saved!' + eol );
  process.exit( 0 );
});

process.stdout.write( '> ' );
rli.output.write( timestamp() + eol );

function printUsageAndExit () {
  console.log('Usage: timestamp <filename>');
  process.exit(1);
}

Make timestamps relative to start time


module.exports = function (expect) {
    expect.output.addStyle('error', function (content) {
        this.text(content, 'red, bold');
    });
    expect.output.addStyle('strings', function (content) {
        this.text(content, '#CC7200');
    });
    expect.output.addStyle('key', function (content) {
        this.text(content);
    });
};

module.exports = function (expect) {
    expect.output.addStyle('error', function (content) {
        this.text(content, 'red, bold');
    });
    expect.output.addStyle('strings', function (content) {
        this.text(content, '#CC7200');
    });
    expect.output.addStyle('key', function (content) {
        this.text(content);
    });
    // Intended to be redefined by a plugin that offers syntax highlighting:
    expect.output.addStyle('code', function (content, language) {
        this.text(content);
    });
};

Define a 'code' style that can be overwritten by plugins.


'use strict';

module.exports = function(){

};

'use strict';

module.exports = function(bin){
  let data = {};
  data.fin = !!(bin[0] >>> 7);
  data.reserves = ((bin[0] >>> 4) - (data.fin ? 8 : 0));
  data.opts = bin[0] - (data.fin ? 128 : 0) - data.reserves;
  data.masked = !!(bin[1] >>> 7);

  console.log(data);

  return data;
};

Add decoding of first byte and mask.


const OS = require("os");
const serveCommand = require("./commands/serve");

const usage =
  "truffle db <sub-command> [options]" +
  OS.EOL +
  OS.EOL +
  "  Available sub-commands: " +
  OS.EOL +
  OS.EOL +
  "  serve \tStart the GraphQL server" +
  OS.EOL;

const command = {
  command: "db",
  description: "Database interface commands",
  builder: function (yargs) {
    return yargs.command(serveCommand).demandCommand();
  },

  subCommands: {
    serve: {
      help: serveCommand.help,
      description: serveCommand.description
    }
  },

  help: {
    usage,
    options: []
  },

  run: async function (args) {
    const [subCommand] = args._;
    switch (subCommand) {
      case "serve":
        await serveCommand.run(args);
        break;

      default:
        console.log(`Unknown command: ${subCommand}`);
    }
  }
};

module.exports = command;

const OS = require("os");
const serveCommand = require("./commands/serve");

const usage =
  "truffle db <sub-command> [options]" +
  OS.EOL +
  "  Available sub-commands: " +
  OS.EOL +
  "                serve \tStart the GraphQL server";

const command = {
  command: "db",
  description: "Database interface commands",
  builder: function (yargs) {
    return yargs.command(serveCommand).demandCommand();
  },

  subCommands: {
    serve: {
      help: serveCommand.help,
      description: serveCommand.description
    }
  },

  help: {
    usage,
    options: []
  },

  run: async function (args) {
    const [subCommand] = args._;
    switch (subCommand) {
      case "serve":
        await serveCommand.run(args);
        break;

      default:
        console.log(`Unknown command: ${subCommand}`);
    }
  }
};

module.exports = command;

Make minor edit to db help output


const name = 'safe';
export default function SafeHelper(config, logger) {
  const _logger = logger.child({helper: name});
  return {
    name,
    cb: (string) => {
      _logger.debug('Executing template safe helper.', {string});
      if (string) {
        return string.replace(/(\\[bfnrt"\\])/g, '\\$1');
      } else {
        return '';
      }
    }
  };
}

const chars = {
  '\n': '\\\\n',
  '\\': '\\\\'
};

const name = 'safe';
export default function SafeHelper(config, logger) {
  const _logger = logger.child({helper: name});
  return {
    name,
    cb: (string) => {
      _logger.debug('Executing template safe helper.', {string});
      if (string) {
        return string.replace(/([\b\f\n\r\t"\\])/g, (match, char) => {
          return chars[char] || '';
        });
      } else {
        return '';
      }
    }
  };
}

Refactor JSON escaping to handle slashes better.


/**
 * Main CLI parser class
 */
class Config
{
    /**
     * @param {Command} argv
     */
    constructor (argv)
    {
        /**
         * @private
         * @type {boolean}
         */
        this.dev = argv.d || argv.dev;

        /**
         * @private
         * @type {boolean}
         */
        this.debug = argv.debug;

        /**
         * @private
         * @type {boolean}
         */
        this.sourceMaps = argv.withSourceMaps;

        /**
         * @private
         * @type {boolean}
         */
        this.watch = argv.watch;

        /**
         * @private
         * @type {boolean}
         */
        this.lint = argv.lint;
    }

    /**
     * Returns whether this is a debug build
     *
     * @return {boolean}
     */
    isDebug ()
    {
        return this.debug || this.dev;
    }

    /**
     * Returns whether to include sourcemaps
     *
     * @return {boolean}
     */
    includeSourceMaps ()
    {
        return this.sourceMaps || this.dev;
    }

    /**
     * Returns whether the watcher should be activated
     *
     * @return {boolean}
     */
    isWatch ()
    {
        return this.watch || this.dev;
    }

    /**
     * Returns whether the code should be linted
     *
     * @return {boolean}
     */
    isLint ()
    {
        return this.lint || this.dev;
    }
}

module.exports = Config;

/**
 * Main CLI parser class
 */
class Config
{
    /**
     * @param {Command} argv
     */
    constructor (argv)
    {
        // commander just returns undefined for missing flags, so transform them to boolean
        /**
         * @private
         * @type {boolean}
         */
        this.dev = !!argv.d || !!argv.dev;

        /**
         * @private
         * @type {boolean}
         */
        this.debug = !!argv.debug;

        /**
         * @private
         * @type {boolean}
         */
        this.sourceMaps = !!argv.withSourceMaps;

        /**
         * @private
         * @type {boolean}
         */
        this.watch = !!argv.watch;

        /**
         * @private
         * @type {boolean}
         */
        this.lint = !!argv.lint;
    }

    /**
     * Returns whether this is a debug build
     *
     * @return {boolean}
     */
    isDebug ()
    {
        return this.debug || this.dev;
    }

    /**
     * Returns whether to include sourcemaps
     *
     * @return {boolean}
     */
    includeSourceMaps ()
    {
        return this.sourceMaps || this.dev;
    }

    /**
     * Returns whether the watcher should be activated
     *
     * @return {boolean}
     */
    isWatch ()
    {
        return this.watch || this.dev;
    }

    /**
     * Returns whether the code should be linted
     *
     * @return {boolean}
     */
    isLint ()
    {
        return this.lint || this.dev;
    }
}

module.exports = Config;

Fix parsing of commander options


import html from "innerself";
import Scene from "./Scene";
import { SCENES } from "./actions"
import { connect } from "./store";

function LevelScore(score, idx) {
  return html`

       ${score}

  `;
}

function LevelSelect({results}) {
  return Scene(
    {id: SCENES.LEVELS, from: "black", to: "black"},
    html`

          ${results.map(LevelScore)}

next

`
  );
}

export default connect(LevelSelect);

import html from "innerself";
import Scene from "./Scene";
import { SCENES } from "./actions"
import { connect } from "./store";

function LevelScore(score, idx) {
  return html`

       ${score}

  `;
}

function LevelSelect({results}) {
  const total = results.reduce((acc, cur) => acc + cur);
  const average = Math.floor(total / results.length);
  // An inverted hyperbola with lim(x → ∞) = 1.
  const threshold = 100 * (1 - 2.5 / results.length);

  return Scene(
    {id: SCENES.LEVELS, from: "black", to: "black"},
    html`

          ${results.map(LevelScore)}
          ${ average > threshold
            ? `
next
`
            : `

                …?

`
           }

`
  );
}

export default connect(LevelSelect);

Add a scalable threshold for advancing


var karma = require('karma');
var karmaConfig = require('karma/lib/config');

var config = karmaConfig.parseConfig(__dirname + '/../karma.conf.js', {
  browsers: [],
  oneShot: true,
});

var server = new karma.Server(config);

server.on('browser_complete', (completedBrowser) => {
  if (completedBrowser.lastResult.failed > 0) {
    process.exit(1);
  } else {
    process.exit(0);
  }
});

server.start();

var karma = require('karma');
var karmaConfig = require('karma/lib/config');

var config = karmaConfig.parseConfig(__dirname + '/../karma.conf.js', {
  browsers: [],
  oneShot: true,
});

var server = new karma.Server(config);

server.on('browser_complete', (completedBrowser) => {
  console.log(completedBrowser);
  if (completedBrowser.lastResult.error || completedBrowser.lastResult.failed > 0) {
    process.exit(1);
  } else {
    process.exit(0);
  }
});

server.start();

Make sure test errors report as a failure


'use babel'

import Opener from '../opener'

export default class AtomPdfOpener extends Opener {
  open (filePath, texPath, lineNumber, callback) {
    // Opens PDF in a new pane -- requires pdf-view module
    const openPanes = atom.workspace.getPaneItems()
    for (const openPane of openPanes) {
      // File is already open in another pane
      if (openPane.filePath === filePath) { return }
    }

    const pane = atom.workspace.getActivePane()
    // TODO: Make this configurable?
    // FIXME: Migrate to Pane::splitRight.
    const newPane = pane.split('horizontal', 'after')
    // FIXME: Use public API instead.
    atom.workspace.openURIInPane(filePath, newPane)

    // TODO: Check for actual success?
    if (callback) {
      callback(0)
    }
  }
}

'use babel'

import Opener from '../opener'

export default class AtomPdfOpener extends Opener {
  open (filePath, texPath, lineNumber, callback) {
    // Opens PDF in a new pane -- requires pdf-view module

    function forwardSync(pdfView) {
      if (pdfView != null && pdfView.forwardSync != null) {
        pdfView.forwardSync(texPath, lineNumber)
      }
    }

    const openPaneItems = atom.workspace.getPaneItems()
    for (const openPaneItem of openPaneItems) {
      // File is already open in another pane
      if (openPaneItem.filePath === filePath) {
        forwardSync(openPaneItem)
        return
      }
    }

    const activePane = atom.workspace.getActivePane()
    // TODO: Make this configurable?
    atom.workspace.open(filePath, {'split': 'right'}).done(function (pdfView) {
      forwardSync(pdfView)
    })

    // TODO: Check for actual success?
    if (callback) {
      callback(0)
    }
  }
}

Add forward sync support to Atom PDF opener


import domainSale from '@/assets/images/icons/domain-sale.svg';
import domainSaleHov from '@/assets/images/icons/domain-sale-hov.svg';
import registerDomain from '@/assets/images/icons/domain.svg';
import registerDomainHov from '@/assets/images/icons/domain-hov.svg';
import secureTransaction from '@/assets/images/icons/button-key-hover.svg';
import secureTransactionHov from '@/assets/images/icons/button-key.svg';
import { ETH, GOERLI, ROP, RIN } from '@/networks/types';

const dapps = {
  registerDomain: {
    route: '/interface/dapps/register-domain',
    icon: registerDomain,
    iconDisabled: registerDomainHov,
    title: 'interface.registerEns',
    desc: 'interface.registerENSDescShort',
    supportedNetworks: [ETH.name, GOERLI.name, ROP.name, RIN.name]
  },
  domainSale: {
    route: '/interface/dapps/buy-subdomain',
    icon: domainSale,
    iconDisabled: domainSaleHov,
    title: 'interface.subdomains',
    desc: 'interface.buySubDomains',
    supportedNetworks: [ETH.name]
  },
  secureTransaction: {
    route: '/interface/dapps/secure-transaction',
    icon: secureTransaction,
    iconDisabled: secureTransactionHov,
    title: 'dapps.safesend_title',
    desc: 'dapps.safesend_desc',
    supportedNetworks: [ETH.name]
  }
};

export default dapps;

import domainSale from '@/assets/images/icons/domain-sale.svg';
import domainSaleHov from '@/assets/images/icons/domain-sale-hov.svg';
import registerDomain from '@/assets/images/icons/domain.svg';
import registerDomainHov from '@/assets/images/icons/domain-hov.svg';
import secureTransaction from '@/assets/images/icons/button-key-hover.svg';
import secureTransactionHov from '@/assets/images/icons/button-key.svg';
import { ETH, GOERLI } from '@/networks/types';

const dapps = {
  registerDomain: {
    route: '/interface/dapps/register-domain',
    icon: registerDomain,
    iconDisabled: registerDomainHov,
    title: 'interface.registerEns',
    desc: 'interface.registerENSDescShort',
    supportedNetworks: [ETH.name, GOERLI.name]
  },
  domainSale: {
    route: '/interface/dapps/buy-subdomain',
    icon: domainSale,
    iconDisabled: domainSaleHov,
    title: 'interface.subdomains',
    desc: 'interface.buySubDomains',
    supportedNetworks: [ETH.name]
  },
  secureTransaction: {
    route: '/interface/dapps/secure-transaction',
    icon: secureTransaction,
    iconDisabled: secureTransactionHov,
    title: 'dapps.safesend_title',
    desc: 'dapps.safesend_desc',
    supportedNetworks: [ETH.name]
  }
};

export default dapps;

Disable ens for rin and rop since they have a different implementation


var path = require('path');
var webpack = require('webpack');
var babelConfig = require('./babel.json');

module.exports = {
  devtool: 'eval',
  entry: [
    'webpack-dev-server/client?http://localhost:3000',
    'webpack/hot/only-dev-server',
    './app/lib/App'
  ],
  output: {
    path: path.join(process.cwd(), 'public'),
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ],
  resolve: {
    extensions: ['', '.js', '.jsx'],
    // These aliases are used to make it easier to import
    // our own modules.
    'alias': {
      'app': path.join(process.cwd(), 'app'),
      'components': 'app/lib/components',
      'actions': 'app/lib/actions',
      'etc': 'app/lib/etc',
      'stores': 'app/lib/stores',
      'pages': 'app/lib/pages'
    }
  },
  module: {
    loaders: [{
      test: /\.jsx?$/,
      loaders: ['react-hot', 'babel-loader?' + JSON.stringify(babelConfig)],
      include: path.join(process.cwd(), 'app')
    }]
  }
};

var path = require('path');
var webpack = require('webpack');
var babelConfig = require('./babel.json');

module.exports = {
  devtool: 'eval',
  entry: [
    'webpack-dev-server/client?http://localhost:3000',
    'webpack/hot/only-dev-server',
    './app/lib/App'
  ],
  output: {
    path: path.join(process.cwd(), 'public'),
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ],
  resolve: {
    extensions: ['', '.js', '.jsx'],
    // These aliases are used to make it easier to import our own modules.
    // This works in tandem with the import path described in package.json,
    // which is used for the HTML rendering server code.
    'alias': {
      'app': path.join(process.cwd(), 'app'),
      'components': 'app/lib/components',
      'actions': 'app/lib/actions',
      'etc': 'app/lib/etc',
      'stores': 'app/lib/stores',
      'pages': 'app/lib/pages'
    }
  },
  module: {
    loaders: [{
      test: /\.jsx?$/,
      loaders: ['react-hot', 'babel-loader?' + JSON.stringify(babelConfig)],
      include: path.join(process.cwd(), 'app')
    }]
  }
};

Clarify the use of import aliases


module.exports = {
  stylePrefix: 'nv-',

  // Enable/Disable globally the possibility to sort layers
  sortable: 1,

  // Enable/Disable globally the possibility to hide layers
  hidable: 1,

  // Hide textnodes
  hideTextnode: 1,

  // Indicates if the wrapper is visible in layers
  showWrapper: 1
};

module.exports = {
  stylePrefix: 'nv-',

  // Specify where to render layers on init, string (query) or HTMLElement
  // With the empty value, layers won't be visible
  el: '',

  // Enable/Disable globally the possibility to sort layers
  sortable: 1,

  // Enable/Disable globally the possibility to hide layers
  hidable: 1,

  // Hide textnodes
  hideTextnode: 1,

  // Indicates if the wrapper is visible in layers
  showWrapper: 1
};

Add el option to layers


import React from 'react';

const { string, bool } = React.PropTypes;

const Console = (props) => {
  if (props.log) {
    console.log(props.log);
  }

  if (props.info) {
    console.info(props.info);
  }

  if (props.error) {
    console.error(props.error);
  }

  if (props.clear) {
    console.clear();
  }

  return null;
};

Console.propTypes = {
  log: string,
  info: string,
  error: string,
  clear: bool,
};

Console.defaultProps = {
  log: '',
  info: '',
  error: '',
  clear: false,
};

export default Console;

import React from 'react';

const { array, string, bool, shape } = React.PropTypes;

const Console = (props) => {
  if (props.assert.assertion) {
    console.assert(props.assert.assertion, props.assert.message);
  }

  if (props.clear) {
    console.clear();
  }

  if (props.count) {
    console.count(props.count);
  }

  if (props.error) {
    console.error(props.error);
  }

  if (props.group) {
    console.group();
  }

  if (props.groupColapsed) {
    console.groupCollapsed();
  }

  if (props.groupEnd) {
    console.groupEnd();
  }

  if (props.info) {
    console.info(props.info);
  }

  if (props.log) {
    console.log(props.log);
  }

  if (props.table.data.length) {
    console.table(props.table.data, props.table.columns);
  }

  if (props.time) {
    console.time(props.time);
  }

  if (props.timeEnd) {
    console.timeEnd(props.timeEnd);
  }

  if (props.trace) {
    console.trace();
  }

  if (props.warn) {
    console.warn(props.warn);
  }

  return null;
};

Console.propTypes = {
  assert: shape({
    assertion: bool,
    message: string,
  }),
  clear: bool,
  count: string,
  error: string,
  group: bool,
  groupColapsed: bool,
  groupEnd: bool,
  info: string,
  log: string,
  table: shape({
    data: array,
    columns: array,
  }),
  time: string,
  timeEnd: string,
  trace: bool,
  warn: string,
};

Console.defaultProps = {
  assert: {
    assertion: false,
    message: '',
  },
  clear: false,
  count: '',
  error: '',
  group: false,
  groupColapsed: false,
  groupEnd: false,
  info: '',
  log: '',
  table: {
    data: [],
    columns: [],
  },
  time: '',
  timeEnd: '',
  trace: false,
  warn: '',
};

export default Console;

Add missing functions from console api


/*
tests validator.js implementation.

steps to run this test:
 1. install mocha
 $ npm install -g mocha

 2. run RedPen in server mode
 $ cd $REDPEN_HOME/bin
 $ ./redpen-server

 3. rename validator.js.example to enable the validator implementation
 $ cd $REDPEN_HOME/sample
 $ mv validator.js.example validator.js

 4. run mocha
 $ cd $REDPEN_HOME/sample
 $ mocha

 */
var assert = require('assert');
var redpen = require('./redpen');

describe('redpen-test', function () {
    it('test validator.js', function (done) {
        var request = {
            "document": "This sentence contains toolongword. This sentence doesn't contain too long word.",
            "format": "json2",
            "documentParser": "PLAIN",
            "config": {
                "lang": "en",
                "validators": {
                    "JavaScript": {}

                }
            }
        };
        var assertion = function (errorSentences) {
            // only one sentence contains error
            assert.equal(errorSentences.length, 1);
            firstErrorSentence = errorSentences[0];
            assert.equal(firstErrorSentence.sentence, 'This sentence contains toolongword.');
            // there is one too word exceeds 10 chalacteres long
            assert.equal(1, firstErrorSentence.errors.length);
            assert.equal('[validator.js] word [toolongword.] is too long. length: 12', firstErrorSentence.errors[0].message);
            done();
        };

        redpen.callRedPen(request, assertion);
    });
});

/*
tests validator.js implementation.

steps to run this test:
 1. install mocha
 $ npm install -g mocha

 2. run RedPen in server mode
 $ cd $REDPEN_HOME/bin
 $ ./redpen-server

 3. rename validator.js.example to enable the validator implementation
 $ cd $REDPEN_HOME/js
 $ mv validator.js.example validator.js

 4. run mocha
 $ cd $REDPEN_HOME/js
 $ mocha

 */
var assert = require('assert');
var redpen = require('./redpen');

describe('redpen-test', function () {
    it('test validator.js', function (done) {
        var request = {
            "document": "This sentence contains toolongword. This sentence doesn't contain too long word.",
            "format": "json2",
            "documentParser": "PLAIN",
            "config": {
                "lang": "en",
                "validators": {
                    "JavaScript": {}

                }
            }
        };
        var assertion = function (errorSentences) {
            // only one sentence contains error
            assert.equal(errorSentences.length, 1);
            firstErrorSentence = errorSentences[0];
            assert.equal(firstErrorSentence.sentence, 'This sentence contains toolongword.');
            // there is one too word exceeds 10 chalacteres long
            assert.equal(1, firstErrorSentence.errors.length);
            assert.equal('[validator.js] word [toolongword.] is too long. length: 12', firstErrorSentence.errors[0].message);
            done();
        };

        redpen.callRedPen(request, assertion);
    });
});

Correct directory names in test sample of javascript validator


var data = require("sdk/self").data;
var tabs = require("sdk/tabs");
var { ToggleButton } = require("sdk/ui/button/toggle");
var btn_config = {};
var btn;

function tabToggle(tab) {
  if (btn.state('window').checked) {
    tab.attach({
      contentScriptFile: data.url('embed.js')
    });
  } else {
    tab.attach({
      contentScript: [
        'var s = document.createElement("script");',
        's.setAttribute("src", "' + data.url('destroy.js') + '");',
        'document.body.appendChild(s);'
      ]
    });
  }
}

btn_config = {
  id: "hypothesis",
  label: "Annotate",
  icon: {
    "18": './images/sleeping_18.png',
    "32": './images/sleeping_32.png',
    "36": './images/sleeping_36.png',
    "64": './images/sleeping_64.png'
  },
  onClick: function(state) {
    tabToggle(tabs.activeTab)
  }
};

if (undefined === ToggleButton) {
  btn = require("sdk/widget").Widget(btn_config);
} else {
  btn = ToggleButton(btn_config);
}

tabs.on('open', function(tab) {
  tab.on('activate', tabToggle);
  tab.on('pageshow', tabToggle);
});

const data = require("sdk/self").data;
const tabs = require("sdk/tabs");
const { ToggleButton } = require("sdk/ui/button/toggle");
var btn_config = {};
var btn;

function tabToggle(tab) {
  if (btn.state('window').checked) {
    tab.attach({
      contentScriptFile: data.url('embed.js')
    });
  } else {
    tab.attach({
      contentScript: [
        'var s = document.createElement("script");',
        's.setAttribute("src", "' + data.url('destroy.js') + '");',
        'document.body.appendChild(s);'
      ]
    });
  }
}

btn_config = {
  id: "hypothesis",
  label: "Annotate",
  icon: {
    "18": './images/sleeping_18.png',
    "32": './images/sleeping_32.png',
    "36": './images/sleeping_36.png',
    "64": './images/sleeping_64.png'
  },
  onClick: function(state) {
    tabToggle(tabs.activeTab)
  }
};

if (undefined === ToggleButton) {
  btn = require("sdk/widget").Widget(btn_config);
} else {
  btn = ToggleButton(btn_config);
}

tabs.on('open', function(tab) {
  tab.on('activate', tabToggle);
  tab.on('pageshow', tabToggle);
});

Use const instead of var


const buffer = Buffer.from('This is a test string');
const base64 = buffer.toString('base64');
const hexString = buffer.toString('hex');

module.exports = {
  buffer,
  base64,
  hexString
};

const buffer = Buffer.from([0x72, 0x98, 0x4f, 0x96, 0x75, 0x03, 0xcd, 0x28, 0x38, 0xbf]);
const base64 = buffer.toString('base64');
const hexString = buffer.toString('hex');

module.exports = {
  buffer,
  base64,
  hexString
};

Use non utf8 buffer to help catch decoding errors e.g When using a utf8 string buffer we could store the buffer as a utf8 string and tests would pass. If you then tried to encode/decode an image you'd get incorrect results.


// These URL paths will be transformed to CN mirrors.
var mirrors = {
  "//developers.google.com" : "//developers.google.cn",
  "//firebase.google.com"   : "//firebase.google.cn",
  "//developer.android.com" : "//developer.android.google.cn",
  "//angular.io"            : "//angular.cn",
  "google.com/maps"         : "google.cn/maps",
}

// These URL paths are not available on CN mirrors, therefore won't be transformed.
var whitelist = [
  "//developers.google.com/groups",
  "//developers.google.com/events",
  "//firebase.google.com/support/contact/",
]

function mirrorUrl(url) {
  // Check for whitelisting.
  for (var key in whitelist) {
    if (url.includes(whitelist[key])) {
      return url;
    }
  }

  // Check for mapping.
  for (var key in mirrors) {
  	if (url.includes(key)) {
  		url = url.replace(key, mirrors[key]);
  		break;
  	}
  }
  return url;
}

// These URL paths will be transformed to CN mirrors.
var mirrors = {
  "//developers.google.com" : "//developers.google.cn",
  "//firebase.google.com"   : "//firebase.google.cn",
  "//developer.android.com" : "//developer.android.google.cn",
  "//angular.io"            : "//angular.cn",
  "//maps.google.com"       : "//maps.google.cn",
  "google.com/maps"         : "google.cn/maps",
}

// These URL paths are not available on CN mirrors, therefore won't be transformed.
var whitelist = [
  "//developers.google.com/groups",
  "//developers.google.com/events",
  "//firebase.google.com/support/contact/",
]

function mirrorUrl(url) {
  // Check for whitelisting.
  for (var key in whitelist) {
    if (url.includes(whitelist[key])) {
      return url;
    }
  }

  // Check for mapping.
  for (var key in mirrors) {
  	if (url.includes(key)) {
  		url = url.replace(key, mirrors[key]);
  		break;
  	}
  }
  return url;
}

Add another Google Maps URL.


exports.config = {
  allScriptsTimeout: 11000,

  specs: [
    'e2e/*.js'
  ],

  capabilities: {
    'browserName': 'chrome',
	'phantomjs.binary.path': './node_modules/karma-phantomjs-launcher/node_modules/phantomjs/bin/phantomjs',
	'phantomjs.cli.args': ['--debug=true', '--webdriver', '--webdriver-logfile=webdriver.log', '--webdriver-loglevel=DEBUG']
  },

  baseUrl: 'http://localhost:8000/',

  framework: 'jasmine',

  jasmineNodeOpts: {
    defaultTimeoutInterval: 30000
  }
};

exports.config = {
  allScriptsTimeout: 11000,

  specs: [
    'e2e/*.js'
  ],

  capabilities: {
    'browserName': 'chrome',
	'phantomjs.binary.path': './node_modules/karma-phantomjs-launcher/node_modules/phantomjs/bin/phantomjs',
	'phantomjs.cli.args': ['--debug=true', '--webdriver', '--webdriver-logfile=webdriver.log', '--webdriver-loglevel=DEBUG']
  },

  baseUrl: 'http://localhost:8000/',

  framework: 'jasmine',

  jasmineNodeOpts: {
    defaultTimeoutInterval: 30000,
    onComplete: function(){
      browser.driver.executeScript("return __coverage__;").then(function(val) {
        fs.writeFileSync("./coverage/e2e/coverage.json", JSON.stringify(val));
      });
    }
  }
};

Add code coverage to protractor


/* eslint-env jasmine */
import { assertions } from 'redux-actions-assertions-js';

const toDispatchActions = () => {
  return {
    compare(action, expectedActions, done) {
      assertions.toDispatchActions(action, expectedActions, done, done.fail);
      return { pass: true };
    }
  };
};

const toNotDispatchActions = () => {
  return {
    compare(action, expectedActions, done) {
      assertions.toNotDispatchActions(action, expectedActions, done, done.fail);
      return { pass: true };
    }
  };
};

const toDispatchActionsWithState = () => {
  return {
    compare(action, state, expectedActions, done) {
      assertions.toDispatchActionsWithState(state, action, expectedActions, done, done.fail);
      return { pass: true };
    }
  };
};

const toNotDispatchActionsWithState = () => {
  return {
    compare(action, state, expectedActions, done) {
      assertions.toNotDispatchActionsWithState(state, action, expectedActions, done, done.fail);
      return { pass: true };
    }
  };
};

const matchers = {
  toDispatchActions,
  toNotDispatchActions,
  toDispatchActionsWithState,
  toNotDispatchActionsWithState
};

const registerAssertions = () => {
  jasmine.addMatchers(matchers);
};

export {
  registerAssertions,
  matchers
};

/* eslint-env jasmine */
import { assertions } from 'redux-actions-assertions-js';

function toDispatchActions() {
  return {
    compare(action, expectedActions, done) {
      assertions.toDispatchActions(action, expectedActions, done, done.fail);
      return { pass: true };
    },
    negativeCompare(action, expectedActions, done) {
      assertions.toNotDispatchActions(action, expectedActions, done, done.fail);
      return { pass: true };
    }
  };
}

function toNotDispatchActions() {
  return {
    compare(action, expectedActions, done) {
      assertions.toNotDispatchActions(action, expectedActions, done, done.fail);
      return { pass: true };
    }
  };
}

function toDispatchActionsWithState() {
  return {
    compare(action, state, expectedActions, done) {
      assertions.toDispatchActionsWithState(state, action, expectedActions, done, done.fail);
      return { pass: true };
    },
    negativeCompare(action, state, expectedActions, done) {
      assertions.toNotDispatchActionsWithState(state, action, expectedActions, done, done.fail);
      return { pass: true };
    }
  };
}

function toNotDispatchActionsWithState() {
  return {
    compare(action, state, expectedActions, done) {
      assertions.toNotDispatchActionsWithState(state, action, expectedActions, done, done.fail);
      return { pass: true };
    }
  };
}

const matchers = {
  toDispatchActions,
  toNotDispatchActions,
  toDispatchActionsWithState,
  toNotDispatchActionsWithState
};

function registerAssertions() {
  jasmine.addMatchers(matchers);
}

export {
  registerAssertions,
  matchers
};

Add support for negativeCompare, use function syntax


let Preprocessor = require('./Preprocessor');

class Sass extends Preprocessor {
    /**
     * Required dependencies for the component.
     */
    dependencies() {
        this.requiresReload = true;

        let dependencies = ['sass', 'sass-loader@7.*'];

        if (Config.processCssUrls) {
            dependencies.push('resolve-url-loader@2.3.1');
        }

        return dependencies;
    }

    /**
     * Register the component.
     *
     * @param {*} src
     * @param {string} output
     * @param {Object} pluginOptions
     * @param {Array}  postCssPlugins
     */
    register(src, output, pluginOptions = {}, postCssPlugins = []) {
        return this.preprocess(
            'sass',
            src,
            output,
            this.pluginOptions(pluginOptions),
            postCssPlugins
        );
    }

    /**
     * Build the plugin options for sass-loader.
     *
     * @param {Object} pluginOptions
     * @returns {Object}
     */
    pluginOptions(pluginOptions) {
        return Object.assign(
            {
                precision: 8,
                outputStyle: 'expanded',
                implementation: () => require('sass')
            },
            pluginOptions,
            { sourceMap: true }
        );
    }
}

module.exports = Sass;

let Preprocessor = require('./Preprocessor');

class Sass extends Preprocessor {
    /**
     * Required dependencies for the component.
     */
    dependencies() {
        this.requiresReload = true;

        let dependencies = ['sass-loader@7.*'];

        try {
            require.resolve('node-sass');

            dependencies.push('node-sass');
        } catch (e) {
            dependencies.push('sass');
        }

        if (Config.processCssUrls) {
            dependencies.push('resolve-url-loader@2.3.1');
        }

        return dependencies;
    }

    /**
     * Register the component.
     *
     * @param {*} src
     * @param {string} output
     * @param {Object} pluginOptions
     * @param {Array}  postCssPlugins
     */
    register(src, output, pluginOptions = {}, postCssPlugins = []) {
        return this.preprocess(
            'sass',
            src,
            output,
            this.pluginOptions(pluginOptions),
            postCssPlugins
        );
    }

    /**
     * Build the plugin options for sass-loader.
     *
     * @param {Object} pluginOptions
     * @returns {Object}
     */
    pluginOptions(pluginOptions) {
        return Object.assign(
            {
                precision: 8,
                outputStyle: 'expanded',
                implementation: () => require('sass')
            },
            pluginOptions,
            { sourceMap: true }
        );
    }
}

module.exports = Sass;

Determine which sass package to install


import { PropTypes } from 'react'
import {connect} from 'react-redux'

import {updateTodo, deleteTodo, showDeleteButtonOnTodo} from 'actions/actionCreators'
import TodoPresentation from 'components/TodoPresentation/TodoPresentation'

const mapStateToProps = (state, {todo}) => {
  return {
    deleteButtonVisible: state.deleteButtonOnTodo === todo.id
  }
}

const mapDispatchToProps = (dispatch, {todo}) => {
  return {
    onCompleted: () => {
      dispatch(updateTodo(todo, {completed: !todo.completed}))
    },

    onDelete: () => {
      dispatch(deleteTodo(todo))
    },

    onDeleteButtonShown: () => {
      dispatch(showDeleteButtonOnTodo(todo.id))
    },

    onDeleteButtonHidden: () => {
      dispatch(showDeleteButtonOnTodo(null))
    }
  }
}

const Todo = connect(mapStateToProps, mapDispatchToProps)(TodoPresentation)

Todo.propTypes = {
  todo: PropTypes.object.isRequired
}

export default Todo

import { PropTypes } from 'react'
import {connect} from 'react-redux'

import {updateTodo, deleteTodo, showDeleteButtonOnTodo} from 'actions/actionCreators'
import TodoPresentation from 'components/TodoPresentation/TodoPresentation'

const mapStateToProps = (state, {todo}) => {
  return {
    deleteButtonVisible: state.deleteButtonOnTodo === todo.id
  }
}

const mapDispatchToProps = (dispatch, {todo}) => {
  return {
    onCompleted () {
      dispatch(updateTodo(todo, {completed: !todo.completed}))
    },

    onDelete () {
      dispatch(deleteTodo(todo))
    },

    onDeleteButtonShown () {
      dispatch(showDeleteButtonOnTodo(todo.id))
    },

    onDeleteButtonHidden () {
      dispatch(showDeleteButtonOnTodo(null))
    }
  }
}

const Todo = connect(mapStateToProps, mapDispatchToProps)(TodoPresentation)

Todo.propTypes = {
  todo: PropTypes.object.isRequired
}

export default Todo

Use object literal syntax in mapDispatchToProps


import Vue from 'vue';
import Content from 'src/components/editor-viewer/Content';

describe('Content.vue', () => {
  it('should render Editor component', () => {
    const Constructor = Vue.extend(Content);
    const vm = new Constructor().$mount();
    expect(vm.$el.querySelector('.group .editor'))
      .to.not.equal(null);
  });
  it('should render Viewer component', () => {
    const Constructor = Vue.extend(Content);
    const vm = new Constructor().$mount();
    expect(vm.$el.querySelector('.group .viewer'))
      .to.not.equal(null);
  });
});

import Vue from 'vue';
import Content from 'src/components/editor-viewer/Content';

const VIEWER_CONTAINER_ID = 'viewer-container';

describe('Content.vue', () => {
  // A mock viewer-container DOM is needed for this test.
  // It will be created before all the test are ran, and remove
  // when all the tests are finished.
  before(() => {
    const mockContainer = document.createElement('DIV');
    mockContainer.setAttribute('id', VIEWER_CONTAINER_ID);
    document.body.appendChild(mockContainer);
  });

  after(() => {
    const mockContainer = document.getElementById(VIEWER_CONTAINER_ID);
    if (mockContainer) { mockContainer.remove(); }
  });

  it('should render Editor component', () => {
    const Constructor = Vue.extend(Content);
    const vm = new Constructor().$mount();
    expect(vm.$el.querySelector('.group .editor'))
      .to.not.equal(null);
  });
  it('should render Viewer component', () => {
    const Constructor = Vue.extend(Content);
    const vm = new Constructor().$mount();
    expect(vm.$el.querySelector('.group .viewer'))
      .to.not.equal(null);
  });
});

Fix Vue test which require the presence of viewer-element dom object


// This is a karma config file. For more details see
//   http://karma-runner.github.io/0.13/config/configuration-file.html
// we are also using it with karma-webpack
//   https://github.com/webpack/karma-webpack

var webpackConfig = require('../../build/webpack.test.conf')

module.exports = function (config) {
  config.set({
    browsers: ['Chrome'],
    frameworks: ['mocha', 'sinon-chai'],
    reporters: ['spec', 'coverage'],
    files: ['./index.js'],
    preprocessors: {
      './index.js': ['webpack', 'sourcemap']
    },
    webpack: webpackConfig,
    webpackMiddleware: {
      noInfo: true
    },
    coverageReporter: {
      dir: './coverage',
      reporters: [
        { type: 'lcov', subdir: '.' },
        { type: 'text-summary' }
      ]
    }
  })
}

// This is a karma config file. For more details see
//   http://karma-runner.github.io/0.13/config/configuration-file.html
// we are also using it with karma-webpack
//   https://github.com/webpack/karma-webpack

var webpackConfig = require('../../build/webpack.test.conf')

module.exports = function (config) {
  config.set({
    browsers: ['Chrome'],
    browserNoActivityTimeout: 180000, // 3 mins
    frameworks: ['mocha', 'sinon-chai'],
    reporters: ['spec', 'coverage'],
    files: ['./index.js'],
    preprocessors: {
      './index.js': ['webpack', 'sourcemap']
    },
    webpack: webpackConfig,
    webpackMiddleware: {
      noInfo: true
    },
    coverageReporter: {
      dir: './coverage',
      reporters: [
        { type: 'lcov', subdir: '.' },
        { type: 'text-summary' }
      ]
    }
  })
}

Extend browser no activity timeout to 3 minutes.


/*
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

var assert = require('assert');
var prepareConfig = require('../helpers/prepare-config');
var Kernel = require('../../lib/kernel');

describe('kernel load test', function() {

  it('should load the kernel on a good config', function(done){
    var config = prepareConfig(require('../data/basic-config'));
    new Kernel(config, function(err) {
      assert(!err);
      done();
    });
  });
});

/*
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

var assert = require('assert');
var prepareConfig = require('../helpers/prepare-config');
var Kernel = require('../../lib/kernel');

describe('kernel load test', function() {

  it('should load the kernel on a good config', function(done){
    this.timeout(10000);
    var config = prepareConfig(require('../data/basic-config'));
    new Kernel(config, function(err) {
      assert(!err);
      done();
    });
  });
});

Add timeout for kernel boot test for travis.


import React from 'react';
import PropTypes from 'prop-types';

import FaStar from 'react-icons/fa/star';
import FaStarO from 'react-icons/fa/star-o';

const Star = ({ colored, ...props }) => (
  <div {...props}>
    { colored ? <FaStar /> : <FaStarO /> }
  </div>
);

Star.propTypes = {
  colored: PropTypes.bool
}

export default Star;

import React from 'react';
import PropTypes from 'prop-types';

import FaStar from 'react-icons/fa/star';
import FaStarO from 'react-icons/fa/star-o';

const Star = ({ colored, fillColor, strokeColor, width = 14, height = 14, ...props }) => (
  <div {...props}>
    { colored ?
      <FaStar  style={ fillColor ? { color: fillColor, width, height } : { width, height } } /> :
      <FaStarO style={ strokeColor ? { color: strokeColor, width, height } : { width, height } } />
    }
  </div>
);

Star.propTypes = {
  colored: PropTypes.bool,
  fillColor: PropTypes.string,
  strokeColor: PropTypes.string
}

export default Star;

Allow star fill and stroke color to be editted along with width and height


// @flow
// @jsx glam
import React from 'react';
import glam from 'glam';

import { Div, Img } from '../primitives';
import { type PropsWithStyles } from '../types';
import { className } from '../utils';
import { getSource } from './component-helpers';
import componentBaseClassNames from './componentBaseClassNames';

type Props = PropsWithStyles & {
  data: Object,
  isFullscreen: boolean,
  isModal: boolean,
};

export const viewCSS = () => ({
  lineHeight: 0,
  position: 'relative',
  textAlign: 'center',
});

const viewBaseClassName = componentBaseClassNames.View;

const View = (props: Props) => {
  const { data, formatters, getStyles, index, isFullscreen, isModal } = props;
  const innerProps = {
    alt: formatters.getAltText({ data, index }),
    src: getSource({ data, isFullscreen }),
  };

  return (
    <Div
      css={getStyles(viewBaseClassName, props)}
      className={className(viewBaseClassName, { isFullscreen, isModal })}
    >
      <Img
        {...innerProps}
        className={className('view-image', { isFullscreen, isModal })}
        css={{
          height: 'auto',
          maxHeight: '100vh',
          maxWidth: '100vw',
          userSelect: 'none',
        }}
      />
    </Div>
  );
};

export default View;

// @flow
// @jsx glam
import React from 'react';
import glam from 'glam';

import { Div, Img } from '../primitives';
import { type PropsWithStyles } from '../types';
import { className } from '../utils';
import { getSource } from './component-helpers';
import componentBaseClassNames from './componentBaseClassNames';

type Props = PropsWithStyles & {
  data: Object,
  isFullscreen: boolean,
  isModal: boolean,
};

export const viewCSS = () => ({
  lineHeight: 0,
  position: 'relative',
  textAlign: 'center',
});

const viewBaseClassName = componentBaseClassNames.View;

const View = (props: Props) => {
  const { data, formatters, getStyles, index, isFullscreen, isModal } = props;
  const innerProps = {
    alt: formatters.getAltText({ data, index }),
    src: getSource({ data, isFullscreen }),
  };

  return (
    <Div
      css={getStyles(viewBaseClassName, props)}
      className={className(viewBaseClassName, { isFullscreen, isModal })}
    >
      <Img
        {...innerProps}
        className={className('view-image', { isFullscreen, isModal })}
        css={{
          height: 'auto',
          maxHeight: '100%',
          maxWidth: '100%',
          userSelect: 'none',
        }}
      />
    </Div>
  );
};

export default View;

Fix oversized images in carousel component


(function (ng) {
  'use strict';

  var $inject = function (cls, self, args) {
    var i;
    var key;
    var str;
    var func;
    var depNames = [];
    var deps = [];
    var l = cls.$inject.length;

    for (i = 0; i < l; i++) {
      key = '_' + cls.$inject[i];
      self[key] = args[i];
    }

    for (key in self) {
      if (typeof self[key] === 'function') {
        func = self[key];
        str = func.toString();

        // Skip functions with dependency injection not enabled.
        if (str.indexOf('/* $inject: enabled */') === -1) continue;

        // List of parameter names in the function signature.
        depNames = str.match(/^function\s*[^\(]*\(\s*([^\)]*)\)/m)[1].split(',');

        // Map the dependency names to the actual dependencies.
        args = depNames.map(function (name) {
          return self['_' + name.trim()];
        });

        // Add the "this" value to the arguments list.
        args.unshift(func);

        self[key] = func.bind.apply(func, args);
      }
    }
  };

  ng.module('cg.inject', [])
    .service('$inject', $inject);
})(angular);

(function (ng) {
  'use strict';

  /**
   * Angular.js utility for cleaner dependency injection.
   */
  var $inject = function (cls, self, args) {
    var i;
    var key;
    var str;
    var func;
    var depNames = [];
    var deps = [];
    var l = cls.$inject.length;

    // Inject all dependencies into the self reference.
    for (i = 0; i < l; i++) {
      key = '_' + cls.$inject[i];
      self[key] = args[i];
    }

    for (key in cls.prototype) {
      if (typeof cls.prototype[key] === 'function') {
        func = cls.prototype[key];
        str = func.toString();

        // List of dependencies.
        depNames = str.match(/\/\*\s*\$inject:([^*]+)/);

        // Skip methods without the $inject comment.
        if (depNames.length < 2) continue;

        depNames = depNames[1].split(',');

        // Map the dependency names to the actual dependencies.
        args = depNames.map(function (name) {
          return self['_' + name.trim()];
        });

        // Add the "this" value to the arguments list.
        args.unshift(func);

        self[key] = func.bind.apply(func, args);
      }
    }
  };

  var $injectService = function () {
    return $inject;
  };

  ng.module('cg.inject', [])
    .factory('$inject', $injectService);
})(angular);

Add $inject comment for DI in methods


const Formatter = require('knex/lib/formatter');

class FormatterSOQL extends Formatter {
  wrap(value) {
    if (typeof value === 'function') {
      return this.outputQuery(this.compileCallback(value), true);
    }

    const raw = this.unwrapRaw(value);
    if (raw) return raw;

    if (typeof value === 'number') return value;
    return this._wrapString(`${value}`);
  }

  outputQuery(compiled) {
    let sql = compiled.sql || '';

    if (sql) {
      if (compiled.method === 'select') {
        sql = `(${sql})`;
        if (compiled.as) return this.alias(sql, this.wrap(compiled.as));
      }
    }

    return sql;
  }
}

module.exports = FormatterSOQL;

const Formatter = require('knex/lib/formatter');

class FormatterSOQL extends Formatter {
  wrap(value) {
    if (typeof value === 'function') {
      return this.outputQuery(this.compileCallback(value), true);
    }

    const raw = this.unwrapRaw(value);
    if (raw) return raw;

    if (typeof value === 'number') return value;
    // save compatibility with older knex.js versions
    return (this.wrapString || this._wrapString).call(this, `${value}`);
  }

  outputQuery(compiled) {
    let sql = compiled.sql || '';

    if (sql) {
      if (compiled.method === 'select') {
        sql = `(${sql})`;
        if (compiled.as) return this.alias(sql, this.wrap(compiled.as));
      }
    }

    return sql;
  }
}

module.exports = FormatterSOQL;

Support for wrapString in newer knex versions


define([
	'goo/fsmpack/statemachine/actions/Action',
	'goo/entities/components/ProximityComponent'
],
/** @lends */
function(
	Action,
	ProximityComponent
) {
	'use strict';

	function TagAction(/*id, settings*/) {
		Action.apply(this, arguments);
	}

	TagAction.prototype = Object.create(Action.prototype);
	TagAction.prototype.constructor = TagAction;

	TagAction.external = {
		name: 'Tag',
		type: 'collision',
		description: 'Sets a tag on the entity. Use tags to be able to capture collision events with the \'Collides\' action',
		parameters: [{
			name: 'Tag',
			key: 'tag',
			type: 'dropdown',
			description: 'Checks for collisions with other ojects having this tag',
			'default': 'red',
			options: ['red', 'blue', 'green', 'yellow']
		}],
		transitions: []
	};

	TagAction.prototype._run = function (fsm) {
		var entity = fsm.getOwnerEntity();
		if (entity.proximityComponent) {
			if (entity.proximityComponent.tag !== this.tag) {
				entity.clearComponent('ProximityComponent');
				entity.setComponent(new ProximityComponent(this.tag));
			}
		} else {
			entity.setComponent(new ProximityComponent(this.tag));
		}
	};

	return TagAction;
});

define([
	'goo/fsmpack/statemachine/actions/Action',
	'goo/entities/components/ProximityComponent'
],
/** @lends */
function(
	Action,
	ProximityComponent
) {
	'use strict';

	function TagAction(/*id, settings*/) {
		Action.apply(this, arguments);
	}

	TagAction.prototype = Object.create(Action.prototype);
	TagAction.prototype.constructor = TagAction;

	TagAction.external = {
		name: 'Tag',
		type: 'collision',
		description: 'Sets a tag on the entity. Use tags to be able to capture collision events with the \'Collides\' action',
		parameters: [{
			name: 'Tag',
			key: 'tag',
			type: 'dropdown',
			description: 'Checks for collisions with other ojects having this tag',
			'default': 'red',
			options: ['red', 'blue', 'green', 'yellow']
		}],
		transitions: []
	};

	TagAction.prototype._run = function (fsm) {
		var entity = fsm.getOwnerEntity();
		if (entity.proximityComponent) {
			if (entity.proximityComponent.tag !== this.tag) {
				entity.clearComponent('ProximityComponent');
				entity.setComponent(new ProximityComponent(this.tag));
			}
		} else {
			entity.setComponent(new ProximityComponent(this.tag));
		}
	};

	TagAction.prototype.cleanup = function (fsm) {
		var entity = fsm.getOwnerEntity();
		entity.clearComponent('ProximityComponent');
	};

	return TagAction;
});

Fix for tags not being cleaned up


import PropTypes from 'prop-types';
import React, { Component } from 'react';
import ProfileData from './ProfileData'

export default class OtherProfileDataList extends Component {

    static propTypes = {
        profileWithMetadata: PropTypes.array.isRequired,
        metadata           : PropTypes.object.isRequired,
    };

    render() {
        const {profileWithMetadata, metadata} = this.props;
        let lines = [];
        profileWithMetadata.forEach(
            category => {
                if (Object.keys(category.fields).length === 0 || !Object.keys(category.fields).some(profileDataKey => category.fields[profileDataKey].value)) {
                    return;
                }
                lines.push(

{category.label}

);
                Object.keys(category.fields).forEach(
                    profileDataKey => {
                        if (category.fields[profileDataKey].value && metadata[profileDataKey].visible !== false) {
                            lines.push(<ProfileData key={profileDataKey} name={category.fields[profileDataKey].text} value={category.fields[profileDataKey].value} forceLong={category.fields[profileDataKey].type === 'textarea'}/>);
                        }
                    });
            });
        return (

                {lines}

        );
    }

}

import PropTypes from 'prop-types';
import React, { Component } from 'react';
import ProfileData from './ProfileData'

export default class OtherProfileDataList extends Component {

    static propTypes = {
        profileWithMetadata: PropTypes.array.isRequired,
        metadata           : PropTypes.object.isRequired,
    };

    render() {
        const {profileWithMetadata, metadata} = this.props;
        let lines = [];
        profileWithMetadata.forEach(
            category => {
                if (Object.keys(category.fields).length === 0 || !Object.keys(category.fields).some(profileDataKey => category.fields[profileDataKey].value)) {
                    return;
                }
                lines.push(

{category.label}

);
                Object.keys(category.fields).forEach(
                    profileDataKey => {
                        if (category.fields[profileDataKey].value && metadata[profileDataKey].hidden !== true) {
                            lines.push(<ProfileData key={profileDataKey} name={category.fields[profileDataKey].text} value={category.fields[profileDataKey].value} forceLong={category.fields[profileDataKey].type === 'textarea'}/>);
                        }
                    });
            });
        return (

                {lines}

        );
    }

}

Enable hidden metadata option on other people´s profile


import { ADD_RECIPE, EDIT_RECIPE, REMOVE_RECIPE } from '../actions';
import { v4 } from 'uuid';

export const defaultRecipes = [
  {
    titleDetails: {
      title: 'Pumpkin Pie',
      servings: '4',
      allergens: 'Eggs, Milk'
    },
    ingredients: [
      { name: 'Pumpkin Puree'},
      { name: 'Sweetened Condensed Milk' },
      { name: 'Eggs' },
      { name: 'Pumpkin Pie Spice' },
      { name: 'Pie Crust'}
    ],
    id: v4()
  },
  {
    titleDetails: {
      title: 'Spaghetti',
      ingredients: 'Noodles, Pasta Sauce, Meatballs',
    },
    ingredients: [
      { name: 'Noodles' },
      { name: 'Pasta Sauce' },
      { name: 'Meatballs' }
    ],
    id: v4()
  }
];

const Recipes = (state = defaultRecipes, action) => {
  switch (action.type) {
    case ADD_RECIPE:
      return [
        ...state,
        action.recipe
      ];
    case EDIT_RECIPE:
      return state.map(recipe => {
        return recipe.id === action.id ? action.recipe : recipe;
      });
    case REMOVE_RECIPE:
      return state.filter(recipe => recipe.id !== action.id);
    default:
      return state;
  }
};

export default Recipes;

import { ADD_RECIPE, EDIT_RECIPE, REMOVE_RECIPE } from '../actions';
import { v4 } from 'uuid';

export const defaultRecipes = [
  {
    titleDetails: {
      title: 'Pumpkin Pie',
      servings: '4',
      allergens: 'Eggs, Milk'
    },
    ingredients: [
      { name: 'Pumpkin Puree'},
      { name: 'Sweetened Condensed Milk' },
      { name: 'Eggs' },
      { name: 'Pumpkin Pie Spice' },
      { name: 'Pie Crust'}
    ],
    id: v4()
  },
  {
    titleDetails: {
      title: 'Spaghetti',
      servings: '4',
      allergens: 'Tomatoes'
    },
    ingredients: [
      { name: 'Noodles' },
      { name: 'Pasta Sauce' },
      { name: 'Meatballs' }
    ],
    id: v4()
  }
];

const Recipes = (state = defaultRecipes, action) => {
  switch (action.type) {
    case ADD_RECIPE:
      return [
        ...state,
        action.recipe
      ];
    case EDIT_RECIPE:
      return state.map(recipe => {
        return recipe.id === action.id ? action.recipe : recipe;
      });
    case REMOVE_RECIPE:
      return state.filter(recipe => recipe.id !== action.id);
    default:
      return state;
  }
};

export default Recipes;

Update Spaghetti default recipe to have servings and allergens.


//placing both controllers her b/c short program, otherwise would make partial

Todos.TodosController = Ember.ArrayController.extend ({
	actions: {
		createNewTodo: function() {
			var newVal = this.get('newTodo'); // gets new todo Val
			var todo = this.store.createRecord('todo' , { //Creates a new todo type
				val: newVal , // gets new val
				completed: false // sets to false completed
			});
			this.set('newtodo', ''); // set new todo to blank and save prev value to list
			todo.save();
		} ,
		clearCompleted: function() {
			var completed = this.filterBy('completed', true);//find all TRUE completed items
			completed.invoke('deleteRecord'); //invoke this deletedRecord on all TRUE completed
			completed.invoke('save'); // Save the view with after deleted TRUE completed items
		}
	}
});

Todos.TodoController = Ember.ObjectController.extend({
	//Will define actions that can happen to the object
	actions: {
		removeTodo: function () {
			var todo = this.get('model');
			todo.deleteRecord();
			todo.save();
		}
	}
});

//placing both controllers her b/c short program, otherwise would make partial

Todos.TodosController = Ember.ArrayController.extend ({
	actions: {
		createNewTodo: function() {
			var newVal = this.get('newTodo'); // gets new todo Val
			var todo = this.store.createRecord('todo' , { //Creates a new todo type
				val: newVal , // gets new val
				completed: false // sets to false completed
			});
			this.set('newTodo', ''); // set new todo to blank and save prev value to list
			todo.save();
		} ,
		clearCompleted: function() {
			var completed = this.filterBy('completed', true);//find all TRUE completed items
			completed.invoke('deleteRecord'); //invoke this deletedRecord on all TRUE completed
			completed.invoke('save'); // Save the view with after deleted TRUE completed items
		}
	}
});

Todos.TodoController = Ember.ObjectController.extend({
	//Will define actions that can happen to the object
	actions: {
		removeTodo: function () {
			var todo = this.get('model');
			todo.deleteRecord();
			todo.save();
		}
	}
});

Clear input after submit now functinal


var Logger = function (config) {
  this.config  = config;
  this.backend = this.config.backend || 'stdout'
  this.level   = this.config.level || "LOG_INFO"
  if (this.backend == 'stdout') {
    this.util = require('util');
  } else {
    if (this.backend == 'syslog') {
      this.util = require('node-syslog');
      this.util.init(config.application || 'statsd', this.util.LOG_PID | this.util.LOG_ODELAY, this.util.LOG_LOCAL0);
    } else {
      throw "Logger: Should be 'stdout' or 'syslog'."
    }
  }
}

Logger.prototype = {
  log: function (msg, type) {
    if (this.backend == 'stdout') {
      if (!type) {
        type = 'DEBUG: ';
      }
      this.util.log(type + msg);
    } else {
      if (!type) {
        type = this.level
        if (!this.util[type]) {
          throw "Undefined log level: " + type;
        }
      } else if (type == 'debug') {
        type = "LOG_DEBUG";
      }
      this.util.log(this.util[type], msg);
    }
  }
}

exports.Logger = Logger

var Logger = function (config) {
  this.config  = config;
  this.backend = this.config.backend || 'stdout'
  this.level   = this.config.level || "LOG_INFO"
  if (this.backend == 'stdout') {
    this.util = require('util');
  } else {
    if (this.backend == 'syslog') {
      this.util = require('node-syslog');
      this.util.init(config.application || 'statsd', this.util.LOG_PID | this.util.LOG_ODELAY, this.util.LOG_LOCAL0);
    } else {
      throw "Logger: Should be 'stdout' or 'syslog'."
    }
  }
}

Logger.prototype = {
  log: function (msg, type) {
    if (this.backend == 'stdout') {
      if (!type) {
        type = 'DEBUG';
      }
      this.util.log(type + ": " + msg);
    } else {
      if (!type) {
        type = this.level
        if (!this.util[type]) {
          throw "Undefined log level: " + type;
        }
      } else if (type == 'debug') {
        type = "LOG_DEBUG";
      }
      this.util.log(this.util[type], msg);
    }
  }
}

exports.Logger = Logger

Tweak logging to add space and colon between type and msg


Router.configure({
  layoutTemplate: '_layout'
});

//From what I can tell this has been depreciated
//https://github.com/iron-meteor/iron-router/blob/739bcc1445db3ad6bf90bda4e0cab3203a0ae526/lib/router.js#L88
Router.map(function() {
  // initiative routes
  this.route('initiatives', {
    path: '/',
    template: 'initiatives',
    data: {
      initiatives: function() {
        return Initiatives.find({}, {$sort: {votes: -1}});
      }
    }
  });

  this.route('initiative', {
    path: '/initiative/:_id',
    template: 'initiativeShow',
    data: function () {
      return {
        initiative: Initiatives.findOne(this.params._id)
      }
    }
  });

  // login routes
  this.route('login', {
    path: '/login',
    template: 'publicLogin'
  });

  this.route('about');
});

Router.configure({
  layoutTemplate: '_layout'
});

//From what I can tell this has been depreciated
//https://github.com/iron-meteor/iron-router/blob/739bcc1445db3ad6bf90bda4e0cab3203a0ae526/lib/router.js#L88
Router.map(function() {
  // initiative routes
  this.route('initiatives', {
    path: '/',
    template: 'initiatives',
    data: function() {
        return {
          initiatives: Initiatives.find({}, {sort: {votes: -1}})
      }
    }
  });

  this.route('initiative', {
    path: '/initiative/:_id',
    template: 'initiativeShow',
    data: function () {
      return {
        initiative: Initiatives.findOne(this.params._id)
      }
    }
  });

  // login routes
  this.route('login', {
    path: '/login',
    template: 'publicLogin'
  });

  this.route('about');
});

Fix Initiative sorting on landing page.


import { browserHistory }               from 'react-router';
import { createStore, applyMiddleware } from 'redux';
import { routerMiddleware }             from 'react-router-redux';
import thunkMiddleware                  from 'redux-thunk';

import { loadState, saveState } from '../utils/local-storage';
import { reducer }              from '../reducers';

const initialState = loadState();
const router = routerMiddleware(browserHistory);

export const Store = createStore(reducer, initialState, applyMiddleware(thunkMiddleware, router));

Store.subscribe(() => saveState(Store.getState()));

import { browserHistory }                        from 'react-router';
import { createStore, applyMiddleware, compose } from 'redux';
import { routerMiddleware }                      from 'react-router-redux';
import thunkMiddleware                           from 'redux-thunk';

import { loadState, saveState } from '../utils/local-storage';
import { reducer }              from '../reducers';

const initialState = loadState();
const router = routerMiddleware(browserHistory);

const composeEnhancers = process.env.NODE_ENV !== 'production' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

export const Store = createStore(reducer, initialState, composeEnhancers(applyMiddleware(thunkMiddleware, router)));

Store.subscribe(() => saveState(Store.getState()));

Add hook for redux devtools


import { ObjectId } from 'mongodb';

import Type from '../core/type';

export const MongoIdType = new Type({
  name: 'mongoid',
  generatePrimaryKeyVal() {
    return new ObjectId();
  },
  fromString(str) {
    return ObjectId(str);
  },
  fromClient(field, value) {
    if (value instanceof ObjectId) {
      return value;
    }

    if (value) {
      const str = value.toString();
      // we don't want to accept 12-byte strings from the client
      if (str.length !== 24) {
        throw new Error('Invalid ObjectId');
      }

      return ObjectId(str);
    }

    //return undefined;
  },
  toClient(field, value) {
    return value ? value.toString() : value;
  }
});

import { ObjectId } from 'mongodb';

import Type from '../core/type';

export const MongoIdType = new Type({
  name: 'mongoid',
  generatePrimaryKeyVal() {
    return new ObjectId();
  },
  fromString(str) {
    return ObjectId(str);
  },
  fromClient(field, value) {
    if (value instanceof ObjectId) {
      return value;
    }

    if (value) {
      const str = value.toString();
      // we don't want to accept 12-byte strings from the client
      if (str.length !== 24) {
        throw new Error(`Invalid ObjectId for field ${field}`);
      }

      return ObjectId(str);
    }

    //return undefined;
  },
  toClient(field, value) {
    return value ? value.toString() : value;
  }
});

Add `field` to error message


var build = require("../index").build;

module.exports = function(grunt){

	grunt.registerMultiTask("steal-build", "Build a steal project into bundles.", function(){
		var done = this.async();
		var options = this.options();

		var system = options.system;
		var buildOptions = options.buildOptions;

		// Run the build with the provided options
		build(system, buildOptions).then(function(){
			grunt.log.writeln("Build was successful.");

			done();
		});

	});

};

var build = require("../index").build;

module.exports = function(grunt){

	grunt.registerMultiTask("steal-build", "Build a steal project into bundles.", function(){
		var done = this.async();
		var options = this.options();

		var system = options.system;
		var buildOptions = options.buildOptions;

		// Run the build with the provided options
		var promise = build(system, buildOptions);
		if(promise.then) {
			promise.then(function(){
				grunt.log.writeln("Build was successful.");

				done();
			});
		}

	});

};

Allow the grunt task to work with the watch mode


$( function() {
	var socket = io.connect('http://localhost:8080');

	var timeouts = { 37:null, 38:null, 39:null, 40:null } ;

	var LEFT = 37,
		UP = 38,
		RIGHT = 39,
		DOWN = 40 ;

	var getDisableCallback = function getDisableCallback( key ) {
		return function() {
			socket.emit( 'message', { key: key, active: false } ) ;
			clearTimeout( timeouts[key] );
			timeouts[key] = null ;
		} ;
	}

	$( document ).keydown( function( event ) {
		var key = event.which;
		if( key >= LEFT && key <= DOWN ) {
			var hasTimeout = ( timeouts[key] !== null ) ;
			if( hasTimeout ) {
				clearTimeout( timeouts[key] );
			}
			else {
				socket.emit( 'message', { key: key, active: true } ) ;
			}
			timeouts[key] = setTimeout( getDisableCallback( key ), 1000 ) ;
		}
	});

	$(document).keyup(function( event ){
		var key = event.which;
		if( key >= LEFT && key <= DOWN ) {
			getDisableCallback( key )() ;
		}
	});
}) ;

Add minimal keyboard handling for control


var PreferencesManager = function() {
};

PreferencesManager.exec = function() {

    var logRestoredValue = function(value) {
      console.log(value)
    }
    var successfully = function () {
        console.log("Success");
        cordova.exec(logRestoredValue, failed, "PreferencesManager", "restore", [2, "test"]);
    };

    var failed = function ()  {
        console.log("Failed");
    };
    cordova.exec(successfully, failed, "PreferencesManager", "store", [2, "test", 1080982423420]);
};

module.exports = PreferencesManager;

var PreferencesManager = function() {
};

PreferencesManager.store = function(type, key, value) {

    var logStoredValue = function(value) {
      console.log(value)
    }

    var successfully = function () {
        cordova.exec(logStoredValue, failed, "PreferencesManager", "restore", [type, key]);
    };

    var failed = function ()  {
        console.log("Failed");
    };
    cordova.exec(successfully, failed, "PreferencesManager", "store", [type, key, value]);
};

PreferencesManager.restore = function(type, key) {

    var successfully = function (value) {
        console.log(value)
    };

    var failed = function ()  {
        console.log("Failed");
    };
    cordova.exec(successfully, failed, "PreferencesManager", "restore", [type, key]);
};

module.exports = PreferencesManager;

Add function do restore data from SharedPreferences


module.exports = {
  main: {
    files: [
      {
        expand: true,
        cwd: 'assets/',
        src: ['**'],
        dest: 'out/'
      }
    ]
  },
  require: {
    src: 'vendor/requirejs/require.js',
    dest: 'out/js/lib/require.js'
  },
  jQuery: {
    src: 'vendor/jquery/dist/jquery.js',
    dest: 'out/js/lib/jquery.js'
  },
  jQueryMin: {
    src: 'vendor/jquery/dist/jquery.min.js',
    dest: 'out/js/lib/jquery.min.js'
  },
  js: {
    files: [
      {
        expand: true,
        cwd: 'src/js/app',
        src: ['**/*.js'],
        dest: 'out/js/app'
      }
    ]
  },
  libs: {
    files: [
      {
        expand: true,
        cwd: 'vendor',
        src: ['**/*.js'],
        dest: 'out/js/lib'
      }
    ]
  },
  fonts: {
    files: [
      {
        expand: true,
        cwd: 'src/icons/generated/',
        src: ['*.eot', '*.svg', '*.ttf', '*.woff'],
        dest: 'out/fonts'
      }
    ]
  },
  leaflet: {
    files: [
      {
        expand: true,
        cwd: 'vendor/leaflet-dist/images',
        src: ['**'],
        dest: 'out/leaflet/images'
      }
    ]
  }
};

module.exports = {
  main: {
    files: [
      {
        expand: true,
        cwd: 'assets/',
        src: ['**', '.htaccess'],
        dest: 'out/'
      }
    ]
  },
  require: {
    src: 'vendor/requirejs/require.js',
    dest: 'out/js/lib/require.js'
  },
  jQuery: {
    src: 'vendor/jquery/dist/jquery.js',
    dest: 'out/js/lib/jquery.js'
  },
  jQueryMin: {
    src: 'vendor/jquery/dist/jquery.min.js',
    dest: 'out/js/lib/jquery.min.js'
  },
  js: {
    files: [
      {
        expand: true,
        cwd: 'src/js/app',
        src: ['**/*.js'],
        dest: 'out/js/app'
      }
    ]
  },
  libs: {
    files: [
      {
        expand: true,
        cwd: 'vendor',
        src: ['**/*.js'],
        dest: 'out/js/lib'
      }
    ]
  },
  fonts: {
    files: [
      {
        expand: true,
        cwd: 'src/icons/generated/',
        src: ['*.eot', '*.svg', '*.ttf', '*.woff'],
        dest: 'out/fonts'
      }
    ]
  },
  leaflet: {
    files: [
      {
        expand: true,
        cwd: 'vendor/leaflet-dist/images',
        src: ['**'],
        dest: 'out/leaflet/images'
      }
    ]
  }
};

Copy the .htaccess to the release directory


const userModel = require('../../models/users')
const validator = require('./registerValidation')
const { buildResponse } = require('../../utils/responseService')

module.exports = (req, res) => {
  const {error, value} = validator.validate(req.body)
  if (error) {
    return buildResponse(res, 400, { message: error.details[0].message })
  }

  userModel.findOne(value.username)
    .then(user => {
      if (user) {
        buildResponse(res, 403, { message: 'user already exists' })
      } else {
         userModel.createUser(value)
          .then(user => {
            buildResponse(res, 200, { message: 'successfully created user.', user })
          })

          .catch(error => {
            buildResponse(res, 500, { message: 'something happened', error })
          })
      }
    })

}

const userModel = require('../../models/users')
const validator = require('./registerValidation')
const { buildResponse } = require('../../utils/responseService')

module.exports = (req, res) => {
  const {error, value} = validator.validate(req.body)
  if (error) {
    return buildResponse(res, 400, { message: error.details[0].message })
  }

  userModel.findOne(value.username)
    .then(user => {
      if (user) {
        buildResponse(res, 422, { message: 'user already exists' })
      } else {
         userModel.createUser(value)
          .then(user => {
            buildResponse(res, 200, { message: 'successfully created user.', user })
          })

          .catch(error => {
            buildResponse(res, 500, { message: 'something happened', error })
          })
      }
    })

}

Change status code for resouce confilcts to 422 'Unprocessable Entity'


module.exports = function(RED) {
	"use strict";

	function Fifo(config) {
		RED.nodes.createNode(this,config);

		var me = this;

		this.queue = [];
		this.depth = config.depth;
		this.status({fill:"red",shape:"ring",text:"no value"});

		this.on('input', function (msg) {
			// are we full?
			// if so, boot some out
			if (this.queue.length >= this.depth) {
				while (this.queue.length >= this.depth) {
					var msg = this.queue.shift();
					this.send(msg);
				}
			}

			// clone the message and whack it in the queue
			this.queue.push(msg);

			if (this.queue.length == this.depth) {
				// queue is full
				this.status({fill: "green", shape: "dot",
				             text: this.queue.length + "/" +
				             this.depth + " msgs"});
			} else {
				// queue is partially full
				this.status({fill: "green", shape: "ring",
				             text: this.queue.length + "/" +
				                   this.depth + " msgs"});
			}

		});

		this.on("close", function() {
		});
	}

	RED.nodes.registerType("fifo",Fifo);

}

module.exports = function(RED) {
	"use strict";

	function Fifo(config) {
		RED.nodes.createNode(this,config);

		this.queue = [];
		this.depth = config.depth;
		this.status({fill:"red",shape:"ring",text:"no value"});

		this.on('input', function (msg) {
			// are we full?
			// if so, boot some out
			if (this.queue.length >= this.depth) {
				while (this.queue.length >= this.depth) {
					var outgoing = this.queue.shift();
					this.send(outgoing);
				}
			}

			// clone the message and whack it in the queue
			this.queue.push(msg);

			if (this.queue.length == this.depth) {
				// queue is full
				this.status({fill: "green", shape: "dot",
				             text: this.queue.length + "/" +
				             this.depth + " msgs"});
			} else {
				// queue is partially full
				this.status({fill: "green", shape: "ring",
				             text: this.queue.length + "/" +
				                   this.depth + " msgs"});
			}

		});

		this.on("close", function() {
		});
	}

	RED.nodes.registerType("fifo",Fifo);

}

Fix some code idiocies that lambdagrrl found.


import React, { Component } from 'react';
import hoistStatics         from 'hoist-non-react-statics';

/*
    When this decorator is used, it MUST be the first (outermost) decorator.
    Otherwise, we cannot find and call the fetchData methods.
*/

export default function connectData(fetchData, clientOnly = false) {

    return function wrapWithFetchData(WrappedComponent) {
        class ConnectData extends Component {
            render() {
                return <WrappedComponent { ...this.props } />;
            }
        }

        ConnectData.fetchData = fetchData;
        ConnectData.fetchInClientOnly = clientOnly;

        return hoistStatics(ConnectData, WrappedComponent);
    };
}

import React, { Component } from 'react';
import hoistStatics         from 'hoist-non-react-statics';

/*
    When this decorator is used, it MUST be the first (outermost) decorator.
    Otherwise, we cannot find and call the fetchData methods.
*/

export default function connectData(fetchData, clientOnly = false) {

    return function wrapWithFetchData(WrappedComponent) {
        class ConnectData extends Component {
            static fetchData = fetchData;
            static fetchInClientOnly = clientOnly;

            render() {
                return <WrappedComponent { ...this.props } />;
            }
        }

        return hoistStatics(ConnectData, WrappedComponent);
    };
}

Move statics to the class


var fs = require("fs");
var PEG = require("pegjs");

var parser = PEG.buildParser(fs.readFileSync("parser.pegjs", "utf8"));
var tunes = fs.readFileSync("test/tunes.txt", "utf8").split("\n\n");
var mistakes = 0;

tunes.forEach(function(p) {
    try {
        var parsed = parser.parse(p + "\n");
        console.log("\x1B[0;32m:)\x1B[0m " + parsed.header.title)
        console.log(JSON.stringify(parsed, null, 2))
    }
    catch (error) {
        console.log("\x1B[0;31m\nSyntax Error:");
        console.log(error.message);
        console.log("line: " + error.line);
        console.log("column: " + error.column);

        var debugLine = Array(error.column).join("-") + "^";
        var debugMargin = Array(error.line.toString().length + 2).join("-");

        var i;
        var lines = p.split("\n");
        for (i = 0; i < error.line; i++)
            console.log((i + 1) + "  " + lines[i]);

        console.log(debugMargin + debugLine)
        for (i = error.line + 1; i < lines.length; i++)
            console.log(i + " " + lines[i]);

        mistakes += 1;
    }
});

process.exit(mistakes.length);

var fs = require("fs");
var PEG = require("pegjs");

var parser = PEG.buildParser(fs.readFileSync("parser.pegjs", "utf8"));
var tunes = fs.readFileSync("test/tunes.txt", "utf8").split("\n\n");
var mistakes = 0;

tunes.forEach(function(p) {
    try {
        var parsed = parser.parse(p + "\n");
        console.log("\x1B[0;32m:)\x1B[0m " + parsed.header.title)
        //console.log(JSON.stringify(parsed, null, 2))
    }
    catch (error) {
        console.log("\x1B[0;31m\nSyntax Error:");
        console.log(error.message);
        console.log("line: " + error.line);
        console.log("column: " + error.column);

        var debugLine = Array(error.column).join("-") + "^";
        var debugMargin = Array(error.line.toString().length + 2).join("-");

        var i;
        var lines = p.split("\n");
        for (i = 0; i < error.line; i++)
            console.log((i + 1) + "  " + lines[i]);

        console.log(debugMargin + debugLine)
        for (i = error.line + 1; i < lines.length; i++)
            console.log(i + " " + lines[i]);

        mistakes += 1;
    }
});

process.exit(mistakes.length);

Comment too-verbose JSON structure logging


var net = require('net');
var server, port;
server = net.createServer(function (conn) {
    console.log("Server: connected");
    conn.on("data", function (data) {

        if (data.length == 1 && data[0] == 0xef) {
            console.log("Server: abridgedFlag detected");
        } else {
            console.log("Server: echo data of length %s:", data.length);
            console.log(data);
            conn.write(data);
        }
    })
});
port = 2001;
server.listen(port, function () {
    console.log("Server bound on port %s\n", port);
})

exports.port = port;

var net = require('net');
var server, port;
server = net.createServer(function (conn) {
    console.log("Server: connected");
    conn.on("data", function (data) {

        if (data.length == 1 && data[0] == 0xef) {
            console.log("Server: abridgedFlag detected");
        } else {
            console.log("Server: echo data of length %s:", data.length);
            console.log(data);
            conn.write(data);
        }
    })
});
port = 2002;
server.listen(port, function () {
    console.log("Server bound on port %s\n", port);
})

exports.port = port;

Move tcp test to port 2002


import { combineReducers } from 'redux'
import { WS_MSG } from './actions'

function auth(state = {isAuthenticated: false}, action) {
  switch (action.type) {
    case WS_MSG:
      let msg = action.msg
      switch (msg.type) {
        case "valid_login":
          return {
            ...state,
            isAuthenticated: true
          }
        default:
          break
      }
      break
    default:
      break
  }
  return state;
}

const tarabishApp = combineReducers({
  auth
})

export default tarabishApp

import { combineReducers } from 'redux'
import { WS_MSG } from './actions'

// TODO: Should just one reducer accept WS_MSG
// and repack the message and resend them?

function auth(state = {isAuthenticated: false}, action) {
  switch (action.type) {
    case WS_MSG:
      let msg = action.msg
      switch (msg.type) {
        case "valid_login":
          return {
            ...state,
            isAuthenticated: true
          }
        default:
          break
      }
      break
    default:
      break
  }
  return state;
}

function tables(state = {tableList: []}, action) {
  switch (action.type) {
    // get-tables, sit, stand...
    case WS_MSG:
      let msg = action.msg
      switch (msg.type) {
        case "tables":
          return {
            ...state,
            tableList: msg.tables,
          }
        default:
          break
      }
      break
    default:
      break
  }
  return state;
}

const tarabishApp = combineReducers({
  auth,
  tables
})

export default tarabishApp

Add a tables reducer to the lobby table list.


/*
 * Looking for the full, uncompressed source? Try here:
 *
 * https://github.com/js/{{ REPOSITORY_NAME }}
 *
 * The following files are included in this compressed version:
 *{% for path in paths %}
 * {{ path }}{% endfor %}
 */

/*
 * Looking for the full, uncompressed source? Try here:
 *
 * https://github.com/inn/{{ REPOSITORY_NAME }}
 *
 * The following files are included in this compressed version:
 *{% for path in paths %}
 * {{ path }}{% endfor %}
 */

Fix the js header for real


import React from 'react';
import { action } from '@storybook/addon-actions';

import Button from '@ichef/gypcrete/src/Button';
import List from '@ichef/gypcrete/src/List';
import ListRow from '@ichef/gypcrete/src/ListRow';

function DemoButton(props) {
    return (
        <Button
            bold
            minified={false}
            color="black"
            {...props} />
    );
}

function DemoList() {
    return (
        <List>
            <ListRow>
                <DemoButton basic="Row 1" onClick={action('click.1')} />
            </ListRow>
            <ListRow>
                <DemoButton basic="Row 2" onClick={action('click.2')} />
            </ListRow>
            <ListRow>
                <DemoButton basic="Row 3" onClick={action('click.3')} />
            </ListRow>
        </List>
    );
}

export default DemoList;

import React from 'react';
import { action } from '@storybook/addon-actions';

import Button from '@ichef/gypcrete/src/Button';
import List from '@ichef/gypcrete/src/List';
import ListRow from '@ichef/gypcrete/src/ListRow';

function ButtonRow(props) {
    return (
        <ListRow>
            <Button
                minified={false}
                {...props} />
        </ListRow>
    );
}

function DemoList() {
    return (
        <List>
            <ButtonRow basic="Row 1" onClick={action('click.1')} />
            <ButtonRow basic="Row 2" onClick={action('click.2')} />
            <ButtonRow basic="Row 3" onClick={action('click.3')} />

            <ButtonRow basic="Link row" tagName="a" href="https://apple.com" />
        </List>
    );
}

export default DemoList;

Update demo list for popover to add row of hyperlink button


// This is a temporary fix for supporting es6 scripts
// We will add support for es6->es5 transforms in Lively and this can go away

var babel = require("babel-core");
var child_process = require("child_process");
var fs = require("fs");
var lang = require("lively.lang");

var found = child_process.execSync('find core \\( -iname ".svn" -o -iname ".git" -o -iname "node_modules" -o -iname "combined.js" -o -iname "BootstrapDebugger.js" \\) -prune -o -type f -iname "*.js" -print'),
    files = lang.arr.compact(lang.string.lines(found.toString()));

var visitor = {ArrowFunctionExpression: function (node, st) { st.es6 = true; }},
    es6Files = files.filter(f => {
      try {
        var ast = acorn.parse(fs.readFileSync(f).toString(), {ecmaVersion: 6});
        var state = {es6: false};
        acorn.walk.simple(ast, visitor, null, state);
        return state.es6
      } catch (e) {
        console.log(f);
        console.error(e);
        return false;
      }
    });

es6Files.forEach(f => {
  var tfmed = babel.transformFileSync(f, {compact: false, comments: true}).code;
  tfmed = tfmed.replace(/^'use strict';\n*/m, "");
  fs.writeFileSync(f, tfmed);
});

// This is a temporary fix for supporting es6 scripts
// We will add support for es6->es5 transforms in Lively and this can go away

var babel = require("babel-core");
var child_process = require("child_process");
var fs = require("fs");
var lang = require("lively.lang");

var found = child_process.execSync('find core \\( -iname ".svn" -o -iname ".git" -o -iname "node_modules" -o -iname "combined.js" -o -iname "BootstrapDebugger.js" \\) -prune -o -type f -iname "*.js" -print'),
    files = lang.string.lines(found.toString()).compact();

var visitor = {ArrowFunctionExpression: function (node, st) { st.es6 = true; }},
    es6Files = files.filter(f => {
      try {
        var ast = acorn.parse(fs.readFileSync(f).toString(), {ecmaVersion: 6});
        var state = {es6: false};
        acorn.walk.simple(ast, visitor, null, state);
        return state.es6
      } catch (e) {
        console.log(f);
        console.error(e);
        return false;
      }
    });

es6Files.forEach(f => {
  var tfmed = babel.transformFileSync(f, {compact: false, comments: true}).code;
  tfmed = tfmed.replace(/^'use strict';\n*/m, "");
  fs.writeFileSync(f, tfmed);
});

Revert "es6 fix fix fix"


var HTMLCS_WCAG2AAA_Sniffs_Principle2_Guideline2_4_2_4_4 = {
    register: function()
    {
        return ['a'];

    },

    process: function(element)
    {
        HTMLCS.addMessage(HTMLCS.NOTICE, element, 'Check that the link text combined with programmatically determined link context identifies the purpose of the link.', 'H77-81');

    }
};

var HTMLCS_WCAG2AAA_Sniffs_Principle2_Guideline2_4_2_4_4 = {
    register: function()
    {
        return ['a'];

    },

    process: function(element)
    {
        if (element.hasAttribute('title') === true) {
            HTMLCS.addMessage(HTMLCS.NOTICE, element, 'Check that the link text combined with programmatically determined link context, or its title attribute, identifies the purpose of the link.', 'H77-81H33');
        } else {
            HTMLCS.addMessage(HTMLCS.NOTICE, element, 'Check that the link text combined with programmatically determined link context identifies the purpose of the link.', 'H77-81');
        }

    }
};

Add title attribute test to 2.4.4 (H33).


import {
    Email,
    Box,
    Item,
    Span,
    A,
    renderEmail
}                    from 'react-html-email'
import React         from 'react'
import ReactMarkdown from 'react-markdown'
import feeFactory    from '../../shared/fee/feeFactory.js'

export function html(values) {

    const participantsList = values.participants.map(p =>
{p.name}
);

    return renderEmail(
        <Email title={`${values.event.customQuestions.emailSubjectTag} Booking Updated`}>
            <Item>

Hi {values.emailUser.userName},

{values.userName} has updated booking to {values.event.name}. They have
                    booked {values.participants.length} {values.participants.length === 1 ? 'person' : 'people'}:

{participantsList}

Blue Skies

Woodcraft Folk

            </Item>
        </Email>
    )
}

export function subject(values) {
    return `${values.event.customQuestions.emailSubjectTag} Booking Updated`;
}

import {
    Email,
    Box,
    Item,
    Span,
    A,
    renderEmail
}                    from 'react-html-email'
import React         from 'react'
import ReactMarkdown from 'react-markdown'
import feeFactory    from '../../shared/fee/feeFactory.js'

export function html(values) {

    const participantsList = values.participants.map(p =>
{p.name}
);

    return renderEmail(
        <Email title={`${values.event.customQuestions.emailSubjectTag} Booking Updated`}>
            <Item>

Hi {values.emailUser.userName},

{values.userName} has updated their booking {values.district ? `for ${values.district}` : ''} to {values.event.name}. They have
                    booked {values.participants.length} {values.participants.length === 1 ? 'person' : 'people'}:

{participantsList}

Blue Skies

Woodcraft Folk

            </Item>
        </Email>
    )
}

export function subject(values) {
    return `${values.event.customQuestions.emailSubjectTag} Booking Updated`;
}

Include district in updated e-mail.


const { it } = require('mocha');

const initSocket = require('../../src/ws');
const { IO } = require('./util');

module.exports = ({ server }) => {
  let connect;
  initSocket({
    on(connection, f) {
      connect = f;
    },
  }, server.handler);
  it('connects', () => {
    const io = new IO();
    connect(io);
  });
  it('joins the correct room', done => {
    const io = new IO();
    io.join = function (room) {
      if (room === '/status') {
        return done();
      }
      done(false);
    };
    connect(io);
    process.nextTick(() => {
      io.emit('join', '/status');
    });
  });
  it('receives status messages', done => {
    const io = new IO();
    io.on('message', arg => {
      if (['ok', '👍', '⚠', '🚨'].includes(arg)) {
        done();
      } else {
        done(new Error('bad message: ' + require('util').inspect(arg, { depth: null })));
      }
    });
    connect(io);
    process.nextTick(() => {
      io.emit('join', '/status');
    });
    io.on('error', err => {
      console.log('err!', err);
      done(err);
      io.close();
    });
  });
};

const { it } = require('mocha');

const debug = require('debug')('nails:test');

const initSocket = require('../../src/ws');
const { IO } = require('./util');

module.exports = ({ server }) => {
  let connect;
  initSocket({
    on(connection, f) {
      connect = f;
    },
  }, server.handler);
  it('connects', () => {
    const io = new IO();
    connect(io);
  });
  it('joins the correct room', done => {
    const io = new IO();
    io.join = function (room) {
      if (room === '/status') {
        return done();
      }
      done(false);
    };
    connect(io);
    process.nextTick(() => {
      io.emit('join', '/status');
    });
  });
  it('receives status messages', done => {
    const io = new IO();
    io.on('message', arg => {
      if (['ok', '👍', '⚠', '🚨'].includes(arg)) {
        done();
      } else {
        done(new Error('bad message: ' + require('util').inspect(arg, { depth: null })));
      }
    });
    connect(io);
    process.nextTick(() => {
      io.emit('join', '/status');
    });
    io.on('error', err => {
      debug('err!', err);
      done(err);
      io.close();
    });
  });
};

Use `debug` instead of `console.log`


function setupBoolPreference(name) {
    document.getElementById(name).checked = Preferences[name].value;

    document.getElementById(name).addEventListener("change", function(e) {
        Preferences[name].value = e.target.checked;
    });
}

function setupNumberPreference(name) {
    document.getElementById(name).value = Preferences[name].value;

    document.getElementById(name).addEventListener("change", function(e) {
        console.log("ping");
        Preferences[name].value = e.target.value;
    });
}

for(var name in Preferences) {
    if(Preferences[name].type == "bool") {
        setupBoolPreference(name);
    }
    else {
        setupNumberPreference(name);
    }
}

if(!navigator.vibrate) {
    document.getElementById("vibration").setAttribute("disabled", true);
}

var strbundle = new StringBundle(document.getElementById("strings"));

document.querySelector("[data-l10n-id='settings_highscores_clear']").addEventListener("click", function() {
    if(window.confirm(strbundle.getString("highscores_confirm_clear"))) {
        Highscores.clear();
        removeDynamicItems();
        noresults.classList.remove("hidden");
    }
});

document.getElementById("header").addEventListener("action", function() {
    window.location = "index.html";
}, false);

function setupBoolPreference(name) {
    document.getElementById(name).checked = Preferences[name].value;

    document.getElementById(name).addEventListener("change", function(e) {
        Preferences[name].value = e.target.checked;
    });
}

function setupNumberPreference(name) {
    document.getElementById(name).value = Preferences[name].value;

    document.getElementById(name).addEventListener("change", function(e) {
        Preferences[name].value = e.target.value;
    });
}

for(var name in Preferences) {
    if(Preferences[name].type == "bool") {
        setupBoolPreference(name);
    }
    else {
        setupNumberPreference(name);
    }
}

if(!navigator.vibrate) {
    document.getElementById("vibration").setAttribute("disabled", true);
}

var strbundle = new StringBundle(document.getElementById("strings"));

document.querySelector("[data-l10n-id='settings_highscores_clear']").addEventListener("click", function() {
    if(window.confirm(strbundle.getString("highscores_confirm_clear"))) {
        Highscores.clear();
        removeDynamicItems();
        noresults.classList.remove("hidden");
    }
});

document.getElementById("header").addEventListener("action", function() {
    window.location = "index.html";
}, false);

Remove a debug logging call