require('dotenv').config();

module.exports = shipit => {
    shipit.initConfig({
        production: {
            servers: process.env.SHIPIT_PRODUCTION_SERVER,
            dir: process.env.SHIPIT_PRODUCTION_DIR,
        },
    });

    const dockerApp = 'sudo docker-compose exec app ';

    function options() {
        return { cwd: shipit.config.dir };
    }

    // Git

    shipit.blTask('pull', () => {
        return shipit.remote('git pull', options());
    });

    // JS

    shipit.blTask('install-js-deps', () => {
        return shipit.remote(dockerApp + 'yarn install --production', options());
    });

    shipit.blTask('build-js', ['install-js-deps'], () => {
        return shipit.remote(dockerApp + 'yarn run build', options());
    });

    shipit.blTask('deploy-js', [
        'pull',
        'build-js',
    ]);

    // App

    shipit.blTask('restart-app', () => {
        return shipit.remote('sudo docker-compose restart app', options());
    });

    // Combination Tasks

    shipit.blTask('deploy', [
        'pull',
        'deploy-js',
        'restart-app',
    ]);
}

require('dotenv').config();

module.exports = shipit => {
    shipit.initConfig({
        production: {
            servers: process.env.SHIPIT_PRODUCTION_SERVER,
            dir: process.env.SHIPIT_PRODUCTION_DIR,
        },
    });

    const dockerApp = 'sudo docker-compose exec app ';

    function options() {
        return { cwd: shipit.config.dir };
    }

    // Git

    shipit.blTask('pull', () => {
        return shipit.remote('git pull', options());
    });

    // JS

    shipit.blTask('install-js-deps', () => {
        return shipit.remote(dockerApp + 'yarn install --production', options());
    });

    shipit.blTask('build-js', ['install-js-deps'], () => {
        return shipit.remote(dockerApp + 'yarn run build', options());
    });

    shipit.blTask('deploy-js', [
        'pull',
        'build-js',
    ]);

    // App

    shipit.blTask('restart-app', () => {
        return shipit.remote('sudo docker-compose restart app', options());
    });

    shipit.blTask('splatnet', () => {
        return shipit.remote(dockerApp + 'yarn splatnet', options());
    });

    // Combination Tasks

    shipit.blTask('deploy', [
        'pull',
        'deploy-js',
        'restart-app',
    ]);
}

Add a SplatNet updater command to the Shipit file


"use strict";

const angular = require("angular");

function homeRouting($urlRouterProvider, $stateProvider) {
  $urlRouterProvider.otherwise("/home");

  $stateProvider
    .state("home", {
      url: "/home",
      template: require("./views/home.html"), // include small templates into routing configuration
      lazyLoad: ($transition$) => {
        const $ocLazyLoad = $transition$.injector().get("$ocLazyLoad");

        return new Promise((resolve, reject) => {
          require.ensure([], () => {
            // load whole module
            const module = require("./home");

            $ocLazyLoad.load({
              name: "home"
            });

            if (module.name) {
              resolve(module.name);
            } else {
              reject("Ooops, somethig went wrong!");
            }
          });
        });
      }
    })

    .state("home.about", {
      url: "/about",
      template: require("./views/home.about.html"),
      controller: "HomeAboutController as vm",
    });
}

export default angular
  .module("home.routing", [])
  .config(homeRouting);

"use strict";

const angular = require("angular");

function homeRouting($urlRouterProvider, $stateProvider) {
  $urlRouterProvider.otherwise("/home");

  $stateProvider
    .state("home", {
      url: "/home",
      //template: require("./views/home.html"), // include small templates into routing configuration
      component: "homeComponent",
      lazyLoad: ($transition$) => {
        const $ocLazyLoad = $transition$.injector().get("$ocLazyLoad");

        return new Promise((resolve, reject) => {
          require.ensure([], () => {
            // load whole module
            const module = require("./home");

            $ocLazyLoad.load({
              name: "home"
            });

            if (module.name) {
              resolve(module.name);
            } else {
              reject("Ooops, somethig went wrong!");
            }
          });
        });
      }
    })

    .state("home.about", {
      url: "/about",
      template: require("./views/home.about.html"),
      controller: "HomeAboutController as vm",
    });
}

export default angular
  .module("home.routing", [])
  .config(homeRouting);

Replace template to component. Use component


var Router = require('routes');
var router = new Router();
var st = require('st');
var fs = require('fs');

var mount = st({
  path: __dirname + '/static/', // resolved against the process cwd
  url: 'boot/', // defaults to '/'

  // indexing options
  index: 'index', // auto-index, the default

  dot: false // default: return 403 for any url with a dot-file part

});

router.addRoute('/chain', function(req, res, params, splats) {
  console.log("Got a CHAIN request");
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end("#!ipxe\n\necho Attempting to boot into the chain... \nchain http://${next-server}/boot/${mac}\n");
});

router.addRoute('/boot/:macaddr', function boot(req, res, params, splats) {
  console.log("Got a BOOT request");
  console.log("Just got word that "+params.params.macaddr+" just booted");

  var stat = fs.statSync(__dirname + '/static/' + params.params.macaddr);
  if(stat && stat.isDirectory()){
    mount(req, res);
  } else {
    req.url = '/boot/default';
    mount(req, res);
  }
});

module.exports = router;

var Router = require('routes');
var router = new Router();
var st = require('st');
var fs = require('fs');

var mount = st({
  path: __dirname + '/static/', // resolved against the process cwd
  url: 'boot/', // defaults to '/'

  // indexing options
  index: 'index', // auto-index, the default

  dot: false // default: return 403 for any url with a dot-file part

});

router.addRoute('/chain', function(req, res, params, splats) {
  console.log("Got a CHAIN request");
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end("#!ipxe\n\necho Attempting to boot into the chain... \nchain http://${next-server}/boot/${mac}\n");
});

router.addRoute('/boot/:macaddr', function boot(req, res, params, splats) {
  console.log("Got a BOOT request");
  console.log("Just got word that "+params.params.macaddr+" just booted");

  try {
    var stat = fs.statSync(__dirname + '/static/' + params.params.macaddr);
  } catch (error){
    if(error.code != 'ENOENT')
      throw error;
  }

  if(stat && stat.isDirectory()){
    mount(req, res);
  } else {
    req.url = '/boot/default';
    mount(req, res);
  }
});

module.exports = router;

Fix crash when using default folder


'use strict'

function run (db, msg) {
	// Get random favorite to ask about
	db.getRandomFavorite((err, message) => {
		if (err) {
			console.error(err)
		}
		msg.say({
			text: 'What is your favorite ' + message + "?",
			"attachments": [
			{
				"callback_id": 'nextcancel_callback',
				actions: [
					{ name: 'answer', text: 'Next', type: 'button', value: 'next' },
					{ name: 'answer', text: 'Cancel', type: 'button', value: 'cancel' },
				]
			}]
		})
			.route('favorite_response', { 'favorite' : message }, 60)

	})
}

function response (db, msg, state) {
	var favorite = state.favorite
	db.saveUserFavorite(msg.body.event.user, {  favorite : msg.body.event.text, type : favorite }, (err, convo) => {
		if (err)
		{
			console.log(err)
		}
	})
	msg.say('Great, thanks.')
}

module.exports = {
	run: run,
	response: response
}

'use strict'

function run (db, msg) {
	// Get random favorite to ask about
	db.getRandomFavorite((err, message) => {
		if (err) {
			console.error(err)
		}
		msg.say({
			"attachments": [
			{
				'text' : 'What is your favorite ' + message + '?',
				"callback_id": 'nextcancel_callback',
				actions: [
					{ name: 'answer', text: 'Next', type: 'button', value: 'next' },
					{ name: 'answer', text: 'Cancel', type: 'button', value: 'cancel' },
				]
			}]
		})
			.route('favorite_response', { 'favorite' : message }, 60)

	})
}

function response (db, msg, state) {
	var favorite = state.favorite
	db.saveUserFavorite(msg.body.event.user, {  favorite : msg.body.event.text, type : favorite }, (err, convo) => {
		if (err)
		{
			console.log(err)
		}
	})
	msg.say('Great, thanks.')
}

module.exports = {
	run: run,
	response: response
}

Move text to body of attachment.


import { ROUTER_DID_CHANGE, REPLACE_ROUTES, HISTORY_API } from './constants';

/**
 * Action creator for signaling that the router has changed.
 * @private
 * @param  {RouterState} state - New router state
 * @return {Action} Action object
 */
export function routerDidChange(state) {
  return {
    type: ROUTER_DID_CHANGE,
    payload: state
  };
}

/**
 * Action creator that replaces the current route config
 * @private
 * @param {Array<Route>|ReactElement} routes - New routes
 */
export function replaceRoutes(routes) {
  return {
    type: REPLACE_ROUTES,
    payload: routes
  };
}

/**
 * Creates an action creator for calling a history API method.
 * @param {string} method - Name of method
 * @returns {ActionCreator} Action creator with same parameters as corresponding
 * history method
 */
export function historyAPI(method) {
  return (...args) => ({
    type: HISTORY_API,
    payload: {
      method,
      args
    }
  });
}

export const pushState = historyAPI('pushState');
export const replaceState = historyAPI('replaceState');
export const setState = historyAPI('setState');
export const go = historyAPI('go');
export const goBack = historyAPI('goBack');
export const goForward = historyAPI('goForward');

import { ROUTER_DID_CHANGE, REPLACE_ROUTES, HISTORY_API } from './constants';

/**
 * Action creator for signaling that the router has changed.
 * @private
 * @param  {RouterState} state - New router state
 * @return {Action} Action object
 */
export function routerDidChange(state) {
  return {
    type: ROUTER_DID_CHANGE,
    payload: state
  };
}

/**
 * Action creator that replaces the current route config
 * @private
 * @param {Array<Route>|ReactElement} routes - New routes
 */
export function replaceRoutes(routes) {
  return {
    type: REPLACE_ROUTES,
    payload: routes
  };
}

/**
 * Creates an action creator for calling a history API method.
 * @param {string} method - Name of method
 * @returns {ActionCreator} Action creator with same parameters as corresponding
 * history method
 */
export function historyAPI(method) {
  return (...args) => ({
    type: HISTORY_API,
    payload: {
      method,
      args
    }
  });
}

export const pushState = historyAPI('pushState');
export const push = historyAPI('push');
export const replaceState = historyAPI('replaceState');
export const replace = historyAPI('replace');
export const setState = historyAPI('setState');
export const go = historyAPI('go');
export const goBack = historyAPI('goBack');
export const goForward = historyAPI('goForward');

Add "push" and "replace" handy methods


// Copyright 2019 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.

function initMap(mapLocation) {
  let myLatLng = {lat: mapLocation.lat, lng: mapLocation.lng};
  map = new google.maps.Map(document.getElementById('map'), {
    zoom: 13,
    center: myLatLng,
    mapTypeControl: false
  });
}

function createMarker(listing, cardNumber) {
  let marker = new google.maps.Marker({
    position: {lat: listing.mapLocation.lat, lng: listing.mapLocation.lng},
  });

  marker.addListener('click', function() {
    map.setZoom(16);
    map.setCenter(marker.getPosition());
    let firstId = document.getElementById('results-content').firstChild.id;
    console.log(firstId);
    displayCards(cardNumber-firstId);
    document.getElementById(cardNumber).style.backgroundColor = '#b3ffb3';
  });
  marker.setMap(map);
}

