describe('person', function() {

  var Person = require('../lib/Person').Person;

  before(function(done) {
      var databank = require('databank'),
          DatabankObject = databank.DatabankObject,
          Databank = databank.Databank,
          db = Databank.get("memory", {});

      db.connect({}, function(err) {
          if (err) {
              done(err);
          } else {
              DatabankObject.bank = db;
              done();
          }
      });
  });

  it('should fail to find an unknown person', function(done) {
      Person.get("unknown", done);
  });
});

var assert = require("assert");

describe('person', function() {

  var Person = require('../lib/Person').Person,
      Identifier = require('../lib/Identifier').Identifier;

  before(function(done) {
      var databank = require('databank'),
          DatabankObject = databank.DatabankObject,
          Databank = databank.Databank,
          db = Databank.get("memory", {});

      db.connect({}, function(err) {
          if (err) {
              done(err);
          } else {
              DatabankObject.bank = db;
              done();
          }
      });
  });

  it('should fail to find an unknown person', function(done) {
      Person.get("unknown", done);
  });

  it('should automatically add a uuid to a new person', function(done) {
      Person.create({}, function(err, person) {
          assert.ok(person);
          assert.ok(person.uuid);
      });
  });
});

Add a create and get test


#!/usr/bin/env node
'use strict';

var program = require('commander');
var core = require('./lib/core');

var words = [];
program
  .version(core.VERSION)
  .option(
    '-c, --config-file <config-file>',
    'Path to config file. Defaults to ~/.tanager.json',
    String,
    core.DEFAULT_CONFIG_PATH
  )
  .option(
    '-d, --date <date>',
    'Date of the entry. Yesterday, dec5, "dec 5", etc'
  )
  .option(
    '-e, --editor-cmd <editor-cmd>',
    'Editor used to edit. Defaults to config.editor, $VISUAL, then $EDITOR',
    String
  )
  .option(
    '-r, --recent',
    'Edit the most recently modified file in a notebook'
  )
  .arguments('<words...>')
  .action(function(cliWords) {
    words = cliWords;
  })
  .parse(process.argv);

core.handleRawInput(
  {
    editRecent: program.recent,
    configFile: program.configFile,
    date: program.date,
    editorCmd: program.editorCmd
  },
  words
);

#!/usr/bin/env node
'use strict';

var program = require('commander');
var core = require('./lib/core');

var words = [];
program
  .version(core.VERSION)
  .option(
    '-c, --config-file <config-file>',
    'Path to config file. Defaults to ~/.tanager.json',
    String,
    core.DEFAULT_CONFIG_PATH
  )
  .option(
    '-d, --date <date>',
    'Date of the entry. Yesterday, dec5, "dec 5", etc'
  )
  .option(
    '-e, --editor-cmd <editor-cmd>',
    'Editor used to edit. Defaults to config.editor, $VISUAL, then $EDITOR',
    String
  )
  .option(
    '-r, --recent',
    'Edit the most recently modified file in a notebook (same as --last)'
  )
  .option(
    '-l, --last',
    'Edit the most last modified file in a notebook (same as --recent)'
  )
  .arguments('<words...>')
  .action(function(cliWords) {
    words = cliWords;
  })
  .parse(process.argv);

core.handleRawInput(
  {
    editRecent: program.recent || program.last,
    configFile: program.configFile,
    date: program.date,
    editorCmd: program.editorCmd
  },
  words
);

Add -l/--last flag as alias for --recent


/* eslint-env node */
'use strict';

const DEFAULT_ASSET_VERSION = 'latest';
const getJSSource = ver => `https://cdn.reflect.io/${ver}/reflect.js`;
const getCSSSource = ver => `https://cdn.reflect.io/${ver}/reflect.css`;

const getHead = function(config) {
  let cssSource = getCSSSource(DEFAULT_ASSET_VERSION);
  let jsSource = getJSSource(DEFAULT_ASSET_VERSION);

  if (config.reflect) {
    if (config.reflect.version) {
      jsSource = getJSSource(config.reflect.version);
      cssSource = getCSSSource(config.reflect.version);
    }

    if (config.reflect.js) {
      jsSource = config.reflect.js;
    }

    if (config.reflect.css) {
      cssSource = config.reflect.css;
    }
  }

  return '<script src="' + jsSource  + '" type="text/javascript"></script> \
<link rel="stylesheet" href="' + cssSource + '">';
};

module.exports = {
  name: 'ember-cli-reflect',
  contentFor: function(type, config) {
    if (type === 'head') {
      return getHead(config);
    }
  },

  isDevelopingAddon() {
    return true;
  },
};

/* eslint-env node */
'use strict';

const DEFAULT_ASSET_VERSION = 'latest';
const getJSSource = ver => `https://cdn.reflect.io/${ver}/reflect.js`;
const getCSSSource = ver => `https://cdn.reflect.io/${ver}/reflect.css`;

const getHead = function(config) {
  let cssSource = getCSSSource(DEFAULT_ASSET_VERSION);
  let jsSource = getJSSource(DEFAULT_ASSET_VERSION);
  let outputTag = '';

  if (config.reflect) {
    if (config.reflect.version) {
      jsSource = getJSSource(config.reflect.version);
      cssSource = getCSSSource(config.reflect.version);
    }

    if (config.reflect.js) {
      jsSource = config.reflect.js;
    }

    if (config.reflect.css) {
      cssSource = config.reflect.css;
    }
  }

  let cssTag = `<link rel="stylesheet" href="${cssSource}">`;
  let jsTag = `<script src="${jsSource}" type="text/javascript"></script>`;

  if (config.reflect.excludeCss || config.reflect.excludeJs) {
    if (config.reflect.excludeCss && !config.reflect.excludeJs) {
      outputTag = jsTag;
    } else if (!config.reflect.excludeCss && config.reflect.excludeJs) {
      outputTag = cssTag;
    }
  } else {
    outputTag = cssTag + jsTag;
  }

  return outputTag;
};

module.exports = {
  name: 'ember-cli-reflect',
  contentFor: function(type, config) {
    if (type === 'head') {
      return getHead(config);
    }
  },

  isDevelopingAddon() {
    return true;
  },
};

Add config flag to exclude css and/or js


$(document).ready(function(){

    /**
     * This makes a webpage accessible to screen readers if one follows
     * proper HTML and WebBlocks conventions.
     */

    $('body').ariaWebBlocksMapper();

    /**
     * This takes all forms in the DOM and processes data-required="true" on the
     * controls.
     */

    $('body').blocks().processFormRequired();

});

$(document).ready(function(){

    /**
     * This makes a webpage accessible to screen readers if one follows
     * proper HTML and WebBlocks conventions.
     */

    $('body').blocks().ariaMapper();

    /**
     * This takes all forms in the DOM and processes data-required="true" on the
     * controls.
     */

    $('body').blocks().processFormRequired();

});

Fix bug with ariaMapper initialization


import React from 'react';
import Button from './Button';
import FormGroup from './FormGroup';
import InputBase from './InputBase';
import childrenValueValidation from './utils/childrenValueInputValidation';

class ButtonInput extends InputBase {
  renderFormGroup(children) {
    let {bsStyle, value, ...other} = this.props; // eslint-disable-line object-shorthand
    return <FormGroup {...other}>{children}</FormGroup>;
  }

  renderInput() {
    let {children, value, ...other} = this.props; // eslint-disable-line object-shorthand
    let val = children ? children : value;
    return <Button {...other} componentClass="input" ref="input" key="input" value={val} />;
  }
}

ButtonInput.types = ['button', 'reset', 'submit'];

ButtonInput.defaultProps = {
  type: 'button'
};

ButtonInput.propTypes = {
  type: React.PropTypes.oneOf(ButtonInput.types),
  bsStyle(props) {
    //defer to Button propTypes of bsStyle
    return null;
  },
  children: childrenValueValidation,
  value: childrenValueValidation
};

export default ButtonInput;

import React from 'react';
import Button from './Button';
import FormGroup from './FormGroup';
import InputBase from './InputBase';
import childrenValueValidation from './utils/childrenValueInputValidation';

class ButtonInput extends InputBase {
  renderFormGroup(children) {
    let {bsStyle, value, ...other} = this.props; // eslint-disable-line object-shorthand, no-unused-vars
    return <FormGroup {...other}>{children}</FormGroup>;
  }

  renderInput() {
    let {children, value, ...other} = this.props; // eslint-disable-line object-shorthand
    let val = children ? children : value;
    return <Button {...other} componentClass="input" ref="input" key="input" value={val} />;
  }
}

ButtonInput.types = ['button', 'reset', 'submit'];

ButtonInput.defaultProps = {
  type: 'button'
};

ButtonInput.propTypes = {
  type: React.PropTypes.oneOf(ButtonInput.types),
  bsStyle(props) {
    //defer to Button propTypes of bsStyle
    return null;
  },
  children: childrenValueValidation,
  value: childrenValueValidation
};

export default ButtonInput;

Comment out no-unused-vars eslint warnings back.


var point = require('turf-point');

/**
 * Takes a bounding box and a cell depth and outputs points in a grid.
 *
 * @module turf/grid
 * @param {Array<number>} extent extent in [xmin, ymin, xmax, ymax] order
 * @param {Number} depth how many cells to output
 * @return {FeatureCollection} grid as FeatureCollection with {@link Point} features
 * @example
 * var grid = turf.grid([0,0,10,10], 10);
 * //=grid
 */
module.exports = function(extents, depth) {
  var xmin = extents[0];
  var ymin = extents[1];
  var xmax = extents[2];
  var ymax = extents[3];
  var interval = (xmax - xmin) / depth;
  var coords = [];
  var fc = {
    type: 'FeatureCollection',
    features: []
  };

  for (var x=0; x<=depth; x++){
    for (var y=0;y<=depth; y++){
      fc.features.push(point((x * interval) + xmin, (y * interval) + ymin));
    }
  }
  return fc;
}

var point = require('turf-point');

/**
 * Takes a bounding box and a cell depth and outputs points in a grid.
 *
 * @module turf/grid
 * @param {Array<number>} extent extent in [xmin, ymin, xmax, ymax] order
 * @param {Number} depth how many cells to output
 * @return {FeatureCollection} grid as FeatureCollection with {@link Point} features
 * @example
 * var extent = [-70.823364, -33.553984, -70.473175, -33.302986];
 * var depth = 10;
 *
 * var grid = turf.grid(extent, depth);
 *
 * //=grid
 */
module.exports = function(extents, depth) {
  var xmin = extents[0];
  var ymin = extents[1];
  var xmax = extents[2];
  var ymax = extents[3];
  var interval = (xmax - xmin) / depth;
  var coords = [];
  var fc = {
    type: 'FeatureCollection',
    features: []
  };

  for (var x=0; x<=depth; x++){
    for (var y=0;y<=depth; y++){
      fc.features.push(point((x * interval) + xmin, (y * interval) + ymin));
    }
  }
  return fc;
}

Change example from Null Island to Santiago, Chile


'use strict';

var infector = require('infector'),
  Server = require('./server'),
  argv = require('optimist').argv;

var server = new Server({
  hostname: argv.HOSTNAME,
  port: argv.PORT,
  env: argv.ENV
});

server.on('listening', function(hostname, port, env){
  console.log('Server listening in %s mode at http://%s:%s/', env, hostname, port);
});

infector.register({
  'panelController': { type: require('./controllers/panelController') },
  'panelModel': { type: require('./models/panelModel') },
  'panelRouter': { value: require('./routers/panelRouter') },
  'server': { value: server }
});

server.start();

'use strict';

var infector = require('infector'),
  Server = require('./server'),
  argv = require('optimist').argv;

