var argv = require('minimist')(process.argv.slice(2));
var marco = require('./lib/marco.js');
var through = require('through2');
var split = require('split');

var usage = function() {
  var text = [];
  text.push('Usage: node example.js [-s] [-m] [-h]');
  text.push('');
  text.push('  --centroid returns a Point feature');
  text.push('  --shape returns a Polygon feature');
  text.push('  --state matches query to states');
  text.push('  --municipality matches query against municipalities');
  text.push('  --multiple returns all matches found per query');
  text.push('  --help prints this help message.');
  text.push('');
  return text.join('\n');
}

var state = argv.state || argv.s;
var municipality = argv.municipality || argv.m;

// STREAMING

function setPipe() {
  var ts = through(() => {});
  if (state) {
    ts = marco.toStatePolygon();
  }

  process.stdin
    .pipe(split())
    .pipe(ts)
    .pipe(process.stdout);
}

setPipe();

// SIMPLE QUERIES

if (state) {
  marco.findState(state, (err, data) => {
    if (!err && data)
      console.log(JSON.stringify(data));
  });
} else if (municipality) {
  marco.findMunicipality(municipality, (err, data) => {
    if (!err && data)
      console.log(JSON.stringify(data));
  });
} else {
  console.log(usage());
  process.exit(1);
}

var argv = require('minimist')(process.argv.slice(2));
var marco = require('./lib/marco.js');
var through = require('through2');
var split = require('split');

var usage = function() {
  var text = [];
  text.push('Usage: node example.js [-s] [-m] [-h]');
  text.push('');
  text.push('  --state matches query to states');
  text.push('  --municipality matches query against municipalities');
  text.push('  --help prints this help message.');
  text.push('');
  return text.join('\n');
}

var state = argv.state || argv.s;
var municipality = argv.municipality || argv.m;

// STREAMING

function setPipe() {
  var ts = through(() => {});
  if (state) {
    ts = marco.toStatePolygon();
  }

  process.stdin
    .pipe(split())
    .pipe(ts)
    .pipe(process.stdout);
}

setPipe();

// SIMPLE QUERIES

if (state) {
  marco.findState(state, (err, data) => {
    if (!err && data)
      console.log(JSON.stringify(data));
  });
} else if (municipality) {
  marco.findMunicipality(municipality, (err, data) => {
    if (!err && data)
      console.log(JSON.stringify(data));
  });
} else {
  console.log(usage());
  process.exit(1);
}

Remove unimplemented methods from help


const axios = require('axios')

// let params = {
//   t: 'la la land',
//   type: 'movie',
//   tomatoes: 'true'
// }
let params = {
  t: 'passengers',
  type: 'movie',
  tomatoes: 'true',
  y: '2016',    // if not specified, will pick the 2008 movie
}
let url = 'http://omdbapi.com/'

axios.get(url, {params})
.then(res => {
  let movie = res.data
  // console.log(data)
  console.log(movie.Title)
  console.log(movie.Year)
  console.log('Directed by', movie.Director)
  console.log('Metascore:', movie.Metascore)
  console.log('IMDB rating:', movie.imdbRating)
  console.log('Tomato rating:', movie.tomatoRating)
})

const axios = require('axios')

let params = {
  type: 'movie',
  tomatoes: 'true',
  title: 'la la land',
  year: '2016'
}
// params = Object.assign({title: 'jackie', year: '2016'}, params)
// If year is not specified, will pick the 2008 movie.
// params = Object.assign({title: 'passengers', year: '2016'}, params)

let url = 'http://omdbapi.com/'

axios.get(url, {params})
.then(res => {
  let data = res.data
  if (data.Error !== undefined) {
    console.log('Status: %s, Error: %s', res.status, data.Error)
    return
  }

  let movie = data
  // console.log(movie)
  console.log(movie.Title)
  console.log(movie.Year)
  console.log('Directed by', movie.Director)
  console.log('Metascore:', movie.Metascore)
  console.log('IMDB rating:', movie.imdbRating)
  console.log('Tomato rating:', movie.tomatoRating)
})

Use Object.assign() to specify an alternate movie to search for


import React, { Component, PropTypes } from 'react'
import { FormGroup, Checkbox, Glyphicon } from 'react-bootstrap'

const propTypes = {
  phenotypes      : PropTypes.array,
  togglePhenotype : PropTypes.func,
  removePhenotype : PropTypes.func
}

class SelectedPhenotypes extends Component {
  render () {
    const { phenotypes, togglePhenotype, removePhenotype } = this.props
    return <div>
      Selected phenotypes:
      <form>
        <FormGroup>
          {phenotypes.map((pheno, index) => <Checkbox inline checked={pheno.active}
            onClick={() => togglePhenotype(index)}>
            {pheno.value.name}
            <Glyphicon glyph='remove' onClick={() => removePhenotype(index)} />
          </Checkbox>)}
        </FormGroup>
      </form>
    </div>
  }
}

SelectedPhenotypes.propTypes = propTypes
export default SelectedPhenotypes

import React, { Component, PropTypes } from 'react'
import { FormGroup, Checkbox, Glyphicon } from 'react-bootstrap'

const propTypes = {
  phenotypes      : PropTypes.array,
  togglePhenotype : PropTypes.func,
  removePhenotype : PropTypes.func
}

class SelectedPhenotypes extends Component {
  render () {
    const { phenotypes, togglePhenotype, removePhenotype } = this.props
    return <div>
      Selected phenotypes:
      <form>
        <FormGroup>
          {phenotypes.map((pheno, index) => <Checkbox key={index} inline checked={pheno.active}
            onChange={() => togglePhenotype(index)}>
            {pheno.value.name}
            <Glyphicon glyph='remove' onClick={() => removePhenotype(index)} />
          </Checkbox>)}
        </FormGroup>
      </form>
    </div>
  }
}

SelectedPhenotypes.propTypes = propTypes
export default SelectedPhenotypes

Fix warning: checkbox without onChange attribute


(function() {
    'use strict';

    moment.locale('nb');

    var module = angular.module('billett', [
        'ngRoute',
        'billett.auth',

        // common
        'billett.common.directives',
        'billett.common.filters',
        'billett.common.CsrfInceptorService',
        'billett.common.HeaderController',
        'billett.common.PageController',
        'billett.common.PageService',
        'billett.common.ResponseDataService',

        // admin
        'billett.admin.event',
        'billett.admin.eventgroup',
        'billett.admin.index',
        'billett.admin.order',
        'billett.admin.paymentgroup',
        'billett.admin.ticketgroup',

        // guest
        'billett.event',
        'billett.eventgroup',
        'billett.index',
        'billett.infopages.HjelpController',
        'billett.infopages.SalgsBetController',
        'billett.order'
    ]);

    module.config(function ($locationProvider, $routeProvider) {
        $routeProvider.otherwise({templateUrl: 'infopages/404.html'});

        // use HTML5 history API for nice urls
        $locationProvider.html5Mode(true);
    });
})();

(function() {
    'use strict';

    moment.locale('nb');

    var module = angular.module('billett', [
        'ngRoute',
        'billett.auth',

        // common
        'billett.common.directives',
        'billett.common.filters',
        'billett.common.CsrfInceptorService',
        'billett.common.HeaderController',
        'billett.common.PageController',
        'billett.common.PageService',
        'billett.common.ResponseDataService',

        // admin
        'billett.admin.event',
        'billett.admin.eventgroup',
        'billett.admin.index',
        'billett.admin.order',
        'billett.admin.paymentgroup',
        'billett.admin.ticketgroup',

        // guest
        'billett.event',
        'billett.eventgroup',
        'billett.index',
        'billett.infopages.HjelpController',
        'billett.infopages.SalgsBetController',
        'billett.order'
    ]);

    module.config(function ($locationProvider, $routeProvider) {
        $routeProvider.otherwise({templateUrl: 'assets/views/guest/infopages/404.html'});

        // use HTML5 history API for nice urls
        $locationProvider.html5Mode(true);
    });
})();

Fix url to 404 template


const { injectBabelPlugin, getLoader, getBabelLoader, loaderNameMatches } = require('react-app-rewired')

module.exports = function override(config, env) {
  // Inject all babel plugins before rewiring typescript

  // Inject babel-plugin-emotion
  config = injectBabelPlugin(
    [
      'emotion',
      {
        autoLabel: true,
        hoist: env === 'production',
        labelFormat: '[filename]--[local]'
      }
    ],
    config
  )

  // Rewire typescript with our rewired babel config
  const babelLoader = getBabelLoader(config.module.rules)
  const tsLoader = getLoader(config.module.rules, rule => /ts|tsx/.test(rule.test))
  tsLoader.use.unshift({
    loader: babelLoader.loader,
    options: babelLoader.options
  })

  return config
}

const { injectBabelPlugin, getLoader, getBabelLoader, loaderNameMatches } = require('react-app-rewired')

module.exports = function override(config, env) {
  // Inject all babel plugins before rewiring typescript

  // Inject babel-plugin-emotion
  config = injectBabelPlugin(
    [
      'emotion',
      {
        autoLabel: true,
        hoist: env === 'production',
        labelFormat: '[filename]--[local]',
        sourceMap: env !== 'production'
      }
    ],
    config
  )

  // Rewire typescript with our rewired babel config
  const babelLoader = getBabelLoader(config.module.rules)
  const tsLoader = getLoader(config.module.rules, rule => /ts|tsx/.test(rule.test))
  tsLoader.use.unshift({
    loader: babelLoader.loader,
    options: babelLoader.options
  })

  return config
}

Add sourceMap to babel-emotion-plugin config


const path = require('path');
var webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
    devtool: 'source-map',
    entry: {
        'main': './main.ts'
    },
    module: {
        loaders: [
            {test: /\.css$/, loader: 'raw-loader'},
            {test: /\.html$/, loader: 'raw-loader'},
            {test: /\.ts$/, loader: 'ts-loader'},
            {test: /\.woff($|\?)|\.woff2($|\?)|\.ttf($|\?)|\.eot($|\?)|\.svg($|\?)/, loader: 'url-loader'}
        ]
    },
        output: {
        path: './dist',
        filename: 'bundle.js'
    },
    plugins: [
        new CopyWebpackPlugin(
            [
                {
                    from: './index.html',
                    to: 'index.html'
                }
            ]
        ),
        new webpack.ProvidePlugin(
            {
                jQuery: 'jquery',
                $: 'jquery',
                jquery: 'jquery'
            }
        )
    ],
  resolve: {
    extensions: ['.ts', '.js']
  }
};

const path = require('path');
var webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');

module.exports = {
    devtool: 'source-map',
    entry: {
        'main': './main.ts'
    },
    module: {
        loaders: [
            {test: /\.ts$/, exclude: /node_modules/, loader: 'ts-loader'},
            {test: /\.css$/, loader: 'style-loader!css-loader'},
            {test: /\.woff($|\?)|\.woff2($|\?)|\.ttf($|\?)|\.eot($|\?)|\.svg($|\?)/, loader: 'url-loader'}
        ]
    },
        output: {
        path: './dist',
        filename: 'bundle.js'
    },
    plugins: [
        new CopyWebpackPlugin(
            [
                {
                    from: './index.html',
                    to: 'index.html'
                }
            ]
        ),
        new webpack.ProvidePlugin(
            {
                jQuery: 'jquery',
                $: 'jquery',
                jquery: 'jquery'
            }
        )
    ],
  resolve: {
    extensions: ['.ts', '.js']
  }
};

Use style loader instead of raw loader for css and get rid of html-raw loader