// Copyright 2019 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.

let highlightedId = 0;

function initMap(mapLocation) {
  let myLatLng = {lat: mapLocation.lat, lng: mapLocation.lng};
  map = new google.maps.Map(document.getElementById('map'), {
    zoom: 13,
    center: myLatLng,
    mapTypeControl: false
  });
}

function createMarker(listing, cardNumber) {
  let marker = new google.maps.Marker({
    position: {lat: listing.mapLocation.lat, lng: listing.mapLocation.lng},
  });

  marker.addListener('click', function() {
    document.getElementById(highlightedId).style.backgroundColor = '#fff';
    map.setCenter(marker.getPosition());
    map.setZoom(16);
    let firstId = document.getElementById('results-content').firstChild.id;
    // If card is not on the current page
    if ((cardNumber-firstId < 0) || (cardNumber-firstId >= 3)) {
      displayCards(cardNumber-firstId);
    }
    document.getElementById(cardNumber).style.backgroundColor = '#b3ffb3';
    highlightedId = cardNumber;
  });
  marker.setMap(map);
}

Fix for if on the same page


import React from 'react'
import { Grid, Typography} from '@material-ui/core'
import { Edit, Delete} from '@material-ui/icons'

export default function TravelObject(props) {
    let content = null;
    switch(props.type) {
        case 'event':
            content =
Event!

            break;
        case 'flight':
            content = <Typography variant="h4" gutterBottom>Flight!</Typography>
            break;
        case 'hotel':
            content =
Hotel!

            break;
        default:
            console.log("Invalid type");
            break;
    }
    return (
        <Grid container direction="row" justify="center" alignItems="center">
            <Grid item>
                {content}
            </Grid>
            <Grid item>
                <Grid container direction="column">
                    <Edit
                        onClick={() => console.log("editing")}
                    />
                    <Delete
                        onClick={() => console.log("deleting")}
                    />
                </Grid>
            </Grid>
        </Grid>
    )

}

import React from 'react'
import { Box, Grid, Typography, Card, CardContent } from '@material-ui/core'
import { makeStyles } from '@material-ui/core/styles'
import { Edit, Delete } from '@material-ui/icons'

export default function TravelObject(props) {
    let content = null;
    switch (props.type) {
        case 'event':
            content = <Typography variant="h4" gutterBottom>Event!</Typography>
            break;
        case 'flight':
            content = <Typography variant="h4" gutterBottom>Flight!</Typography>
            break;
        case 'hotel':
            content = <Typography variant="h4" gutterBottom>Hotel!</Typography>
            break;
        default:
            console.log("Invalid type");
            break;
    }
    return (
        <Card>
            <CardContent>
                <Grid container direction="row" justify="flex-end" alignItems="center">
                    <Box mr={10} width="100%" height="100%">
                        {content}
                    </Box>
                    <Grid item>
                            <Grid container direction="column">
                                <Edit
                                    onClick={() => console.log("editing")}
                                />
                                <Delete
                                    onClick={() => console.log("deleting")}
                                />
                            </Grid>
                    </Grid>
                </Grid>
            </CardContent>
        </Card>
    )
}

Add proper spacing to inner elements


class GameboardController {
  constructor(Game, GlobalEvents) {
    "ngInject";

    this.currentAnswer = '';
    this.currentMangledWord = Game.currentWord.mangled;
    this.game = Game;
    this.playerName = Game.playerName;
    this.remainingTime = Game.remainingSeconds;
    this.score = 0;
    GlobalEvents.on(Game.NEW_WORD, (newWord) => this.onNewWord(newWord));
    GlobalEvents.on(Game.SCORE_UPDATED, (newScore) => this.onNewScore(newScore));
    GlobalEvents.on(Game.TIME_UPDATED, (newTime) => this.onTimeUpdated(newTime));
    GlobalEvents.on(Game.TIME_IS_UP, () => this.onTimeIsUp());
  }

  onAnswerChange() {
    this.game.setAnswer(this.currentAnswer);
  }

  onNewScore(newScore) {
    this.score = newScore;
  }

  onNewWord(newWord) {
    this.currentAnswer = '';
    this.currentMangledWord = newWord.mangled;
  }

  onTimeIsUp() {
    // TODO
  }

  onTimeUpdated(newTime) {
    this.remainingTime = newTime;
  }
}

export default GameboardController;

class GameboardController {
  constructor(Game, GlobalEvents) {
    "ngInject";

    this.currentAnswer = '';
    this.currentMangledWord = Game.currentWord.mangled;
    this.game = Game;
    this.remainingTime = Game.remainingSeconds;
    this.score = 0;
    GlobalEvents.on(Game.NEW_WORD, (newWord) => this.onNewWord(newWord));
    GlobalEvents.on(Game.SCORE_UPDATED, (newScore) => this.onNewScore(newScore));
    GlobalEvents.on(Game.TIME_UPDATED, (newTime) => this.onTimeUpdated(newTime));
    GlobalEvents.on(Game.TIME_IS_UP, () => this.onTimeIsUp());
  }

  get playerName() {
    return this.game.playerName;
  }

  onAnswerChange() {
    this.game.setAnswer(this.currentAnswer);
  }

  onNewScore(newScore) {
    this.score = newScore;
  }

  onNewWord(newWord) {
    this.currentAnswer = '';
    this.currentMangledWord = newWord.mangled;
  }

  onTimeIsUp() {
    // TODO
  }

  onTimeUpdated(newTime) {
    this.remainingTime = newTime;
  }
}

export default GameboardController;

Move GameboardController.playerName to a getter


const express = require('express');

const router = express.Router();

router.use(require('cookie-parser')());

router.get('/:owner/:project/:type?/:asset?', [
  function (req, res, next) {
    if (req.subdomains.indexOf('edit') > -1) next('route');
    next();
  },
  require('../api/get-project-from-db'),
  require('../api/get-gist-from-github'),
  require('../api/get-repo-from-github'),
  require('../api/redirect-if-missing-type'),
  require('./verify-or-get-short-url'),
  require('./render-report'),
  require('./render-presentation'),
  require('./serve-asset')
]);

module.exports = router;

const express = require('express');

const router = express.Router();

router.use(require('cookie-parser')());

router.get('/:owner/:project/:type?/:asset?', [
  function (req, res, next) {
    if (req.subdomains.indexOf('edit') > -1) return next('route');
    next();
  },
  require('../api/get-project-from-db'),
  require('../api/get-gist-from-github'),
  require('../api/get-repo-from-github'),
  require('../api/redirect-if-missing-type'),
  require('./verify-or-get-short-url'),
  require('./render-report'),
  require('./render-presentation'),
  require('./serve-asset')
]);

module.exports = router;

Make sure the view router bails out when not needed


import React from 'react';
import Radium from 'radium';

import { lightActive, lightInactive } from 'theme/variables';

const size = 18;
const innerPadding = 4;

@Radium
class Light extends React.Component {
  render() {
    const { active } = this.props;
    const styles = {
      outer: {
        position: 'relative',
        backgroundColor: 'rgba(0,0,0,0.4)',
        width: size, height: size,
        borderRadius: '50%'
      },
      inner: {
        position: 'absolute',
        left: innerPadding,
        right: innerPadding,
        top: innerPadding,
        bottom: innerPadding,
        borderRadius: '50%',

        backgroundColor: active ? lightActive : lightInactive
      }
    };

    return (

    );
  }
}

Light.propTypes = {
  active: React.PropTypes.bool.isRequired
};

export default Light;

import React from 'react';
import Radium from 'radium';

import { lightActive, lightInactive } from 'theme/variables';

const size = 18;
const innerPadding = 4;

@Radium
class Light extends React.Component {
  render() {
    const { active } = this.props;

    const baseInnerStyle = {
      position: 'absolute',
      left: innerPadding,
      right: innerPadding,
      top: innerPadding,
      bottom: innerPadding,
      borderRadius: '50%',
    };

    const styles = {
      outer: {
        position: 'relative',
        backgroundColor: 'rgba(0,0,0,0.4)',
        width: size, height: size,
        borderRadius: '50%'
      },
      innerInactive: {
        ...baseInnerStyle,
        backgroundColor: lightInactive
      },
      innerActive: {
        ...baseInnerStyle,
        backgroundColor: lightActive,
        opacity: active ? 1 : 0
      }
    };

    return (

    );
  }
}

Light.propTypes = {
  active: React.PropTypes.bool.isRequired
};

export default Light;

Update light component to use changing opacity instead of changing color


/* eslint-env node */
module.exports = {
  test_page: 'tests/index.html?hidepassed',
  disable_watching: true,
  launch_in_ci: ['Chrome', 'Firefox'],
  launch_in_dev: ['Chrome', 'Firefox'],
  browser_args: {
    Chrome: {
      mode: 'ci',
      args: ['--disable-gpu', '--headless', '--remote-debugging-port=0', '--window-size=1440,900'],
    },
    Firefox: {
      mode: 'ci',
      args: ['-headless'],
    },
  },
};

/* eslint-env node */
module.exports = {
  test_page: 'tests/index.html?hidepassed',
  disable_watching: true,
  launch_in_ci: ['Chrome', 'Firefox'],
  launch_in_dev: ['Chrome', 'Firefox'],
  browser_args: {
    Chrome: {
      mode: 'ci',
      args: [
        '--disable-gpu',
        '--headless',
        '--remote-debugging-port=0',
        '--window-size=1440,900',
        '--no-sandbox',
      ],
    },
    Firefox: {
      mode: 'ci',
      args: ['-headless'],
    },
  },
};

Work around Chrome sandbox issue 😭


import React from 'react';
import './user-export.scss';

class UserExport extends React.Component {
  render() {
    return (

User Export

Generate a .csv of all users who have opted in to receive additional messaging. The generated file will be emailed to you when complete.

          <a className="button -secondary" href={`/exports/${this.props.campaign.id}`}>📩 Export

    );
  }
}
export default UserExport;

import React from 'react';
import './user-export.scss';

class UserExport extends React.Component {
  render() {
    return (

User Export

Generate a .csv of all users signuped via web and who have opted in to receive additional messaging. The generated file will be emailed to you when complete.

          <a className="button -secondary" href={`/exports/${this.props.campaign.id}`}>📩 Export

    );
  }
}
export default UserExport;

Update messaging to reflect only web signups will be exported


'use strict';

const Debug = require('debug');

module.exports = function MessageEventDefinition(eventDefinition) {
  const id = eventDefinition.id;
  const type = eventDefinition.type;
  const debug = Debug(`bpmn-engine:${type.toLowerCase()}`);
  const cancelActivity = eventDefinition.cancelActivity;
  const eventState = {};

  const definitionApi = {
    id,
    type,
    cancelActivity,
    activate
  };

  return definitionApi;

  function activate(parentApi, parentExecutionContext, emit) {
    let deactivated;

    debug(`<${id}> listen for messages`);

    const activityApi = {
      type,
      cancelActivity,
      deactivate,
      getState,
      execute,
      stop
    };

    parentExecutionContext.addStateSource(getState);
    parentExecutionContext.addStopFn(stop);

    emit('enter', activityApi, parentExecutionContext);

    return activityApi;

    function execute() {
      if (deactivated) return;

      eventState.waiting = true;

      parentExecutionContext.postpone((err, ...args) => {
        eventState.waiting = undefined;

        if (err) return emit('error', err);

        parentExecutionContext.assignResult(...args);

        debug(`<${id}> message received`);

        emit('end', activityApi, parentExecutionContext);
        emit('leave', activityApi, parentExecutionContext);
      });

      emit('wait', activityApi, parentExecutionContext);
    }

    function getState() {
      return Object.assign({}, eventState);
    }

    function stop() {
      deactivate();
    }

    function deactivate() {
      deactivated = true;
    }
  }
};