var server = new Server({
  hostname: argv.HOSTNAME,
  port: argv.PORT,
  env: argv.ENV
});

server.on('listening', function(hostname, port, env){
  console.log('Server listening in %s mode at http://%s:%s/', env, hostname, port);
});

infector.registerModules({
  'panelController': { type: require('./controllers/panelController') },
  'panelModel': { type: require('./models/panelModel') },
  'panelRouter': { value: require('./routers/panelRouter') },
  'server': { value: server }
});

server.start();

Revert "Fixed infector api call."


const argv = require('yargs').argv
const command = require('node-cmd')
const mix = require('laravel-mix')
const OnBuild = require('on-build-webpack')
const Watch = require('webpack-watch')
const tailwind = require('./../lib/index.js')
const config = require('./../defaultConfig.js')
const fs = require('fs')

fs.writeFileSync('./tailwind.json', JSON.stringify(require('./tailwind.js')))

const env = argv.e || argv.env || 'local'
const plugins = [
    new OnBuild(() => {
        command.get('./vendor/bin/jigsaw build ' + env, (error, stdout, stderr) => {
            if (error) {
                console.log(stderr)
                process.exit(1)
            }
            console.log(stdout)
        })
    }),
    new Watch({
        paths: ['source/**/*.md', 'source/**/*.php'],
        options: { ignoreInitial: true }
    }),
]

mix.webpackConfig({ plugins })
mix.setPublicPath('source')

mix
  .js('source/_assets/js/nav.js', 'source/js')
  .js('source/_assets/js/app.js', 'source/js')
  .less('source/_assets/less/main.less', 'source/css')
  .options({
    postCss: [
      tailwind('tailwind.js'),
    ]
  })
  .version()

const argv = require('yargs').argv
const command = require('node-cmd')
const mix = require('laravel-mix')
const OnBuild = require('on-build-webpack')
const Watch = require('webpack-watch')
const tailwind = require('./../lib/index.js')
const config = require('./../defaultConfig.js')
const fs = require('fs')

fs.writeFileSync('./tailwind.json', JSON.stringify(config()))

const env = argv.e || argv.env || 'local'
const plugins = [
    new OnBuild(() => {
        command.get('./vendor/bin/jigsaw build ' + env, (error, stdout, stderr) => {
            if (error) {
                console.log(stderr)
                process.exit(1)
            }
            console.log(stdout)
        })
    }),
    new Watch({
        paths: ['source/**/*.md', 'source/**/*.php'],
        options: { ignoreInitial: true }
    }),
]

mix.webpackConfig({ plugins })
mix.setPublicPath('source')

mix
  .js('source/_assets/js/nav.js', 'source/js')
  .js('source/_assets/js/app.js', 'source/js')
  .less('source/_assets/less/main.less', 'source/css')
  .options({
    postCss: [
      tailwind('tailwind.js'),
    ]
  })
  .version()

Write default config to JSON for Jigsaw, not docs config


(function () {
    'use strict';

    function WordCtrl(Term, Session, $location, $route, $scope) {
        var time;

        $scope.word = 'tralala';
        $scope.started = false;
        $scope.term = Term.get({
            discipline: $route.current.params.discipline,
            language: $route.current.params.language
        });

        $scope.start = function() {
            $scope.started = true;
            time = new Date().getTime();
        };

        $('.progress-bar').on('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function(){
            new Audio('/mp3/bike.mp3').play();
            $(this).unbind('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend');
        });

        $scope.redirect = function() {
            // post results back here
            time = new Date().getTime() - time;
            Session.save({
                    time: time,
                    termId: $scope.term.id,
                    discipline: $route.current.params.discipline,
                    language: $route.current.params.language
                },
                function(response){
                    $location.path('/' + $route.current.params.language + '/summary');
            });

        };
    }

    angular.module('controllers') // [] instantiates controller module
           .controller('WordCtrl', ['Term', 'Session', '$location', '$route', '$scope', WordCtrl]);
})();

(function () {
    'use strict';

    function WordCtrl(Term, Session, $location, $route, $scope) {
        var time,
            soundEffect = new Audio('/mp3/bike.mp3');

        $scope.word = 'tralala';
        $scope.started = false;
        $scope.term = Term.get({
            discipline: $route.current.params.discipline,
            language: $route.current.params.language
        });

        $scope.start = function() {
            $scope.started = true;
            time = new Date().getTime();
        };

        $('.progress-bar').on('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend', function(){
            soundEffect.play();
            window.navigator.vibrate([200, 100, 200, 100, 200]);
            $(this).unbind('webkitTransitionEnd otransitionend oTransitionEnd msTransitionEnd transitionend');
        });

        $scope.redirect = function() {
            // post results back here
            time = new Date().getTime() - time;
            Session.save({
                    time: time,
                    termId: $scope.term.id,
                    discipline: $route.current.params.discipline,
                    language: $route.current.params.language
                },
                function(response){
                    $location.path('/' + $route.current.params.language + '/summary');
            });

        };
    }

    angular.module('controllers') // [] instantiates controller module
           .controller('WordCtrl', ['Term', 'Session', '$location', '$route', '$scope', WordCtrl]);
})();

Add vibration when time runs out.


import { getSignInData, isSignedIn } from '../users/authentication'

/**
 * Updates page title.
 * This is called whenever the name of the street changes and it affects
 * the document title.
 */
export function updatePageTitle (street) {
  let title = ''

  if (street.creatorId && (!isSignedIn() || (getSignInData().userId !== street.creatorId))) {
    title = getPageTitleWithAuthor(street)
  } else {
    title = getPageTitle(street)
  }

  document.title = title
}

/**
 * Gets page title without author name.
 * Displayed when a street has an anonymous creator, if the creator is the
 * current user, and for uses where displaying an author name is not needed,
 * e.g. Facebook sharing
 */
export function getPageTitle (street) {
  return `${street.name} – Streetmix`
}

/**
 * Gets page title with author name.
 * Displayed when a street has an creator
 */
export function getPageTitleWithAuthor (street) {
  return `${street.name} (by ${street.creatorId}) – Streetmix`
}

import { getSignInData, isSignedIn } from '../users/authentication'
import { t } from '../app/locale'

/**
 * Updates page title.
 * This is called whenever the name of the street changes and it affects
 * the document title.
 */
export function updatePageTitle (street) {
  let title = ''

  if (street.creatorId && (!isSignedIn() || (getSignInData().userId !== street.creatorId))) {
    title = getPageTitleWithAuthor(street)
  } else {
    title = getPageTitle(street)
  }

  document.title = title
}

/**
 * Gets page title without author name.
 * Displayed when a street has an anonymous creator, if the creator is the
 * current user, and for uses where displaying an author name is not needed,
 * e.g. Facebook sharing
 */
export function getPageTitle (street) {
  const streetName = street.name || t('street.default-name', 'Unnamed St')
  return `${streetName} – Streetmix`
}

/**
 * Gets page title with author name.
 * Displayed when a street has an creator
 */
export function getPageTitleWithAuthor (street) {
  const streetName = street.name || t('street.default-name', 'Unnamed St')
  return `${streetName} (by ${street.creatorId}) – Streetmix`
}

Handle lack of street name in page title


'use strict';

import ENTITY from '../entities';
import insertAtomicBlock from './insert-atomic-block';
import { replaceAtomicBlock } from './replace-block';
import { Entity } from 'draft-js';
import removeBlock from './remove-block';

const handleAtomicEdit = (editorState, blockKey, valueChanged) => {
	const block = editorState.getCurrentContent().getBlockForKey(blockKey);
	const entityKey = block.getEntityAt(0);
	let blockType;
	try {
		blockType = entityKey ? Entity.get(entityKey).getType() : null;
	} catch (e) {
		console.log('Get entity type in the block occurs error ', e);
		return editorState;
	}

	switch (blockType) {
		case ENTITY.audio.type:
		case ENTITY.blockQuote.type:
		case ENTITY.annotation.type:
		case ENTITY.embeddedCode.type:
		case ENTITY.image.type:
		case ENTITY.imageDiff.type:
		case ENTITY.infobox.type:
		case ENTITY.slideshow.type:
			if (valueChanged) {
				return replaceAtomicBlock(editorState, blockKey, valueChanged);
			}
			return removeBlock(editorState, blockKey);
		default:
			return editorState;
	}
};

export default {
	insertAtomicBlock,
	handleAtomicEdit,
};

'use strict';

import ENTITY from '../entities';
import insertAtomicBlock from './insert-atomic-block';
import { replaceAtomicBlock } from './replace-block';
import { Entity } from 'draft-js';
import removeBlock from './remove-block';

const handleAtomicEdit = (editorState, blockKey, valueChanged) => {
	const block = editorState.getCurrentContent().getBlockForKey(blockKey);
	const entityKey = block.getEntityAt(0);
	let blockType;
	try {
		blockType = entityKey ? Entity.get(entityKey).getType() : null;
	} catch (e) {
		console.log('Get entity type in the block occurs error ', e);
		return editorState;
	}

	switch (blockType) {
		case ENTITY.audio.type:
		case ENTITY.blockQuote.type:
		case ENTITY.annotation.type:
		case ENTITY.embeddedCode.type:
		case ENTITY.image.type:
		case ENTITY.imageDiff.type:
		case ENTITY.infobox.type:
		case ENTITY.slideshow.type:
		case ENTITY.youtube.type:
			if (valueChanged) {
				return replaceAtomicBlock(editorState, blockKey, valueChanged);
			}
			return removeBlock(editorState, blockKey);
		default:
			return editorState;
	}
};

export default {
	insertAtomicBlock,
	handleAtomicEdit,
};

Fix bug. youtube block cannot be edited


import Vue from 'vue'

Vue.component('LoginModal', () => import('./views/containers/LoginModal.vue'))
Vue.component('SignUp', () => import('./views/containers/SignUp.vue'))
Vue.component('Mincome', () => import('./views/containers/proposals-form/Mincome.vue'))
Vue.component('RuleChangeRule', () => import('./views/containers/proposals-form/RuleChangeRule.vue'))
Vue.component('RuleAddMember', () => import('./views/containers/proposals-form/RuleAddMember.vue'))
Vue.component('RuleRemoveMember', () => import('./views/containers/proposals-form/RuleRemoveMember.vue'))

import Vue from 'vue'
// NOTE: any modals opened with `LOAD_MODAL` go here
Vue.component('LoginModal', () => import('./views/containers/LoginModal.vue'))
Vue.component('SignUp', () => import('./views/containers/SignUp.vue'))
Vue.component('Mincome', () => import('./views/containers/proposals-form/Mincome.vue'))
Vue.component('RuleChangeRule', () => import('./views/containers/proposals-form/RuleChangeRule.vue'))
Vue.component('RuleAddMember', () => import('./views/containers/proposals-form/RuleAddMember.vue'))
Vue.component('RuleRemoveMember', () => import('./views/containers/proposals-form/RuleRemoveMember.vue'))

Add comment to explain what is expected . to be lazy loaded


import React, { Component } from 'react';
import './BookDetails.scss';

export default class BookDetails extends Component {
  render() {
    const { title, coverUrl } = this.props.book;
    return (

Moby Dick

Herman Melville

Published by Project Gutenberg

            Nulla facilisi. Donec eros erat, molestie et dignissim in, pulvinar
            facilisis risus. Ut at nisl vitae odio malesuada ultrices vitae sit
            amet est. Cras feugiat neque sit amet elementum accumsan. Vestibulum
            pharetra a neque nec congue. Maecenas vel sollicitudin tortor, nec
            mattis mi. Praesent dui lorem, faucibus vitae purus semper, rhoncus
            tincidunt magna.

    );
  }
}