import reactTransform from 'babel-plugin-react-transform'

export default {
  name: 'webpack-babel',
  configure ({ buildTarget }) {
    const hmrEnv = {
      development: {
        plugins: [reactTransform],
        extra: {
          'react-transform': {
            transforms: [{
              transform: 'react-transform-hmr',
              imports: ['react'],
              locals: ['module']
            }]
          }
        }
      }
    }

    return {
      babel: {
        optional: ['runtime'],
        stage: 0,
        env: buildTarget === 'develop' ? hmrEnv : {}
      },

      module: {
        loaders: [
          {
            test: /\.jsx?$/,
            exclude: /node_modules/,
            loader: 'babel'
          }
        ]
      }
    }
  }
}

import reactTransform from 'babel-plugin-react-transform'

export default {
  name: 'webpack-babel',
  configure ({ buildTarget }) {
    const hmrEnv = {
      development: {
        plugins: [reactTransform],
        extra: {
          'react-transform': {
            transforms: [{
              transform: 'react-transform-hmr',
              imports: ['react'],
              locals: ['module']
            }]
          }
        }
      }
    }

    return {
      babel: {
        optional: ['runtime'],
        stage: 0,
        env: buildTarget === 'develop' ? hmrEnv : {}
      },

      module: {
        loaders: [
          {
            test: /\.jsx?$/,
            exclude: /node_modules/,
            loader: 'babel'
          }
        ]
      },

      resolve: {
        extensions: ['', '.js', '.jsx']
      }
    }
  }
}

Resolve also .jsx modules by default


var Gificiency = (function() {
  'use strict';

  var searchField = $('.search'),
      items = $('li'),
      links = $('a');

  var search = function(filter) {
    links.each(function() {
      var elem = $(this);
      if (elem.text().search( new RegExp(filter, 'i') ) < 0) {
        elem.hide();
      } else {
        elem.show();
      }
    });
  };

  var getHash = function() {
    var filter;
    if (window.location.hash != '') {
      filter = window.location.hash.substring(1);
    } else {
      filter = false;
    }
    return filter;
  };

  var clearImages = function() {
    $('img').each(function() {
      $(this).remove();
    });
  };

  var popup = function(image) {
    return $('<img src="'+ image +'" />');
  };

  var init = function() {
    if ( getHash() ) {
      search( getHash() );
    }
    events();
  };

  var events = function() {
    searchField.on('keyup', function() {
      search( $(this).val() );
    });

    items.on('mouseover', function() {
      var elem = $(this).find('a'), image = elem.attr('href');
      elem.parent().append( popup(image) );
    }).on('mouseout', function() {
      clearImages();
    });
  };

  var Gificiency = {
    init: init,
    events: events
  };

  return Gificiency;
})();

var Gificiency = (function() {
  'use strict';

  var searchField = $('.search'),
      items = $('li'),
      links = $('a');

  var init = function() {
    if ( getHash() ) {
      search( getHash() );
    }
    events();
  };

  var events = function() {
    searchField.on('keyup', function() {
      search( $(this).val() );
    });

    items.on('mouseover', function() {
      var elem = $(this).find('a'), image = elem.attr('href');
      elem.parent().append( popup(image) );
    }).on('mouseout', function() {
      clearImages();
    });
  };

  var search = function(filter) {
    links.each(function() {
      var elem = $(this);
      if (elem.text().search( new RegExp(filter, 'i') ) < 0) {
        elem.hide();
      } else {
        elem.show();
      }
    });
  };

  var getHash = function() {
    var filter;
    if (window.location.hash != '') {
      filter = window.location.hash.substring(1);
    } else {
      filter = false;
    }
    return filter;
  };

  var clearImages = function() {
    $('img').each(function() {
      $(this).remove();
    });
  };

  var popup = function(image) {
    return $('<img src="'+ image +'" />');
  };

  var Gificiency = {
    init: init
  };

  return Gificiency;
})();

Move the init/event methods to the private scope


import React, { useCallback } from 'react';
import { useDocked } from 'openfin-react-hooks';
import { snapAndDock } from 'openfin-layouts';

import './Titlebar.css';

export default () => {
  const isDocked = useDocked();

  const onMinimizeClick = useCallback(async () => {
    const currentWindow = await window.fin.Window.getCurrent();
    currentWindow.minimize();
  }, []);

  const onCloseClick = useCallback(async () => {
    const currentWindow = await window.fin.Window.getCurrent();
    currentWindow.close();
  }, []);

  const onUndockClick = useCallback(async () => {
    await snapAndDock.undockWindow();
  }, []);

  return (

        {isDocked &&

}

  )
}

Fix issue with close icon not appearing


module.exports = {
  getViewPortInfo: function getViewPort () {
    var e = document.documentElement
    var g = document.getElementsByTagName('body')[0]
    var x = window.innerWidth || e.clientWidth || g.clientWidth
    var y = window.innerHeight || e.clientHeight || g.clientHeight

    return {
      width: x,
      height: y
    }
  },

  mergeObject: function (o1, o2) {
    var a
    var o3 = {}

    for (a in o1) {
      o3[a] = o1[a]
    }

    for (a in o2) {
      o3[a] = o2[a]
    }

    return o3
  },

  isUndefined: function (obj) {
    return (typeof obj) === 'undefined'
  },

  getCurrentScript: function () {
    // Source http://www.2ality.com/2014/05/current-script.html
    return document.currentScript || (function () {
        var scripts = document.getElementsByTagName('script')
        return scripts[scripts.length - 1]
      })()
  },

  generateUuid: function () {
    function _p8 (s) {
      var p = (Math.random().toString(16) + '000000000').substr(2, 8)
      return s ? '-' + p.substr(0, 4) + '-' + p.substr(4, 4) : p
    }
    return _p8() + _p8(true) + _p8(true) + _p8()
  }

}

module.exports = {
  getViewPortInfo: function getViewPort () {
    var e = document.documentElement
    var g = document.getElementsByTagName('body')[0]
    var x = window.innerWidth || e.clientWidth || g.clientWidth
    var y = window.innerHeight || e.clientHeight || g.clientHeight

    return {
      width: x,
      height: y
    }
  },

  mergeObject: function (o1, o2) {
    var a
    var o3 = {}

    for (a in o1) {
      o3[a] = o1[a]
    }

    for (a in o2) {
      o3[a] = o2[a]
    }

    return o3
  },

  isUndefined: function (obj) {
    return (typeof obj) === 'undefined'
  },

  isCORSSupported: function () {
    var xhr = new window.XMLHttpRequest()
    return 'withCredentials' in xhr
  },

  getCurrentScript: function () {
    // Source http://www.2ality.com/2014/05/current-script.html
    return document.currentScript || (function () {
        var scripts = document.getElementsByTagName('script')
        return scripts[scripts.length - 1]
      })()
  },

  generateUuid: function () {
    function _p8 (s) {
      var p = (Math.random().toString(16) + '000000000').substr(2, 8)
      return s ? '-' + p.substr(0, 4) + '-' + p.substr(4, 4) : p
    }
    return _p8() + _p8(true) + _p8(true) + _p8()
  }

}

Add method to detect if CORS is supported


#!/usr/bin/env node

import yargs from 'yargs';
import path from 'path';
import * as pose from '.';

const opts = yargs
.usage('$ pose <action> [options]')
.help('help')
.options({
  name: {
    alias: ['n'],
    default: path.basename(process.cwd()),
    desc: 'Name of template',
  },

  entry: {
    alias: ['e'],
    default: process.cwd(),
    defaultDesc: 'cwd',
    desc: 'Entry of template',
  },

  help: {
    alias: ['h'],
  },
}).argv;
const action = opts._[0];

pose[action](opts);

export { action, opts };

#!/usr/bin/env node

import yargs from 'yargs';
import path from 'path';
import fs from 'fs';
import * as pose from '.';

const opts = yargs
.usage('$ pose <action> [options]')
.help('help')
.options({
  name: {
    alias: ['n'],
    default: path.basename(process.cwd()),
    desc: 'Name of template',
  },

  entry: {
    alias: ['e'],
    default: process.cwd(),
    defaultDesc: 'cwd',
    desc: 'Entry of template',
  },

  help: {
    alias: ['h'],
  },
}).argv;
const action = opts._[0];

[
  opts._pose = path.join(process.env.HOME, '.pose'),
  opts._templates = path.join(opts._pose, 'templates'),
].forEach(dir => {
  fs.mkdir(dir, () => null);
});

pose[action](opts);
export { action, opts };

Add automatic dir checker to CLI


'use strict';

(function () {
	var editorHasFocus = false;
	var commandIsActive = false;

	CKEDITOR.plugins.add('autoid', {
		init: function (editor) {
			var self = this;

			if (editor && !editor.config.autoid) {
				editor.config.autoid = {};
			}
			self.settings = editor.config.autoid;
			if (!self.settings) {
				self.settings = {};
			}

			editor.addCommand('autoid', {
				exec: function (editor) {
					if (!commandIsActive) {
						start();
					} else {
						stop();
					}
				},
				editorFocus: true
			});

		}
	});
})();

'use strict';

(function () {
	var editorHasFocus = false;
	var commandIsActive = false;

	CKEDITOR.plugins.add('autoid', {
		init: function (editor) {
			var self = this;

			if (editor && !editor.config.autoid) {
				editor.config.autoid = {};
			}
			self.settings = editor.config.autoid;
			if (!self.settings) {
				self.settings = {};
			}

			editor.addCommand('autoid', {
				exec: function (editor) {
					if (!commandIsActive) {
						start();
					} else {
						stop();
					}
				},
				editorFocus: true
			});

			function start() {
				editor.getCommand('autoid').setState(CKEDITOR.TRISTATE_ON);
				commandIsActive = true;

				addAllAnchors();
			}

			function stop() {
				editor.getCommand('autoid').setState(CKEDITOR.TRISTATE_OFF);
				commandIsActive = false;
			}

		}
	});
})();

Add start and stop functions


Remove babel-polyfill in favour of core-js


import events from '@girder/core/events';
import { restRequest } from '@girder/core/rest';
import {init as sentryInit, captureException} from '@sentry/browser';

import './routes';

events.on('g:appload.after', function () {
    restRequest({
        method: 'GET',
        url: 'sentry/dsn'
    }).done((resp) => {
        if (resp.sentry_dsn) {
            // ga('create', resp.sentry_dsn, 'none');
            sentryInit({dsn: resp.sentry_dsn});
            captureException(new Error("This is my fake error message"));
        }
    });
});

import events from '@girder/core/events';
import { restRequest } from '@girder/core/rest';
import {init as sentryInit, captureException} from '@sentry/browser';

import './routes';

events.on('g:appload.after', function () {
    restRequest({
        method: 'GET',
        url: 'sentry/dsn'
    }).done((resp) => {
        if (resp.sentry_dsn) {
            // ga('create', resp.sentry_dsn, 'none');
            sentryInit({dsn: resp.sentry_dsn});
            // captureException(new Error("This is my fake error message"));
        }
    });
});

Comment out unconditional sentry capture


const mapBaseLayers = require('../../../public/static/map/map-base-layers.config.json')

async function fetchMap(resolver, query) {
  const result = await fetch(process.env.GRAPHQL_ENDPOINT, {
    method: 'post',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      query,
    }),
  })

  const json = await result.json()

  return json.data[resolver]
}

export function getMapBaseLayers() {
  return mapBaseLayers
}

export async function getMapLayers() {
  const query = `{ mapLayerSearch { results { id title url params layers external detailUrl detailItem detailIsShape noDetail authScope type } } }`
  const { results: mapLayerResults } = await fetchMap('mapLayerSearch', query)

  return mapLayerResults
}