'use strict';

const Debug = require('debug');

module.exports = function MessageEventDefinition(eventDefinition) {
  const {id, type, cancelActivity} = eventDefinition;
  const debug = Debug(`bpmn-engine:${type.toLowerCase()}`);
  const io = eventDefinition.getIO();

  const eventState = {};

  const definitionApi = {
    id,
    type,
    cancelActivity,
    activate
  };

  return definitionApi;

  function activate(parentApi, parentExecutionContext, emit) {
    let deactivated;

    debug(`<${id}> listen for messages`);

    const activityApi = {
      type,
      cancelActivity,
      deactivate,
      getState,
      execute,
      stop
    };

    parentExecutionContext.addStateSource(getState);
    parentExecutionContext.addIoSource(io);
    parentExecutionContext.addStopFn(stop);

    emit('enter', activityApi, parentExecutionContext);

    return activityApi;

    function execute() {
      if (deactivated) return;

      eventState.waiting = true;

      parentExecutionContext.postpone((err, ...args) => {
        eventState.waiting = undefined;

        if (err) return emit('error', err);

        io.setResult(...args);

        debug(`<${id}> message received`);

        emit('end', activityApi, parentExecutionContext);
        emit('leave', activityApi, parentExecutionContext);
      });

      emit('wait', activityApi, parentExecutionContext);
    }

    function getState() {
      return Object.assign({}, eventState);
    }

    function stop() {
      deactivate();
    }

    function deactivate() {
      deactivated = true;
    }
  }
};

Add io to message event definition


(function($, pm) {

    /*
     * Equal Box heights
     */
    pm.directive('[data-plenty-equal]', function(i, elem) {
        var mediaSizes = $(elem).data('plenty-equal').replace(/\s/g, '').split(',');

        var targets = ( $(elem).find('[data-plenty-equal-target]').length > 0 ) ? $(elem).find('[data-plenty-equal-target]') : $(elem).children();

        var maxHeight = 0;
        $(targets).each(function(j, child) {

            $(child).css('height', '');

            if( $(child).outerHeight(true) > maxHeight ) {
                maxHeight = $(child).outerHeight(true);
            }
        });

        if( !mediaSizes || $.inArray( window.mediaSize, mediaSizes ) >= 0 ) targets.height(maxHeight);

    }, [], true);

    // refresh calculation on window resize
    $(window).on('sizeChange', function() {
        pm.getInstance().bindDirectives( '[data-plenty-equal]' );
    });

}(jQuery, PlentyFramework));

(function($, pm) {

    /*
     * Equal Box heights
     */
    pm.directive('[data-plenty-equal]', function(i, elem, MediaSize) {
        var mediaSizes = $(elem).data('plenty-equal').replace(/\s/g, '').split(',');

        var targets = ( $(elem).find('[data-plenty-equal-target]').length > 0 ) ? $(elem).find('[data-plenty-equal-target]') : $(elem).children();

        var maxHeight = 0;
        $(targets).each(function(j, child) {

            $(child).css('height', '');

            if( $(child).outerHeight(true) > maxHeight ) {
                maxHeight = $(child).outerHeight(true);
            }
        });

        if( !mediaSizes || $.inArray( MediaSize.interval(), mediaSizes ) >= 0 ) targets.height(maxHeight);

    }, ['MediaSize'], true);

    // refresh calculation on window resize
    $(window).on('sizeChange', function() {
        pm.getInstance().bindDirectives( '[data-plenty-equal]' );
    });

}(jQuery, PlentyFramework));

Fix directive for equalizing heights: Injecting MediaSize-Service instead of checking old window.mediaSize


import { describe, it, beforeEach, afterEach } from 'mocha';
import { expect } from 'chai';
import startApp from '../helpers/start-app';
<% if (destroyAppExists) { %>import destroyApp from '../helpers/destroy-app';<% } else { %>import Ember from 'ember';<% } %>

describe('<%= friendlyTestName %>', function() {
  let application;

  beforeEach(function() {
    application = startApp();
  });

  afterEach(function() {
    <% if (destroyAppExists) { %>destroyApp(application);<% } else { %>Ember.run(application, 'destroy');<% } %>
  });

  it('can visit /<%= dasherizedModuleName %>', function() {
    visit('/<%= dasherizedModuleName %>');

    andThen(function() {
      expect(currentURL()).to.equal('/<%= dasherizedModuleName %>');
    });
  });
});

import { describe, it, beforeEach, afterEach } from 'mocha';
import { expect } from 'chai';
import startApp from '<%= dasherizedPackageName %>/helpers/start-app';
<% if (destroyAppExists) { %>import destroyApp from '<%= dasherizedPackageName %>/helpers/destroy-app';<% } else { %>import Ember from 'ember';<% } %>

describe('<%= friendlyTestName %>', function() {
  let application;

  beforeEach(function() {
    application = startApp();
  });

  afterEach(function() {
    <% if (destroyAppExists) { %>destroyApp(application);<% } else { %>Ember.run(application, 'destroy');<% } %>
  });

  it('can visit /<%= dasherizedModuleName %>', function() {
    visit('/<%= dasherizedModuleName %>');

    andThen(function() {
      expect(currentURL()).to.equal('/<%= dasherizedModuleName %>');
    });
  });
});

Use absolute path in mocha acceptance test blueprints


const TypeDocPlugin = require('typedoc-webpack-plugin');
const path = require('path');
const webpack = require('webpack');

module.exports = {
    entry: './spotify-oauth.ts',
    module: {
        rules: [{
            test: /(\.ts)/,
            exclude: /node_modules/,
            use: 'awesome-typescript-loader'
        }]
    },
    resolve: {
        extensions: ['.js', '.ts']
    },
    devtool: 'source-map',
    externals: [
        "cordova",
        "cordova/exec",
        "NativeStorage"
    ],
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: 'spotify-oauth.min.js',
        library: 'spotifyAuth',
        libraryTarget: 'commonjs'
    },
    plugins: [
        new TypeDocPlugin({
            excludeExternals: true,
            excludePrivate: true,
            ignoreCompilerErrors: true,
            name: "Cordova Spotify OAuth Plugin",
            mode: 'module',
            readme: 'none',
            target: 'ES6'
        }, './spotify-oauth.ts')
    ]
};

const TypeDocPlugin = require('typedoc-webpack-plugin');
const path = require('path');
const webpack = require('webpack');

module.exports = {
    entry: './spotify-oauth.ts',
    module: {
        rules: [{
            test: /(\.ts)/,
            exclude: /node_modules/,
            use: 'awesome-typescript-loader'
        }]
    },
    resolve: {
        extensions: ['.js', '.ts']
    },
    devtool: 'source-map',
    externals: [
        "cordova",
        "cordova/exec",
        "NativeStorage"
    ],
    output: {
        path: path.resolve(__dirname, 'build'),
        filename: 'spotify-oauth.min.js',
        library: 'spotifyAuth',
        libraryTarget: 'commonjs'
    },
    plugins: [
        new TypeDocPlugin({
            excludeExternals: true,
            excludePrivate: true,
            ignoreCompilerErrors: true,
            name: "Cordova Spotify OAuth Plugin",
            mode: 'file',
            readme: 'none',
            target: 'ES6'
        }, './spotify-oauth.ts')
    ]
};

Revert ":memo: Switch TypeDoc to modules mode"


var https = require('https');
var yaml = require('js-yaml');
var request = require('request');

// The URL of the data file with GitHub's language colors.
exports.languagesURL = 'https://raw.githubusercontent.com/github/linguist/master/lib/linguist/languages.yml';

// Given an Object of languages (from language name Strings to Objects),
// filterColors will return an Object of language name Strings to String hex
// color values, with all color-less languages removed.
exports.filterColors = function (languages) {
  return Object.keys(languages).reduce(function (memo, languageName) {
    var color = languages[languageName].color;

    if (color) {
      memo[languageName] = color;
    }
    return memo;
  }, {});
};

// get accepts a callback, downloads GitHub's language colors,
// creates a new languages Object, and passes it to the callback when it is
// ready. The languages Object has String language name keys and String hex
// color values (with leading "#"s).
exports.get = function (callback) {
  request(exports.languagesURL, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      var languages = exports.filterColors(yaml.safeLoad(body));
      callback(languages);
    } else {
      callback(undefined);
    }
  })
};

var request = require('request');
var yaml = require('js-yaml');

// The URL of the data file with GitHub's language colors.
exports.languagesURL = 'https://raw.githubusercontent.com/github/linguist/master/lib/linguist/languages.yml';

// Given an Object of languages (from language name Strings to Objects),
// filterColors will return an Object of language name Strings to String hex
// color values, with all color-less languages removed.
exports.filterColors = function (languages) {
  return Object.keys(languages).reduce(function (memo, languageName) {
    var color = languages[languageName].color;

    if (color) {
      memo[languageName] = color;
    }
    return memo;
  }, {});
};

// get accepts a callback, downloads GitHub's language colors,
// creates a new languages Object, and passes it to the callback when it is
// ready. The languages Object has String language name keys and String hex
// color values (with leading "#"s).
exports.get = function (callback) {
  request(exports.languagesURL, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      var languages = exports.filterColors(yaml.safeLoad(body));
      callback(languages);
    } else {
      callback(undefined);
    }
  })
};

Remove the extra require for https


import defaultCommands from './commands'
import cliPrompter from './prompters/cliPrompter'
import cliOutputter from './outputters/cliOutputter'

export default class CommandRunner {
  constructor(handlers = {}, commands = defaultCommands) {
    this.handlers = handlers
    this.commands = commands

    if (!handlers.outputter || !handlers.prompter) {
      throw new Error('`prompter` and `outputter` handlers must be defined')
    }
  }

  runCommand(cmdName, options) {
    const command = this.commands.find(cmd => cmd.name === cmdName)
    if (!command) {
      return this.handlers.outputter.error(new Error(`Command '${cmdName} not defined`))
    }

    const {print, error} = this.handlers.outputter
    const {prompt} = this.handlers.prompter
    command.action({print, error, prompt, options})
  }
}

export function getCliRunner(cmds) {
  return new CommandRunner({
    outputter: cliOutputter,
    prompter: cliPrompter
  }, cmds)
}

import defaultCommands from './commands'
import cliPrompter from './prompters/cliPrompter'
import cliOutputter from './outputters/cliOutputter'

export default class CommandRunner {
  constructor(handlers = {}, commands = defaultCommands) {
    this.handlers = handlers
    this.commands = commands

    if (!handlers.outputter || !handlers.prompter) {
      throw new Error('`prompter` and `outputter` handlers must be defined')
    }
  }