import React, { Component } from 'react';
import { Link } from 'react-router';
import './BookDetails.scss';

export default class BookDetails extends Component {
  render() {
    const { title, coverUrl } = this.props.book;
    return (

        <Link to="/">

          Back to Overview
        </Link>

Moby Dick

Herman Melville

Published by Project Gutenberg

            Nulla facilisi. Donec eros erat, molestie et dignissim in, pulvinar
            facilisis risus. Ut at nisl vitae odio malesuada ultrices vitae sit
            amet est. Cras feugiat neque sit amet elementum accumsan. Vestibulum
            pharetra a neque nec congue. Maecenas vel sollicitudin tortor, nec
            mattis mi. Praesent dui lorem, faucibus vitae purus semper, rhoncus
            tincidunt magna.

    );
  }
}

Add a back to overview button


// (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP

import React, { Component } from 'react';
import Tiles from 'grommet/components/Tiles';
import Box from 'grommet/components/Box';
import Button from 'grommet/components/Button';
import Animate from 'grommet/components/Animate';
import Example from '../../Example';

export default class BoxesAnimation extends Component {
  constructor (props) {
    super(props);
    this.state = {
      boxCount: 3
    };
  }

  render () {
    return (
      <div>
        <Button
          label="Add"
          primary={true}
          onClick={() => this.setState({boxCount: this.state.boxCount + 1})}
        />
        <Button
          label="Remove"
          onClick={() => this.setState({boxCount: this.state.boxCount - 1})}
        />

        <Example name="Boxes" code={
          <Animate
            enter={{ animation: 'slide-up', duration: 1000 }}
            component={Tiles}
          >
            {(this.state.boxCount > 0) &&
              new Array(this.state.boxCount).fill().map((c, i) => {
                return <Box key={`box-${i}`} style={{ width: 50, height: 50 }} colorIndex="brand" />;
              })
            }
          </Animate>
          } />
      </div>
    );
  }
};

// (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP

import React, { Component } from 'react';
import Tiles from 'grommet/components/Tiles';
import Box from 'grommet/components/Box';
import Button from 'grommet/components/Button';
import Animate from 'grommet/components/Animate';
import Example from '../../Example';

export default class BoxesAnimation extends Component {
  constructor (props) {
    super(props);
    this.state = {
      boxCount: 3
    };
  }

  render () {
    const { boxCount } = this.state;

    return (
      <div>
        <Button
          label="Add"
          primary={true}
          onClick={() => this.setState({boxCount: boxCount + 1})}
        />
        <Button
          label="Remove"
          onClick={() => this.setState({boxCount: boxCount ? boxCount - 1 : 0})}
        />

        <Example name="Boxes" code={
          <Animate
            enter={{ animation: 'slide-up', duration: 1000 }}
            component={Tiles}
          >
            {(this.state.boxCount > 0) &&
              new Array(this.state.boxCount).fill().map((c, i) => {
                return <Box key={`box-${i}`} style={{ width: 50, height: 50 }} colorIndex="brand" />;
              })
            }
          </Animate>
          } />
      </div>
    );
  }
};

Set min boxCount to be 0


#!/usr/bin/env node

import path from 'path';
import workshopper from 'workshopper';
import updateNotifier from 'update-notifier';
import Server from './lib/Server';
import pkg from '../package.json';

updateNotifier({pkg: pkg}).notify();

const serverMode = process.argv[2] === 'server';

if (serverMode) {
    const submissionPath = path.resolve(process.cwd(), process.argv[3]);
    const server = new Server(process.env.PORT || 3333, submissionPath);

    server.watch();
} else {
    workshopper({
        name: 'testdrived3',
        appDir: __dirname,
        languages: ['en'],
        helpFile: path.join(__dirname, './i18n/help/{lang}.txt'),
        menuItems: [],
    });
}

#!/usr/bin/env node

import path from 'path';
import workshopper from 'workshopper';
import updateNotifier from 'update-notifier';
import Server from './lib/Server';
import pkg from '../package.json';

updateNotifier({pkg: pkg}).notify();

workshopper({
    name: 'testdrived3',
    appDir: __dirname,
    languages: ['en'],
    helpFile: path.join(__dirname, './i18n/help/{lang}.txt'),
    menuItems: [],
    commands: [
        {
            name: 'server',
            menu: false,
            short: 's',
            handler() {
                const submissionPath = path.resolve(process.cwd(), process.argv[3]);
                const server = new Server(process.env.PORT || 3333, submissionPath);

                server.watch();
            },
        },
    ],
});

Use workshopper commands to run server mode


/**
 * Dependencies
 */

var Waterline = require('waterline');

module.exports = Waterline.Collection.extend({
  tableName: 'userTable2',
  identity: 'user',
  datastore: 'queryable',
  primaryKey: 'id',
  fetchRecordsOnUpdate: true,
  fetchRecordsOnDestroy: false,
  fetchRecordsOnCreate: true,
  fetchRecordsOnCreateEach: true,

  attributes: {
    // Primary Key
    id: {
      type: 'number',
      autoMigrations: {
        columnType: 'integer',
        autoIncrement: true,
        unique: true
      }
    },

    first_name: {
      type: 'string',
      columnName: 'fName',
      autoMigrations: {
        columnType: 'varchar'
      }
    },

    last_name: {
      type: 'string',
      autoMigrations: {
        columnType: 'varchar'
      }
    },

    type: {
      type: 'string',
      autoMigrations: {
        columnType: 'varchar'
      }
    },

    age: {
      type: 'number',
      autoMigrations: {
        columnType: 'integer'
      }
    },

    email: {
      type: 'string',
      autoMigrations: {
        columnType: 'varchar'
      }
    },

    // Timestamps

    updatedAt: {
      type: 'number',
      autoUpdatedAt: true,
      autoMigrations: {
        columnType: 'bigint'
      }
    },

    createdAt: {
      type: 'number',
      autoCreatedAt: true,
      autoMigrations: {
        columnType: 'bigint'
      }
    }
  }

});

/**
 * Dependencies
 */

var Waterline = require('waterline');

module.exports = Waterline.Collection.extend({
  tableName: 'userTable2',
  identity: 'user',
  datastore: 'queryable',
  primaryKey: 'id',
  fetchRecordsOnUpdate: true,
  fetchRecordsOnDestroy: false,
  fetchRecordsOnCreate: true,
  fetchRecordsOnCreateEach: true,

  attributes: {
    // Primary Key
    id: {
      type: 'number',
      autoMigrations: {
        columnType: 'integer',
        autoIncrement: true,
        unique: true
      }
    },

    first_name: {
      type: 'string',
      columnName: 'fName',
      autoMigrations: {
        columnType: 'varchar'
      }
    },

    last_name: {
      type: 'string',
      columnName: 'lName',
      autoMigrations: {
        columnType: 'varchar'
      }
    },

    type: {
      type: 'string',
      columnName: 't',
      autoMigrations: {
        columnType: 'varchar'
      }
    },

    age: {
      type: 'number',
      columnName: 'a',
      autoMigrations: {
        columnType: 'integer'
      }
    },

    email: {
      type: 'string',
      autoMigrations: {
        columnType: 'varchar'
      }
    },

    // Timestamps

    updatedAt: {
      type: 'number',
      autoUpdatedAt: true,
      autoMigrations: {
        columnType: 'bigint'
      }
    },

    createdAt: {
      type: 'number',
      autoCreatedAt: true,
      autoMigrations: {
        columnType: 'bigint'
      }
    }
  }

});

Use custom column names for some attributes in queryable tests


import primerStyles from '!!raw-loader!postcss-loader!../../../../../src/docs.scss'
import {Flex} from '@primer/components'
import {Frame} from '@primer/gatsby-theme-doctocat'
import {MoonIcon, SunIcon} from '@primer/octicons-react'
import React from 'react'

function LivePreviewWrapper({children}) {
  const [colorMode, setColorMode] = React.useState('light')

  return (
    <Frame>
      <link rel="stylesheet" href="https://github.com/site/assets/styleguide.css" />

        <Flex pt={2} px={2} justifyContent="flex-end">
          <button
            className="btn"
            aria-label={colorMode === 'light' ? 'Activate dark mode' : 'Activate light mode'}
            onClick={() => setColorMode(colorMode === 'light' ? 'dark' : 'light')}
          >
            {colorMode === 'light' ? <MoonIcon /> : <SunIcon />}
          </button>
        </Flex>

{children}

    </Frame>
  )
}

export default LivePreviewWrapper

import primerStyles from '!!raw-loader!postcss-loader!../../../../../src/docs.scss'
import {Flex} from '@primer/components'
import {Frame} from '@primer/gatsby-theme-doctocat'
import {MoonIcon, SunIcon} from '@primer/octicons-react'
import React from 'react'

class ColorModeChangeEvent extends CustomEvent {
  constructor(mode) {
    super('color-mode-change', {detail: mode})
  }
}

function LivePreviewWrapper({children}) {
  const [colorMode, setColorModeState] = React.useState('light')

  const setColorMode = mode => {
    setColorModeState(mode)
    window.dispatchEvent(new ColorModeChangeEvent(mode))
  }

  React.useEffect(() => {
    const callback = e => setColorModeState(e.detail)
    window.addEventListener('color-mode-change', callback)

    return () => {
      window.removeEventListener('color-mode-change', callback)
    }
  }, [])

  return (
    <Frame>
      <link rel="stylesheet" href="https://github.com/site/assets/styleguide.css" />

        <Flex pt={2} px={2} justifyContent="flex-end">
          <button
            className="btn"
            aria-label={colorMode === 'light' ? 'Activate dark mode' : 'Activate light mode'}
            onClick={() => setColorMode(colorMode === 'light' ? 'dark' : 'light')}
          >
            {colorMode === 'light' ? <MoonIcon /> : <SunIcon />}
          </button>
        </Flex>

{children}

    </Frame>
  )
}

export default LivePreviewWrapper

Make color mode toggle global


import _       from 'lodash';
import errorer from '../../../errorer/errorer';

export default class {
  constructor(instance, receiver, receiverConfigs, params) {
    let {events = {}} = instance;
    let method = events[receiver] || receiver;
    _.extend(this, {data: {}, instance, method, receiverConfigs, params});
    return this.handler.bind(this);
  }

  handler(result, storeAs) {
    let {data, receiverConfigs, params, instance, method} = this;
    let {complete, reset} = receiverConfigs;
    let previous = Promise.resolve(data[storeAs]);
    let current = Promise.resolve(result);

    previous.then(() => current).then(result => {
      data[storeAs] = result;

      if(complete) {
        if(_.keys(data).length < params.length) {
          return;
        }

        if(reset) {
          setTimeout(() => this.data = {});
        }
      }

      instance[method](_.clone(data));
    }).catch(e => errorer({type: e})).catch(_.noop);
  }
}

import _       from 'lodash';
import errorer from '../../../errorer/errorer';

export default class {
  constructor(instance, receiver, receiverConfigs, params) {
    let {events = {}} = instance;
    let method = events[receiver] || receiver;
    _.extend(this, {data: {}, instance, method, receiverConfigs, params});
    return this.handler.bind(this);
  }

  handler(result, storeAs) {
    let {data, receiverConfigs, params, instance, method} = this;
    let {complete, reset} = receiverConfigs;
    let previous = Promise.resolve(data[storeAs]);
    let current = Promise.resolve(result);

    data[storeAs] = previous.then(() => current).then(result => {
      data[storeAs] = result;

      if(complete) {
        if(_.keys(data).length < params.length) {
          return;
        }

        if(reset) {
          setTimeout(() => this.data = {});
        }
      }

      instance[method](_.clone(data));
    }).catch(e => errorer({type: e})).catch(_.noop);
  }
}