export async function getPanelLayers() {
  const query = `{ mapCollectionSearch { results { id title mapLayers { id title legendItems { id title url params layers iconUrl imageRule notSelectable noDetail } authScope imageRule iconUrl url params layers detailUrl detailItem detailIsShape type noDetail minZoom maxZoom } } } }`
  const { results: mapPanelLayerResults } = await fetchMap('mapCollectionSearch', query)

  return mapPanelLayerResults
}

const mapBaseLayers = require('../../../public/static/map/map-base-layers.config.json')

async function fetchMap(resolver, query) {
  const result = await fetch(process.env.GRAPHQL_ENDPOINT, {
    method: 'post',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      query,
    }),
  })

  const json = await result.json()

  return json.data[resolver]
}

export function getMapBaseLayers() {
  return mapBaseLayers
}

export async function getMapLayers() {
  const query = `{ mapLayerSearch(input: { limit: 9999 }) { results { id title url params layers external detailUrl detailItem detailIsShape noDetail authScope type } } }`
  const { results: mapLayerResults } = await fetchMap('mapLayerSearch', query)

  return mapLayerResults
}

export async function getPanelLayers() {
  const query = `{ mapCollectionSearch(input: { limit: 9999 }) { results { id title mapLayers { id title legendItems { id title url params layers iconUrl imageRule notSelectable noDetail } authScope imageRule iconUrl url params layers detailUrl detailItem detailIsShape type noDetail minZoom maxZoom } } } }`
  const { results: mapPanelLayerResults } = await fetchMap('mapCollectionSearch', query)

  return mapPanelLayerResults
}

Set a high limit on the query to retrieve all the maplayers


import { isString, isArray, isFunction } from './util';

let resolved = {};

function loadScript(url, callback, errorCallback) {
    var invokeCallback = function() {
        resolved[url] = true;

        if (isFunction(callback)) {
            callback();
        }
    };

    if (resolved[url]) {
        invokeCallback();

        return;
    }

    var script = document.createElement('script');
    script.type = 'text/javascript';

    if (script.readyState) {
        //IE
        script.onreadystatechange = function() {
            if (script.readyState == 'loaded' || script.readyState == 'complete') {
                script.onreadystatechange = null;
                invokeCallback();
            }
        };
    } else {
        //Others
        script.onload = function() {
            invokeCallback();
        };
    }

    script.onerror = function(e) {
        resolved[url] = false;
        console.log('error', e);
        if (isFunction(errorCallback)) {
            errorCallback();
        }
    };

    script.src = url;
    var parent = document.body || document.head || document;
    parent.appendChild(script);
}

function get(src, opts) {
    if (isString(src)) {
        return new Promise((resolve, reject) => {
            loadScript(src, () => resolve(true), () => reject());
        });
    } else if (isArray(src)) {
        let p = Promise.resolve(true);

        src.forEach(url => {
            p = p.then(() => get(url));
        });

        return p;
    }

    throw new Error('Invalid argument for get()');
}

export default get;

import { isString, isArray, isFunction } from './util';

let resolved = {};

function loadScript(url, callback, errorCallback) {
    let invokeCallback = function() {
        resolved[url] = true;

        if (isFunction(callback)) {
            callback();
        }
    };

    if (resolved[url]) {
        invokeCallback();

        return;
    }

    let script = document.createElement('script');
    script.type = 'text/javascript';

    if (script.readyState) {
        //IE
        script.onreadystatechange = function() {
            if (script.readyState == 'loaded' || script.readyState == 'complete') {
                script.onreadystatechange = null;
                invokeCallback();
            }
        };
    } else {
        //Others
        script.onload = function() {
            invokeCallback();
        };
    }

    script.onerror = function(e) {
        resolved[url] = false;
        console.log('error', e);
        if (isFunction(errorCallback)) {
            errorCallback();
        }
    };

    script.src = url;
    let parent = document.body || document.head || document;
    parent.appendChild(script);
}

function get(src, opts) {
    if (isString(src)) {
        return new Promise((resolve, reject) => {
            loadScript(src, () => resolve(true), () => reject());
        });
    } else if (isArray(src)) {
        let p = Promise.resolve(true);

        src.forEach(url => {
            p = p.then(() => get(url));
        });

        return p;
    }

    throw new Error('Invalid argument for get()');
}

export default get;

Use let instead of var


var pagination = require('hexo-pagination');
var _ = require('lodash');

hexo.extend.generator.register('category', function(locals){
  var config = this.config;
  var categories = locals.data.categories;
  if (config.category_generator) {
    var perPage = config.category_generator.per_page;
  } else {
    var perPage = 10;
  }
  var paginationDir = config.pagination_dir || 'page';
  var result = [];

  _.each(categories, function(data, title) {
    _.each(data, function(data, lang) {
      result = result.concat({
        path: lang + '/' + data.slug + '/index.html',
        data: {
          posts: getCategoryByName(locals.categories, data.category).posts
        },
        layout: ['archive', 'index']
      });
    });
  });

  return result;
});

function getCategoryByName(categories, name) {
  return categories.toArray().filter(function(category){
    return category.name == name;
  })[0];
}

var pagination = require('hexo-pagination');
var _ = require('lodash');

hexo.extend.generator.register('category', function(locals){
  var config = this.config;
  var categories = locals.data.categories;
  if (config.category_generator) {
    var perPage = config.category_generator.per_page;
  } else {
    var perPage = 10;
  }
  var paginationDir = config.pagination_dir || 'page';
  var result = [];

  _.each(categories, function(data, title) {
    _.each(data, function(data, lang) {
      result = result.concat({
        path: lang + '/' + data.slug + '/index.html',
        data: {
          posts: getCategoryByName(locals.categories, data.category).posts
        },
        layout: ['category', 'archive', 'index']
      });
    });
  });

  return result;
});

function getCategoryByName(categories, name) {
  return categories.toArray().filter(function(category){
    return category.name == name;
  })[0];
}

Use category layout if available in the category generator


const list = require('../../models').list;
const listsubscriber = require('../../models').listsubscriber;

module.exports = (req, res) => {
  // Find all subscribers belonging to a list
  const userId = req.user.id;
  const listId = req.query.listId;

  list.findOne({
    where: {
      userId,
      id: listId
    },
    attributes: ['name', 'createdAt', 'updatedAt'],
    raw: true
  }).then(instancesArray => {
    if (!instancesArray) {
      res.status(401)  //??
        .send({
          message: 'not authorised to view list or list does not exist'
        });
      return;
    } else {
      listsubscriber.findAll({
        where: { listId },
        attributes: ['email', 'subscribed', 'createdAt', 'updatedAt'],
        raw: true
      }).then(instancesArray => {
        res.send({ subscribers: instancesArray });
      });
    }
  });
};

const list = require('../../models').list;
const listsubscriber = require('../../models').listsubscriber;

module.exports = (req, res) => {
  // Find all subscribers belonging to a list
  const userId = req.user.id;
  const listId = req.query.listId;

  list.findOne({
    where: {
      userId,
      id: listId
    },
    attributes: ['name', 'createdAt', 'updatedAt'],
    raw: true
  }).then(instancesArray => {
    if (!instancesArray) {
      res.status(401)  //??
        .send({
          message: 'not authorised to view list or list does not exist'
        });
      return;
    } else {
      listsubscriber.findAll({
        where: { listId },
        attributes: ['email', 'subscribed', 'createdAt', 'updatedAt', 'mostRecentStatus'],
        raw: true
      }).then(instancesArray => {
        res.send({ subscribers: instancesArray });
      });
    }
  });
};

Send most recent status in GET list-subscribers


/*jslint continue: true, devel: true, evil: true, indent: 2, nomen: true, plusplus: true, regexp: true, rhino: true, sloppy: true, sub: true, unparam: true, vars: true, white: true */
/*global _, HostAdapter, hostAdapter */

function pad(num, length, def) {
  'use strict';
  return _('' + (num || def)).pad(length, '0');
}

function generateResults(recognitionResults, q, context) {
  'use strict';

  var recognitionResult = recognitionResults['com.solveforall.recognition.date.DateRange'];

  if (!recognitionResult || (recognitionResult.length === 0)) {
    console.warn("No recognition result found");
    return [];
  }

  var dateRange = recognitionResult[0];
  var start = dateRange.start;

  if (!start) {
    console.warn("No start date found");
    return [];
  }

  var now = new Date();
  var paddedMonth = pad(start.month, 2, now.getMonth() + 1);
  var paddedDay = pad(start.dayOfMonth, 2, now.getDate());
  var year = '' + (start.year || now.getFullYear());

  return [{
    label: 'Calendar',
    iconUrl: 'http://calendar.google.com/googlecalendar/images/favicon_v2010_8.ico',
    summaryHtml: 'Google Calendar',
    uri: 'https://www.google.com/calendar/render?tab=mc&date=' + year +
      paddedMonth + paddedDay,
    embeddable: false,
    relevance: dateRange.recognitionLevel
  }];
}

/*jslint continue: true, devel: true, evil: true, indent: 2, nomen: true, plusplus: true, regexp: true, rhino: true, sloppy: true, sub: true, unparam: true, vars: true, white: true */
/*global _, HostAdapter, hostAdapter */

function pad(num, length, def) {
  'use strict';
  return _('' + (num || def)).pad(length, '0');
}

function generateResults(recognitionResults, q, context) {
  'use strict';

  var recognitionResult = recognitionResults['com.solveforall.recognition.date.DateRange'];

  if (!recognitionResult || (recognitionResult.length === 0)) {
    console.warn("No recognition result found");
    return [];
  }

  var dateRange = recognitionResult[0];
  var start = dateRange.start;

  if (!start) {
    console.warn("No start date found");
    return [];
  }

  var now = new Date();
  var paddedMonth = pad(start.month, 2, now.getMonth() + 1);
  var paddedDay = pad(start.dayOfMonth, 2, now.getDate());
  var year = '' + (start.year || now.getFullYear());

  return [{
    label: 'Google Calendar',
    iconUrl: 'http://calendar.google.com/googlecalendar/images/favicon_v2010_8.ico',
    tooltip: 'View a date in Google Calendar',
    uri: 'https://www.google.com/calendar/render?tab=mc&date=' + year +
      paddedMonth + paddedDay,
    embeddable: false,
    relevance: dateRange.recognitionLevel
  }];
}

Make Google Calendar Answer Generator eligible to be a Quick Link


import React from 'react'
import moment from 'moment'
import {markdown} from 'markdown'
import Person from '../../components/person'
import deindent from 'deindent'

export default Episode

function Episode({episodeData}) {
  const {hangoutUrl, title, date, guests, description} = episodeData
  const time = episodeData.time || '12:00 PM (CT)'
  const dateDisplay = moment(date).format('dddd, MMMM Do, YYYY')
  return (

        {title}

        {dateDisplay} at {time}

        {guests.map((guest, index) => (
          <Person imgSrc={`episodes/${date}/${guest.twitter}.png`} key={index} {...guest} />
        ))}

  )
}

function getDescriptionHTML(description) {
  return {__html: markdown.toHTML(deindent(description))}
}

import React from 'react'
import moment from 'moment'
import {markdown} from 'markdown'
import Person from '../../components/person'
import deindent from 'deindent'

export default Episode