  runCommand(cmdName, options) {
    const command = this.commands.find(cmd => cmd.name === cmdName)
    if (!command) {
      return this.handlers.outputter.error(new Error(`Command "${cmdName}" not defined`))
    }

    const {print, error} = this.handlers.outputter
    const {prompt} = this.handlers.prompter
    command.action({print, error, prompt, options})
  }
}

export function getCliRunner(cmds) {
  return new CommandRunner({
    outputter: cliOutputter,
    prompter: cliPrompter
  }, cmds)
}

Fix error message if command is not found


import React from 'react';
import classNames from 'classnames';

class UserAvatar extends React.Component {
  static propTypes = {
    user: React.PropTypes.shape({
      name: React.PropTypes.string.isRequired,
      avatar: React.PropTypes.shape({
        url: React.PropTypes.string.isRequired
      })
    }).isRequired,
    className: React.PropTypes.string,
    style: React.PropTypes.object
  };

  render() {
    return (
      <img src={this.props.user.avatar.url}
        className={classNames("circle", this.props.className)}
        alt={this.props.user.name}
        style={this.props.style} />
    );
  }
}

export default UserAvatar;

import React from 'react';
import classNames from 'classnames';

class UserAvatar extends React.Component {
  static propTypes = {
    user: React.PropTypes.shape({
      name: React.PropTypes.string.isRequired,
      avatar: React.PropTypes.shape({
        url: React.PropTypes.string.isRequired
      })
    }).isRequired,
    className: React.PropTypes.string,
    style: React.PropTypes.object
  };

  render() {
    return (
      <img src={this.props.user.avatar.url}
        className={classNames("circle", this.props.className)}
        alt={this.props.user.name}
        title={this.props.user.name}
        style={this.props.style} />
    );
  }
}

export default UserAvatar;

Add a title for avatar hover


Minimitter = function Minimitter() {
  this.listeners = {};
};

Minimitter.prototype.on = function(name, callback) {
  this.listeners[name] = this.listeners[name] || [];
  this.listeners[name].push(callback);
};

Minimitter.prototype.off = function(name, callback) {
  this.listeners[name] = this.listeners[name] || [];
  var indexOfCallback = this.listeners[name].indexOf(callback);
  if(~indexOfCallback) {
    this.listeners[name].slice(indexOfCallback, 1);
  }
};

Minimitter.prototype.emit = function(name, data) {
  this.listeners[name].forEach(function(callback) {
    try {
      callback(data);
    } catch(err) {
      console.error('A callback has failed.');
    }
  });
};

Minimitter = function Minimitter() {
  this.listeners = {};
};

Minimitter.prototype.on = function(name, callback) {
  this.listeners[name] = this.listeners[name] || [];
  this.listeners[name].push(callback);
};

Minimitter.prototype.off = function(name, callback) {
  this.listeners[name] = this.listeners[name] || [];
  var indexOfCallback = this.listeners[name].indexOf(callback);
  if(~indexOfCallback) {
    this.listeners[name].splice(indexOfCallback, 1);
  }
};

Minimitter.prototype.emit = function(name, data) {
  this.listeners[name].forEach(function(callback) {
    try {
      callback(data);
    } catch(err) {
      console.error('A callback has failed.');
    }
  });
};

Change slice to splice in .off.


let config = {
  reveal: {
    height: 720,
    history: true,
    keyboard: {
      //27: function() { console.log("ESC pressed"); return true },
      //37: function() { console.log("LEFT pressed"); return true },
      //38: function() { console.log("UP pressed"); return true },
      //39: function() { console.log("RIGHT pressed"); return true },
      //40: function() { console.log("DOWN pressed"); return true },
      //66: function() { console.log("b pressed"); return true },
      // disable overview
      27: function() { return false; },
      79: function() { return false; }
      //116: function() { console.log("F5 pressed"); return true }
    },
    onReady: function() {
      window.focus();
    },
    transition: "convex",
    width: 1280
  },
};

export default config;

let config = {
  reveal: {
    controls: false,
    height: 720,
    help: false,
    history: true,
    keyboard: false,
    overview: false,
    touch: false,
    transition: "convex",
    width: 1280
  },
};

export default config;

Disable most keyboard commands and controls


define(['knockout'], function (ko) {
	ko.bindingHandlers.datepicker = {
		init: function (element, valueAccessor, allBindingsAccessor) {
			//initialize datepicker with some optional options
			var options = allBindingsAccessor().datepickerOptions || {};
			$(element).datepicker(options);

			//handle the field changing
			ko.utils.registerEventHandler(element, "change", function () {
				var observable = valueAccessor();
				observable($(element).datepicker("getDate"));
			});

			//handle disposal (if KO removes by the template binding)
			ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
				$(element).datepicker("destroy");
			});

		},
		update: function (element, valueAccessor) {
			var value = ko.utils.unwrapObservable(valueAccessor());

			//handle date data coming via json from Microsoft
			if (String(value).indexOf('/Date(') == 0) {
				value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
			}
			else
				value = new Date(value);

			var current = $(element).datepicker("getDate");

			if (value - current !== 0) {
				$(element).datepicker("setDate", value);
			}
		}
	};
});