Adjust receiver class in connector module


/*
 * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
function runBlock ($rootScope, $window, $http) {
  'ngInject';

  $rootScope.$on('authenticationSuccess', function() {
  	$http.defaults.headers.common.Authorization = 'Basic ' + $window.sessionStorage.getItem('GraviteeAuthentication');
  });

  $http.defaults.headers.common.Authorization = 'Basic ' + $window.sessionStorage.getItem('GraviteeAuthentication');
}

export default runBlock;

/*
 * Copyright (C) 2015 The Gravitee team (http://gravitee.io)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
function runBlock ($rootScope, $window, $http) {
  'ngInject';

  function setAuthorization() {
    var graviteeAuthentication = $window.sessionStorage.getItem('GraviteeAuthentication');
    if (graviteeAuthentication) {
      $http.defaults.headers.common.Authorization = 'Basic ' + graviteeAuthentication;
    }
  }

  $rootScope.$on('authenticationSuccess', function() {
  	setAuthorization();
  });

  setAuthorization();
}

export default runBlock;

Set authorization only if exists


import ReactiveBase from "./components/basic/ReactiveBase";
import TextField from "./components/basic/TextField";
import SelectedFilters from "./components/basic/SelectedFilters";
import ToggleButton from "./components/basic/ToggleButton";

import SingleList from "./components/list/SingleList";
import MultiList from "./components/list/MultiList";

import DataSearch from "./components/search/DataSearch";

import ReactiveList from "./components/result/ReactiveList";

export {
	// basic
	ReactiveBase,
	TextField,
	SelectedFilters,
	ToggleButton,

	// list
	SingleList,
	MultiList,

	// search
	DataSearch,

	// result
	ReactiveList
}

import ReactiveBase from "./components/basic/ReactiveBase";
import TextField from "./components/basic/TextField";
import SelectedFilters from "./components/basic/SelectedFilters";
import ToggleButton from "./components/basic/ToggleButton";
import NumberBox from "./components/basic/NumberBox";

import SingleList from "./components/list/SingleList";
import MultiList from "./components/list/MultiList";

import DataSearch from "./components/search/DataSearch";

import ReactiveList from "./components/result/ReactiveList";

export {
	// basic
	ReactiveBase,
	TextField,
	SelectedFilters,
	ToggleButton,
	NumberBox,

	// list
	SingleList,
	MultiList,

	// search
	DataSearch,

	// result
	ReactiveList
}

Update exports - include NumberBox


// @flow

import deviceInfo from 'react-native-device-info'
import networkInfo from 'react-native-network-info'
import pkg from '../../../package.json'

export const getIpAddress = (): Promise<?string> =>
	new Promise(resolve => {
		try {
			networkInfo.getIPAddress(resolve)
		} catch (err) {
			resolve(null)
		}
	})

export const getPosition = (): Promise<Object> =>
	new Promise(resolve => {
		navigator.geolocation.getCurrentPosition(resolve, err => { throw err })
	})

export const collectData = async () => ({
	id: deviceInfo.getUniqueID(),
	brand: deviceInfo.getBrand(),
	model: deviceInfo.getModel(),
	deviceKind: deviceInfo.getDeviceId(),
	os: deviceInfo.getSystemName(),
	osVersion: deviceInfo.getSystemVersion(),
	appVersion: deviceInfo.getReadableVersion(),
	jsVersion: pkg.version,
	ua: deviceInfo.getUserAgent(),
	ip: await getIpAddress(),
	dateRecorded: new Date().toJSON(),
})

export const reportToServer = (url: string, data: Object) =>
	fetch(url, {method: 'POST', body: JSON.stringify(data)})

// @flow

import deviceInfo from 'react-native-device-info'
import networkInfo from 'react-native-network-info'
import pkg from '../../../package.json'

export const getIpAddress = (): Promise<?string> =>
	new Promise(resolve => {
		try {
			networkInfo.getIPAddress(resolve)
		} catch (err) {
			resolve(null)
		}
	})

export const getPosition = (): Promise<Object> =>
	new Promise((resolve, reject) => {
		navigator.geolocation.getCurrentPosition(resolve, err => reject(err))
	})

export const collectData = async () => ({
	id: deviceInfo.getUniqueID(),
	brand: deviceInfo.getBrand(),
	model: deviceInfo.getModel(),
	deviceKind: deviceInfo.getDeviceId(),
	os: deviceInfo.getSystemName(),
	osVersion: deviceInfo.getSystemVersion(),
	appVersion: deviceInfo.getReadableVersion(),
	jsVersion: pkg.version,
	ua: deviceInfo.getUserAgent(),
	ip: await getIpAddress(),
	dateRecorded: new Date().toJSON(),
})

export const reportToServer = (url: string, data: Object) =>
	fetch(url, {method: 'POST', body: JSON.stringify(data)})

Use a rejection instead of throwing


export class Searchbar {

  constructor() {
    $('a.toggle-searchbar').on('click', $.proxy(this.toggleSearchbar, this));
  }

  toggleSearchbar() {
    $('.search-box').toggleClass('open');
    $('input[name=term]'.focus());
  }
}

export class Searchbar {

  constructor() {
    $('a.toggle-searchbar').on('click', $.proxy(this.toggleSearchbar, this));
  }

  toggleSearchbar() {
    $('.search-box').toggleClass('open');
    $('input[name=term]').focus();
  }
}

Fix focus in js selector searchbar


import React from "react";

import CircularProgress from "material-ui/CircularProgress";

import "./style.css";

/*
    This is a very basic graph that displays a text message
*/
export default class SimpleTextGraph extends React.Component {
    render() {
        const {
            response,
            queryConfiguration,
            width,
            height,
            configuration: {
                data: {
                    circle,
                    circleColor
                }
            }
        } = this.props;

        let body;
        if (response && !response.isFetching) {
            body = (

                    {(() => {
                        if(circle){
                            const padding = 16;
                            const side = width * 0.3;
                            return (

                            );
                        }
                    })()}

                      {response.results.length}

                    {queryConfiguration.title}

            );
        }
        else {

            body = (
                <CircularProgress color="#eeeeee" />
            );
        }

        return (

                { body }

        );
    }
}

SimpleTextGraph.propTypes = {
  configuration: React.PropTypes.object,
  response: React.PropTypes.object
};

import React from "react";

import CircularProgress from "material-ui/CircularProgress";

import "./style.css";

/*
    This is a very basic graph that displays a text message
*/
export default class SimpleTextGraph extends React.Component {
    render() {
        const {
            response,
            queryConfiguration,
            width,
            height,
            configuration: {
                data: {
                    circle,
                    circleColor
                }
            },
            onMarkClick
        } = this.props;

        let body;
        if (response && !response.isFetching) {
            body = (

                    {(() => {
                        if(circle){
                            const padding = 16;
                            const side = width * 0.3;
                            return (

                            );
                        }
                    })()}

                      {response.results.length}

                    {queryConfiguration.title}

            );
        }
        else {

            body = (
                <CircularProgress color="#eeeeee" />
            );
        }

        const cursor = onMarkClick ? "pointer" : undefined;

        return (

                { body }

        );
    }
}

SimpleTextGraph.propTypes = {
  configuration: React.PropTypes.object,
  response: React.PropTypes.object
};

Add onMarkClick event to SimpleTextGraph, set pointer cursor


/**
 * Renders a formulate Field.
 * @param fieldData The data for the form field to render.
 * @param fieldRenderers The associative array of field rendering functions.
 * @param fieldValidators The associative array of the field validating functions.
 * @param extraOptions Additional options that are less commonly used.
 * @returns {HTMLInputElement} The DOM element created by the field renderer.
 */
function renderField(fieldData, fieldRenderers, fieldValidators, extraOptions) {

    // Variables.
    let renderer, cssClasses, renderResult;

    // Get the field rendering function for the current field type.
    renderer = fieldRenderers[fieldData.fieldType];

    // Create CSS classes to be attached to the DOM element.
    cssClasses = [];
    cssClasses.push("formulate__field");
    cssClasses.push("formulate__field--" + fieldData.fieldType);

    // Render the field.
    renderResult = new renderer(fieldData, fieldValidators, cssClasses, extraOptions);

    // Return the rendered field (an object that has information about the rendered field).
    return renderResult;

}

// Export the function that renders a field.
module.exports = renderField;

/**
 * Renders a formulate Field.
 * @param fieldData The data for the form field to render.
 * @param fieldRenderers The associative array of field rendering functions.
 * @param fieldValidators The associative array of the field validating functions.
 * @param extraOptions Additional options that are less commonly used.
 * @returns {HTMLInputElement} The DOM element created by the field renderer.
 */
function renderField(fieldData, fieldRenderers, fieldValidators, extraOptions) {

    // Variables.
    let renderer, cssClasses, renderResult;

    // Get the field rendering function for the current field type.
    renderer = fieldRenderers[fieldData.fieldType];

    // Create CSS classes to be attached to the DOM element.
    cssClasses = [];
    cssClasses.push("formulate__field");
    cssClasses.push("formulate__field--" + fieldData.fieldType);

    // Render the field.
    if (!renderer) {
        throw Error("Unable to find renderer for field of type " + fieldData.fieldType + ".");
    }
    renderResult = new renderer(fieldData, fieldValidators, cssClasses, extraOptions);

    // Return the rendered field (an object that has information about the rendered field).
    return renderResult;

}

// Export the function that renders a field.
module.exports = renderField;

Throw error when a field renderer is missing in the Plain JavaScript template.


import React, { Component } from 'react'
import { connect } from 'react-redux'
import Meal from '../views/Meal'
import NewMeal from '../components/NewMeal'
import { Container } from 'semantic-ui-react'
import { Link, Route, Switch } from 'react-router-dom'

class MealsContainer extends Component {
  render() {
    return (
      <Container textAlign="center">

All Meals

          {this.props.meals.map(m =>
            <Link key={m.id} to={`/meals/${m.id}`}>

                {m.name}

            </Link>
          )}

        <Switch>
          <Route path="/meals/new" component={NewMeal} />
          <Route exact path={`/meals/:id`} render={() => <Meal />} />
        </Switch>
      </Container>
    )
  }
}

export default connect(state => {
  return {
    meals: state.meals,
  }
})(MealsContainer)

import React, { Component } from 'react'
import { connect } from 'react-redux'
import Meal from '../views/Meal'
import NewMeal from '../components/NewMeal'
import { Container } from 'semantic-ui-react'
import { Link, Route, Switch } from 'react-router-dom'
import { css } from 'glamor'

class MealsContainer extends Component {
  render() {
    return (
      <Container>

All Meals

              {this.props.meals.map(m =>
                <Link key={m.id} to={`/meals/${m.id}`}>

                    {m.name}

                </Link>
              )}

            <Switch>
              <Route path="/meals/new" component={NewMeal} />
              <Route exact path={`/meals/:id`} render={() => <Meal />} />
            </Switch>

      </Container>
    )
  }
}

export default connect(state => {
  return {
    meals: state.meals,
  }
})(MealsContainer)

let gridContainer = css({
  marginTop: '20px',
  display: 'grid',
  gridTemplateAreas: `"navigation main main"`,
})

let gridNav = css({
  gridArea: 'navigation',
})

let gridMain = css({
  gridArea: 'main',
})

Test css grid in meals container


const paths = require('./paths');

// TODO - Verfiy that we don't need eslint-import-resolver-webpack anymore
// const webpackConfig = process.env.NODE_ENV === "development"
//   ? path.join(paths.kickstarterConfig, "webpack.dev.js")
//   : path.join(paths.kickstarterConfig, "webpack.prod.js");