function Episode({episodeData}) {
  const {hangoutUrl, title, date, guests, description} = episodeData
  const time = episodeData.time || '12:00 PM (CT)'
  const dateDisplay = moment(date).format('dddd, MMMM Do, YYYY')
  return (

        {getTitle(hangoutUrl, title)}

        {dateDisplay} at {time}

        {guests.map((guest, index) => (
          <Person imgSrc={`episodes/${date}/${guest.twitter}.png`} key={index} {...guest} />
        ))}

  )
}

function getDescriptionHTML(description) {
  return {__html: markdown.toHTML(deindent(description))}
}

function getTitle(hangoutUrl, title) {
  if (hangoutUrl) {
    return {title}
  } else {
    return {title} (hangout url coming soon)
  }
}

Add special text if there's not yet a hangout URL


'use strict';

/*
 * Binds the attributes to the expression once at startup,
 * Usage:
 */

angular.module('fastBind.bindAttrOnce', []).
  directive('fastBindAttrOnce', ['$parse', function($parse) {
    return {
      compile: function compile(element, attributes) {
        var expr = $parse(attributes.fastBindOnce);

        return function link(scope, element) {
          var values = expr(scope);

          angular.forEach(values, function(value, key) {
            attrs.$set(key, value);
          });
        };
      }
    };
  }]);

'use strict';

/*
 * Binds the attributes to the expression once at startup,
 * Usage:
 */

angular.module('fastBind.bindAttrOnce', []).
  directive('fastBindAttrOnce', ['$parse', function($parse) {
    return {
      compile: function compile(element, attributes) {
        var expr = $parse(attributes.fastBindOnce);

        return function link(scope, element) {
          var values = expr(scope);

          angular.forEach(values, function(value, key) {
            attributes.$set(key, value);
          });
        };
      }
    };
  }]);

Fix use of attrs => attributes


'use strict'

angular.module('ludwigApp')
  .controller('AccountCtrl', function ($scope, $state, LoginService, RepositoryService) {
    LoginService.get()
      .then(function (user) {
        if (!user) {
          return $state.go('layout.home')
        }

        $scope.user = user
        RepositoryService.getCandidates()
          .then(function (repositories) {
            $scope.candidates = repositories.filter(function (repo) {
              return $scope.user.repositories.indexOf(repo.full_name) < 0
            })
          })
      })

    $scope.logout = function () {
      LoginService.delete()
        .then(function () {
          $state.go('layout.home')
        })
    }

    $scope.activateRepository = RepositoryService.activateRepository
  })

'use strict'

angular.module('ludwigApp')
  .controller('AccountCtrl', function ($scope, $state, LoginService, RepositoryService) {
    LoginService.get()
      .then(function (user) {
        if (!user) {
          return $state.go('layout.home')
        }

        $scope.user = user
        RepositoryService.getCandidates()
          .then(function (repositories) {
            $scope.candidates = repositories.filter(function (repo) {
              return $scope.user.repositories.indexOf(repo.full_name) < 0
            })
          })
      })

    $scope.logout = function () {
      LoginService.delete()
        .then(function () {
          $state.go('layout.home')
        })
    }

    $scope.activateRepository = function (repository) {
      RepositoryService.activateRepository(repository)
        .then(function (repository) {
          if (repository.error) {
            return
          }

          $scope.user.repositories.push(repository.id)
          var idx = $scope.candidates.findIndex(function (repo) {
            return repo.full_name === repository.id
          })
          if (idx > -1) {
            $scope.candidates.splice(idx, 1)
          }
        })
    }
  })

Improve front to prevent double activation


;(function() {
  "use strict";

  var BaseController = require("BaseController"),
      _ = require("lodash");

  var multiSelectors = {
    playPause: ["#play-pause", "#play", "[title='Pause'],[title='Play']"],
    playNext: ["#next", "#next", "[title='Next']"],
    playPrev: ["#previous", "#previous", "[title='Previous']"],
    playState: ["#play-pause.playing", "#play.playing", "[title='Pause']"],
    iframe: ["#app-player", "#main", null],
    like: [".thumb.up", ".thumb.up", null],
    dislike: [".thumb.down", ".thumb.down", null],
    song: ["#track-name", ".caption .track", ".now-playing-bar div div [href*='/album/']"],
    artist: ["#track-artist", ".caption .artist", ".now-playing-bar div div [href*='/artist/']"]
  };

  var controller = new BaseController({
    siteName: "Spotify"
  });

  controller.checkPlayer = function() {
    var that = this;
    var selectorIndex;

    if (window.location.hostname === "open.spotify.com") {
      selectorIndex = 2;
    } else {
      if (document.querySelector(multiSelectors.iframe[0])) { selectorIndex = 0; }
      if (document.querySelector(multiSelectors.iframe[1])) { selectorIndex = 1; }
    }

    _.each(multiSelectors, function(value, key) {
      that.selectors[key] = value[selectorIndex];
    });
  };
})();

;(function() {
  "use strict";

  var BaseController = require("BaseController"),
      _ = require("lodash");

  var multiSelectors = {
    play: [null, ".now-playing-bar .spoticon-play-32"],
    pause: [null, ".now-playing-bar .spoticon-pause-32"],
    playPause: ["#play-pause", null],
    playNext: ["#next", ".now-playing-bar .spoticon-skip-forward-24"],
    playPrev: ["#previous", ".now-playing-bar .spoticon-skip-back-24"],
    playState: ["#play-pause.playing", ".now-playing-bar .spoticon-pause-32"],
    iframe: ["#app-player", null],
    like: [".thumb.up", null],
    dislike: [".thumb.down", null],
    song: ["#track-name", ".now-playing-bar div div [href*='/album/']"],
    artist: ["#track-artist", ".now-playing-bar div div [href*='/artist/']"]
  };

  var controller = new BaseController({
    siteName: "Spotify"
  });

  controller.checkPlayer = function() {
    var that = this;
    var selectorIndex = window.location.hostname === "open.spotify.com" ? 1 : 0;

    _.each(multiSelectors, function(value, key) {
      that.selectors[key] = value[selectorIndex];
    });
  };
})();

Fix old spotify player & use class names instead of title for new player.


#!/usr/bin/env node
var fs = require('fs')
var resolve = require('path').resolve
var meow = require('meow')

var initdify = require('./')

var cli = meow(`
  Usage
    $ systemdify <folder>

  Options
    -o, --output Write output to file

  Examples
    $ systemdify /path/to/my/app

    [Unit]
    description=My Application
    ...
`, {
  string: 'output',
  alias: {
    o: 'output'
  }
})

var folder = cli.input[0] || process.cwd()
var pjson = require(resolve(folder, 'package.json'))

var file = initdify({
  name: pjson.name,
  command: resolve(folder, pjson.main),
  description: pjson.description
})

if (cli.flags.output) {
  fs.writeFileSync(resolve('./', cli.flags.output), file)
  process.exit(0)
}

console.log(file)

#!/usr/bin/env node
var fs = require('fs')
var resolve = require('path').resolve
var meow = require('meow')

var initdify = require('./')

var cli = meow(`
  Usage
    $ systemdify <folder>

  Options
    -o, --output Write output to file

  Examples
    $ systemdify /path/to/my/app

    [Unit]
    description=My Application
    ...
`, {
  string: 'output',
  alias: {
    o: 'output'
  }
})

var folder = cli.input[0] || process.cwd()
var pjson

try {
  pjson = require(resolve(folder, 'package.json'))
} catch (_) {
  console.error('No package.json found!')
  process.exit(1)
}

var file = initdify({
  name: pjson.name,
  command: resolve(folder, pjson.main),
  description: pjson.description
})

if (cli.flags.output) {
  fs.writeFileSync(cli.flags.output, file)
  process.exit(0)
} else {
  fs.writeFileSync(`/etc/systemd/system/${pjson.name}.service`)
}

console.log(file)

Write to systemd by default


import React, { Component } from 'react';
import TextField from 'material-ui/TextField';
import Card, { CardActions, CardContent } from 'material-ui/Card';
import Button from 'material-ui/Button';

import TaxCalculator from '../libs/TaxCalculator';

class TextFields extends Component {
  state = {
    income: 0,
    expenses: 0
  };

  calculate() {
    const { income, expenses } = this.state;
    const taxInfo = TaxCalculator.getTaxInfo(income, expenses);
    this.props.handleUpdate(taxInfo);
  }

  render() {
    return (
      <Card>
        <CardContent>

            <TextField
              label="Income"
              type="number"
              value={this.state.income}
              onChange={event => this.setState({ income: event.target.value })}
              inputProps={{ step: '10000' }}
              margin="normal"
              style={{ width: '100%' }}
            />

            <TextField
              label="Expenses"
              type="number"
              value={this.state.expenses}
              onChange={event =>
                this.setState({ expenses: event.target.value })}
              inputProps={{ step: '10000' }}
              margin="normal"
              style={{ width: '100%' }}
            />

        </CardContent>
        <CardActions>
          <Button onClick={() => this.calculate()}>Calculate</Button>
        </CardActions>
      </Card>
    );
  }
}

export default TextFields;

import React, { Component } from 'react';
import TextField from 'material-ui/TextField';
import Card, { CardActions, CardContent } from 'material-ui/Card';
import Button from 'material-ui/Button';

import TaxCalculator from '../libs/TaxCalculator';

class TextFields extends Component {
  state = {
    income: 0,
    expenses: 0
  };

  calculate() {
    const { income, expenses } = this.state;
    const taxInfo = TaxCalculator.getTaxInfo(income, expenses);
    this.props.handleUpdate(taxInfo);
  }

  render() {
    return (
      <Card>
        <CardContent>

            <TextField
              label="Income"
              type="number"
              value={this.state.income}
              onChange={event => this.setState({ income: event.target.value })}
              inputProps={{ min: 0, step: 10000 }}
              margin="normal"
              style={{ width: '100%' }}
            />

            <TextField
              label="Expenses"
              type="number"
              value={this.state.expenses}
              onChange={event =>
                this.setState({ expenses: event.target.value })}
              inputProps={{ min: 0, step: 10000 }}
              margin="normal"
              style={{ width: '100%' }}
            />

        </CardContent>
        <CardActions>
          <Button onClick={() => this.calculate()}>Calculate</Button>
        </CardActions>
      </Card>
    );
  }
}

export default TextFields;

Set min value to text field.


import recaptcha from './recaptcha.js';

let widgetId = null;

export default {
  props: {
    key: {
      type: String,
      required: true
    },
    options: {
      type: Object,
      default() {
        return {};
      }
    }
  },
  created() {
    recaptcha.checkRecaptchaLoad();
  },
  mounted() {
    const self = this;
    const opts = Object.assign({}, this.options, {
      callback: this.emitVerify,
      'expired-callback': this.emitExpired
    });
    recaptcha.render(this.$refs.container, this.key, opts)
      .then((id) => {
        widgetId = id;
        self.$emit('render', widgetId);
      });
  },
  methods: {
    reset() {
      recaptcha.reset(widgetId);
    },
    emitVerify(response) {
      this.$emit('verify', response);
    },
    emitExpired() {
      this.$emit('expired');
    }
  },
  events: {
    recaptchaReset() {
      this.reset();
    }
  },
  render(h) {
    return (

    );
  }
};

import recaptcha from './recaptcha.js';

let widgetId = null;