define(['knockout'], function (ko) {
	ko.bindingHandlers.datepicker = {
		init: function (element, valueAccessor, allBindingsAccessor) {
			//initialize datepicker with some optional options
			var options = allBindingsAccessor().datepickerOptions || {};
			$(element).datepicker(options);

			//handle the field changing
			ko.utils.registerEventHandler(element, "change", function () {
				var observable = valueAccessor();
				observable($(element).datepicker("getDate"));
			});

			//handle disposal (if KO removes by the template binding)
			ko.utils.domNodeDisposal.addDisposeCallback(element, function () {
				$(element).datepicker("destroy");
			});

		},
		update: function (element, valueAccessor) {
			var value = ko.utils.unwrapObservable(valueAccessor());

			//handle date data coming via json from Microsoft
			if (typeof value === "string")
			{
				if (String(value).indexOf('/Date(') == 0) {
					value = new Date(parseInt(value.replace(/\/Date\((.*?)\)\//gi, "$1")));
				}
				else
					value = new Date(value);

				// offset this timezone to UTC
				var localOffset = value.getTimezoneOffset() * 60000;
				value = new Date(value.getTime() + localOffset);
			}

			var current = $(element).datepicker("getDate");

			if (value - current !== 0) {
				$(element).datepicker("setDate", value);
			}
		}
	};
});

Handle timezones in datepicker binding by resolving the string yy-mm-dd date into the local offset date. This prevents 2013-12-31 resolving into 2013-12-30 19:00 EST (for example).


/*
---

script: Delete.js

description: Deletes a widget or element

license: Public domain (http://unlicense.org).

authors: Yaroslaff Fedin

requires:
  - LSD.Action

provides:
  - LSD.Action.Delete

...
*/

LSD.Action.Delete = LSD.Action.build({
  enable: function(target) {
    if (!target.lsd) LSD.Module.DOM.walk(target, function(node) {
      widget.dispatchEvent('nodeRemoved', node);
    })
    target.dispose();
    if (target.getModel) return target.getModel()['delete']()
  }
});

/*
---

script: Delete.js

description: Deletes a widget or element

license: Public domain (http://unlicense.org).

authors: Yaroslaff Fedin

requires:
  - LSD.Action

provides:
  - LSD.Action.Delete

...
*/

LSD.Action.Delete = LSD.Action.build({
  enable: function(target) {
    if (!target.lsd) {
      var widget = LSD.Module.DOM.find(target);
      LSD.Module.DOM.walk(target, function(node) {
        widget.dispatchEvent('nodeRemoved', node);
      });
    }
    target.dispose();
    if (target.getModel) return target.getModel()['delete']()
  }
});

Fix a bug in delete action, wrong widget notified


/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* 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.
*/

'use strict';

/**
* The Carnegie Mellon Pronouncing Dictionary (CMUdict).
*
* @module @stdlib/datasets/cmudict
*
* @example
* var dict = require( '@stdlib/datasets/cmudict' );
*
* var data = dict();
* // returns {}
*/

// MODULES //

var IS_BROWSER = require( '@stdlib/assert/is-browser' );

// MAIN //

var dict;
if ( IS_BROWSER ) {
	dict = require( './browser.js' );
} else {
	dict = require( './main.js' );
}

// EXPORTS //

module.exports = dict;

/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* 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.
*/

'use strict';

/**
* The Carnegie Mellon Pronouncing Dictionary (CMUdict).
*
* @module @stdlib/datasets/cmudict
*
* @example
* var dict = require( '@stdlib/datasets/cmudict' );
*
* var data = dict();
* // returns {...}
*/

// MODULES //

var IS_BROWSER = require( '@stdlib/assert/is-browser' );

// MAIN //

var dict;
if ( IS_BROWSER ) {
	dict = require( './browser.js' );
} else {
	dict = require( './main.js' );
}

// EXPORTS //

module.exports = dict;

Add dots to indicate a non-empty object


var kunstmaanbundles = kunstmaanbundles || {};

kunstmaanbundles.mediaChooser = (function(window, undefined) {

    var init, initDelBtn;

    var $body = $('body');

    init = function() {
        // Save and update preview can be found in url-chooser.js
        initDelBtn();
    };

    // Del btn
    initDelBtn = function() {
        $body.on('click', '.js-media-chooser-del-preview-btn', function(e) {
            var $this = $(this),
                linkedID = $this.data('linked-id'),
                $widget = $('#' + linkedID + '-widget'),
                $input = $('#' + linkedID);

            $('.media-chooser__preview__img').attr({'src': '', 'srcset': '', 'alt': ''});

            $(".media-thumbnail__icon").remove();

            $widget.removeClass('media-chooser--choosen');
            $input.val('');
        });
    };

    return {
        init: init
    };

})(window);

var kunstmaanbundles = kunstmaanbundles || {};

kunstmaanbundles.mediaChooser = (function(window, undefined) {

    var init, initDelBtn;

    var $body = $('body');

    init = function() {
        // Save and update preview can be found in url-chooser.js
        initDelBtn();
    };

    // Del btn
    initDelBtn = function() {
        $body.on('click', '.js-media-chooser-del-preview-btn', function(e) {
            var $this = $(this),
                linkedID = $this.data('linked-id'),
                $widget = $('#' + linkedID + '-widget'),
                $input = $('#' + linkedID);

            $this.parent('.media-chooser__preview').find('.media-chooser__preview__img').attr({'src': '', 'srcset': '', 'alt': ''});

            $(".media-thumbnail__icon").remove();

            $widget.removeClass('media-chooser--choosen');
            $input.val('');
        });
    };

    return {
        init: init
    };

})(window);

Remove only current image preview in pageparts


import Model from '../base/model';

export default class ViewSwitcher {
  constructor(app, element) {
    this.app = app;
    this.element = element;

    this.currentView = null;
  }

  switchView(ViewClass, args) {
    if (this.currentView instanceof ViewClass) {
      this.currentView.model.replace(args);
      return;
    }

    if (this.currentView) {
      this.currentView.unbind();
      this.element.removeChild(this.currentView.element);
    }

    const model = new Model(args);
    const view = new ViewClass({app: this.app, model: model});
    view.render();

    this.element.appendChild(view.element);

    this.currentView = view;
  }
}

import Model from '../base/model';

export default class ViewSwitcher {
  constructor(app, element) {
    this.app = app;
    this.element = element;

    this.currentView = null;
  }

  switchView(ViewClass, args={}) {
    if (this.currentView instanceof ViewClass) {
      this.currentView.model.replace(args);
      return;
    }

    if (this.currentView) {
      this.currentView.unbind();
      this.element.removeChild(this.currentView.element);
    }

    const model = new Model(args);
    const view = new ViewClass({app: this.app, model: model});
    view.render();

    this.element.appendChild(view.element);

    this.currentView = view;
  }
}

Add default empty args to switchView


import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { createUploadLink } from 'apollo-upload-client';
import csrf from '~/lib/utils/csrf';

export default (resolvers = {}, baseUrl = '') => {
  let uri = `${gon.relative_url_root}/api/graphql`;

  if (baseUrl) {
    // Prepend baseUrl and ensure that `///` are replaced with `/`
    uri = `${baseUrl}${uri}`.replace(/\/{3,}/g, '/');
  }

  return new ApolloClient({
    link: createUploadLink({
      uri,
      headers: {
        [csrf.headerKey]: csrf.token,
      },
    }),
    cache: new InMemoryCache(),
    resolvers,
  });
};

import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { createUploadLink } from 'apollo-upload-client';
import csrf from '~/lib/utils/csrf';

export default (resolvers = {}, config = {}) => {
  let uri = `${gon.relative_url_root}/api/graphql`;

  if (config.baseUrl) {
    // Prepend baseUrl and ensure that `///` are replaced with `/`
    uri = `${config.baseUrl}${uri}`.replace(/\/{3,}/g, '/');
  }

  return new ApolloClient({
    link: createUploadLink({
      uri,
      headers: {
        [csrf.headerKey]: csrf.token,
      },
    }),
    cache: new InMemoryCache(config.cacheConfig),
    resolvers,
  });
};

Add support for second `config` param


var assert = require("chai").assert;
var fs = require("fs-extra");
var glob = require("glob");
var Box = require("truffle-box");
var Profiler = require("truffle-compile/profiler.js");
var Resolver = require("truffle-resolver");
var Artifactor = require("truffle-artifactor");

// TOOD: Move this to truffle-compile!

describe('profiler', function() {
  var config;

  before("Create a sandbox", function(done) {
    this.timeout(10000);
    Box.sandbox(function(err, result) {
      if (err) return done(err);
      config = result;
      config.resolver = new Resolver(config);
      config.artifactor = new Artifactor(config.contracts_build_directory);
      config.network = "development";
      done();
    });
  });

  after("Cleanup tmp files", function(done){
    glob('tmp-*', (err, files) => {
      if(err) done(err);
      files.forEach(file => fs.removeSync(file));
      done();
    })
  })

  it('profiles example project successfully', function(done) {
    Profiler.required_sources(config.with({
      paths: ["./ConvertLib.sol"],
      base_path: config.contracts_directory
    }), function(err, allSources, compilationTargets) {
      if (err) return done(err);

      assert.equal(Object.keys(allSources).length, 3);
      assert.equal(Object.keys(compilationTargets).length, 2);
      done();
    });
  });
});

var assert = require("chai").assert;
var fs = require("fs-extra");
var glob = require("glob");
var Box = require("truffle-box");
var Profiler = require("truffle-compile/profiler.js");
var Resolver = require("truffle-resolver");
var Artifactor = require("truffle-artifactor");

// TOOD: Move this to truffle-compile!

describe('profiler', function() {
  var config;

  before("Create a sandbox", function(done) {
    this.timeout(10000);
    Box.sandbox(function(err, result) {
      if (err) return done(err);
      config = result;
      config.resolver = new Resolver(config);
      config.artifactor = new Artifactor(config.contracts_build_directory);
      config.network = "development";
      done();
    });
  });

  after("Cleanup tmp files", function(done){
    glob('tmp-*', (err, files) => {
      if(err) done(err);
      files.forEach(file => fs.removeSync(file));
      done();
    })
  })

  it('profiles example project successfully', function(done) {
    Profiler.required_sources(config.with({
      paths: ["./ConvertLib.sol"],
      base_path: config.contracts_directory
    }), function(err, allSources, compilationTargets) {
      if (err) return done(err);

      assert.equal(Object.keys(allSources).length, 3);
      assert.equal(compilationTargets.length, 2);
      done();
    });
  });
});

Update test to accept compTarget array


import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import webpackConfig from '../../webpack.config.js';

const webpackCompiler = webpack(webpackConfig);

// enable webpack middleware for hot-reloads in development
export default function useWebpackMiddleware(app) {

    app.use(webpackDevMiddleware(webpackCompiler, {
        publicPath: webpackConfig.output.publicPath,
        stats: {
            colors: true,
            chunks: false,
            'errors-only': true
        }
    }));

    app.use(webpackHotMiddleware(webpackCompiler, {
        log: console.log
    }));

    return app;
}

import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import webpackConfig from '../../../config/webpack.dev.js';

const webpackCompiler = webpack(webpackConfig);

// enable webpack middleware for hot-reloads in development
export default function useWebpackMiddleware(app) {

    app.use(webpackDevMiddleware(webpackCompiler, {
        publicPath: webpackConfig.output.publicPath,
        stats: {
            colors: true,
            chunks: false,
            'errors-only': true
        }
    }));

    app.use(webpackHotMiddleware(webpackCompiler, {
        log: console.log
    }));

    return app;
}

Change webpack config import path


/*
 * action types
 */

export const UPDATE = 'UPDATE'
export const NEXT_LOCATION = 'NEXT_LOCATION'
export const PREVIOUS_LOCATION = 'PREVIOUS_LOCATION'

/*
* action creators
*/

export function update() {
  return { type: UPDATE }
}

export function nextLocation() {
  return { type: NEXT_LOCATION }
}

export function previousLocation() {
  return { type: PREVIOUS_LOCATION }
}

import request from 'axios';
import fetch from 'isomorphic-fetch'

/*
 * action types
 */

export const REQUEST_DATA = 'REQUEST_DATA'
export const RECEIVE_DATA = 'RECEIVE_DATA'
export const INVALIDE_DATA = 'INVALIDE_DATA'

/*
 * action creators
 */

const API_URL = 'http://api.openweathermap.org/data/2.5/weather?q=${location}&appid=2de143494c0b295cca9337e1e96b00e0'

export function requestWeather(location) {
    return {
        type: REQUEST_DATA,
        location
    }
}

export function invalideWeather(err, msg) {
    return {
        type: INVALIDE_DATA,
        error: err,
        message: msg
    }
}

export function receiveWeather(json) {
    return {
        type: RECEIVE_DATA,
        daily: json,
        receivedAt: Date.now()
    }
}

export function fetchLocations(location) {
    return dispatch => {
        dispatch(requestWeather(location))
        return fetch(API_URL)
            .then(req => req.json())
            .then(fetchStatus)
            .then(function(data) {
                dispatch(receiveWeather(data))
            }).catch(function(error) {
                dispatch(invalideWeather(error.response.cod, error.message))
            })
    }
}

function fetchStatus(response){
    if(response.cod >= 200 && response.cod < 300){
        return response
    }else{
        var error = new Error(response.message)
        error.response = response
        throw error
    }
}

Add fetching to the API and related actions


'use strict'

// native runtime support for AWS
// https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html

// .NET CORE
const supportedDotnetcore = [
  // deprecated
  // 'dotnetcore1.0',
  // 'dotnetcore2.0',
  // supported
  // 'dotnetcore2.1'
]

// GO
const supportedGo = ['go1.x']

// JAVA
const supportedJava = [
  // 'java8'
]

// NODE.JS
const supportedNodejs = [
  // deprecated, but still working
  'nodejs4.3',
  'nodejs6.10',
  // supported
  'nodejs8.10',
  'nodejs10.x',
]

// PROVIDED
const supportedProvided = ['provided']

// PYTHON
const supportedPython = ['python2.7', 'python3.6', 'python3.7']

// RUBY
const supportedRuby = ['ruby2.5']

exports.supportedDotnetcore = supportedDotnetcore
exports.supportedGo = supportedGo
exports.supportedJava = supportedJava
exports.supportedNodejs = supportedNodejs
exports.supportedProvided = supportedProvided
exports.supportedPython = supportedPython
exports.supportedRuby = supportedRuby

// deprecated runtimes
// https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html
exports.supportedRuntimes = new Set([
  ...supportedDotnetcore,
  ...supportedGo,
  ...supportedJava,
  ...supportedNodejs,
  ...supportedProvided,
  ...supportedPython,
  ...supportedRuby,
])

'use strict'

// native runtime support for AWS
// https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html

// .NET CORE
const supportedDotnetcore = [
  // deprecated
  // 'dotnetcore1.0',
  // 'dotnetcore2.0',
  // supported
  // 'dotnetcore2.1'
]

// GO
const supportedGo = [
  // 'go1.x'
]

// JAVA
const supportedJava = [
  // 'java8'
]

// NODE.JS
const supportedNodejs = [
  // deprecated, but still working
  'nodejs4.3',
  'nodejs6.10',
  // supported
  'nodejs8.10',
  'nodejs10.x',
]

// PROVIDED
const supportedProvided = ['provided']

// PYTHON
const supportedPython = ['python2.7', 'python3.6', 'python3.7']

// RUBY
const supportedRuby = ['ruby2.5']

exports.supportedDotnetcore = supportedDotnetcore
exports.supportedGo = supportedGo
exports.supportedJava = supportedJava
exports.supportedNodejs = supportedNodejs
exports.supportedProvided = supportedProvided
exports.supportedPython = supportedPython
exports.supportedRuby = supportedRuby

// deprecated runtimes
// https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html
exports.supportedRuntimes = new Set([
  ...supportedDotnetcore,
  ...supportedGo,
  ...supportedJava,
  ...supportedNodejs,
  ...supportedProvided,
  ...supportedPython,
  ...supportedRuby,
])

Remove 'go' from suported runtimes


/**
 * Copyright 2016 Google Inc.
 *
 * 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.
 */

export function classCallCheck() {}

export function createClass(Constructor, protoProps) {
  const prototype = Constructor.prototype;
  for (let i = 0; i < protoProps.length; i++) {
    const descriptor = protoProps[i];
    descriptor.enumerable = descriptor.configurable = true;
    Object.defineProperty(prototype, descriptor.key, descriptor);
  }
}

export function inherits(subClass, superClass) {
  subClass.prototype = Object.create(superClass.prototype, {
    constructor: {
      configurable: true,
      writable: true,
      value: subClass,
    },
  });
}

export function possibleConstructorReturn(self, call) {
  return call || self;
}

export function interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : {
    default: obj,
  };
}

/**
 * Copyright 2016 Google Inc.
 *
 * 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.
 */

export function classCallCheck() {}

export function createClass(Constructor, protoProps) {
  const prototype = Constructor.prototype;
  for (let i = 0; i < protoProps.length; i++) {
    const descriptor = protoProps[i];
    descriptor.configurable = true;
    Object.defineProperty(prototype, descriptor.key, descriptor);
  }
}

export function inherits(subClass, superClass) {
  subClass.prototype = Object.create(superClass.prototype, {
    constructor: {
      configurable: true,
      writable: true,
      value: subClass,
    },
  });
}

export function possibleConstructorReturn(self, call) {
  return call || self;
}

export function interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : {
    default: obj,
  };
}

Fix enumerability of getter/setter functions


import { CLEAN_STATE, GET_ALL_LISTS_IN_BOARD, GET_ONE_BOARD, SAVE_BOARD_TITLE } from './constants'

export const cleanState = () => (dispatch) => {
  dispatch({
    type: CLEAN_STATE,
  })
}

export const getAllListsInBoard = boardId => (dispatch) => {
  dispatch({
    type: GET_ALL_LISTS_IN_BOARD,
    payload: {
      request: {
        method: 'GET',
        url: `/api/boards/${boardId}/lists`,
      },
    },
  })
}

export const getOneBoard = boardId => (dispatch) => {
  dispatch({
    type: GET_ONE_BOARD,
    payload: {
      request: {
        method: 'GET',
        url: `/api/boards/${boardId}`,
      },
    },
  })
}

export const saveBoardTitle = (boardId, boardTitle) => (dispatch) => {
  dispatch({
    type: SAVE_BOARD_TITLE,
    payload: {
      request: {
        method: 'PUT',
        url: `/boards/${boardId}`,
        data: {
          title: boardTitle,
        },
      },
    },
  })
}

import { CLEAN_STATE, GET_ALL_LISTS_IN_BOARD, GET_ONE_BOARD, SAVE_BOARD_TITLE } from './constants'

export const cleanState = () => (dispatch) => {
  dispatch({
    type: CLEAN_STATE,
  })
}

export const getAllListsInBoard = boardId => (dispatch) => {
  dispatch({
    type: GET_ALL_LISTS_IN_BOARD,
    payload: {
      request: {
        method: 'GET',
        url: `/api/boards/${boardId}/lists`,
      },
    },
  })
}

export const getOneBoard = boardId => (dispatch) => {
  dispatch({
    type: GET_ONE_BOARD,
    payload: {
      request: {
        method: 'GET',
        url: `/api/boards/${boardId}`,
      },
    },
  })
}

export const saveBoardTitle = (boardId, boardTitle) => (dispatch) => {
  dispatch({
    type: SAVE_BOARD_TITLE,
    payload: {
      request: {
        method: 'PUT',
        url: `/api/boards/${boardId}`,
        data: {
          title: boardTitle,
        },
      },
    },
  })
}

FIX Rename board hit /api


'use strict';

var EventEmitter = require('events').EventEmitter

class Config {
  constructor() {
    this.requiredKeys = ['url','type']
    this.messages = {
      missingConfig: 'DataService missing config object',
      missingKey: 'DataService config missing required key: '
    }
  }

  error(msg) {
    throw new Error(msg)
  }

  checkKeyValidity(config, key) {
    if (!config.hasOwnProperty(key))
      this.error(this.messages.missingKey + key)
  }

  validate(config) {
    if (!config) this.error(this.messages.missingConfig)
    this.requiredKeys.forEach((key) => this.checkKeyValidity(config, key))
    return config
  }
}

class DataService extends EventEmitter {
  constructor(config) {
    this.config = new Config().validate(config)
    this.data = null
  }

  getData() {
    return this.data
  }

  load(cb) {
    return cb(null, {})
  }
}

module.exports = function(config) {
  return new DataService(config)
}

'use strict';

var EventEmitter = require('events').EventEmitter

class Config {
  constructor() {
    this.requiredKeys = ['url','type']
    this.messages = {
      missingConfig: 'DataService missing config object',
      missingKey: 'DataService config missing required key: '
    }
  }

  error(msg) {
    throw new Error(msg)
  }

  checkKeyValidity(config, key) {
    if (!config.hasOwnProperty(key))
      this.error(this.messages.missingKey + key)
  }

  validate(config) {
    if (!config) this.error(this.messages.missingConfig)
    this.requiredKeys.forEach((key) => this.checkKeyValidity(config, key))
    return config
  }
}

class DataService extends EventEmitter {
  constructor(config) {
    this.config = new Config().validate(config)
    this.data = null
  }

  getData() {
    return this.data
  }

  start() {
    this.emit('data')
  }

}

module.exports = function(config) {
  return new DataService(config)
}

Delete load method, add start method


import React from 'react'
import Stats from '../Stats'
import Hit from '../Hit'

import './style.scss'

function renderHasMore (refine) {
  return (

        Load more

  )
}

function CustomHits (props) {
  const {toggle, get, hits, refine, hasMore} = props
  const _props = { toggle, get }

  return (

      <Stats />

        {hits.map((item, key) => <Hit item={item} key={key} {..._props} />)}

      {hasMore && renderHasMore(refine)}

  )
}

export default CustomHits

import React from 'react'
import Stats from '../Stats'
import Hit from '../Hit'

import './style.scss'

function renderHasMore (refine) {
  return (

        Load More

  )
}

function CustomHits (props) {
  const {toggle, get, hits, refine, hasMore} = props
  const _props = { toggle, get }

  return (

      <Stats />

        {hits.map((item, key) => <Hit item={item} key={key} {..._props} />)}

      {hasMore && renderHasMore(refine)}

  )
}

export default CustomHits

Improve load more button style


// apikey module validates the apikey argument used in the call. It
// caches the list of users to make lookups faster.
var http = require('http');
module.exports = function(apikeyCache) {
    'use strict';
    // validateAPIKey Looks up the apikey. If none is specified, or a
    // bad key is passed then abort the calls and send back an 401.
    return function* validateAPIKey(next) {
        let apikey = this.query.apikey || this.throw(401, 'Invalid apikey');
        let user = yield apikeyCache.find(apikey);
        if (! user) {
            this.throw(401, 'Invalid apikey');
        }
        this.mcapp = {
            user: user
        };
        yield next;
    };
};

// apikey module validates the apikey argument used in the call. It
// caches the list of users to make lookups faster.
var http = require('http');
var httpStatus = require('http-status');
module.exports = function(apikeyCache) {
    'use strict';
    // validateAPIKey Looks up the apikey. If none is specified, or a
    // bad key is passed then abort the calls and send back an 401.
    return function* validateAPIKey(next) {
        let UNAUTHORIZED = httpStatus.UNAUTHORIZED;
        let apikey = this.query.apikey || this.throw(UNAUTHORIZED, 'Invalid apikey');
        let user = yield apikeyCache.find(apikey);
        if (! user) {
            this.throw(UNAUTHORIZED, 'Invalid apikey');
        }
        this.mcapp = {
            user: user
        };
        yield next;
    };
};

Remove hard coding the 401 and instead give it a readable name.


Handlebars.registerViewHelper('collection', Thorax.CollectionHelperView, function(collection, view) {
  if (arguments.length === 1) {
    view = collection;
    collection = view.declaringView.collection;
  }
  // Need additional check here to see if it is the
  // primary collection as templates can do:
  // #collection this.collection
  if (collection && collection === this._view.collection) {
    ensureDataObjectCid('collection', collection);
    view.setAsPrimaryCollectionHelper(collection);
  }
  collection && view.setCollection(collection);
});

Handlebars.registerHelper('collection-element', function(options) {
  options.hash.tag = options.hash.tag || options.hash.tagName || 'div';
  options.hash[collectionElementAttributeName] = true;
  return new Handlebars.SafeString(Thorax.Util.tag.call(this, options.hash, '', this));
});

Handlebars.registerViewHelper('collection', Thorax.CollectionHelperView, function(collection, view) {
  if (arguments.length === 1) {
    view = collection;
    collection = view.declaringView.collection;
  }
  // Need additional check here to see if it is the
  // primary collection as templates can do:
  // #collection this.collection
  if (collection && collection === view.declaringView.collection) {
    ensureDataObjectCid('collection', collection);
    view.setAsPrimaryCollectionHelper(collection);
  }
  collection && view.setCollection(collection);
});

Handlebars.registerHelper('collection-element', function(options) {
  options.hash.tag = options.hash.tag || options.hash.tagName || 'div';
  options.hash[collectionElementAttributeName] = true;
  return new Handlebars.SafeString(Thorax.Util.tag.call(this, options.hash, '', this));
});

Use view.declaringView instead of this._view


/**
 * @link https://github.com/datarhei/restreamer
 * @copyright 2015 datarhei.org
 * @license Apache-2.0
 */

'use strict';

// Global config for frontend
const config = {
    'urls': {
        // project
        'issueTracker': 'https://github.com/datarhei/restreamer/issues/new',
        'projectPage': 'https://github.com/datarhei/restreamer',
        'updatePage': 'https://datarhei.github.io/restreamer/docs/references-updates.html',

        // help
        'embedPlayerHelp': 'https://datarhei.github.io/restreamer/docs/guides-embed-upon-your-website.html',
        'portForwardingHelp': 'https://datarhei.github.io/restreamer/wiki/portforwarding.html',
        'rtmpServerHelp': 'https://datarhei.github.io/restreamer/docs/references-external-rtmp-streaming-server.html'

    }
};

window.angular.module('app').constant('config', config);

/**
 * @link https://github.com/datarhei/restreamer
 * @copyright 2015 datarhei.org
 * @license Apache-2.0
 */

'use strict';

// Global config for frontend
const config = {
    'urls': {
        // project
        'issueTracker': 'https://github.com/datarhei/restreamer/issues/new',
        'projectPage': 'https://github.com/datarhei/restreamer',
        'updatePage': 'https://datarhei.github.io/restreamer/docs/guides-updates.html',

        // help
        'embedPlayerHelp': 'https://datarhei.github.io/restreamer/docs/guides-embedding.html',
        'portForwardingHelp': 'https://datarhei.github.io/restreamer/wiki/portforwarding.html',
        'rtmpServerHelp': 'https://datarhei.github.io/restreamer/docs/guides-external-rtmp.html'

    }
};

window.angular.module('app').constant('config', config);

Fix URLs to the gh-pages


chrome.webRequest.onCompleted.addListener(function(details) {
    if (details.statusCode == 404) {
        //
        var newUrl = new URL(details.url);
        newUrl.host = "obamawhitehouse.archives.gov";
        chrome.tabs.query({'active': true}, function(tabs) {
          chrome.tabs.update(tabs[0].id, {url: newUrl.href});
        });
    }
},
{urls: ["*://*.whitehouse.gov/*"]},
["responseHeaders"]
);

chrome.webRequest.onCompleted.addListener(function(details) {
        if (details.statusCode == 404) {
            chrome.tabs.query({'active': true}, function(tabs) {

                // Detect if this is the url in the tab. Avoid redirects when
                // it's a 404 in the page
                var currentUrl = tabs[0].url;
                if (details.url == currentUrl) {
                    // Rewrite URL
                    var newUrl = new URL(details.url);
                    newUrl.host = "obamawhitehouse.archives.gov";

                    // Redirect
                    chrome.tabs.update(tabs[0].id, {url: newUrl.href});
                }
            });
        }
    },
    {urls: ["*://*.whitehouse.gov/*"]},
    ["responseHeaders"]
);

Patch for if there is a 404 inside the page


import React, { Component } from 'react'
import {
  Link
} from 'react-router-dom'
import { connect } from 'react-redux'
import { Menu } from 'semantic-ui-react'
import { css } from 'glamor'

class Nav extends Component {
  handleLogout = () => this.props.logout()

  render() {
    return (
      <Menu secondary {...rules}>
        <Menu.Item
          name='home'
          as={Link}
          to="/"
        >
        Home
        </Menu.Item>

        {
          this.props.isAuthenticated ?

            <Menu.Menu position="right">
              <Menu.Item
                name='households'
                as={Link}
                to="/households"
              >
                Households
              </Menu.Item>

              <Menu.Item
                as={Link}
                to="/households/new"
              >
                New Household
              </Menu.Item>

              <Menu.Item
                as={Link}
                to="/"
                onClick={this.handleLogout}>
                Logout
              </Menu.Item>

            </Menu.Menu>

            :

            <Menu.Menu position="right">
              <Menu.Item
                as={Link}
                to="/login">
                Login
              </Menu.Item>

              <Menu.Item
                as={Link}
                to="/signup">
                Sign Up
              </Menu.Item>
            </Menu.Menu>

          }
      </Menu>
    )
  }
}

export default connect(state => {
  return {
    isAuthenticated: state.auth.isAuthenticated
  }
}, null)(Nav)

let rules = css({
  height: '60px'
})

import React, { Component } from 'react'
import {
  Link
} from 'react-router-dom'
import { connect } from 'react-redux'
import { Menu } from 'semantic-ui-react'
import { css } from 'glamor'

class Nav extends Component {
  handleLogout = () => this.props.logout()

  render() {
    return (
      <Menu secondary {...rules}>
        <Menu.Item
          name='home'
          as={Link}
          to="/"
        >
        Home
        </Menu.Item>

        {
          this.props.isAuthenticated ?

            <Menu.Menu position="right">
              <Menu.Item
                name='households'
                as={Link}
                to="/households"
              >
                Households
              </Menu.Item>

              <Menu.Item
                as={Link}
                to="/households/new"
              >
                New Household
              </Menu.Item>

              <Menu.Item
                as={Link}
                to="/meals/new">
                New Meal
              </Menu.Item>

              <Menu.Item
                as={Link}
                to="/"
                onClick={this.handleLogout}>
                Logout
              </Menu.Item>

            </Menu.Menu>

            :

            <Menu.Menu position="right">
              <Menu.Item
                as={Link}
                to="/login">
                Login
              </Menu.Item>

              <Menu.Item
                as={Link}
                to="/signup">
                Sign Up
              </Menu.Item>
            </Menu.Menu>

          }
      </Menu>
    )
  }
}

export default connect(state => {
  return {
    isAuthenticated: state.auth.isAuthenticated
  }
}, null)(Nav)

let rules = css({
  height: '60px'
})

Add link to new meal form


/*
 * Optics / subcommand.js
 * copyright (c) 2016 Susisu
 */

/**
 * @module subcommand
 */

"use strict";

function endModule() {
    module.exports = Object.freeze({
        Subcommand,
        CommandGroup
    });
}

const command = require("./command.js");

/**
 * The `Subcommand` associates a name with a command.
 * @static
 */
class Subcommand {
    constructor(name, cmd) {
        this.name = name;
        this.cmd  = cmd;
    }
}

/**
 * A `CommandGroup` instance represents a group of subcommands.
 * @static
 * @extends {module:command.CommandBase}
 */
class CommandGroup extends command.CommandBase {
    /**
     * Creates a new `CommandGroup` instance.
     * @param {string} desc The description of the command group.
     * @param {module:subcommand.Subcommand[]]} subcmds Subcommands the group contains.
     * @param {(module:command.CommandBase | null)} defaultCmd Default command, invoked when no subcommand is specified.
     */
    constructor(desc, subcmds, defaultCmd) {
        super();
        this.desc       = desc;
        this.subcmds    = subcmds;
        this.defaultCmd = defaultCmd;
    }
}

endModule();

/*
 * Optics / subcommand.js
 * copyright (c) 2016 Susisu
 */

/**
 * @module subcommand
 */

"use strict";

function endModule() {
    module.exports = Object.freeze({
        Subcommand,
        CommandGroup
    });
}

const command = require("./command.js");

/**
 * The `Subcommand` associates a name with a command.
 * @static
 */
class Subcommand {
    /**
     * Creates a new `Subcommand` instance.
     * @param {string} name The name of the subcommand.
     * @param {module:command.CommandBase} cmd The command associated to the name.
     */
    constructor(name, cmd) {
        this.name = name;
        this.cmd  = cmd;
    }
}

/**
 * A `CommandGroup` instance represents a group of subcommands.
 * @static
 * @extends {module:command.CommandBase}
 */
class CommandGroup extends command.CommandBase {
    /**
     * Creates a new `CommandGroup` instance.
     * @param {string} desc The description of the command group.
     * @param {module:subcommand.Subcommand[]} subcmds Subcommands the group contains.
     * @param {(module:command.CommandBase | null)} defaultCmd Default command, invoked when no subcommand is specified.
     */
    constructor(desc, subcmds, defaultCmd) {
        super();
        this.desc       = desc;
        this.subcmds    = subcmds;
        this.defaultCmd = defaultCmd;
    }
}

endModule();

Fix doc comment for CommandGroup and add one for the Subcommand constructor


import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

import 'malihu-custom-scrollbar-plugin/jquery.mCustomScrollbar';
import 'jquery-mousewheel/jquery.mousewheel';

const mountApp = () => {
  ReactDOM.render(<App />, document.getElementById('root'));
};

if (window.fin) {
  window.fin.desktop.main(mountApp);
} else {
  console.error("This application can only be run in an OpenFin container.");
}

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

const mountApp = () => {
  ReactDOM.render(<App />, document.getElementById('root'));
};

if (window.fin) {
  window.fin.desktop.main(mountApp);
} else {
  console.error("This application can only be run in an OpenFin container.");
}

Remove mousewheel and scrollbar plugin from watchlist


angular.module("oxymoron.config.http", [])
  .config(['$httpProvider', '$locationProvider', '$stateProvider', function($httpProvider, $locationProvider, $stateProvider) {
    /*
     *  Set token for AngularJS ajax methods
    */
    $httpProvider.defaults.headers.common['X-Requested-With'] = 'AngularXMLHttpRequest';
    $httpProvider.defaults.paramSerializer = '$httpParamSerializerJQLike';
  }])

  .config(["$httpProvider", function ($httpProvider) {
      $httpProvider.defaults.transformResponse.push(function(responseData){
          convertDateStringsToDates(responseData);
          return responseData;
      });
  }]);

  var regexIso8601 = /^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))([T\s]((([01]\d|2[0-3])((:?)[0-5]\d)?|24\:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)?$/;

  function convertDateStringsToDates(input) {
      // Ignore things that aren't objects.
      if (typeof input !== "object") return input;

      for (var key in input) {
          if (!input.hasOwnProperty(key)) continue;

          var value = input[key];
          var match;
          // Check for string properties which look like dates.
          // TODO: Improve this regex to better match ISO 8601 date strings.
          if (typeof value === "string" && (match = value.match(regexIso8601))) {
              // Assume that Date.parse can parse ISO 8601 strings, or has been shimmed in older browsers to do so.
              var milliseconds = Date.parse(match[0]);
              if (!isNaN(milliseconds)) {
                  input[key] = new Date(milliseconds);
              }
          } else if (typeof value === "object") {
              // Recurse into object
              convertDateStringsToDates(value);
          }
      }
  }

Build real Date instance from response


/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import Realm from 'realm';

export class Address extends Realm.Object {}
export { Item } from './Item';
export { ItemBatch } from './ItemBatch';
export class ItemDepartment extends Realm.Object {}
export class ItemCategory extends Realm.Object {}
export { ItemStoreJoin } from './ItemStoreJoin';
export { Transaction } from './Transaction';
export class TransactionCategory extends Realm.Object {}
export { TransactionItem } from './TransactionItem';
export { TransactionBatch } from './TransactionBatch';
export { MasterList } from './MasterList';
export { MasterListItem } from './MasterListItem';
export { MasterListNameJoin } from './MasterListNameJoin';
export { Name } from './Name';
export { NameStoreJoin } from './NameStoreJoin';
export { NumberSequence } from './NumberSequence';
export { NumberToReuse } from './NumberToReuse';
export { Requisition } from './Requisition';
export { RequisitionItem } from './RequisitionItem';
export class Setting extends Realm.Object {}
export class SyncOut extends Realm.Object {}
export { Stocktake } from './Stocktake';
export { StocktakeItem } from './StocktakeItem';
export { StocktakeBatch } from './StocktakeBatch';
export { Period } from './Period';
export { PeriodSchedule } from './PeriodSchedule';
export { Options } from './Options';
export class User extends Realm.Object {}

/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import Realm from 'realm';

export class Address extends Realm.Object {}
export { Item } from './Item';
export { ItemBatch } from './ItemBatch';
export class ItemDepartment extends Realm.Object {}
export class ItemCategory extends Realm.Object {}
export { ItemStoreJoin } from './ItemStoreJoin';
export { Transaction } from './Transaction';
export class TransactionCategory extends Realm.Object {}
export { TransactionItem } from './TransactionItem';
export { TransactionBatch } from './TransactionBatch';
export { MasterList } from './MasterList';
export { MasterListItem } from './MasterListItem';
export { MasterListNameJoin } from './MasterListNameJoin';
export { Name } from './Name';
export { NameStoreJoin } from './NameStoreJoin';
export { NumberSequence } from './NumberSequence';
export { NumberToReuse } from './NumberToReuse';
export { Options } from './Options';
export { Period } from './Period';
export { PeriodSchedule } from './PeriodSchedule';
export { Requisition } from './Requisition';
export { RequisitionItem } from './RequisitionItem';
export class Setting extends Realm.Object {}
export class SyncOut extends Realm.Object {}
export { Stocktake } from './Stocktake';
export { StocktakeItem } from './StocktakeItem';
export { StocktakeBatch } from './StocktakeBatch';
export class User extends Realm.Object {}

Add reorder data type exports


const InventoryError = require('../../errors').InventoryError
const models = require('./')

const Payment = models.payments

module.exports = (sequelize, DataTypes) => {
  const Pokemon = sequelize.define('pokemons', {
    name: DataTypes.STRING,
    price: DataTypes.FLOAT,
    stock: DataTypes.INTEGER
  }, {
    classMethods: {
      associate () {
      },
      getPokemonWithLockForUpdate: (pokemonId, t) =>
        Pokemon
          .findOne({
            where: {
              id: pokemonId
            },
            lock: {
              level: t.LOCK.UPDATE,
              of: Payment
            }
          }),
      decreasePokemonStock: (pokemon, quantity) =>
        Pokemon
          .update({
            stock: pokemon.stock - quantity
          }, {
            where: {
              id: pokemon.id
            }
          }),
      increasePokemonStock: (pokemon, quantity) =>
        Pokemon
          .update({
            stock: pokemon.stock + quantity
          }, {
            where: {
              id: pokemon.id
            }
          })
    },
    instanceMethods: {
      checkInventory (quantity) {
        if (this.stock < quantity) {
          throw new InventoryError(this.name, this.stock)
        }
      }
    }
  })

  return Pokemon
}

const InventoryError = require('../../errors').InventoryError
const models = require('./')

const Payment = models.payments

module.exports = (sequelize, DataTypes) => {
  const Pokemon = sequelize.define('pokemons', {
    name: DataTypes.STRING,
    price: DataTypes.FLOAT,
    stock: DataTypes.INTEGER
  }, {
    classMethods: {
      associate () {
      },
      getPokemonWithLockForUpdate: (pokemonId, t) =>
        Pokemon
          .findOne({
            where: {
              id: pokemonId
            },
            lock: {
              level: t.LOCK.UPDATE,
              of: Payment
            }
          }),
      decreasePokemonStock: (pokemon, quantity) =>
        Pokemon
          .update({
            stock: pokemon.stock - quantity
          }, {
            where: {
              id: pokemon.id
            }
          }),
      increasePokemonStock: (pokemon, quantity) =>
        Pokemon
          .update({
            stock: pokemon.stock + quantity
          }, {
            where: {
              id: pokemon.id
            }
          })
    },
    instanceMethods: {
      checkInventory: (quantity) => {
        if (this.stock < quantity) {
          throw new InventoryError(this.name, this.stock)
        }
      }
    }
  })

  return Pokemon
}

Fix way of declare method in a object.


import SlimSelect from 'slim-select';
import 'slim-select/dist/slimselect.min.css';

AutoForm.addHooks(['activityForm'], {
  'onSuccess': function (formType) {
    // Hide the modal dialogue
    Modal.hide('activityForm');

    // placeholder for success message text
    let successMessage;

    // Get relevant success message for form type
    if (formType === 'update') {
      successMessage = TAPi18n.__("activityForm-update-success");
    } else {
      successMessage = TAPi18n.__("activityForm-add-success");
    }

    const successMessageWithIcon = '<i class="fa fa-check"></i> ' + successMessage

    // Alert user that activity was added
    FlashMessages.sendSuccess(successMessageWithIcon);
  }
});

Template.autoForm.onRendered(function () {
  const instance = this;

  // Make sure the new activity form is being rendered
  if (instance.data.id === "activityForm") {
    // Get localized placeholder text
    const placeholder = TAPi18n.__('activityForm-residentSelect-placeholder');

    // Render multi-select widget on 'select residents' field
    new SlimSelect({
      select: '[name=residentIds]',
      closeOnSelect: false,
      placeholder,
    });
  }
});

import SlimSelect from 'slim-select';
import 'slim-select/dist/slimselect.min.css';
import flatpickr from "flatpickr";
import 'flatpickr/dist/flatpickr.min.css';
import moment from 'moment';

AutoForm.addHooks(['activityForm'], {
  'onSuccess': function (formType) {
    // Hide the modal dialogue
    Modal.hide('activityForm');

    // placeholder for success message text
    let successMessage;

    // Get relevant success message for form type
    if (formType === 'update') {
      successMessage = TAPi18n.__("activityForm-update-success");
    } else {
      successMessage = TAPi18n.__("activityForm-add-success");
    }

    const successMessageWithIcon = '<i class="fa fa-check"></i> ' + successMessage

    // Alert user that activity was added
    FlashMessages.sendSuccess(successMessageWithIcon);
  }
});

Template.autoForm.onRendered(function () {
  const instance = this;

  // Make sure the new activity form is being rendered
  if (instance.data.id === "activityForm") {
    // Get localized placeholder text
    const placeholder = TAPi18n.__('activityForm-residentSelect-placeholder');

    // Render multi-select widget on 'select residents' field
    new SlimSelect({
      select: '[name=residentIds]',
      closeOnSelect: false,
      placeholder,
    });

    // Render cross-platform date picker on date field
    flatpickr("#activityDate", {
      minDate: moment().subtract(7, 'days').startOf('day').toDate(),
      maxDate: moment().endOf('day').toDate(),
    });
  }
});

Add flatpickr widget to activityDate field


define([], function() {

	var PageController = ['$scope', 'api', function($scope, api) {
		console.log("controller");

		$scope.sendForm = function() {
			var message = {
				"firstName": form.firstName.value,
				"lastName": form.lastName.value,
				"emailAddress": form.emailAddress.value,
				"subject": form.subject.value,
				"message": form.message.value
			};
			console.log(message);

			api.contactForm.send({}, message).$promise.then(function(response) {
				console.log(response);
			}, function(e) {
				console.error("Error submitting form", e);
			});
		}
	}];

	return {
		PageController: PageController
	};
});

define([], function() {

	var PageController = ['$scope', 'api', function($scope, api) {
		$scope.sendForm = function() {
			var message = {
				"firstName": form.firstName.value,
				"lastName": form.lastName.value,
				"emailAddress": form.emailAddress.value,
				"subject": form.subject.value,
				"message": form.message.value
			};

			api.contactForm.send({}, message).$promise.then(function(response) {
				console.log("Success");
				// TODO: Inform user accordingly
			}, function(e) {
				console.error("Error submitting form", e);
			});
		}
	}];

	return {
		PageController: PageController
	};
});

Remove some old logging messages


const fs = require('fs');
const path = require('path');

const cases = path.join(__dirname, 'cases');

const caseDirs = fs.readdirSync(cases).filter((name) => {
  let exists = true;
  try {
    fs.accessSync(path.join(cases, name, 'main.js'));
  } catch (err) {
    exists = false;
  }
  return exists;
});

const entry = {};
caseDirs.forEach((c) => {
  entry[`cases/${c}/main`] = `./cases/${c}/main.js`;
});

module.exports = {
  context: __dirname,
  target: 'web',
  entry: entry,
  devtool: 'source-map',
  stats: 'minimal',
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: path.join(
            __dirname,
            '../../examples/webpack/worker-loader.js'
          ),
        },
        include: [path.join(__dirname, '../../src/ol/worker')],
      },
    ],
  },
};