module.exports = {
  parser: 'babel-eslint',
  extends: ['airbnb', 'plugin:flowtype/recommended'],
  env: {
    browser: true,
    node: true,
    es6: true,
  },
  plugins: ['flowtype'],
  rules: {
    // airbnb config modifications
    'no-unused-vars': 'warn', // easier for development
    'linebreak-style': 'off',
    'react/prop-types': 'off', // disable rule until update to Flow v0.53
    'arrow-parens': 'off', // conflict with Prettier
    'react/jsx-filename-extension': ['error', { extensions: ['.js', '.jsx'] }],

    // flowtype
    'flowtype/delimiter-dangle': ['error', 'always-multiline'],
    'flowtype/no-primitive-constructor-types': 'error',
    'flowtype/object-type-delimiter': ['error', 'comma'],
    'flowtype/require-valid-file-annotation': 'error',
    'flowtype/semi': ['error', 'always'],
    'flowtype/space-before-generic-bracket': ['error', 'never'],
  },
  settings: {
    'import/resolver': {
      // TODO - Verfiy we don't need eslint-import-resolver-webpack anymore
      // "webpack": {
      //   "config": webpackConfig
      // },
      node: {
        paths: [paths.appMain],
      },
    },
  },
};

const paths = require('./paths');

// TODO - Verfiy that we don't need eslint-import-resolver-webpack anymore
// const webpackConfig = process.env.NODE_ENV === "development"
//   ? path.join(paths.kickstarterConfig, "webpack.dev.js")
//   : path.join(paths.kickstarterConfig, "webpack.prod.js");

module.exports = {
  parser: 'babel-eslint',
  extends: ['airbnb'],
  env: {
    browser: true,
    node: true,
    es6: true,
  },
  rules: {
    // airbnb config modifications
    'no-unused-vars': 'warn', // easier for development
    'linebreak-style': 'off',
    'react/prop-types': 'off', // disable rule until update to Flow v0.53
    'arrow-parens': 'off', // conflict with Prettier
    'react/jsx-filename-extension': ['error', { extensions: ['.js', '.jsx'] }],
  },
  settings: {
    'import/resolver': {
      // TODO - Verfiy we don't need eslint-import-resolver-webpack anymore
      // "webpack": {
      //   "config": webpackConfig
      // },
      node: {
        paths: [paths.appMain],
      },
    },
  },
};

Remove flowtype rules, because prettier will format flow syntax


import { Menu, MenuItem } from "electron";

export class MainMenu {
  constructor(app, tray) {
    this.app = app;
    this.tray = tray;
  }
  initialize() {
    this.setTrayHandler();
  }
  setTrayHandler(menu) {
    menu = menu || this.buildMenu();
    this.tray.setContextMenu(menu);
    this.tray.on("right-click", () => {
      this.tray.popUpContextMenu();
    });
  }
  buildMenu() {
    const menu = new Menu();
    const quitItem = new MenuItem({
      type: "normal",
      label: "Quit",
      id: "quit",
      accelerator: "Q",
      click: (menuItem, browserWindow, event) => {
        this.app.quit();
      }
    });
    menu.append(quitItem);
    return menu;
  }
}

import { Menu, MenuItem } from "electron";

export class MainMenu {
  constructor(app, tray) {
    this.app = app;
    this.tray = tray;
  }
  initialize() {
    this.setTrayHandler();
  }
  setTrayHandler() {
    this.tray.on("right-click", () => {
      this.tray.popUpContextMenu(this.buildMenu());
    });
  }
  buildMenu() {
    const menu = new Menu();
    const quitItem = new MenuItem({
      type: "normal",
      label: "Quit",
      id: "quit",
      accelerator: "Q",
      click: (menuItem, browserWindow, event) => {
        this.app.quit();
      }
    });
    menu.append(quitItem);
    return menu;
  }
}