export default {
  props: {
    sitekey: {
      type: String,
      required: true
    },
    options: {
      type: Object,
      default() {
        return {};
      }
    }
  },
  created() {
    recaptcha.checkRecaptchaLoad();
  },
  mounted() {
    const self = this;
    const opts = Object.assign({}, this.options, {
      callback: this.emitVerify,
      'expired-callback': this.emitExpired
    });
    recaptcha.render(this.$refs.container, this.key, opts)
      .then((id) => {
        widgetId = id;
        self.$emit('render', widgetId);
      });
  },
  methods: {
    reset() {
      recaptcha.reset(widgetId);
    },
    emitVerify(response) {
      this.$emit('verify', response);
    },
    emitExpired() {
      this.$emit('expired');
    }
  },
  events: {
    recaptchaReset() {
      this.reset();
    }
  },
  render(h) {
    return (

    );
  }
};

Rename prop key -> sitekey


// Parameters for the application

export default {
  queryURL: 'https://unece-stardog.ichec.ie/annex/classifications/sparql/query'
}

// Parameters for the application

export default {
  queryURL: 'http://graphdb.linked-open-statistics.org/repositories/classifications'
}

Change backend to LOS GraphDB


"use strict";

angular.module('arethusa.search').directive('pluginSearch', function(injectables) {
  return {
    restrict: 'AE',
    scope: true,
    replace: true,
    link: function(scope, element, attrs) {
      scope.plugin = scope.$eval(attrs.pluginSearch);
      scope.template = 'templates/arethusa.' + scope.plugin.name + '/search.html';
    },
    template: '

'
  };
});

"use strict";

angular.module('arethusa.search').directive('pluginSearch', function() {
  return {
    restrict: 'AE',
    scope: true,
    replace: true,
    link: function(scope, element, attrs) {
      scope.plugin = scope.$eval(attrs.pluginSearch);
      scope.template = 'templates/arethusa.' + scope.plugin.name + '/search.html';
    },
    template: '

'
  };
});

Fix plugin in pluginSeach directive


import React, {Component} from 'react';
import { Link } from 'react-router-dom';
import NavbarLogo from './NavbarLogo.js';
import '../stylesheets/components/navbar.css';

class NavComponent extends Component{

	resetHueTest(){
        this.setState({
            results:false
          });
      }

	render() {
		return (

			<nav>
				<Link to="/">HOME</Link>
				<Link to="/instructions">CB TEST</Link>
        <Link to="/hue-test" onClick={this.resetHueTest}>HUE TEST</Link><span> | </span>
				<Link to="/color-view">CB SIMULATOR</Link>
	      <Link to="/about-us">ABOUT US</Link>
	      <Link to="/contact-us">CONTACT US</Link>
			</nav>
		);
	}
}

export default NavComponent;

import React, {Component} from 'react';
import { Link } from 'react-router-dom';
import NavbarLogo from './NavbarLogo.js';
import '../stylesheets/components/navbar.css';

class NavComponent extends Component{

	render() {
		return (

			<nav>
				<Link to="/">HOME</Link>
				<Link to="/instructions">CB TEST</Link>
        <Link to="/hue-test" onClick={this.resetHueTest}>HUE TEST</Link><span> | </span>
				<Link to="/color-view">CB SIMULATOR</Link>
	      <Link to="/about-us">ABOUT US</Link>
	      <Link to="/contact-us">CONTACT US</Link>
			</nav>
		);
	}
}

export default NavComponent;

Fix Hue test result error


'use strict';

const process = require('process');
const { promisify } = require('util');

const { watch, series, parallel } = require('gulp');

const FILES = require('../files');

// Returns a watch task
// E.g. with `tasks` `{ FORMAT: format }`, the `format` task will be fired
// everytime `FILES.FORMAT` is changed.
// If specified, `initialTask` is fired first
const getWatchTask = function (tasks, initialTask) {
  const watchTask = getWatchTasks(tasks);

  if (initialTask === undefined) { return watchTask; }

  // Runs the task before watching
  // Using `ignoreInitial` chokidar option does not work because the task would
  // be marked as complete before the initial run.
  return series(initialTask, watchTask);
};

const getWatchTasks = tasks => function watchTasks () {
  const promises = Object.entries(tasks)
    .map(([type, task]) => watchByType({ type, task }));
  return Promise.all(promises);
};

const watchByType = async function ({ type, task }) {
  const taskA = Array.isArray(task) ? parallel(task) : task;
  const watcher = watch(FILES[type], taskA);

  // Otherwise the task will hang when fired through npm scripts
  process.on('SIGINT', watcher.close.bind(watcher));

  // Wait for watching to be setup to mark the `watch` task as complete
  await promisify(watcher.on.bind(watcher))('ready');
};

module.exports = {
  getWatchTask,
};

'use strict';

const process = require('process');
const { promisify } = require('util');

const { watch, series, parallel } = require('gulp');

const FILES = require('../files');

// Returns a watch task
// E.g. with `tasks` `{ FORMAT: format }`, the `format` task will be fired
// everytime `FILES.FORMAT` is changed.
// If specified, `initialTask` is fired first
const getWatchTask = function (tasks, initialTask) {
  const watchTask = getWatchTasks(tasks);

  if (initialTask === undefined) { return watchTask; }

  // Runs the task before watching
  // Using `ignoreInitial` chokidar option does not work because the task would
  // be marked as complete before the initial run.
  return series(initialTask, watchTask);
};

const getWatchTasks = tasks => function watchTasks () {
  const promises = Object.entries(tasks)
    .map(([type, task]) => watchByType({ type, task }));
  return Promise.all(promises);
};

const watchByType = async function ({ type, task }) {
  const taskA = Array.isArray(task) ? parallel(task) : task;
  const watcher = watch(FILES[type], taskA);

  // Wait for watching to be setup to mark the `watch` task as complete
  await promisify(watcher.on.bind(watcher))('ready');
};

module.exports = {
  getWatchTask,
};

Fix Nodemon + Yarn integration problem


import Ember from 'ember';
import layout from '../templates/components/resize-detector';

const {
  inject: { service },
  run: { scheduleOnce, bind }
} = Ember;

export default Ember.Component.extend({
  layout,
  tagName: '',
  resizeDetector: service(),

  didInsertElement() {
    this._super(...arguments);

    scheduleOnce('afterRender', this, this.setup);
  },

  setup() {
    this.callback = bind(this, this.onResize);
    this.get('resizeDetector').setup(this.get('selector'), this.callback);
  },

  teardown() {
    this.get('resizeDetector').teardown(this.get('selector'), this.callback);
  },

  onResize(element) {
    if (this.get('isDestroyed') || this.get('isDestroying')) {
      return;
    }
    let $el = Ember.$(element);
    this.sendAction('on-resize', {
      width: $el.width(),
      height: $el.height()
    }, element);
  },

  willDestroyElement() {
    this.teardown();

    this._super(...arguments);
  }
}).reopenClass({
  positionalParams: ['selector']
});

import Ember from 'ember';
import layout from '../templates/components/resize-detector';

const {
  inject: { service },
  run: { scheduleOnce, bind }
} = Ember;

export default Ember.Component.extend({
  layout,
  tagName: '',
  resizeDetector: service(),

  didInsertElement() {
    this._super(...arguments);

    scheduleOnce('afterRender', this, this.setup);
  },

  setup() {
    this.callback = bind(this, this.onResize);
    this.get('resizeDetector').setup(this.get('selector'), this.callback);
  },

  teardown() {
    this.get('resizeDetector').teardown(this.get('selector'), this.callback);
  },

  onResize(element) {
    if (this.get('isDestroyed') || this.get('isDestroying')) {
      return;
    }
    let $el = Ember.$(element);
    this.get('on-resize')({
      width: $el.width(),
      height: $el.height()
    }, element);
  },

  willDestroyElement() {
    this.teardown();

    this._super(...arguments);
  }
}).reopenClass({
  positionalParams: ['selector']
});

Replace sendAction with a function call.


export default {
  app: 'Designates the component as part of the application layout. Used for dynamically adjusting content sizing',
  auto: 'Centers list on selected element',
  centered: 'Centers the tabs',
  clearable: 'Add input clear functionality, default icon is Material Icons **clear**',
  dontFillMaskBlanks: 'Disables the automatic character display when typing',
  falseValue: 'Sets value for falsy state',
  fullWidth: 'Forces 100% width',
  fixedTabs: '`v-tabs-item` min-width 160px (72px mobile), max-width 264px',
  height: 'Sets the component height',
  id: 'Sets the DOM id on the component',
  mask: 'Apply a custom character mask to the input. See mask table above for more information',
  max: 'Sets maximum width',
  maxHeight: 'Sets the maximum height for the content',
  min: 'Sets minimum width',
  mobileBreakPoint: 'Sets the designated mobile breakpoint',
  overlap: 'Component will overlap the slotted content',
  persistent: 'Clicking outside will not dismiss the dialog',
  returnMaskedValue: 'Returns the unmodified masked string',
  ripple: 'Applies the `v-ripple` directive',
  shift: 'Hide text of button when not active',
  singleLine: 'Label does not move on focus/dirty',
  size: 'Sets the height and width of the element',
  textColor: 'Applies a specified color to the control text',
  tile: 'Removes border radius',
  trueValue: 'Sets value for truthy state',
  value: 'Controls visibility',
  width: 'The width of the content'
}

export default {
  app: 'Designates the component as part of the application layout. Used for dynamically adjusting content sizing',
  auto: 'Centers list on selected element',
  centered: 'Centers the tabs',
  clearable: 'Add input clear functionality, default icon is Material Icons **clear**',
  dontFillMaskBlanks: 'Disables the automatic character display when typing',
  falseValue: 'Sets value for falsy state',
  fullWidth: 'Forces 100% width',
  fixedTabs: '`v-tabs-item` min-width 160px (72px mobile), max-width 264px',
  height: 'Sets the component height',
  id: 'Sets the DOM id on the component',
  mask: 'Apply a custom character mask to the input. See mask table below for more information',
  max: 'Sets maximum width',
  maxHeight: 'Sets the maximum height for the content',
  min: 'Sets minimum width',
  mobileBreakPoint: 'Sets the designated mobile breakpoint',
  overlap: 'Component will overlap the slotted content',
  persistent: 'Clicking outside will not dismiss the dialog',
  returnMaskedValue: 'Returns the unmodified masked string',
  ripple: 'Applies the `v-ripple` directive',
  shift: 'Hide text of button when not active',
  singleLine: 'Label does not move on focus/dirty',
  size: 'Sets the height and width of the element',
  textColor: 'Applies a specified color to the control text',
  tile: 'Removes border radius',
  trueValue: 'Sets value for truthy state',
  value: 'Controls visibility',
  width: 'The width of the content'
}

Update prop message for v-text-field masks


import React from 'react'
import { translate } from 'react-i18next'
import PropTypes from 'prop-types'

import Filter from 'components/Filter'

import styles from './FiltersSummary.scss'

const FiltersSummary = ({ filters, removeFilter, t }) => (

      {filters.length
        ? t('FiltersSummary.activeFilters', { count: filters.length })
        : t('FiltersSummary.noFilters')}

    {filters.map((filter, idx) => (
      <Filter
        key={idx}
        detail
        remove
        filter={filter}
        onClick={filter => removeFilter(filter)}
      />
    ))}

)

FiltersSummary.propTypes = {
  filters: PropTypes.array.isRequired,
  removeFilter: PropTypes.func.isRequired,
  t: PropTypes.func.isRequired
}

export default translate('Search')(FiltersSummary)

import React from 'react'
import { translate } from 'react-i18next'
import PropTypes from 'prop-types'

import Filter from 'common/components/Filter'

import styles from './FiltersSummary.scss'