const fs = require('fs');
const path = require('path');

const cases = path.join(__dirname, 'cases');

const caseDirs = fs.readdirSync(cases).filter((name) => {
  let exists = true;
  try {
    fs.accessSync(path.join(cases, name, 'main.js'));
  } catch (err) {
    exists = false;
  }
  return exists;
});

const entry = {};
caseDirs.forEach((c) => {
  entry[`cases/${c}/main`] = `./cases/${c}/main.js`;
});

module.exports = {
  context: __dirname,
  target: 'web',
  entry: entry,
  devtool: 'source-map',
  stats: 'minimal',
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: path.join(
            __dirname,
            '../../examples/webpack/worker-loader.js'
          ),
        },
        include: [path.join(__dirname, '../../src/ol/worker')],
      },
    ],
  },
  resolve: {
    alias: {
      // ol-mapbox-style imports ol/style/Style etc
      ol: path.join(__dirname, '..', '..', 'src', 'ol'),
    },
  },
};

Allow ol-mapbox-style to import from ol/style/Style etc.


var webpack = require('webpack');
var precss = require('precss');
var autoprefixer = require('autoprefixer');

module.exports = {
  entry: [
    './src/js/main.js'
  ],
  module: {
    loaders: [
      { test: /\.css$/, loader: 'style-loader!css-loader!postcss-loader' },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: ['babel'],
        query: {
          'presets': ['react', 'es2015'],
          'plugins': [
            'transform-object-rest-spread'
          ]
        }
      }
    ]
  },
  postcss: function () {
    return [precss, autoprefixer({ browsers: ['> 5%'] })]
  },
  resolve: {
    extensions: ['', '.js']
  },
  output: {
    path: __dirname + '/dist/js',
    publicPath: 'js', // it depends on what we set as content-base option with
                      // the CLI
    filename: 'model-explorer.js'
  }
}