Fix right click issue (:


'use strict';

angular.module('members').filter('getMemberIdByFullName', [
  function() {
    /**
     * @param members A member object arrays.
     * @param fullName A member full name that want to find, format is 'firstname-lastname'.
     *
     * @return A member object.
     */
    return function(members, fullName) {
      var nameAry = fullName.split('-');
      var firstName = nameAry[0],
          lastName = nameAry[1];
      var i=0, len=members.length;
      for (; i<len; i++) {
        if (members[i].firstnameen === firstName &&
            members[i].lastnameen === lastName) {
          return members[i];
        }
      }
      return null;
    };
  }
]).filter('thumbnail', [
  function() {
    return function(imgurUrl) {
      if (imgurUrl === undefined) {
        return null;
      } else {
        return (imgurUrl.slice(0, 27) + 'm' + imgurUrl.slice(27));
      }
    };
  }
]);

'use strict';

angular.module('members').filter('getMemberIdByFullName', [
  function() {
    /**
     * @param members A member object arrays.
     * @param fullName A member full name that want to find, format is 'firstname-lastname'.
     *
     * @return A member object.
     */
    return function(members, fullName) {
      var nameAry = fullName.split('-');
      var firstName = nameAry[0],
          lastName = nameAry[1];
      var i=0, len=members.length;
      for (; i<len; i++) {
        if (members[i].firstnameen === firstName &&
            members[i].lastnameen === lastName) {
          return members[i];
        }
      }
      return null;
    };
  }
]).filter('thumbnail', [
  function() {
    return function(imgurUrl) {
      if (imgurUrl === undefined) {
        return null;
      } else {
        return (imgurUrl.slice(0, 26) + 'm' + imgurUrl.slice(26));
      }
    };
  }
]);

Fix imgur `https` to `http`.


import fileReader from './fileReader.js';
import fs from 'fs';
import path from 'path';

var reader = function(folder){
	var result = getFile(folder, '');
	return result;
};

var getFile = function(folder, prefix){
	var files = fs.readdirSync(folder);
	var result = [];
	for(var i = 0; i < files.length; i++){
		var file = files[i];
		if(file.startsWith('.')){ continue; }
		var fullpath = path.join(folder, file);
		if(fs.lstatSync(fullpath).isDirectory()){
			var children = getFile(path.join(folder, file), prefix + file + "/");
			if(children.length > 0){
				result.push({
					fileName: file,
					filePath: prefix + file,
					fullPath: path.join(folder, file),
					children: children
				});
			}
		}
		else{
			result.push({
			fileName: file,
			filePath: prefix + file,
			fullPath: path.join(folder, file),
				...fileReader(fs.readFileSync(fullpath, "utf8"))
			});
		}
	}
	return result;
}

export default reader;

import fileReader from './fileReader.js';
import fs from 'fs';
import path from 'path';

var reader = function(folder){
	var result = getFile(folder, '');
	return result;
};

var getFile = function(folder, prefix){
	var files = fs.readdirSync(folder);
	var result = [];
	for(var i = 0; i < files.length; i++){
		var file = files[i];
		if(file.startsWith('.')){ continue; }
		if(file.endsWith(".link")) {
			var destinationFolder = fs.readFileSync(path.join(folder, file), "utf8");
			var children = getFile(destinationFolder, file + "/");
			console.log("children", children);
			if(children.length > 0){
				result.push({
					fileName: file,
					filePath: file,
					fullPath: destinationFolder,
					children: children
				});
			}
			continue;
		}

		var fullpath = path.join(folder, file);
		if(fs.lstatSync(fullpath).isDirectory()){
			var children = getFile(path.join(folder, file), prefix + file + "/");
			if(children.length > 0){
				result.push({
					fileName: file,
					filePath: prefix + file,
					fullPath: path.join(folder, file),
					children: children
				});
			}
		}
		else{
			result.push({
			fileName: file,
			filePath: prefix + file,
			fullPath: path.join(folder, file),
				...fileReader(fs.readFileSync(fullpath, "utf8"))
			});
		}
	}
	return result;
}

export default reader;

Add link file for queries


declare var foo
declare var foo;
declare function foo(): void
declare function foo(): void;
declare function foo<T>(): void;
declare function foo(x: number, y: string): void;
declare class A {}
declare class A<T> extends B<T> { x: number }
declare class A { static foo(): number; static x : string }
declare class A { static [ indexer: number]: string }
declare class A { static () : number }
declare class A mixins B<T>, C {}

Add test ensuring declare class w/ mixins are stripped


if ( typeof exports === 'object' && typeof exports.nodeName !== 'string' && typeof define !== 'function' ) {
    var define = function( factory ) {
        factory( require, exports, module );
    };
}
/**
 * Detects features. Replacement for Modernizr.
 */

define( function( require, exports, module ) {
    'use strict';
    var features = {
            inputtypes: {}
        },
        inputTypesToTest = [ 'date', 'datetime', 'time' ];

    // test input types
    inputTypesToTest.forEach( function( inputType ) {
        var input = document.createElement( 'input' );
        input.setAttribute( 'type', inputType );
        features.inputtypes[ inputType ] = input.type !== 'text';
    } );

    // test touchscreen presence
    if ( ( 'ontouchstart' in window ) || window.DocumentTouch && document instanceof DocumentTouch ) {
        features.touch = true;
        document.documentElement.classList += ' ' + 'touch';
    } else {
        features.touch = false;
    }

    module.exports = features;
} );

if ( typeof exports === 'object' && typeof exports.nodeName !== 'string' && typeof define !== 'function' ) {
    var define = function( factory ) {
        factory( require, exports, module );
    };
}
/**
 * Detects features. Replacement for Modernizr.
 */

define( function( require, exports, module ) {
    'use strict';
    var features = {
            inputtypes: {}
        },
        inputTypesToTest = [ 'date', 'datetime', 'time' ];

    // test input types
    inputTypesToTest.forEach( function( inputType ) {
        var input = document.createElement( 'input' );
        input.setAttribute( 'type', inputType );
        features.inputtypes[ inputType ] = input.type !== 'text';
    } );

    // test touchscreen presence
    if ( ( 'ontouchstart' in window ) || window.DocumentTouch && document instanceof DocumentTouch ) {
        features.touch = true;
        document.documentElement.classList.add( 'touch' );
    } else {
        features.touch = false;
    }

    module.exports = features;
} );

Fix initialisation issue on Android


(function(global) {

    if(typeof process != 'object') {
        console.log('Running in browser, disabling NodeJS functionality.');
        config.map['fs'] = '@empty';
        config.map['electron'] = '@empty';
        config.map['express'] = '@empty';
        config.map['express-pouchdb'] = '@empty';
    } else {
        console.log('Running as electron app, enabling NodeJS functionality.');
        // ensure that pouchdb is loaded using node's require
        // in order to trigger use of leveldb backend
        //config.map['pouchdb'] = '@node/pouchdb';
        // make sure papaparse is loaded using node's require
        // in order to avoid errors
        config.map['papaparse'] = '@node/papaparse';
    }
    System.config(config);

})(this);

(function(global) {
    config.map['papaparse'] = '@node/papaparse';
    System.config(config);
})(this);

Revert "Revert "remove obsolete browser electron distinction""


'use strict';

var _ = require('lodash');
var glimpse = require('glimpse');

var processMessages = function(messages) {
    return {
        messages: messages,
        groupedById: _.groupBy(messages, 'context.id')
    };
};

// republish Found Summary
(function () {
    function republishFoundSummary(messages) {
        var payload = processMessages(messages);

        glimpse.emit('data.message.summary.found', payload);
    }

    glimpse.on('data.message.summary.found.stream', republishFoundSummary);
    glimpse.on('data.message.summary.found.remote', republishFoundSummary);  //TODO: not yet implemented
})();

// republish Found Details
(function () {
    function republishFoundDetail(messages) {
        var payload = processMessages(messages);

        glimpse.emit('data.message.detail.found', payload);
    }

    glimpse.on('data.message.detail.found.stream', republishFoundDetail);
    glimpse.on('data.message.detail.found.remote', republishFoundDetail);  //TODO: not yet implemented
})();

'use strict';

var _ = require('lodash');
var glimpse = require('glimpse');

var processMessages = function(messages) {
    return {
        messages: messages,
        groupedById: _.groupBy(messages, 'context.id')
    };
};

// republish Found Summary
(function () {
    function republishFoundSummary(messages) {
        var payload = processMessages(messages);

        glimpse.emit('data.message.summary.found', payload);
    }

    glimpse.on('data.message.summary.found.stream', republishFoundSummary);
    glimpse.on('data.message.summary.found.remote', republishFoundSummary);
})();

// republish Found Details
(function () {
    function republishFoundDetail(messages) {
        var payload = processMessages(messages);

        glimpse.emit('data.message.detail.found', payload);
    }

    glimpse.on('data.message.detail.found.stream', republishFoundDetail);
    glimpse.on('data.message.detail.found.remote', republishFoundDetail);
})();

Remove no longer needed comments


'use strict';

angular.module('copayApp.controllers').controller('CopayersController',
  function($scope, $rootScope, $location, backupService, walletFactory, controllerUtils) {

    $scope.backup = function() {
      var w = $rootScope.wallet;
      w.setBackupReady();
      backupService.download(w);
    };

    $scope.downloadBackup = function() {
      var w = $rootScope.wallet;
      backupService.download(w);
    }

    $scope.goToWallet = function() {
      $location.path('/receive');
    };

    $scope.deleteWallet = function() {
      var w = $rootScope.wallet;
      w.disconnect();
      walletFactory.delete(w.id, function() {
        controllerUtils.logout();
      });
    };

    // Cached list of copayers
    $scope.copayers = $rootScope.wallet.getRegisteredPeerIds();

    $scope.copayersList = function() {
      return $rootScope.wallet.getRegisteredPeerIds();
    }

    $scope.isBackupReady = function(copayer) {
      return $rootScope.wallet.publicKeyRing.isBackupReady(copayer.copayerId);
    }

  });

'use strict';

angular.module('copayApp.controllers').controller('CopayersController',
  function($scope, $rootScope, $location, backupService, walletFactory, controllerUtils) {

    $scope.backup = function() {
      var w = $rootScope.wallet;
      w.setBackupReady();
      backupService.download(w);
    };

    $scope.downloadBackup = function() {
      var w = $rootScope.wallet;
      backupService.download(w);
    }

    $scope.goToWallet = function() {
      controllerUtils.updateGlobalAddresses();
      $location.path('/receive');
    };

    $scope.deleteWallet = function() {
      var w = $rootScope.wallet;
      w.disconnect();
      walletFactory.delete(w.id, function() {
        controllerUtils.logout();
      });
    };

    // Cached list of copayers
    $scope.copayers = $rootScope.wallet.getRegisteredPeerIds();

    $scope.copayersList = function() {
      return $rootScope.wallet.getRegisteredPeerIds();
    }

    $scope.isBackupReady = function(copayer) {
      return $rootScope.wallet.publicKeyRing.isBackupReady(copayer.copayerId);
    }

  });

Refresh global addresses when joining


"use strict";

const fs = require('fs');
const fsx = require('../helpers/fs-additions');
const path = require('path');
const _ = require('lodash');

/**
 *  Helper function for populating the context with API spec path and
 *  whether API should be skipped or not
 */
function populateAPIContext(context) {
    return context.logger.task('Deriving API', function() {
        // Make sure the API spec exists
        const appendedContext = {
            api: {

            }
        };

        const apiPath = path.join(context.directories.cwd, 'api.json');
        if (!fsx.fileExists(apiPath)) {
            context.logger.log('API spec missing, skipping');
            appendedContext.api.configuration = undefined;
            appendedContext.api.skip = true;
        } else {
            // Write the API to the staging directory
            const deploymentAPIPath = path.join(context.directories.staging, 'deployment.api.json');
            fs.writeFileSync(deploymentAPIPath, fs.readFileSync(apiPath, 'utf8'));

            appendedContext.api.configuration = deploymentAPIPath;
            appendedContext.api.skip = false;
        }

        return _.assign({}, context, appendedContext);
    });
}

//
// Step that preps the API spec/context for deployment in the next step
//
module.exports = populateAPIContext;

"use strict";

const fs = require('fs');
const fsx = require('../helpers/fs-additions');
const path = require('path');
const _ = require('lodash');

/**
 *  Helper function for populating the context with API spec path and
 *  whether API should be skipped or not
 */
function populateAPIContext(context) {
    return context.logger.task('Deriving API', function() {
        // Make sure the API spec exists
        const appendedContext = {
            api: {

            }
        };

        const apiPath = path.join(context.directories.cwd, 'api.json');
        if (!fsx.fileExists(apiPath)) {
            context.logger.log('API spec missing, skipping');
            appendedContext.api.configuration = undefined;
            appendedContext.api.skip = true;
        } else {
            // Write the API to the staging directory
            const deploymentAPIPath = path.join(context.directories.staging, 'deployment.api.json');

            // Read in the Swagger file
            const apiDefinition = fsx.readJSONFileSync(apiPath);

            // Update the title to match the service name from context
            // (We do similar step with cf.json, so it makes sense to also do it with api.json)
            apiDefinition.info.title = context.project.name;

            // Write the spec back
            fs.writeFileSync(deploymentAPIPath, JSON.stringify(apiDefinition, null, 4));

            appendedContext.api.configuration = deploymentAPIPath;
            appendedContext.api.skip = false;
        }

        return _.assign({}, context, appendedContext);
    });
}

//
// Step that preps the API spec/context for deployment in the next step
//
module.exports = populateAPIContext;

Make sure api.json contains correct title on deployment


'use strict';

var request = require('request');
var Promise = require('bluebird');

var ResourceBase = function(endpoint, config) {
  this.uri    = config.options.baseURI + endpoint;
  this.config = config.options;
};

(function() {

  this._transmit = function(method, uri, qs, form, formData, callback) {

    var opts = {
      url:     this.uri + (uri ? '/' + uri : ''),
      method:  method,
      auth:    { user: this.config.apiKey, password: '' },
      headers: this.config.headers,
      json:    true
    };

    if(qs)       { opts.qs = qs;             }
    if(form)     { opts.form = form;         }
    if(formData) { opts.formData = formData; }

    return new Promise(function(resolve, reject) {
      request(opts, function(err, resp, body) {

        if(err) {
          return reject(err);
        }

        if(body && body.errors) {
          return reject(body.errors);
        }

        return resolve(body);
      });
    }).nodeify(callback);
  };

}).call(ResourceBase.prototype);

module.exports = ResourceBase;

'use strict';

var request   = require('request');
var BBPromise = require('bluebird');

var ResourceBase = function(endpoint, config) {
  this.uri    = config.options.baseURI + endpoint;
  this.config = config.options;
};

(function() {

  this._transmit = function(method, uri, qs, form, formData, callback) {

    var opts = {
      url:     this.uri + (uri ? '/' + uri : ''),
      method:  method,
      auth:    { user: this.config.apiKey, password: '' },
      headers: this.config.headers,
      json:    true
    };

    if(qs)       { opts.qs = qs;             }
    if(form)     { opts.form = form;         }
    if(formData) { opts.formData = formData; }

    return new BBPromise(function(resolve, reject) {
      request(opts, function(err, resp, body) {

        if(err) {
          return reject(err);
        }

        if(body && body.errors) {
          return reject(body.errors);
        }

        return resolve(body);
      });
    }).nodeify(callback);
  };

}).call(ResourceBase.prototype);

module.exports = ResourceBase;

Change Promise to BBPromise as Promise is now a global variable in node and we don't want to overwrite it.


/*
    ___ usage ___ en_US ___
    usage: prolific stdio

        -o, --stdout                    use stdout (default)
        -e, --stderr                    use stderr
            --help                      display this message

    ___ $ ___ en_US ___

        log is required:
            the `--log` address and port is a required argument

        port is not an integer:
            the `--log` port must be an integer

    ___ . ___
*/
require('arguable')(module, require('cadence')(function (async, program) {
    program.helpIf(program.command.params.help)
    var response = {
        moduleName: 'prolific.stdio/stdio.processor',
        parameters: { params: program.command.param },
        argv: program.argv,
        terminal: program.command.terminal
    }
    if (process.mainModule == module) {
        console.log(response)
    }
    return response
}))

module.exports.isProlific = true

Add flag for new module loader.


import mediumZoom from 'src/config/medium-zoom';
import lqip from 'src/modules/lqip';

window.addEventListener('DOMContentLoaded', () => {
  // Run medium zoom
  // mediumZoom();
  lqip({
    afterReplace: (lqipImage, originalImage) => {
      lqipImage.remove();
      mediumZoom(originalImage);
    },
  });
});

import mediumZoom from 'src/config/medium-zoom';
import lqip from 'src/modules/lqip';

window.addEventListener('DOMContentLoaded', () => {
  // Run medium zoom
  // mediumZoom();
  lqip({
    afterReplace: (lqipImage, originalImage) => {
      if (lqipImage) lqipImage.remove();
      mediumZoom(originalImage);
    },
  });
});

Test for the presence of if, before removing


require.config({
    baseUrl: '.',
    paths: {
        jquery: 'js/vendor/jquery',
        underscore: 'js/vendor/underscore',
        backbone: 'js/vendor/backbone',
        localstorage: 'js/vendor/backbone.localStorage',
        foundation: 'js/vendor/foundation',
        reveal: 'js/vendor/foundation.reveal',
        d3: 'js/vendor/d3',
        modernizr: 'js/vendor/modernizr'
    },
    shim: {
        underscore: {
            exports: '_'
        },
        backbone: {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        },
        d3: {
            exports: 'd3'
        },
        modernizr: {
            exports: 'Modernizr'
        },
        foundation: ['jquery'],
        reveal: ['foundation']
    }
});

require(['js/app', 'jquery', 'modernizr', 'reveal'], function(App, $, Modernizr) {
    var checkCompatibility = function() {
        var requiredApis = ['inlinesvg', 'audio', 'localstorage', 'notification'];
        var missingApi = false;
        for (var i = 0 ; i < requiredApis.length && !missingApi ; i++) {
            missingApi = !Modernizr[requiredApis[i]];
        }

        if (missingApi) {
            $('#compatibility-alert').show();
        }
    };
    checkCompatibility();

    var app = new App();
    app.run();

    $(document).foundation();

});

require.config({
    baseUrl: '.',
    paths: {
        jquery: 'js/vendor/jquery',
        underscore: 'js/vendor/underscore',
        backbone: 'js/vendor/backbone',
        localstorage: 'js/vendor/backbone.localStorage',
        foundation: 'js/vendor/foundation',
        reveal: 'js/vendor/foundation.reveal',
        d3: 'js/vendor/d3',
        modernizr: 'js/vendor/modernizr'
    },
    shim: {
        underscore: {
            exports: '_'
        },
        backbone: {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
        },
        d3: {
            exports: 'd3'
        },
        modernizr: {
            exports: 'Modernizr'
        },
        foundation: ['jquery'],
        reveal: ['foundation']
    }
});

require(['js/app', 'jquery', 'modernizr', 'reveal'], function(App, $, Modernizr) {
    var checkCompatibility = function() {
        var requiredApis = ['inlinesvg', 'audio', 'localstorage'];
        var missingApi = false;
        for (var i = 0 ; i < requiredApis.length && !missingApi ; i++) {
            missingApi = !Modernizr[requiredApis[i]];
        }

        if (missingApi) {
            $('#compatibility-alert').show();
        }
    };
    checkCompatibility();

    var app = new App();
    app.run();

    $(document).foundation();

});

Disable notification test for now


import * as reducers from './reducers';
import * as sagas from './sagas';
import * as selectorCreators from './selectorCreators';

export {
  reducers,
  sagas,
  selectorCreators,
};

import * as actions from './actions';
import * as types from './types';
import * as reducers from './reducers';
import * as sagas from './sagas';
import * as selectorCreators from './selectorCreators';

export {
  actions,
  types,
  reducers,
  sagas,
  selectorCreators,
};

Fix actions and types export in settings.


import Worker from '../../universal/models/Worker.js';

export default function () {
  Meteor.methods({
    'launchAllWorkers'() {
      Worker.update({status: 'waiting'}, {$set: {status: 'confirm'}});

      // TODO: Very bad, refactor (rewrite) when time permits
      let query = Worker.find({status: 'confirm'});

      let count = query.count();

      var handle = query.observeChanges({
        removed: function(id) {
          count--;

          if (count == 0) {
            Worker.update({status: 'confirmed'}, {$set: {status: 'launch'}});
            handle.stop();
          }
        }
      });
    }
  });
}

import Worker from '../../universal/models/Worker.js';

export default function () {
  Meteor.methods({
    'launchAllWorkers'() {
      Worker.update({status: 'waiting'}, {$set: {status: 'confirm'}}, {multi: true});

      // TODO: Very bad, refactor (rewrite) when time permits
      let query = Worker.find({status: 'confirm'});

      let count = query.count();

      var handle = query.observeChanges({
        removed: function(id) {
          count--;

          if (count == 0) {
            Worker.update({status: 'confirmed'}, {$set: {status: 'launch'}}, {multi: true});
            handle.stop();
          }
        }
      });
    }
  });
}

Allow multiple workers to be updated from launch method


(function (Andamio) {

  var methodSplitter = /\s+/;

  function _bind(target, obj, name, callback, methodName) {
    if (!callback) {
      throw new Error('Method "' + methodName + '" was configured as an event handler, but does not exist.');
    }

    target.listenTo(obj, name, callback);
  }

  function _unbind(target, obj, name, callback) {
    target.stopListening(obj, name, callback);
  }

  function handler(target, obj, name, methodNames, method) {
    _.each(methodNames, function (methodName) {
      var callback = target[methodName];
      method(target, obj, name, callback, methodName);
    });
  }

  // Iterate the bindings and apply corresponding bidning method
  function iterateEvents(target, obj, bindings, method) {
    if (!obj || !bindings) {
      return;
    }

    // Iterate the bindings and un/bind them
    _.each(bindings, function (methods, name) {
      var methodNames = methods.split(methodSplitter);
      handler(target, obj, name, methodNames, method);
    });
  }

  Andamio.bindEvents = function (target, obj, bindings) {
    iterateEvents(target, obj, bindings, _bind);
  };

  Andamio.unbindEvents = function (target, obj, bindings) {
    iterateEvents(target, obj, bindings, _unbind);
  };

})(Andamio);

(function (Andamio) {

  var methodSplitter = /\s+/;

  function _bind(target, obj, name, callback, methodName) {
    if (!callback) {
      throw new Error('Method "' + methodName + '" was configured as an event handler, but does not exist.');
    }

    target.listenTo(obj, name, callback);
  }

  function _unbind(target, obj, name, callback) {
    target.stopListening(obj, name, callback);
  }

  function handler(target, obj, name, methodNames, method) {
    _.each(methodNames, function (methodName) {
      var callback = target[methodName];
      method(target, obj, name, callback, methodName);
    });
  }

  // Iterate the bindings and apply corresponding bidning method
  function iterateEvents(target, obj, bindings, method) {
    if (!obj || !bindings) {
      return;
    }

    // Iterate the bindings and bind/unbind them
    _.each(bindings, function (methods, name) {
      var methodNames = methods.split(methodSplitter);
      handler(target, obj, name, methodNames, method);
    });
  }

  Andamio.bindEvents = function (target, obj, bindings) {
    iterateEvents(target, obj, bindings, _bind);
  };

  Andamio.unbindEvents = function (target, obj, bindings) {
    iterateEvents(target, obj, bindings, _unbind);
  };

}(Andamio));

Put IIFE brackets inside, improve comment


import { default as React, Component } from 'react';
import { render } from 'react-dom';
import {queryObject} from './ImmutableQuery.js';
import InputRange from 'react-input-range';
var Style = require('./Style.js');

export class AppbaseSlider extends Component {

  constructor(props) {
    super(props);
    this.state = {
      values: {
        min: 0,
        max: 20,
      },
      minThreshold: 0,
      maxThreshold:20
    };
  }
  handleValuesChange(component, values) {
    this.setState({
      values: values,
    });
  }
  handleResults(component, values) {
    queryObject.addShouldClause(this.props.fieldName, values, "Range");
  }
  render() {
    return (
      <div style={Style.divContainer}>
        <InputRange
          maxValue={this.state.maxThreshold}
          minValue={this.state.minThreshold}
          value={this.state.values}
          onChange={this.handleValuesChange.bind(this) }
          onChangeComplete={this.handleResults.bind(this) }
          />
      </div>
    );
  }

}

import { default as React, Component } from 'react';
import { render } from 'react-dom';
import {queryObject} from './ImmutableQuery.js';
import InputRange from 'react-input-range';
var Style = require('./Style.js');

export class AppbaseSlider extends Component {

  constructor(props) {
    super(props);
    this.state = {
      values: {
        min: 0,
        max: 20,
      },
      minThreshold: 0,
      maxThreshold:20,
      currentValues: [],
    };
  }
  handleValuesChange(component, values) {
    this.setState({
      values: values,
    });
  }
  handleResults(component, values) {
    queryObject.removeShouldClause(this.props.fieldName, this.state.currentValues, "Range", true);
    queryObject.addShouldClause(this.props.fieldName, values, "Range");
    this.setState({
      currentValues: values
    });

  }
  render() {
    return (
      <div style={Style.divContainer}>
        <InputRange
          maxValue={this.state.maxThreshold}
          minValue={this.state.minThreshold}
          value={this.state.values}
          onChange={this.handleValuesChange.bind(this) }
          onChangeComplete={this.handleResults.bind(this) }
          />
      </div>
    );
  }

}

Add the current value to the state


import {RequestUtil} from 'mesosphere-shared-reactjs';

import ActionTypes from '../constants/ActionTypes';
import Config from '../config/Config';

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

module.exports = {

  open: function () {
    AppDispatcher.handleSidebarAction({
      type: ActionTypes.REQUEST_SIDEBAR_OPEN,
      data: true
    });
  },

  close: function () {
    AppDispatcher.handleSidebarAction({
      type: ActionTypes.REQUEST_SIDEBAR_CLOSE,
      data: false
    });
  },

  openCliInstructions: function () {
    AppDispatcher.handleSidebarAction({
      type: ActionTypes.REQUEST_CLI_INSTRUCTIONS,
      data: false
    });
  },

  showVersions: function () {
    var host = Config.rootUrl.replace(/:[0-9]{0,4}$/, '');
    var url = host + '/pkgpanda/active.buildinfo.full.json';

    RequestUtil.json({
      url: url,
      success: function (response) {
        AppDispatcher.handleSidebarAction({
          type: ActionTypes.REQUEST_VERSIONS_SUCCESS,
          data: response
        });
      },
      error: function (e) {
        AppDispatcher.handleSidebarAction({
          type: ActionTypes.REQUEST_VERSIONS_ERROR,
          data: e.message
        });
      }
    });
  },

  sidebarWidthChange: function () {
    AppDispatcher.handleSidebarAction({
      type: ActionTypes.SIDEBAR_WIDTH_CHANGE
    });
  }
};

import {RequestUtil} from 'mesosphere-shared-reactjs';

import {
  REQUEST_SIDEBAR_OPEN,
  REQUEST_SIDEBAR_CLOSE,
  REQUEST_CLI_INSTRUCTIONS,
  REQUEST_VERSIONS_SUCCESS,
  REQUEST_VERSIONS_ERROR,
  REQUEST_SIDEBAR_WIDTH_CHANGE
} from '../constants/ActionTypes';
import Config from '../config/Config';

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

module.exports = {

  open: function () {
    AppDispatcher.handleSidebarAction({
      type: REQUEST_SIDEBAR_OPEN,
      data: true
    });
  },

  close: function () {
    AppDispatcher.handleSidebarAction({
      type: REQUEST_SIDEBAR_CLOSE,
      data: false
    });
  },

  openCliInstructions: function () {
    AppDispatcher.handleSidebarAction({
      type: REQUEST_CLI_INSTRUCTIONS,
      data: false
    });
  },

  showVersions: function () {
    var host = Config.rootUrl.replace(/:[0-9]{0,4}$/, '');
    var url = host + '/pkgpanda/active.buildinfo.full.json';

    RequestUtil.json({
      url: url,
      success: function (response) {
        AppDispatcher.handleSidebarAction({
          type: REQUEST_VERSIONS_SUCCESS,
          data: response
        });
      },
      error: function (e) {
        AppDispatcher.handleSidebarAction({
          type: REQUEST_VERSIONS_ERROR,
          data: e.message
        });
      }
    });
  },

  sidebarWidthChange: function () {
    AppDispatcher.handleSidebarAction({
      type: REQUEST_SIDEBAR_WIDTH_CHANGE
    });
  }
};

Fix action type which has incorrectly named ActionType.


function overlay(name) {
    var path = 'story/' + name + '.html';
    var $overlay = $('#overlay').load(path, function() {
        $overlay.show()
            .append($('Close').attr({
                'class': 'button'
            }).click(function() {
                $overlay.hide();
            }));
    });
}

function printHelp() {
    lograw('Navigate with the "hjkl yubn" keys (help)')
        .addClass('important')
        .find('a').click(function() {
            overlay('intro');
            return false;
        });
}

(function() {
    printHelp();
    if (World.load()) {
        log('Game restored. Welcome back, %s.', world.player);
    } else {
        if (!Save.exists('playedBefore')) {
            overlay('intro');
            Save.save('playedBefore', true);
        }
        World.reset();
    }
}());

$(window).unload(function() {
    world.save();
});

/* Get things going. */
world.display();
world.run();

function overlay(name) {
    var path = 'story/' + name + '.html';
    var $overlay = $('#overlay').load(path, function() {
        $overlay.show()
            .append($('Close').attr({
                'class': 'button close'
            }).click(function() {
                $overlay.hide();
            }));
    });
}

function printHelp() {
    lograw('Navigate with the "hjkl yubn" keys (help)')
        .addClass('important')
        .find('a').click(function() {
            overlay('intro');
            return false;
        });
}

(function() {
    printHelp();
    if (World.load()) {
        log('Game restored. Welcome back, %s.', world.player);
    } else {
        if (!Save.exists('playedBefore')) {
            overlay('intro');
            Save.save('playedBefore', true);
        }
        World.reset();
    }
}());

$(window).unload(function() {
    world.save();
});

/* Get things going. */
world.display();
world.run();

Add a close class to the close button.


/*

As a user, I want to be able to find the sum, median, or mean of any list of numbers, whether it has an even or odd number of items. Each of these should be able to be called on their own, not just with each other.
*/

Edit file for readability on Github


// Ref: https://github.com/yangshun/nusmods-v3/tree/master/src/js

export const FIRST_HOUR = 8;
export const LAST_HOUR = 22;
export const CELLS_COUNT = ((LAST_HOUR - FIRST_HOUR) + 1) * 2;
export const LESSON_TYPE_ABBREV = {
  'Design Lecture': 'DLEC',
  Laboratory: 'LAB',
  Lecture: 'LEC',
  'Packaged Lecture': 'PLEC',
  'Packaged Tutorial': 'PTUT',
  Recitation: 'REC',
  'Sectional Teaching': 'SEC',
  'Seminar-Style Module Class': 'SEM',
  Tutorial: 'TUT',
  'Tutorial Type 2': 'TUT2',
  'Tutorial Type 3': 'TUT3',
};

// Ref: https://github.com/yangshun/nusmods-v3/tree/master/src/js

export const FIRST_HOUR = 8;
export const LAST_HOUR = 24;
export const CELLS_COUNT = ((LAST_HOUR - FIRST_HOUR) + 1) * 2;
export const LESSON_TYPE_ABBREV = {
  'Design Lecture': 'DLEC',
  Laboratory: 'LAB',
  Lecture: 'LEC',
  'Packaged Lecture': 'PLEC',
  'Packaged Tutorial': 'PTUT',
  Recitation: 'REC',
  'Sectional Teaching': 'SEC',
  'Seminar-Style Module Class': 'SEM',
  Tutorial: 'TUT',
  'Tutorial Type 2': 'TUT2',
  'Tutorial Type 3': 'TUT3',
};

Add more hours to the day


'use strict';

/**
* Calculate logical lines of code (LLOC) for list of JavaScript files.
*
* @module @stdlib/_tools/js/lloc-file-list
*
* @example
* var analyze = require( '@stdlib/_tools/js/lloc-file-list' );
*
* var files = [ __filename ];
*
* analyze( files, clbk );
*
* function clbk( error, results ) {
*     if ( error ) {
*         throw error;
*     }
*     console.log( JSON.stringify( results ) );
* }
*
* @example
* var analyze = require( '@stdlib/_tools/js/lloc-file-list' ).sync;
*
* var files = [ __filename ];
*
* var results = analyze( files );
* if ( results instanceof Error ) {
*     throw results;
* }
* console.log( JSON.stringify( results ) );
*/

// MODULES //

var setReadOnly = require( '@stdlib/utils/define-read-only-property' );
var analyze = require( './main.js' );
var sync = require( './sync.js' );

// MAIN //

setReadOnly( analyze, 'sync', sync );

// EXPORTS //

module.exports = analyze;

'use strict';

/**
* Calculate logical lines of code (LLOC) for a list of JavaScript files.
*
* @module @stdlib/_tools/static-analysis/js/lloc-file-list
*
* @example
* var analyze = require( '@stdlib/_tools/static-analysis/js/lloc-file-list' );
*
* var files = [ __filename ];
*
* analyze( files, clbk );
*
* function clbk( error, results ) {
*     if ( error ) {
*         throw error;
*     }
*     console.log( JSON.stringify( results ) );
* }
*
* @example
* var analyze = require( '@stdlib/_tools/static-analysis/js/lloc-file-list' ).sync;
*
* var files = [ __filename ];
*
* var results = analyze( files );
* if ( results instanceof Error ) {
*     throw results;
* }
* console.log( JSON.stringify( results ) );
*/

// MODULES //

var setReadOnly = require( '@stdlib/utils/define-read-only-property' );
var analyze = require( './main.js' );
var sync = require( './sync.js' );

// MAIN //

setReadOnly( analyze, 'sync', sync );

// EXPORTS //

module.exports = analyze;

Fix require paths and description


const RtmpServer = require('rtmp-server');
const rtmpToHLS = require('./rtmpToHLS');
const { streamKey } = require('../config.json');
const { deleteVideos, log } = require('./utils');

function server(socket) {
  const rtmpServer = new RtmpServer();

  rtmpServer.listen(1935);

  rtmpServer.on('error', (err) => {
    log('RTMP server error:', 'yellow');
    log(err, 'yellow');
  });

  rtmpServer.on('client', (client) => {
    client.on('connect', () => log(`CONNECT ${client.app}`, 'blue'));

    client.on('play', () => log('PLAY   ', 'blue'));

    client.on('publish', ({ streamName }) => {
      if (streamKey !== streamName) {
        log('Publishing error: Wrong stream key', 'red');

        return;
      }

      deleteVideos();
      rtmpToHLS();
      socket.broadcast.emit('restart', {});
      socket.broadcast.emit('published', {});

      log('PUBLISH', 'blue');
    });

    client.on('stop', () => {
      setTimeout(() => {
        deleteVideos();
      }, 2000);
      socket.broadcast.emit('restart', {});
      socket.broadcast.emit('disconeted', {});
      log('DISCONNECTED', 'red');
    });
  });
}

module.exports = server;

const RtmpServer = require('rtmp-server');
const rtmpToHLS = require('./rtmpToHLS');
const { streamKey } = require('../config.json');
const { deleteVideos, log } = require('./utils');

function server(socket) {
  const rtmpServer = new RtmpServer();

  rtmpServer.listen(1935);

  rtmpServer.on('error', (err) => {
    log('RTMP server error:', 'yellow');
    log(err, 'yellow');
  });

  rtmpServer.on('client', (client) => {
    client.on('connect', () => log(`CONNECT ${client.app}`, 'blue'));

    client.on('play', () => log('PLAY   ', 'blue'));

    client.on('publish', ({ streamName }) => {
      if (streamKey !== streamName) {
        log('Publishing error: Wrong stream key', 'red');

        return;
      }

      deleteVideos();
      rtmpToHLS();
      socket.broadcast.emit('restart', {});
      socket.broadcast.emit('published', {});

      log('PUBLISH', 'blue');
    });

    client.on('stop', () => {
      socket.broadcast.emit('restart', {});
      socket.broadcast.emit('disconeted', {});
      log('DISCONNECTED', 'red');
    });
  });
}

module.exports = server;

Remove delete from stop to avoid errors


/**
 * Tag Manager module initialization.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import { addFilter } from '@wordpress/hooks';

/**
 * Internal dependencies
 */
import { fillFilterWithComponent } from '../../util';
import './datastore';
import { SetupMain } from './components/setup';
import { SettingsMain } from './components/settings';

addFilter(
	'googlesitekit.ModuleSetup-tagmanager',
	'googlesitekit.TagManagerModuleSetup',
	fillFilterWithComponent( SetupMain )
);

addFilter(
	'googlesitekit.ModuleSettingsDetails-tagmanager',
	'googlesitekit.TagManagerModuleSettings',
	fillFilterWithComponent( SettingsMain )
);

/**
 * Tag Manager module initialization.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * WordPress dependencies
 */
import domReady from '@wordpress/dom-ready';
import { addFilter } from '@wordpress/hooks';

/**
 * Internal dependencies
 */
import './datastore';
import Data from 'googlesitekit-data';
import { SetupMain } from './components/setup';
import { SettingsEdit, SettingsView } from './components/settings';
import { STORE_NAME as CORE_MODULES } from '../../googlesitekit/modules/datastore/constants';
import { fillFilterWithComponent } from '../../util';

addFilter(
	'googlesitekit.ModuleSetup-tagmanager',
	'googlesitekit.TagManagerModuleSetup',
	fillFilterWithComponent( SetupMain )
);

domReady( () => {
	Data.dispatch( CORE_MODULES ).registerModule(
		'tagmanager',
		{
			settingsEditComponent: SettingsEdit,
			settingsViewComponent: SettingsView,
		}
	);
} );

Refactor Tag Manager with registered components.


'use strict'

const sass = require('sass')

/**
 * Transform SASS to CSS.
 * @public
 * @param {String} folderPath - Path to the folder containing the SASS file.
 * @param {String} str - SASS.
 * @param {Object} opts - Optional options for the task.
 * @returns {Promise<String>} CSS.
 */
module.exports = async function(folderPath, str, opts) {

	// SASS can't handle empty files
	if (str === '') return str

	// Dismiss sourceMap when output should be optimized
	const sourceMap = opts.optimize !== true

	const result = sass.renderSync({
		data: str,
		includePaths: [ folderPath ],
		sourceMap: sourceMap === true ? '' : false,
		sourceMapEmbed: sourceMap,
		sourceMapContents: sourceMap
	})

	return result.css.toString()

}

'use strict'

const sass = require('sass')

/**
 * Transform SASS to CSS.
 * @public
 * @param {String} folderPath - Path to the folder containing the SASS file.
 * @param {String} str - SASS.
 * @param {Object} opts - Optional options for the task.
 * @returns {Promise<String>} CSS.
 */
module.exports = async function(folderPath, str, opts) {

	// SASS can't handle empty files
	if (str === '') return str

	// Dismiss sourceMap when output should be optimized
	const sourceMap = opts.optimize !== true

	// Use sync method of SASS, because it's faster than the async version
	const result = sass.renderSync({
		data: str,
		includePaths: [ folderPath ],
		sourceMap: sourceMap === true ? '' : false,
		sourceMapEmbed: sourceMap,
		sourceMapContents: sourceMap
	})

	return result.css.toString()

}

Add comment about sync method


/**
 * Get the version of Internet Explorer in use, or undefined
 *
 * @return {?number} ieVersion - IE version as an integer, or undefined if not IE
 */
export function getIEVersion() {
    const match = navigator.userAgent.match(/(?:MSIE |Trident\/.*; rv:)(\d+)/);
    return match ? parseInt(match[1], 10) : undefined;
}

/**
 * Placeholder for future translate functionality
 */
export function translate(str, replaceStrings = null) {
    if (!str) {
        return '';
    }

    let translated = str;
    if (replaceStrings) {
        Object.keys(replaceStrings).forEach((placeholder) => {
            translated = translated.replace(placeholder, replaceStrings[placeholder]);
        });
    }

    return translated;
}

export function getWindowWidth() {
    return window.innerWidth ||
        document.documentElement.clientWidth ||
        document.body.clientWidth;
}

export function getWindowHeight() {
    return window.innerHeight ||
        document.documentElement.clientHeight ||
        document.body.clientHeight;
}

// Returns true if this window is rendered as an iframe inside another window
export function isInIframe() {
    try {
        return window.self !== window.top;
    } catch (e) {
        return true;
    }
}

/**
 * Get the version of Internet Explorer in use, or undefined
 *
 * @return {?number} ieVersion - IE version as an integer, or undefined if not IE
 */
export function getIEVersion() {
    const match = navigator.userAgent.match(/(?:MSIE |Trident\/.*; rv:)(\d+)/);
    return match ? parseInt(match[1], 10) : undefined;
}

/**
 * Placeholder for future translate functionality
 */
export function translate(str, replaceStrings = null) {
    if (!str) {
        return '';
    }

    let translated = str;
    if (replaceStrings) {
        Object.keys(replaceStrings).forEach((placeholder) => {
            translated = translated.replace(placeholder, replaceStrings[placeholder]);
        });
    }

    return translated;
}

export function getWindowWidth() {
    if (typeof window === 'undefined') {
        return 0;
    } else {
        return window.innerWidth ||
        document.documentElement.clientWidth ||
        document.body.clientWidth;
    }
}

export function getWindowHeight() {
    if (typeof window === 'undefined') {
        return 0;
    } else {
    return window.innerHeight ||
        document.documentElement.clientHeight ||
        document.body.clientHeight;
    }
}

// Returns true if this window is rendered as an iframe inside another window
export function isInIframe() {
    try {
        return window.self !== window.top;
    } catch (e) {
        return true;
    }
}

Return 0 for window if server side rendering