const FiltersSummary = ({ filters, removeFilter, t }) => (

      {filters.length
        ? t('FiltersSummary.activeFilters', { count: filters.length })
        : t('FiltersSummary.noFilters')}

    {filters.map((filter, idx) => (
      <Filter
        key={idx}
        detail
        remove
        filter={filter}
        onClick={filter => removeFilter(filter)}
      />
    ))}

)

FiltersSummary.propTypes = {
  filters: PropTypes.array.isRequired,
  removeFilter: PropTypes.func.isRequired,
  t: PropTypes.func.isRequired
}

export default translate('Search')(FiltersSummary)

Fix broken Filter import in FilterSummary


import EditorControllerMixin from 'ghost/mixins/editor-base-controller';

var EditorNewController = Ember.ObjectController.extend(EditorControllerMixin, {
    actions: {
        /**
          * Redirect to editor after the first save
          */
        save: function () {
            var self = this;
            this._super().then(function (model) {
                if (model.get('id')) {
                    self.transitionTo('editor.edit', model);
                }
                return model;
            });
        }
    }
});

export default EditorNewController;

import EditorControllerMixin from 'ghost/mixins/editor-base-controller';

var EditorNewController = Ember.ObjectController.extend(EditorControllerMixin, {
    actions: {
        /**
          * Redirect to editor after the first save
          */
        save: function () {
            var self = this;
            this._super().then(function (model) {
                if (model.get('id')) {
                    self.transitionToRoute('editor.edit', model);
                }
                return model;
            });
        }
    }
});

export default EditorNewController;

Fix warning about transitionTo being deprecated


/**
 * window.c.ProjectSuggestedContributions component
 * A Project-show page helper to show suggested amounts of contributions
 *
 * Example of use:
 * view: () => {
 *   ...
 *   m.component(c.ProjectSuggestedContributions, {project: project})
 *   ...
 * }
 */
import m from 'mithril';
import _ from 'underscore';
import projectVM from '../vms/project-vm';

const projectSuggestedContributions = {
    view: function({attrs}) {
        const project = attrs.project();

        const subscriptionSuggestionUrl = amount => `/projects/${project.project_id}/subscriptions/start?value=${amount * 100}`,
            contributionSuggestionUrl = amount => `/projects/${project.project_id}/contributions/new?value=${amount * 100}`,
            suggestionUrl = projectVM.isSubscription(project) ? subscriptionSuggestionUrl : contributionSuggestionUrl,
            suggestedValues = [10, 25, 50, 100];

        return m('#suggestions', _.map(suggestedValues, amount => project ? m(`${project.open_for_contributions ? `a[href="${suggestionUrl(amount)}"].card-reward` : ''}.card-big.card-secondary.u-marginbottom-20`, [
            m('.fontsize-larger', `R$ ${amount}`)
        ]) : ''));
    }
};

export default projectSuggestedContributions;

/**
 * window.c.ProjectSuggestedContributions component
 * A Project-show page helper to show suggested amounts of contributions
 *
 * Example of use:
 * view: () => {
 *   ...
 *   m.component(c.ProjectSuggestedContributions, {project: project})
 *   ...
 * }
 */
import m from 'mithril';
import _ from 'underscore';
import projectVM from '../vms/project-vm';

const projectSuggestedContributions = {
    view: function({attrs}) {
        const project = attrs.project();

        const subscriptionSuggestionUrl = amount => `/projects/${project.project_id}/subscriptions/start?value=${amount * 100}`,
            contributionSuggestionUrl = amount => `/projects/${project.project_id}/contributions/new?value=${amount * 100}`,
            suggestionUrl = projectVM.isSubscription(project) ? subscriptionSuggestionUrl : contributionSuggestionUrl,
            suggestedValues = [10, 25, 50, 100];

        return m('#suggestions', _.map(suggestedValues, amount => project ? m(`${project.open_for_contributions ? `a[href="${suggestionUrl(amount)}"].card-reward` : ''}.card-big.u-text-center.card-secondary.u-marginbottom-20`, [
            m('.fontsize-jumbo', `R$ ${amount}`)
        ]) : ''));
    }
};

export default projectSuggestedContributions;

Add center and increase suggested value font size


// Control-flow utilities.
var cadence = require('cadence')

// Evented message queue.
var Procession = require('.')

// Create a splitter that will split the given queue.

//
function Splitter (queue, splits) {
    this._shifter = queue.shifter()
    this._map = {}
    this._array = []
    for (var key in splits) {
        var queue = new Procession
        var split = {
            selector: splits[key],
            queue: queue,
            shifter: queue.shifter()
        }
        this._map[key] = split
        this._array.push(split)
    }
}

Splitter.prototype.dequeue = cadence(function (async, name) {
    var split = this._map[name]
    var loop = async(function () {
        if (split.queue.size != 0 || split.queue.endOfStream) {
            return [ loop.break, split.shifter.shift() ]
        }
        this._shifter.dequeue(async())
    }, function (envelope) {
        if (envelope == null) {
            this._array.forEach(function (split) { split.queue.push(null) })
        } else {
            for (var i = 0, I = this._array.length; i < I; i++) {
                if (this._array[i].selector(envelope)) {
                    this._array[i].queue.push(envelope)
                    break
                }
            }
        }
    })()
})

module.exports = Splitter

// Control-flow utilities.
var cadence = require('cadence')

// Evented message queue.
var Procession = require('./procession')

// Create a splitter that will split the given queue.

//
function Splitter (queue, splits) {
    this._shifter = queue.shifter()
    this._map = {}
    this._array = []
    for (var key in splits) {
        var queue = new Procession
        var split = {
            selector: splits[key],
            queue: queue,
            shifter: queue.shifter()
        }
        this._map[key] = split
        this._array.push(split)
    }
}

Splitter.prototype.dequeue = cadence(function (async, name) {
    var split = this._map[name]
    var loop = async(function () {
        if (split.queue.size != 0 || split.queue.endOfStream) {
            return [ loop.break, split.shifter.shift() ]
        }
        this._shifter.dequeue(async())
    }, function (envelope) {
        if (envelope == null) {
            this._array.forEach(function (split) { split.queue.push(null) })
        } else {
            for (var i = 0, I = this._array.length; i < I; i++) {
                if (this._array[i].selector(envelope)) {
                    this._array[i].queue.push(envelope)
                    break
                }
            }
        }
    })()
})

module.exports = Splitter

Fix require on Node.js 0.12.


import React from 'react';
import {
	View,
	Text,
	StyleSheet,
} from 'react-native';
import moment from 'moment';

import {
	COLOR_LGREY,
	COLOR_BLACK,
} from '../../styles/ColorConstants';

const SpecialEventsHeader = ({ timestamp }) => (
	<View
		style={styles.header}
	>
		<Text style={styles.headerText}>
			{moment(Number(timestamp)).format('MMM DD hh:mm')}
		</Text>
	</View>
);

const styles = StyleSheet.create({
	header: { justifyContent: 'flex-start', alignItems: 'center', width: 45, backgroundColor: COLOR_LGREY, borderBottomWidth: 1, borderColor: COLOR_LGREY },
	headerText: { textAlign: 'right', alignSelf: 'stretch', color: COLOR_BLACK, fontSize: 12, marginTop: 7 },
});

export default SpecialEventsHeader;

import React from 'react';
import {
	View,
	Text,
	StyleSheet,
} from 'react-native';
import moment from 'moment';

import {
	COLOR_LGREY,
	COLOR_BLACK,
} from '../../styles/ColorConstants';

const SpecialEventsHeader = ({ timestamp }) => (
	<View
		style={styles.header}
	>
		<Text style={styles.headerText}>
			{moment(Number(timestamp)).format('MMM DD h:mm')}
		</Text>
	</View>
);

const styles = StyleSheet.create({
	header: { justifyContent: 'flex-start', alignItems: 'center', width: 45, backgroundColor: COLOR_LGREY, borderBottomWidth: 1, borderColor: COLOR_LGREY },
	headerText: { textAlign: 'right', alignSelf: 'stretch', color: COLOR_BLACK, fontSize: 12, marginTop: 7 },
});

export default SpecialEventsHeader;

Remove leading hour from session time


$(document).ready(function () {
  $('#share_application_amount').observe_field(0.5, function()
  {
    var newShareApplicationAmount = parseInt(this.value);
    if (isNaN(newShareApplicationAmount)) {
      $('p.share_payment').hide();
    } else {
      $('p.share_payment').show();
      $('p.share_payment span').text(newShareApplicationAmount);
    }
  });
});

$(document).ready(function () {
  $('#share_application_amount').observe_field(0.5, function()
  {
    var newShareApplicationAmount = parseInt(this.value, 10);
    if (isNaN(newShareApplicationAmount)) {
      $('p.share_payment').hide();
    } else {
      $('p.share_payment').show();
      $('p.share_payment span').text(newShareApplicationAmount);
    }
  });
});

Make parseInt in JS safer by specifying radix.


'use strict'

var fs = require('../')
var rimraf = require('rimraf')
var mkdirp = require('mkdirp')
var test = require('tap').test
var p = require('path').resolve(__dirname, 'files')

process.chdir(__dirname)

// Make sure to reserve the stderr fd
process.stderr.write('')

var num = 4097
var paths = new Array(num)

test('write files', function (t) {
  rimraf.sync(p)
  mkdirp.sync(p)

  var done = 0
  for (var i = 0; i < num; ++i) {
    paths[i] = 'files/file-' + i
    var stream = fs.createWriteStream(paths[i])
    stream.on('end', function () {
      ++done
      if (done === num) {
        t.pass('success')
        t.end()
      }
    })
    stream.write('content')
    stream.end()
  }

  t.end()
})

test('read files', function (t) {
  // now read them
  var done = 0
  for (var i = 0; i < num; ++i) {
    var stream = fs.createReadStream(paths[i])
    stream.on('data', function (data) {})
    stream.on('end', function () {
      ++done
      if (done === num) {
        t.pass('success')
        t.end()
      }
    })
  }
})

test('cleanup', function (t) {
  rimraf.sync(p)
  t.end()
})

'use strict'

var fs = require('../')
var rimraf = require('rimraf')
var mkdirp = require('mkdirp')
var test = require('tap').test
var p = require('path').resolve(__dirname, 'files')

process.chdir(__dirname)

// Make sure to reserve the stderr fd
process.stderr.write('')

var num = 4097
var paths = new Array(num)

test('write files', function (t) {
  rimraf.sync(p)
  mkdirp.sync(p)

  t.plan(num)
  for (var i = 0; i < num; ++i) {
    paths[i] = 'files/file-' + i
    var stream = fs.createWriteStream(paths[i])
    stream.on('finish', function () {
      t.pass('success')
    })
    stream.write('content')
    stream.end()
  }
})

test('read files', function (t) {
  // now read them
  t.plan(num)
  for (var i = 0; i < num; ++i) (function (i) {
    var stream = fs.createReadStream(paths[i])
    var data = ''
    stream.on('data', function (c) {
      data += c
    })
    stream.on('end', function () {
      t.equal(data, 'content')
    })
  })(i)
})

test('cleanup', function (t) {
  rimraf.sync(p)
  t.end()
})

Validate data in stream test


import AWS from 'aws-sdk';