var webpack = require('webpack');
var precss = require('precss');
var autoprefixer = require('autoprefixer');

module.exports = {
  entry: [
    './src/js/main.js'
  ],
  module: {
    loaders: [
      { test: /\.css$/, loader: 'style-loader!css-loader!postcss-loader' },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: ['babel'],
        query: {
          'presets': ['react', 'es2015'],
          'plugins': [
            'transform-object-rest-spread'
          ]
        }
      }
    ]
  },
  postcss: function () {
    return [precss, autoprefixer({ browsers: ['> 5%'] })]
  },
  resolve: {
    extensions: ['', '.js']
  },
  output: {
    path: __dirname + '/dist',
    publicPath: 'js', // it depends on what we set as content-base option with
                      // the CLI
    filename: './js/model-explorer.js'
  }
}

Fix pathnames issue in webpack


import { moduleForModel, test } from 'ember-qunit';

moduleForModel('element', 'Unit | Model | element', {
    // Specify the other units that are required for this test.
    needs: ['model:page']
});

test('it exists', function(assert) {
    var model = this.subject();
    // var store = this.store();
    assert.ok(!!model);
});

import Ember from 'ember';
import { moduleForModel, test } from 'ember-qunit';

moduleForModel('element', 'Unit | Model | element', {
    needs: ['model:page']
});

test('it has a default element type of ENTRY', function(assert) {
    var element = this.subject();

    assert.equal(element.get('elementType'), 'ENTRY');
});

test('it has a default empty array for choices', function(assert) {
    var element = this.subject();

    assert.deepEqual(element.get('choices'), []);
});

test('it does not have choices when the element type is ENTRY', function(assert) {
    var element = this.subject();

    Ember.run(function() {
        element.set('elementType', 'ENTRY');
    });

    assert.equal(element.get('hasChoices'), false);
});

test('it has choices when the element type is SELECT', function(assert) {
    var element = this.subject();

    Ember.run(function() {
        element.set('elementType', 'SELECT');
    });

    assert.equal(element.get('hasChoices'), true);
});

Add unit tests for element


(function (env) {
    "use strict";
    env.ddg_spice_cve_summary = function(api_result){

        // fail the spice if the API does not return a result
        if(api_result == undefined) {
            return Spice.failed('cve_summary');
        }

        // Validate the response (customize for your Spice)
        if (api_result.error) {
            return Spice.failed('cve_summary');
        }

        // Render the response
        Spice.add({
            id: "cve_summary",

            // Customize these properties
            name: "CVE Summary",
            data: api_result,
            meta: {
                sourceName: "cve.circl.lu",
                sourceUrl: 'http://cve.circl.lu/cve/' + api_result.id
            },
            templates: {
                group: 'text',
                options: {
                    content: Spice.cve_summary.content,
                    moreAt: true
                }
            },
            normalize: function(item) {
                return {
                    title: api_result.id,
                    description: api_result.summary
                };
            }
        });
    };
}(this));

(function (env) {
    "use strict";
    env.ddg_spice_cve_summary = function(api_result){

        // fail the spice if the API does not return a result
        if(api_result == undefined) {
            return Spice.failed('cve_summary');
        }

        // Validate the response (customize for your Spice)
        if (api_result.error) {
            return Spice.failed('cve_summary');
        }

        // Render the response
        Spice.add({
            id: "cve_summary",

            // Customize these properties
            name: "CVE Summary",
            data: api_result,
            meta: {
                sourceName: "cve.circl.lu",
                sourceUrl: 'http://cve.circl.lu/cve/' + api_result.id
            },
            templates: {
                group: 'text',
                options: {
                    content: Spice.cve_summary.content,
                    moreAt: true
                }
            },
            normalize: function(item) {
                return {
                    title: api_result.id,
                    subtitle: "Published " + api_result.Published,
                    description: api_result.summary
                };
            }
        });
    };
}(this));

Set Published date as subtitle