module.exports = class Sessions {
    constructor() {
        this.SESSION_TABLE = `${process.env.SERVERLESS_PROJECT}-sessions-${process.env.SERVERLESS_STAGE}`;
        this.db = new AWS.DynamoDB.DocumentClient();
    }

    read(id) {
        return new Promise((resolve, reject) => {
            const params = {
                Key: { id },
                TableName: this.SESSION_TABLE,
                ConsistentRead: true
            };

            this.db.get(params, (err, data) => {
                if (err) {
                    console.error(err.toString());
                    return reject(err);
                }

                console.log('db read: ' + JSON.stringify(data));

                return resolve(data.Item.context);
            });
        });
    }

    write(id, context) {
        return new Promise((resolve, reject) => {
            if (!id) {
                return reject(new TypeError('No session ID'));
            }

            // coerce session id to string
            id = id + '';

            const params = {
                TableName: this.SESSION_TABLE,
                Item: {
                    id,
                    context
                }
            };

            console.log('db write: ' + JSON.stringify(context));

            this.db.put(params, (err, data) => {
                if (err) {
                    console.error(err.toString());
                    return reject(err);
                }

                return resolve(context);
            });
        });
    }
}

import AWS from 'aws-sdk';

module.exports = class Sessions {
    constructor() {
        this.SESSION_TABLE = `${process.env.SERVERLESS_PROJECT}-sessions-${process.env.SERVERLESS_STAGE}`;
        this.db = new AWS.DynamoDB.DocumentClient();
    }

    read(id) {
        return new Promise((resolve, reject) => {
            const params = {
                Key: { id },
                TableName: this.SESSION_TABLE,
                ConsistentRead: true
            };

            this.db.get(params, (err, data) => {
                if (err) {
                    console.error(err.toString());
                    return reject(err);
                }

                console.log('db read: ' + JSON.stringify(data));

                if (data.Item !== undefined) {
                    return resolve(data.Item.context);
                } else {
                    // return an empty context for new sessions
                    return resolve({});
                }
            });
        });
    }

    write(id, context) {
        return new Promise((resolve, reject) => {
            if (!id) {
                return reject(new TypeError('No session ID'));
            }

            // coerce session id to string
            id = id + '';

            const params = {
                TableName: this.SESSION_TABLE,
                Item: {
                    id,
                    context
                }
            };

            console.log('db write: ' + JSON.stringify(context));

            this.db.put(params, (err, data) => {
                if (err) {
                    console.error(err.toString());
                    return reject(err);
                }

                return resolve(context);
            });
        });
    }
}

Fix 'cannot get context of undefined' exception


Rename function parameter "event" to "e"


// go.campaign.dialogue.diagram
// ============================
// Structures for the dialogue diagram (the main view for the dialogue screen).

(function(exports) {
  var components = go.components;

  var diagrams = components.plumbing.diagrams,
      DiagramView = diagrams.DiagramView;

  var dialogue = go.campaign.dialogue,
      connections = dialogue.connections,
      states = dialogue.states;

  // The main view containing all the dialogue states and connections
  var DialogueDiagramView = DiagramView.extend({
    stateType: states.DialogueStateView,
    stateCollectionType: states.DialogueStateCollection,

    connectionType: connections.DialogueConnectionView,
    connectionCollectionType: connections.DialogueConnectionCollection,

    className: function() {
      return ['diagram', this.grid.className].join(' ');
    },

    initialize: function(options) {
      DiagramView.prototype.initialize.call(this, options);

      this.grid = new components.grid.GridView({
        el: this.$el,
        items: this.states.members.get('states'),
        sortableOptions: {sort: function() { jsPlumb.repaintEverything(); }}
      });

      this.grid.on('render', function() { jsPlumb.repaintEverything(); });
      this.$el.addClass(this.className());
    },

    render: function() {
      this.grid.render();
      this.connections.render();
    }
  });

  _(exports).extend({
    DialogueDiagramView: DialogueDiagramView
  });
})(go.campaign.dialogue.diagram = {});

// go.campaign.dialogue.diagram
// ============================
// Structures for the dialogue diagram (the main view for the dialogue screen).

(function(exports) {
  var components = go.components;

  var diagrams = components.plumbing.diagrams,
      DiagramView = diagrams.DiagramView;

  var dialogue = go.campaign.dialogue,
      connections = dialogue.connections,
      states = dialogue.states;

  // The main view containing all the dialogue states and connections
  var DialogueDiagramView = DiagramView.extend({
    stateType: states.DialogueStateView,
    stateCollectionType: states.DialogueStateCollection,

    connectionType: connections.DialogueConnectionView,
    connectionCollectionType: connections.DialogueConnectionCollection,

    className: function() {
      return ['diagram', this.grid.className].join(' ');
    },

    initialize: function(options) {
      DiagramView.prototype.initialize.call(this, options);

      this.grid = new components.grid.GridView({
        el: this.$el,
        items: this.states.members.get('states'),
        sortableOptions: {
          placeholder: 'placeholder',
          sort: function() { jsPlumb.repaintEverything(); }
        }
      });

      this.grid.on('render', function() { jsPlumb.repaintEverything(); });
      this.$el.addClass(this.className());
    },

    render: function() {
      this.grid.render();
      this.connections.render();
    }
  });

  _(exports).extend({
    DialogueDiagramView: DialogueDiagramView
  });
})(go.campaign.dialogue.diagram = {});

Add placeholder class to sortable options for dialogue screen grid


$(function() {
    function show() {
        playbyplayui.show(function(err, run) {
            if (err) {
                alert(err);
                return;
            }
            if (run) {
                $('#input').text(run.input);
            }
        });
    }

    $('#show').on('click', show).click();
});

$(function() {
    function show() {
        playbyplayui.show(function(err, run) {
            if (err) {
                alert(err);
                return;
            }
            if (run) {
                $('#input').text(run.input);
            }
        });
    }

    $('#show').on('click', show);
});

Stop auto-showing history in demo


var db = require( '../../db' )
var chai = require( 'chai' )
chai.use( require( 'chai-as-promised' ) )
var expect = chai.expect

describe( 'making a post', function() {
    it( 'creates an account and creates a new post', function() {
        browser.get( 'http://localhost:3001' )
        element( by.css( 'nav .register' ) ).click()

        // fill out and submit login form
        element( by.model( 'username' ) ).sendKeys( 'drift' )
        element( by.model( 'password' ) ).sendKeys( 'pass' )
        element( by.css( 'form .btn' ) ).click()

        element( by.css( 'nav .posts' ) ).click()

        // submit a new post on the posts page
        var post = 'my test post' + Math.random()
        element( by.model( 'postBody' ) ).sendKeys( post )
        element( by.css( 'form .btn' ) ).click()
        browser.pause()

        // check that new post appears on the page
        expect( element.all( by.css( 'ul.posts li' ) ).first().getText() )
            .to.eventually.contain( post )
    })
    afterEach( function() {
        db.connection.db.dropDatabase()
    })
})

var db = require( '../../db' )
var chai = require( 'chai' )
chai.use( require( 'chai-as-promised' ) )
var expect = chai.expect

describe( 'making a post', function() {
    it( 'creates an account and creates a new post', function() {
        browser.get( 'http://localhost:3001' )
        element( by.css( 'nav .register' ) ).click()

        // fill out and submit login form
        element( by.model( 'username' ) ).sendKeys( 'drift' )
        element( by.model( 'password' ) ).sendKeys( 'pass' )
        element( by.css( 'form .btn' ) ).click()

        element( by.css( 'nav .posts' ) ).click()

        // submit a new post on the posts page
        var post = 'my test post' + Math.random()
        element( by.model( 'postBody' ) ).sendKeys( post )
        element( by.css( 'form .btn' ) ).click()

        // check that new post appears on the page
        expect( element.all( by.css( 'ul.posts li' ) ).first().getText() )
            .to.eventually.contain( post )
    })
    afterEach( function() {
        db.connection.db.dropDatabase()
    })
})

Remove pause from e2e tests


/*jshint node:true*/
module.exports = {
  name: 'ember-leaflet-openweathermap-tile-layer',
  description: 'openweathermap tile layer',

  normalizeEntityName: function() {
   // this prevents an error when the entityName is
   // not specified (since that doesn't actually matter
   // to us
  },
  afterInstall: function(options) {
   return this.addBowerPackageToProject('git@github.com:buche/leaflet-openweathermap#bb9272b' );
  }
};

/*jshint node:true*/
module.exports = {
  name: 'ember-leaflet-openweathermap-tile-layer',
  description: 'openweathermap tile layer',

  normalizeEntityName: function() {
   // this prevents an error when the entityName is
   // not specified (since that doesn't actually matter
   // to us
  },
  afterInstall: function(options) {
   return this.addBowerPackageToProject('git@github.com:buche/leaflet-openweathermap.git', '#bb9272b' );
  }
};

Remove deprecated usage of addBowerPackageToProject


(function () {
    'use strict';

    var assert = require('assert'),
        Validator = require('../src/validator.js') ;

    describe('validator in the wild', function () {
        this.timeout(10000);

        it('loads all referenced remote schemas', function (done) {

            Validator.simple(
                'https://www.swisshotels.com/availabilities-filtered/schema',

                function (error) {
                    assert.ifError(error);
                    done();
                }
            );
        });

        it('does actual validation', function (done) {
            Validator.simple('http://json-schema.org/geo', function (error, v) {
                assert.ifError(error);

                assert(v.validate(
                    {latitude: 53.0, longitude: 43.0},
                    'http://json-schema.org/geo'
                ).valid);

                done();
            });
        });
    });
}());

(function () {
    'use strict';

    var assert = require('assert'),
        Validator = require('../src/validator.js') ;

    describe('validator in the wild', function () {
        this.timeout(16000);

        it('loads all referenced remote schemas', function (done) {

            Validator.simple(
                'https://www.swisshotels.com/availabilities-filtered/schema',

                function (error) {
                    assert.ifError(error);
                    done();
                }
            );
        });

        it('does actual validation', function (done) {
            Validator.simple('http://json-schema.org/geo', function (error, v) {
                assert.ifError(error);

                assert(v.validate(
                    {latitude: 53.0, longitude: 43.0},
                    'http://json-schema.org/geo'
                ).valid);

                done();
            });
        });
    });
}());

Raise the time-out a bit after a Travis CI failure


$(document).ready(function () {
	var rotationMultiplier = 3.6;
	// For each div that its id ends with "circle", do the following.
	$( "div[id$='circle']" ).each(function() {
		// Save all of its classes in an array.
		var classList = $( this ).attr('class').split(/\s+/);
		// Iterate over the array
		for (var i = 0; i < classList.length; i++) {
		   /* If there's about a percentage class, take the actual percentage and apply the
				css transformations in all occurences of the specified percentage class,
				even for the divs without an id ending with "circle" */
		   if (classList[i].match("^p")) {
			var rotationPercentage = classList[i].substring(1, classList[i].length);
			var rotationDegrees = rotationMultiplier*rotationPercentage;
			$('.c100.p'+rotationPercentage+ ' .bar').css({
			  '-webkit-transform' : 'rotate(' + rotationDegrees + 'deg)',
			  '-moz-transform'    : 'rotate(' + rotationDegrees + 'deg)',
			  '-ms-transform'     : 'rotate(' + rotationDegrees + 'deg)',
			  '-o-transform'      : 'rotate(' + rotationDegrees + 'deg)',
			  'transform'         : 'rotate(' + rotationDegrees + 'deg)'
			});
		   }
		}
	});
});

jQuery(document).ready(function () {
	var rotationMultiplier = 3.6;
	// For each div that its id ends with "circle", do the following.
	jQuery( "div[id$='circle']" ).each(function() {
		// Save all of its classes in an array.
		var classList = jQuery( this ).attr('class').split(/\s+/);
		// Iterate over the array
		for (var i = 0; i < classList.length; i++) {
		   /* If there's about a percentage class, take the actual percentage and apply the
				css transformations in all occurences of the specified percentage class,
				even for the divs without an id ending with "circle" */
		   if (classList[i].match("^p")) {
			var rotationPercentage = classList[i].substring(1, classList[i].length);
			var rotationDegrees = rotationMultiplier*rotationPercentage;
			jQuery('.c100.p'+rotationPercentage+ ' .bar').css({
			  '-webkit-transform' : 'rotate(' + rotationDegrees + 'deg)',
			  '-moz-transform'    : 'rotate(' + rotationDegrees + 'deg)',
			  '-ms-transform'     : 'rotate(' + rotationDegrees + 'deg)',
			  '-o-transform'      : 'rotate(' + rotationDegrees + 'deg)',
			  'transform'         : 'rotate(' + rotationDegrees + 'deg)'
			});
		   }
		}
	});
});

Replace $ with jQuery for noConflict mode


'use strict';

var execSync = require('execSync');
var fs       = require('fs');
var calculatedVersion = require('ember-cli-calculate-version').calculatedVersion();

module.exports = {
  name: 'yuidoc',
  description: 'Generates html documentation using YUIDoc',
  run: function(options, rawArgs) {
    console.log('calculatedVersion', calculatedVersion);
    var config;
    try {
      var buffer = fs.readFileSync('yuidoc.json');
      config = JSON.parse(buffer);
    } catch(e){
      console.log("No yuidoc.json file in root folder. Run `ember g yuidoc` to generate one.");
      process.exit(1);
    }
    console.log('Generating documentation...');
    var command = fs.realpathSync('./node_modules/ember-cli-yuidoc/node_modules/.bin/yuidoc') + ' -q';
    execSync.run("mkdir -p " + config.options.outdir);
    execSync.run(command);
  }
}

'use strict';

var execSync = require('execSync');
var fs       = require('fs');
var calculateVersion = require('ember-cli-calculate-version').calculatedVersion;

module.exports = {
  name: 'yuidoc',
  description: 'Generates html documentation using YUIDoc',
  run: function(options, rawArgs) {
    console.log('calculatedVersion', calculatedVersion);
    var config;
    try {
      var buffer = fs.readFileSync('yuidoc.json');
      config = JSON.parse(buffer);
    } catch(e){
      console.log("No yuidoc.json file in root folder. Run `ember g yuidoc` to generate one.");
      process.exit(1);
    }
    console.log('Generating documentation...');

    var command = fs.realpathSync('./node_modules/ember-cli-yuidoc/node_modules/.bin/yuidoc') + ' -q --project-version ' + calculateVersion();
    execSync.run("mkdir -p " + config.options.outdir);
    execSync.run(command);
  }
}

Add version of the package when generating the documentation


'use strict';

var expect = require('chai').expect;

describe('ScatterChart', function() {
  it('renders scatterchart', function() {
    var React = require('react/addons');
    var ScatterChart = require('../src/scatterchart').ScatterChart;
    var generate = require('../utils/datagen').generateArrayOfPoints;
    var TestUtils = React.addons.TestUtils;
    var points = 5;

    // Render a scatterchart using array data
    var data = {
      series1: generate(points),
      series2: generate(points)
    };
    var scatterchart = TestUtils.renderIntoDocument(
      <ScatterChart data={data} width={400} height={200} />
    );

    var circles = TestUtils.scryRenderedDOMComponentsWithTag(
      scatterchart, 'circle');
    expect(circles).to.have.length(2 * points);
  });
});

'use strict';

var expect = require('chai').expect;

describe('ScatterChart', function() {
  it('renders scatterchart', function() {
    var React = require('react/addons');
    var ScatterChart = require('../src/scatterchart').ScatterChart;
    var generate = require('../utils/datagen').generateArrayOfPoints;
    var TestUtils = React.addons.TestUtils;
    var points = 5;
    var pointRadius = 5;

    // Render a scatterchart using array data
    var data = {
      series1: generate(points),
      series2: generate(points)
    };
    var scatterchart = TestUtils.renderIntoDocument(
      <ScatterChart data={data} width={400} height={200} pointRadius={pointRadius} />
    );

    var circles = TestUtils.scryRenderedDOMComponentsWithTag(
      scatterchart, 'circle');
    expect(circles).to.have.length(2 * points);

    var circleOne = circles[0];
    expect(circleOne.getDOMNode().r.baseVal.value).to.equal(pointRadius);
    TestUtils.Simulate.mouseOver(circleOne);
    expect(circleOne.getDOMNode().r.baseVal.value).to.be.above(pointRadius);
    TestUtils.Simulate.mouseOut(circleOne);
    expect(circleOne.getDOMNode().r.baseVal.value).to.equal(pointRadius);
  });
});

Add test for scatter chart animation


const clickThroughBaseUrl = 'localhost:8080';  // placeholder

function wrapLink(str, trackingId) {
  str = str.replace(/\{(.+?)\/(.+?)\}/g, function(m, label, url) {
    return '' + label + '';
  });
  return str;
}

function insertUnsubscribeLink(body, unsubscribeId) {
  return body + "\nunsubscribe";
}

module.exports = {
  wrapLink,
  insertUnsubscribeLink
}

const clickThroughBaseUrl = 'localhost:8080';  // placeholder

function wrapLink(body, trackingId) {
  body = body.replace(/\{(.+?)\/(.+?)\}/g, function(m, label, url) {
    return '' + label + '';
  });
  return body;
}

function insertUnsubscribeLink(body, unsubscribeId) {
  return body + "\nunsubscribe";
}

module.exports = {
  wrapLink,
  insertUnsubscribeLink
}

Change variable names for consistency


'use strict';

var numberFilter = require('./number');

var units = {
    ml: 1000,
    cl: 100,
    dl: 10,
    l: 1,
    liter: 1
};

module.exports = function volumeFilter(val) {
    if (typeof val === 'number') {
        return val;
    } else if (val === '') {
        return null;
    }

    var unit = val.match(/(ml|cl|dl|l|liter)/) || [];
    var amount = numberFilter.greedy(val);
    var factor = units[unit[1]] || 1;

    return amount / factor;
};

'use strict';

var numberFilter = require('./number');

var units = {
    ml: 1000,
    cl: 100,
    dl: 10,
    l: 1,
    liter: 1
};

module.exports = function volumeFilter(val) {
    if (!val) {
        return null;
    } else if (typeof val === 'number') {
        return val;
    }

    var unit = val.match(/(ml|cl|dl|l|liter)/) || [];
    var amount = numberFilter.greedy(val);
    var factor = units[unit[1]] || 1;

    return amount / factor;
};

Return null if 'val' is falsy


const idea = (state = [], action) => {
  switch (action.type) {
    case "SET_IDEAS":
      return action.ideas
    case "ADD_IDEA":
      return [...state, action.idea]
    case "UPDATE_IDEA" :
      return state.map(idea => {
        return (idea.id === action.ideaId) ? Object.assign({}, idea, action.newAttributes) : idea
      })
    case "DELETE_IDEA" :
      return state.filter(idea => {
        return idea.id !== action.ideaId
      })
    default:
      return state
  }
}

export default idea

const idea = (state = [], action) => {
  switch (action.type) {
    case "SET_IDEAS":
      return action.ideas
    case "ADD_IDEA":
      return [...state, action.idea]
    case "UPDATE_IDEA" :
      return state.map(idea => {
        return (idea.id === action.ideaId) ? Object.assign({}, idea, action.newAttributes) : idea
      })
    case "DELETE_IDEA" :
      return state.filter(idea => idea.id !== action.ideaId)
    default:
      return state
  }
}

export default idea

Refactor to avoid using curly-brace-multi-line filter


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

module.exports = {
    // By default, Marquette accepts connections on all IPv4 interfaces.
    // The following property can be used to listen on a specific interface. For
    // example, the following would only allow connections from the local machine.
    //uiHost: "127.0.0.1",

    // the tcp port that the Marquette web server is listening on
    uiPort: 1890,

    // the hostname of the MQTT broker to send and receive messages to
    mqttHost: "test.mosquitto.org",

    // the tcp port that the MQTT broker listening on
    //mqttPort: 1883,

    // Retry time in milliseconds for MQTT connections
    mqttReconnectTime: 15000

    // Enabled verbose logging output
    //verbose: true
}

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

module.exports = {
    // By default, Marquette accepts connections on all IPv4 interfaces.
    // The following property can be used to listen on a specific interface. For
    // example, the following would only allow connections from the local machine.
    //uiHost: "127.0.0.1",

    // the tcp port that the Marquette web server is listening on
    uiPort: 1890,

    // the hostname of the MQTT broker to send and receive messages to
    mqttHost: "localhost",

    // the tcp port that the MQTT broker listening on
    //mqttPort: 1883,

    // Retry time in milliseconds for MQTT connections
    mqttReconnectTime: 15000

    // Enabled verbose logging output
    //verbose: true
}

Use localhost by default, not test.mosquitto.org Makes development on trains easier


'use strict';
/**
 * Provides a unified interface for some
 * specific IO related tasks provided from
 * different modules.
 *
 * 	@module lib/dir
 */
const fs = require('fs-extra');
const emptyDir = require('empty-dir');
const cpr = require('cpr');

// Module API
module.exports = {
  isEmpty: emptyDir.sync,
  copy: cpr,
  clean: fs.emptyDirSync
};

Remove bogus character in jsdoc


var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var midi = require('midi');
var port = 80;

// Midi Stuff
// Set up a new input.
var input = new midi.input();
var output = new midi.output();

// Get the name of a specified input port.
console.log('Opening midi port:', input.getPortName(1));
input.openPort(1);
output.openPort(1);

server.listen(port, function () {
  console.log('Server listening at port %d', port);
});

// Routing
app.use(express.static(__dirname + '/public'));

io.on('connection', function (socket) {

  // when the client emits 'new message', this listens and executes
  socket.on('midiout', function (data) {
    console.log('Midi Output:', data);
    output.sendMessage(data);
  });

  // Configure a callback.
  input.on('message', function(deltaTime, message) {
    socket.emit('midiin', message);
  });

});

var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var midi = require('midi');
var debug = false;
var httpPort = 80;
var midiPort = 1;

// Midi Stuff
// Set up a new input.
var input = new midi.input();
var output = new midi.output();

// Get the name of a specified input port.
console.log('Opening midi port:', input.getPortName(midiPort));
input.openPort(midiPort);
output.openPort(midiPort);

server.listen(port, function () {
  console.log('Server listening at port %d', port);
});

// Routing (for index.html)
app.use(express.static(__dirname + '/public'));

io.on('connection', function (socket) {

  // When we recieve a midiout event, push that to the midi port
  socket.on('midiout', function (data) {
  	debugMessage('OUTPUT', data);
    output.sendMessage(data);
  });

  // Emit a socket for input from the piano to clients
  input.on('message', function(deltaTime, message) {
  	debugMessage('INPUT ', message);
    socket.emit('midiin', message);
  });

});

// Helper func for message debug
function debugMessage() {
  if (debug) console.debug(arguments);
}

Refactor server and add simple debug mode


import { combineReducers } from 'redux';
import fuelSavingsAppState from './fuelSavings';
import cmsAppState from './cms.js';

const rootReducer = combineReducers({
  fuelSavingsAppState,
  cmsAppState
});

export default rootReducer;

import { combineReducers } from 'redux';
import fuelSavingsAppState from './fuelSavings';
import cmsAppState from './cms.js';

const rootReducer = combineReducers({
  fuelSavingsAppState: fuelSavingsAppState,
  cmsAppState: cmsAppState
});

export default rootReducer;

Add key names for rootReducer