import {
  introJSNext,
  introJSPrevious,
  introJSExit,
  introJSEnsureClosed,
  introJSCurrentStep } from './../helpers/ember-introjs';
import { expect } from 'chai';
import { describe, it, beforeEach, afterEach } from 'mocha';
import { visit } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-mocha';

describe('test helpers', function() {
  setupApplicationTest();

  beforeEach(async function(){
    await visit('/');
  });

  afterEach(async function(){
    return await introJSEnsureClosed();
  });

  it('can use the next helper', async function(){
    await introJSNext();

    expect(introJSCurrentStep().intro).to.equal('Step 2!');
  });

  it('can use the exit helper', async function(){
    await introJSExit();

    expect(document.querySelector('.introjs-overlay')).to.equal(null);
  });

  it('can use the previous helper', async function(){
    await introJSNext();
    await introJSPrevious();

    expect(introJSCurrentStep().intro).to.equal('Step 1!');
  });
});

import {
  introJSNext,
  introJSPrevious,
  introJSExit,
  introJSEnsureClosed,
  introJSCurrentStep } from './../helpers/ember-introjs';
import { module, test } from 'qunit';
import { visit } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';

module('test helpers', function(hooks) {
  setupApplicationTest(hooks);

  hooks.beforeEach(async function() {
    await visit('/');
  });

  hooks.afterEach(async function() {
    return await introJSEnsureClosed();
  })

  test('can use the next helper', async function(assert) {
    await introJSNext();

    assert.equal(introJSCurrentStep().intro, 'Step 2!');
  });

  test('can use the exit helper', async function(assert){
    await introJSExit();

    assert.equal(document.querySelector('.introjs-overlay'), null);
  });

  test('can use the previous helper', async function(assert){
    await introJSNext();
    await introJSPrevious();

    assert.equal(introJSCurrentStep().intro, 'Step 1!');
  });
})

Fix tests in test helpers


// Define search and replace terms for image src link
var search = /s[0-9]+/g;
var replace = 's512';

function getProfileImageContainer() {
    // Get profile image containers
    return $('#channel-header-container');
}

function getProfileImage(container) {
    // Get profile image tag
    return container.find('img');
}

function wrapEnlargeLink() {
    // Get profile image link
    var imageContainer = getProfileImageContainer();

    // No image?
    if (!imageContainer.length) {
        return;
    }

    // Get img tag nested within container
    var imageTag = getProfileImage(imageContainer);

    // No tag?
    if (!imageTag.length) {
        return;
    }

    // Get image src URL
    var src = imageTag.attr('src');

    // Replace image pixel value in URL for a larger 512px image
    src = src.replace(search, replace);

    // Wrap image tag with a link that points to the larger image
    $( imageTag ).wrap( "" );
}

// One-time injection
wrapEnlargeLink();

// Define search and replace terms for image src link
let search = /s[0-9]+/g;
let replace = 's512';

function getProfileImageContainer() {
    // Get profile image containers
    return $('#channel-header-container');
}

function getProfileImage(container) {
    // Get profile image tag
    return container.find('img');
}

function wrapEnlargeLink() {
    // Get profile image link
    let imageContainer = getProfileImageContainer();

    // No image?
    if (!imageContainer.length) {
        return;
    }

    // Get img tag nested within container
    let imageTag = getProfileImage(imageContainer);

    // No tag?
    if (!imageTag.length) {
        return;
    }

    // Get image src URL
    let src = imageTag.attr('src');

    // Replace image pixel value in URL for a larger 512px image
    src = src.replace(search, replace);

    // Wrap image tag with a link that points to the larger image
    $( imageTag ).wrap( "" );
}

// One-time injection
wrapEnlargeLink();

Use ES6 let instead of var


// Send back all owned mangas
Meteor.publish('allOwnedMangas', function(userId) {
	return Mangas.find({
		"user": userId,
		"owned": true
	});
});

// Send back all missing mangas
Meteor.publish('allMissingMangas', function(userId) {
	return Mangas.find({
		"user": userId,
		"owned": false
	});
});

Add publication for missing mangas


var
  test = require('tap').test,
  SDNV = require('../index');

var
  buff = new Buffer([0x0A, 0xBC]),
  sdnv = new SDNV(buff);

test('make sure a valid buffer results in a valid SNDV', function (t) {
  t.ok(sdnv instanceof SDNV, 'should be able to create an SDNV instance');
  t.type(sdnv.buffer, "Buffer", 'should contain buffer data');
  t.end();
});

test('make sure the SDNV is valid when no buffer is passed', function (t) {
  var emptyBufferSDNV = new SDNV(), expectedBuffer = new Buffer(1);
  t.ok(emptyBufferSDNV instanceof SDNV, 'empty buffer should result in a \
    default SDNV');
  t.type(emptyBufferSDNV.buffer, "Buffer", 'default SDNV should contain \
    buffer data');
  t.end();
});

test('make sure the encoding works', function (t) {
  var encoded = SDNV.encode(buff), expected = new Buffer([0x95, 0x3C]);
  // test the sdnv contents
  t.equal(sdnv.buffer.toString('hex'), expected.toString('hex'),
    'the SDNV buffer should match the expected one');
  // test the utility method
  t.equal(encoded.toString('hex'), expected.toString('hex'),
    'the encoded buffer by the utility method should match the expected one');
  t.end();
});

test('make sure the decoding works', function (t) {
  t.end();
});

var
  test = require('tap').test,
  SDNV = require('../index');

var
  buff = new Buffer([0x0A, 0xBC]),
  sdnv = new SDNV(buff);

test('make sure a valid buffer results in a valid SNDV', function (t) {
  t.ok(sdnv instanceof SDNV, 'should be able to create an SDNV instance');
  t.type(sdnv.buffer, "Buffer", 'should contain buffer data');
  t.end();
});

test('make sure the SDNV is valid when no buffer is passed', function (t) {
  var emptyBufferSDNV = new SDNV(), expectedBuffer = new Buffer(1);
  t.ok(emptyBufferSDNV instanceof SDNV, 'empty buffer should result ' +
    'in a default SDNV');
  t.type(emptyBufferSDNV.buffer, "Buffer", 'default SDNV should contain ' +
    'buffer data');
  t.end();
});

test('make sure the encoding works', function (t) {
  var encoded = SDNV.encode(buff), expected = new Buffer([0x95, 0x3C]);
  // test the sdnv contents
  t.equal(sdnv.buffer.toString('hex'), expected.toString('hex'),
    'the SDNV buffer should match the expected one');
  // test the utility method
  t.equal(encoded.toString('hex'), expected.toString('hex'),
    'the encoded buffer by the utility method should match the expected one');
  t.end();
});

test('make sure the decoding works', function (t) {
  t.end();
});

Fix issues pointed out by JSHint


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

    return {
        allByProject: allByProject
    };

    function allByProject() {
        return r.table('access').run().then(function(allAccess) {
            let byProject = {};
            allAccess.forEach(function(a) {
                if (!(a.project_id in byProject)) {
                    byProject[a.project_id] = [];
                }
                byProject[a.project_id].push(a);
            });
            return byProject;
        });
    }
};

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

    return {
        allByProject: allByProject,
        adminUsers: adminUsers,
    };

    function allByProject() {
        return r.table('access').run().then(function(allAccess) {
            let byProject = {};
            allAccess.forEach(function(a) {
                if (!(a.project_id in byProject)) {
                    byProject[a.project_id] = [];
                }
                byProject[a.project_id].push(a);
            });
            return byProject;
        });
    }

    function adminUsers() {
        return r.table('users').filter({admin: true}).run();
    }
};

Add method to get admin users.


import path from 'path'
import colors from 'colors/safe'
import server from '../server'
import logger from '../helpers/logger'

const { red, green, cyan, bold } = colors

export default async ({ entry, port, host, manual }) => {
  const app = server()

  if (entry) {
    await app.register([import(path.resolve(process.cwd(), entry))])
  } else {
    console.log(red('No entry file specified\n'))
    process.exit()
  }

  app.listen(port, host, err => {
    if (err) {
      logger.error(err)
      process.exit()
    }

    const routes = app.routes()

    let status = bold(
      `Accepting connections on ${cyan.underline(`${host}:${port}`)}\n\n`
    )

    if (routes.length) {
      status += bold('Routes:\n')
      routes.forEach(route => {
        status += `- ${green(route.method.padEnd(8))} ${route.url}\n`
      })
    } else {
      status += red('No routes\n')
    }

    console.log(status)
  })
}

import path from 'path'
import colors from 'colors/safe'
import server from '../server'
import logger from '../helpers/logger'

const { red, green, cyan, bold } = colors

export default async ({ entry, port, host, manual }) => {
  const app = server()

  if (entry) {
    await app.register([import(path.resolve(process.cwd(), entry))])
  } else {
    console.log(red('No entry file specified\n'))
    process.exit()
  }

  app.listen(port, host, err => {
    if (err) {
      logger.error(err)
      process.exit()
    }

    const routes = app.routes()

    let status = bold(
      `Accepting connections on ${cyan.underline(`${host}:${port}`)}\n\n`
    )

    if (routes.length) {
      status += bold('Routes:\n')
      routes.forEach(route => {
        status += `- ${green(route.method.padEnd(8))} ${route.url ||
          route.path}\n`
      })
    } else {
      status += red('No routes\n')
    }

    console.log(status)
  })
}

Make sure logging is correct


#!/usr/bin/env node

var fs = require('fs');
var path = require('path');
var tab = require('tabalot');
var noop = function() {}
var homedir = path.resolve(process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'], '.grunt-init');

if (process.argv.indexOf('completion') !== -1 && process.argv.indexOf('--') === -1) {
	process.argv.push('--bin');
	process.argv.push('grunt-init');
	process.argv.push('--completion');
	process.argv.push('grunt-init-completion');
}

tab()
	('--help', '-h')
	('--no-color')
	('--debug', '-d')
	('--stack')
	('--force', '-f')
	('--no-write')
	('--verbose', '-v')
	('--version', '-V')

fs.readdir(path.join(homedir), function(err, files) {
	if (err) process.exit(2);

	files = files.filter(function(file) {
		return fs.statSync(path.join(homedir, file)).isDirectory();
	});

	files.forEach(function(file) {
		tab(file)(noop);
	});

	tab.parse();
});

#!/usr/bin/env node

var fs = require('fs');
var path = require('path');
var tab = require('tabalot');
var noop = function() {}
var homedir = path.resolve(process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'], '.grunt-init');

if (process.argv.indexOf('completion') !== -1 && process.argv.indexOf('--') === -1) {
	process.argv.push('--bin');
	process.argv.push('grunt-init');
	process.argv.push('--completion');
	process.argv.push('grunt-init-completion');
}

tab()
	('--help', '-h')
	('--no-color')
	('--debug', '-d')
	('--stack')
	('--force', '-f')
	('--no-write')
	('--verbose', '-v')
	('--version', '-V')

	(function(callback) {
		fs.readdir(path.join(homedir), function(err, files) {
			if (err) callback(err);

			files = files.filter(function(file) {
				return fs.statSync(path.join(homedir, file)).isDirectory();
			});

			if (files.length) return callback(null, files, {
				exitCode:15,
			});

			callback();
		});
	})
	(noop)

tab.parse();

Make completion work more logically.


$(document).ready(function() {
    /* Our dummy module.exports is full of s*it. Clear it... */
    delete module;

    TRAFFICSIM_APP.utils.logger.log(TRAFFICSIM_APP.utils.logger.LogType.DEBUG, "Starting app!");
    try {
        new TRAFFICSIM_APP.SimulationApp();
    } catch (e) {
        console.log("Unfortunately the app has crashed due to an unknown error.");
        console.log("Error name: " + e.name);
        console.log("Error message: " + e.message);
    }
});

$(document).ready(function() {
    /* Our dummy module.exports is full of s*it. Clear it... */
    module = {};

    TRAFFICSIM_APP.utils.logger.log(TRAFFICSIM_APP.utils.logger.LogType.DEBUG, "Starting app!");
    try {
        new TRAFFICSIM_APP.SimulationApp();
    } catch (e) {
        console.log("Unfortunately the app has crashed due to an unknown error.");
        console.log("Error name: " + e.name);
        console.log("Error message: " + e.message);
    }
});

Clear module instead of deleting it.


var Data = require('./data');
var data = new Data();

function feedPocketPage(req, res) {
  res.render('feedpocket.html', {
    layout: 'layout',
    locals: {
      categories: data.categoryList
    }
  });
}

function feedData(req, res) {
  data.getFeed(req.params.feedId, function (err, articles) {
    if (err) {
      res.error(err);
    } else {
      res.json(articles);
    }
  });
}

function articleData(req, res) {
  data.getArticle(req.params[0], function (err, article) {
    res.json(article);
  });
}

exports.data           = data;
exports.feedPocketPage = feedPocketPage;
exports.feedData       = feedData;
exports.articleData    = articleData;

var Data = require('./data');
var data = new Data();

function feedPocketPage(req, res) {
  res.render('feedpocket.html', {
    layout: 'layout',
    locals: {
      categories: data.categoryList
    }
  });
}

function feedData(req, res) {
  data.getFeed(req.params.feedId, function (err, articles) {
    if (err) {
      res.error(err);
    } else {
      res.json(articles);
    }
  });
}

function articleData(req, res) {
  var url = req.url.match(/http.+$/);
  data.getArticle(url, function (err, article) {
    res.json(article);
  });
}

exports.data           = data;
exports.feedPocketPage = feedPocketPage;
exports.feedData       = feedData;
exports.articleData    = articleData;

Include query string in URL as cache ID.


ActivityTypes = new Mongo.Collection('activityTypes');

var ActivityTypesSchema = new SimpleSchema({
  name: {
    type: String
  }
});

ActivityTypes.allow({
  insert () {
    // Get current user ID
    const currentUserId = Meteor.userId();

    // Chack if user is administrator
    const userIsAdmin = Roles.userIsInRole(currentUserId);

    // Only Admin user can insert
    return userIsAdmin;
  },
  remove () {
    // Get current user ID
    const currentUserId = Meteor.userId();

    // Chack if user is administrator
    const userIsAdmin = Roles.userIsInRole(currentUserId);

    // Only Admin user can remove
    return userIsAdmin;
  },
  update () {
    // Get current user ID
    const currentUserId = Meteor.userId();

    // Chack if user is administrator
    const userIsAdmin = Roles.userIsInRole(currentUserId);

    // Only Admin user can update
    return userIsAdmin;
  }
});

// Add i18n tags
ActivityTypesSchema.i18n("activityTypes");

ActivityTypes.attachSchema(ActivityTypesSchema);

ActivityTypes = new Mongo.Collection('activityTypes');

var ActivityTypesSchema = new SimpleSchema({
  name: {
    type: String
  }
});

ActivityTypes.allow({
  insert () {
    // Get current user ID
    const currentUserId = Meteor.userId();

    // Chack if user is administrator
    const userIsAdmin = Roles.userIsInRole(currentUserId, 'admin');

    // Only Admin user can insert
    return userIsAdmin;
  },
  remove () {
    // Get current user ID
    const currentUserId = Meteor.userId();

    // Chack if user is administrator
    const userIsAdmin = Roles.userIsInRole(currentUserId, 'admin');

    // Only Admin user can remove
    return userIsAdmin;
  },
  update () {
    // Get current user ID
    const currentUserId = Meteor.userId();

    // Chack if user is administrator
    const userIsAdmin = Roles.userIsInRole(currentUserId, 'admin');

    // Only Admin user can update
    return userIsAdmin;
  }
});

// Add i18n tags
ActivityTypesSchema.i18n("activityTypes");

ActivityTypes.attachSchema(ActivityTypesSchema);

Fix allow rules (admin check)


'use strict';

// Load requirements
const ajv = require('ajv')({allErrors: true, removeAdditional: 'all'});

// Load our schemas
ajv.addSchema(require('./schema/tasks.schema'), 'tasks');
ajv.addSchema(require('./schema/task.schema'), 'task');

// Error formatting
function error(errors) {
  return {
    status: false,
    errors: errors.map((err) => {
      return {path: err.dataPath, message: err.message};
    })
  };
}

// Build the module structure
module.exports = {

  // Validate a single task object
  object: (obj) => {

    if ( ! ajv.validate('task', obj) ) {
      return error(ajv.errors);
    }

    return {status: true, errors: []};
  },

  // Validate a task config file
  file: (obj) => {

    if ( ! ajv.validate('tasks', obj) ) {
      return error(ajv.errors);
    }

    return {status: true, errors: []};
  }

};

'use strict';

// Load requirements
const ajv = require('ajv')({allErrors: true, removeAdditional: 'all'});

// Load our schemas
ajv.addSchema(require('./schema/tasks.schema'), 'tasks');
ajv.addSchema(require('./schema/task.schema'), 'task');

// Build the module structure
module.exports = {

  // Error formatting
  error: (errors) => {
    return {
      status: false,
      errors: errors.map((err) => {
        return {path: err.dataPath || '', message: err.message || ''};
      })
    };
  },

  // Validate a single task object
  object: (obj) => {

    if ( ! ajv.validate('task', obj) ) {
      return this.error(ajv.errors);
    }

    return {status: true, errors: []};
  },

  // Validate a task config file
  file: (obj) => {

    if ( ! ajv.validate('tasks', obj) ) {
      return this.error(ajv.errors);
    }

    return {status: true, errors: []};
  }

};

Move error formatting into validation object


/* global SW:true */

var getUnassigned = function (assignmentCount) {
  var ticketTotal = 0;
  for (var property in assignmentCount) {
    ticketTotal += assignmentCount[property];
  }
  var unassignedTickets = data.tickets.length - ticketTotal;
  console.log('unassignedTickets');
  return unassignedTickets;
}

var countAssignments = function(ticket){
  if (assignmentCount[ticket.assignee.id]){
    assignmentCount[ticket.assignee.id] += 1;
  } else {
    assignmentCount[ticket.assignee.id] = 1;
  }
};

$(document).ready(function(){
  'use strict';
  console.log( 'Doing SW things!' );
  var card = new SW.Card();
  var helpdesk = card.services('helpdesk');
  var assignmentCount = {};
  helpdesk.request('tickets')
    .then( function(data){
      $.each(data.tickets, function(index, ticket){
        countAssignments(ticket);
      });
    console.log( assignmentCount + 'final' );
    getUnassigned(assignmentCount);
    });

});

/* global SW:true */

var assignedTix = {},
    tixCount = 0,
    unassignedTix = [];

var appendTicket = function(ticket) {
  'use strict';
  if (ticket.status == 'open') {
    $('.jumbotron').append('<p>' + ticket.id + ' appended for you!</p>');
    console.log(ticket);
  }
};

var determineAssignments = function(ticket){
  'use strict';
  if (assignedTix[ticket.assignee.id]){
    assignedTix[ticket.assignee.id] += 1;
  } else if {
    assignedTix[ticket.assignee.id] = 1;
  } else {
    unassignedTix.concat(ticket.id);
  }
};

$(document).ready(function() {
  'use strict';
  console.log('Doing SW things!');
  var card = new SW.Card();
  var helpdesk = card.services('helpdesk');
  helpdesk.request('tickets')
    .then(function(data) {
      tixCount = data.tickets.length;
      console.log(tixCount);
      $.each(data.tickets, function(index, ticket) {
        appendTicket(ticket);
        determineAssignments(ticket);
      });
    });
});

Consolidate functions and clean up logic. Change function names to be more descriptive.


/** @babel */

import { CompositeDisposable } from 'atom';
import TerminalView from './terminal-view';

export default {

  disposables: null,
  terminalViews: [],

  activate(state) {
    this.disposables = new CompositeDisposable();

    // Register Opener for the Terminal URI (`terminal://`)
    this.disposables.add(atom.workspace.addOpener((uri) => {
      if (uri.indexOf('terminal:') == 0) {
        const terminalView = new TerminalView();
        this.terminalViews.push(terminalView);
        return terminalView;
      }
    }));

    this.disposables.add(atom.commands.add('atom-workspace', {
      'terminal:open': this.open.bind(this),
      'terminal:copy': this.handleCopy.bind(this),
      'terminal:paste': this.handlePaste.bind(this),
      'terminal:clear': this.handleClear.bind(this)
    }));
  },

  deactivate() {
    this.disposables.dispose();
  },

  open() {
    atom.workspace.open('terminal://');
  },

  handleCopy(event) {
    let activeTerminalView = atom.workspace.getActivePaneItem();
    activeTerminalView.copySelection();
  },

  handlePaste(event) {
    let activeTerminalView = atom.workspace.getActivePaneItem();
    activeTerminalView.pasteFromClipboard();
  },

  handleClear(event) {
    let activeTerminalView = atom.workspace.getActivePaneItem();
    activeTerminalView.clear();
  }

};

/** @babel */

import { CompositeDisposable } from 'atom';
import TerminalView from './terminal-view';

export default {

  disposables: null,
  terminalViews: [],

  activate(state) {
    this.disposables = new CompositeDisposable();

    // Register Opener for the Terminal URI (`terminal://`)
    this.disposables.add(atom.workspace.addOpener((uri) => {
      if (uri.indexOf('atom-terminal-tab:') == 0) {
        const terminalView = new TerminalView();
        this.terminalViews.push(terminalView);
        return terminalView;
      }
    }));

    this.disposables.add(atom.commands.add('atom-workspace', {
      'terminal:open': this.open.bind(this),
      'terminal:copy': this.handleCopy.bind(this),
      'terminal:paste': this.handlePaste.bind(this),
      'terminal:clear': this.handleClear.bind(this)
    }));
  },

  deactivate() {
    this.disposables.dispose();
  },

  open() {
    atom.workspace.open('atom-terminal-tab://');
  },

  handleCopy(event) {
    let activeTerminalView = atom.workspace.getActivePaneItem();
    activeTerminalView.copySelection();
  },

  handlePaste(event) {
    let activeTerminalView = atom.workspace.getActivePaneItem();
    activeTerminalView.pasteFromClipboard();
  },

  handleClear(event) {
    let activeTerminalView = atom.workspace.getActivePaneItem();
    activeTerminalView.clear();
  }

};

Use package name in protocol to avoid conflicts


var ws = require('ws'),
    WebSocketServer = ws.Server;

var WSProxy = exports.WSProxy = function (options) {
  options || (options = {});

  if (!options.target) {
    throw new TypeError("No target given");
  }
  this.target = options.target;

  if (!options.proxy || !options.proxy.port) {
    throw new TypeError("No port to listen on given");
  }
  this.proxy = options.proxy;
  this.proxy.host || (this.proxy.host = "127.0.0.1");

  this.server = null;
};

WSProxy.prototype.start = function () {
  var self = this;

  if (this.server) {
    throw new Error("Already running");
  }

  this.server = new WebSocketServer(this.proxy);
  this.server.on('connection', function (incoming) {
    var outgoing = new ws(self.target);

    incoming.on('message', function (msg) {
      outgoing.send(msg);
    });

    outgoing.on('message', function (msg) {
      incoming.send(msg);
    });
  });
};

WSProxy.prototype.stop = function () {
  if (!this.server) {
    throw new Error("Not running");
  }

  this.server.close();
  this.server = null;
};

var util = require('util'),
    ws = require('ws'),
    WebSocketServer = ws.Server,
    EventEmitter2 = require('eventemitter2').EventEmitter2;

exports.createServer = function createServer(target) {
};

var WSProxy = exports.WSProxy = function (target) {
  if (!target) {
    throw new TypeError("No target given");
  }
  this.target = target;

  this.server = null;

  EventEmitter2.call(this, {
    delimeter: '::',
    wildcard: true
  });
};
util.inherits(WSProxy, EventEmitter2);

WSProxy.prototype.listen = function (port, host) {
  var self = this;

  function listening() {
    self.emit('listening');
  }

  if (typeof arguments[arguments.length - 1] == 'function') {
    this.on('listening', arguments[arguments.length - 1]);
  }

  if (this.server) {
    throw new Error("Already running");
  }

  if (typeof port == 'object') {
    this.server = new WebSocketServer({ server: port }, listening);
  }
  else {
    this.server = new WebSocketServer({ port: port, host: host }, listening);
  }

  this.server.on('connection', function (incoming) {
    var outgoing = new ws(self.target);

    incoming.on('message', function (msg) {
      outgoing.send(msg);
    });

    outgoing.on('message', function (msg) {
      incoming.send(msg);
    });
  });
};

WSProxy.prototype.close = function () {
  if (!this.server) {
    throw new Error("Not running");
  }

  this.server.close();
  this.server = null;
};

Make API look like `net.Server`


import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { get } from 'axios';
import FlintLogo from 'components/FlintLogo';
import './LoginContainer.scss';

export default class LoginContainer extends Component {
  static propTypes = {
    children: PropTypes.object.isRequired,
  }

  state = { siteLogo: null, isFetching: true }

  componentWillMount() { document.body.classList.add('body--grey'); }

  componentDidMount() {
    get('/admin/api/site').then(({ data }) => {
      this.setState({ siteLogo: data.siteLogo, isFetching: false });
    });
  }

  componentWillUnmount() { document.body.classList.remove('body--grey'); }

  render() {
    const { siteLogo, isFetching } = this.state;
    const { children } = this.props;

    if (isFetching) return null;

    return (

        {siteLogo ? <img className="login__img" src={`/public/assets/${siteLogo.filename}`} alt="Hello!" /> : <FlintLogo width={140} height={80} />}
        {children}
        <Link to="/fp" className="login__forgot">Forgot your password?</Link>
        {siteLogo && <FlintLogo poweredBy width={100} height={25} />}

    );
  }
}

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { get } from 'axios';
import FlintLogo from 'components/FlintLogo';
import './LoginContainer.scss';

export default class LoginContainer extends Component {
  static propTypes = {
    children: PropTypes.object.isRequired,
  }

  state = { siteLogo: null, isFetching: true }

  componentWillMount() { document.body.classList.add('body--grey'); }

  componentDidMount() {
    get('/admin/api/site').then(({ data }) => {
      this.setState({ siteLogo: data.siteLogo, isFetching: false });
    });
  }

  componentWillUnmount() { document.body.classList.remove('body--grey'); }

  render() {
    const { siteLogo, isFetching } = this.state;
    const { children } = this.props;

    if (isFetching) return null;

    return (

        {siteLogo ? <img className="login__img" src={`/public/assets/${siteLogo.filename}`} alt={siteLogo.filename} /> : <FlintLogo width={140} height={80} />}
        {children}
        <Link to="/fp" className="login__forgot">Forgot your password?</Link>
        {siteLogo && <FlintLogo poweredBy width={100} height={25} />}

    );
  }
}

Tweak img alt in LoginContainer


const join = require('path').join
const execSync = require('child_process').execSync
const Interactive = require('./interactive')

/**
 * Reads a file glob and a commit message, and commits to git.
 * @class
 */
class GitAddAndCommit {
	constructor(options) {
		if (options.help) {
			this.showHelpScreen()
		} else if (options.interactive) {
			new Interactive().run()
		} else {
			this.normal()
		}
	}

	/**
	* Attempts to add and commit. Errors out in a non-ugly way.
	*/
	normal() {
		try {
			let args = process.argv.slice(2)
			let fileGlob = args[0]
			let commitMessage = args[1]

			execSync('git add -- *' + fileGlob + '*')
			execSync('git commit -m "' + commitMessage + '"')
		} catch (e) {
			console.log('Encountered error -- aborting.')
			process.stdin.write('Reset added files...')
			execSync('git reset .')
			process.stdin.write('Done.')
			console.log()
		}
	}

	/**
	* Shows the help screen.
	*/
	showHelpScreen() {
		let helpFile = join(__dirname, './help.txt')
		let helpScreen = execSync('cat ' + helpFile)
		console.log(helpScreen.toString('utf8'))
	}
}

module.exports = GitAddAndCommit

const join = require('path').join
const execSync = require('child_process').execSync
const Interactive = require('./interactive')

/**
 * Reads a file glob and a commit message, and commits to git.
 * @class
 */
class GitAddAndCommit {
	constructor(options) {
		if (options.help) {
			this.showHelpScreen()
		} else if (options.interactive) {
			new Interactive().run()
		} else {
			this.normal()
		}
	}

	/**
	* Attempts to add and commit. Errors out in a non-ugly way.
	*/
	normal() {
		try {
			let args = process.argv.slice(2)
			let fileGlob = args[0]
			let commitMessage = args[1]

			execSync(`git add -- *${fileGlob}*`)
			execSync(`git commit -m "${commitMessage}"`)
		} catch (e) {
			console.log('Encountered error -- aborting.')
			process.stdin.write('Reset added files...')
			execSync('git reset .')
			process.stdin.write('Done.')
			console.log()
		}
	}

	/**
	* Shows the help screen.
	*/
	showHelpScreen() {
		let helpFile = join(__dirname, './help.txt')
		let helpScreen = execSync(`cat ${helpFile}`)
		console.log(helpScreen.toString('utf8'))
	}
}

module.exports = GitAddAndCommit

Make use of es6 template literals


'use strict';

class Switch extends Phaser.Sprite {

  constructor(game, x, y, id, switchSound) {
    super(game, x, y, 'switch', 0);

    this.id = id;

    this.anchor.set(0.5);

    this.game.physics.arcade.enable(this);
    this.body.immovable = true;

    this.animations.add('on', [1, 2], 10, true);
    this.animations.add('off', [2, 1, 0], 10, false);

    this.switchSound = switchSound;
  }

  getId() {
    return this.id;
  }

  on() {
    this.isOn = true;
    this.animations.play('on');
    return this.getId();
  }

  off() {
    this.isOn = false;
    this.animations.play('off');
    return this.getId();
  }

  playSound(){
    this.switchSound.play();
  }

  flick() {
    return this.isOn ? this.off() : this.on();
  }
}

module.exports = Switch;

'use strict';

class Switch extends Phaser.Sprite {

  constructor(game, x, y, id, switchSound) {
    super(game, x, y, 'switch', 0);

    this.id = id;

    this.anchor.set(0.5);

    this.game.physics.arcade.enable(this);
    this.body.setSize(21, 60, 0, 28);
    this.body.immovable = true;

    this.animations.add('on', [1, 2], 10, true);
    this.animations.add('off', [2, 1, 0], 10, false);

    this.switchSound = switchSound;
  }

  getId() {
    return this.id;
  }

  on() {
    this.isOn = true;
    this.animations.play('on');
    return this.getId();
  }

  off() {
    this.isOn = false;
    this.animations.play('off');
    return this.getId();
  }

  playSound(){
    this.switchSound.play();
  }

  flick() {
    return this.isOn ? this.off() : this.on();
  }
}

module.exports = Switch;

Make the torch physics bodies tight to the stem of the torch


var mongoose     = require('mongoose');
var Schema       = mongoose.Schema;

var SurveySchema   = new Schema(
{
    description : String,
    name : String,
    state : {type : Number, default : 0},
    numberChoices : {type : Number, default : 1},
    created : {type : Date, default : Date.now},
    lastEdited : {type : Date, default : Date.now},
    author : {type: mongoose.Schema.Types.ObjectId, ref : "User"}
});

module.exports = mongoose.model('Survey', SurveySchema);

module.exports.State = {};
module.exports.State.IsOpened = 0;
module.exports.State.IsClosed = 1;

var mongoose     = require('mongoose');
var Schema       = mongoose.Schema;

var SurveySchema   = new Schema(
{
    description : String,
    name : String,
    state : {type : Number, default : 1},
    numberChoices : {type : Number, default : 1},
    created : {type : Date, default : Date.now},
    lastEdited : {type : Date, default : Date.now},
    author : {type: mongoose.Schema.Types.ObjectId, ref : "User"}
});

module.exports = mongoose.model('Survey', SurveySchema);

module.exports.State = {};
module.exports.State.IsOpened = 1;
module.exports.State.IsClosed = 0;

Change state constants to be more logic


const photos = Application('photos');
const terminal = Application('terminal');
const albums = photos.albums();

const pad = (val) => String(val).length < 2 ? '0' + val : String(val);
const app = Application.currentApplication()
app.includeStandardAdditions = true;

albums.forEach(album => {
  const name = album.name();
  const items = album.mediaItems();
  const path = Path('./export/' + name);

  items.forEach(item => {
    const filename = item.filename();
    const date = item.date();
    const formattedDate = pad(date.getMonth() + 1) + '/' +
      pad(date.getDate()) + '/' +
      date.getFullYear() + ' ' +
      pad(date.getHours()) + ':' +
      pad(date.getMinutes()) + ':' +
      pad(date.getSeconds());

    console.log(filename + ' => ' + path);
    photos.export([item], {to: path});

    const videoPath = (path + '/' + filename).replace(/(.*)\.[0-9A-Za-z]+$/, '$1.m4v');
    const cmd = 'ls "' + videoPath + '" && ' +
      'SetFile -d "' + formattedDate + '" "' + videoPath + '"';

    try {
      app.doShellScript(cmd);
      console.log('Set creation date for video file');
    } catch (e) {
    }
  });
});

const photos = Application('photos');
const terminal = Application('terminal');
const albums = photos.albums();

const pad = (val) => String(val).length < 2 ? '0' + val : String(val);
const app = Application.currentApplication()
app.includeStandardAdditions = true;

albums.forEach(album => {
  const name = album.name();
  const items = album.mediaItems();
  const path = Path('./export/' + name);

  items.forEach(item => {
    const filename = item.filename();
    const date = item.date();
    const formattedDate = pad(date.getMonth() + 1) + '/' +
      pad(date.getDate()) + '/' +
      date.getFullYear() + ' ' +
      pad(date.getHours()) + ':' +
      pad(date.getMinutes()) + ':' +
      pad(date.getSeconds());

    console.log(filename + ' => ' + path);
    photos.export([item], {to: path});

    const fileRegex = /(.*)\.[0-9A-Za-z]+$/;
    const target = (path + '/' + filename);
    [
      target.replace(fileRegex, '$1.m4v'),
      target.replace(fileRegex, '$1.jpg')
    ].forEach((file) => {
      const cmd = 'ls "' + file + '" && ' +
        'SetFile -d "' + formattedDate + '" "' + file + '"';

      try {
        app.doShellScript(cmd);
      } catch (e) {
      }
    });
  });
});

Set both jpg and m4v creation dates


Package.describe({
  git: 'https://github.com/zimme/meteor-iron-router-active.git',
  name: 'zimme:iron-router-active',
  summary: 'Active route/path template helpers for iron:router',
  version: '1.0.3'
});

Package.onUse(function(api) {
  api.versionsFrom('1.0');

  api.use([
    'check',
    'coffeescript',
    'templating',
    'underscore'
  ]);

  api.use([
    'iron:router@1.0.0',
    'underscorestring:underscore.string@2.4.0'
  ]);

  api.addFiles('client/helpers.coffee', 'client');
});

Package.describe({
  git: 'https://github.com/zimme/meteor-iron-router-active.git',
  name: 'zimme:iron-router-active',
  summary: 'Active route/path template helpers for iron:router',
  version: '1.0.3'
});

Package.onUse(function(api) {
  api.versionsFrom('1.0');

  api.use([
    'check',
    'coffeescript',
    'templating',
    'underscore'
  ]);

  api.use([
    'iron:router@1.0.0',
    'underscorestring:underscore.string@3.0.0'
  ]);

  api.addFiles('client/helpers.coffee', 'client');
});

Update underscore dependency to 3.0.0


'use strict';

// Create, restore, list, and get status of koast DB backups
//
/**
 * @module koastAdminApp.service/koastBackup
 */
angular.module('koastAdminApp.sections.backup.backup-service', [])
  .service('backup', function (api, constants) {
    var service = {
        //make create backup request
        //where name is the name of the backup and type is what kind of backup it will be
        createBackup: function (name, collections, type) {
            return api.callMethod('backup', 'create', {
              collections: collections,
              type: type,
              name: name,
              opts: { bucket: constants.awsBucket }
            });
        },

        //ask for the status of a particular inprogress backup
        //where id is the id from the backup receipt
        status: function (id) {
            return api.callMethod('backup', 'stat', { id: id });
        },

        //get a list of backups
        list: function () {
            return api.callMethod('backup', 'list');
        },

        //restore the db using the selected backup
        //where id is the id of the backup
        restoreBackup: function (id) {
            return api.callMethod('backup', 'restore', { id: id });
        }
    };

    return service;
});

'use strict';

// Create, restore, list, and get status of koast DB backups
//
/**
 * @module koastAdminApp.service/koastBackup
 */
angular.module('koastAdminApp.sections.backup.backup-service', [])
  .service('backup', function (api, constants) {
    var service = {
      //make create backup request
      //where name is the name of the backup and type is what kind of backup it will be
      createBackup: function (name, collections, type) {
        return api.callMethod('backup', 'start', {
          collections: collections,
          type: type,
          name: name,
          opts: {
            name: name,
            bucket: constants.awsBucket
          }
        });
      },

      //ask for the status of a particular inprogress backup
      //where id is the id from the backup receipt
      status: function (id) {
        return api.callMethod('backup', 'stat', id);
      },

      //get a list of backups
      list: function () {
        return api.callMethod('backup', 'list');
      },

      //restore the db using the selected backup
      //where id is the id of the backup
      restoreBackup: function (id) {
        return api.callMethod('backup', 'restore', {
          id: id
        });
      }
    };

    return service;
  });

Change status method to pass object instead of nesting it


var fs = require("fs");
var path = require("path");
var Sequelize = require("sequelize");
var env = process.env.NODE_ENV || "development";
//var config = require('../config/sequelize.json')[env];
var sequelize = new Sequelize(process.env.DATABASE_URL, { dialect: 'postgres' });
var db = {};

sequelize
  .authenticate()
  .then(function (err) {
    console.log('Connection has been established successfully.');
  }, function (err) {
    console.log('Unable to connect to the database:', err);
  });

fs
  .readdirSync(__dirname)
  .filter(function (file) {
    return (file.indexOf(".") !== 0) && (file !== "index.js");
  })
  .forEach(function (file) {
    var model = sequelize.import(path.join(__dirname, file));
    db[model.name] = model;
  });

Object.keys(db).forEach(function (modelName) {
  if ("associate" in db[modelName]) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

var fs = require("fs");
var path = require("path");
var Sequelize = require("sequelize");
var env = process.env.NODE_ENV || "development";
var sequelize = new Sequelize(process.env.DATABASE_URL, {
  dialect: 'postgres',
  dialectOptions: {
    ssl: true
  }
});
var db = {};

sequelize
  .authenticate()
  .then(function (err) {
    console.log('Connection has been established successfully.');
  }, function (err) {
    console.log('Unable to connect to the database:', err);
  });

fs
  .readdirSync(__dirname)
  .filter(function (file) {
    return (file.indexOf(".") !== 0) && (file !== "index.js");
  })
  .forEach(function (file) {
    var model = sequelize.import(path.join(__dirname, file));
    db[model.name] = model;
  });

Object.keys(db).forEach(function (modelName) {
  if ("associate" in db[modelName]) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

Enable SSL for postgre connection


/**
 * Module Dependencies.
 */

var debug = require('debug')('metalsmith-static');
var ncp   = require('ncp').ncp;
var path  = require('path');
var fsextra    = require('fs-extra');

/**
 * Module Exports
 */

module.exports = function(opts) {
  opts = opts || {};
  relSrc = opts.src || 'public';
  relDest = opts.dest || 'public';

  createDest = opts.createDest || false;

  return function(files, metalsmith, done) {
    var src = path.join(metalsmith.dir, relSrc);
    var dst = path.join(metalsmith.dir, metalsmith._dest, relDest);

    if (createDest) {
      var dir = path.dirname(dst);

      debug('creating: %s', dir);
      fsextra.mkdirp(dir);
    }

    ncp(src, dst, function(err) {
      if (err) return done(err);
      done();
    });
  };
};

/**
 * Module Dependencies.
 */

var debug = require('debug')('metalsmith-static');
var ncp   = require('ncp').ncp;
var path  = require('path');
var fsextra    = require('fs-extra');

/**
 * Module Exports
 */

module.exports = function(opts) {
  opts = opts || {};
  relSrc = opts.src || 'public';
  relDest = opts.dest || 'public';

  createDest = opts.createDest || false;

  return function(files, metalsmith, done) {
    var src = path.join(metalsmith.dir, relSrc);
    var dst = path.join(metalsmith.dir, metalsmith._dest, relDest);

    if (createDest) {
      var dir = path.dirname(dst);

      debug('creating: %s', dir);
      fsextra.mkdirpSync(dir);
    }

    ncp(src, dst, function(err) {
      if (err) return done(err);
      done();
    });
  };
};

Use the sync version of mkdirp


var request = require('request');

exports.attach = function(app) {
  var BASE_URL = 'https://coinbase.com/api/v1/',
      EXCHANGE_RATE_ENDPOINT = BASE_URL + 'currencies/exchange_rates';

  app.locals.exchange_rate_map = {};

  app.get('/api/exchange_rate', function(req, res) {
    if (req.query.force) {
      request(EXCHANGE_RATE_ENDPOINT, function (error, response, body) {
        if (!error && response.statusCode == 200) {
          app.locals.exchange_rate_map = JSON.parse(body);
          res.send({
            usd: app.locals.exchange_rate_map['btc_to_usd']
          });
        }
        else {
          res.send({
            error: error
         });
        }
      })
    }
    else {
      res.send(app.locals.exchange_rate_map);
    }
  });
};

var request = require('request'),
    BASE_URL = 'https://coinbase.com/api/v1/',
    EXCHANGE_RATE_ENDPOINT = BASE_URL + 'currencies/exchange_rates';

function refreshExchangeRateCache(app, req, res) {
  request(EXCHANGE_RATE_ENDPOINT, function (error, response, body) {
    if (!error && response.statusCode == 200) {
      app.locals.exchange_rate_map = JSON.parse(body);
      res.send({
        usd: app.locals.exchange_rate_map['btc_to_usd']
      });
    }
    else {
      res.send({
        error: error
      });
    }
  });
}

function getExchangeRate(app, from, to, value) {
  var exchangeRateObj = {},
      exchangeRateString = '',
      exchangeRate = 1,
      exchangeKey = from + '_to_' + to;

  from = from.toLowerCase();
  to = to.toLowerCase();
  value = parseFloat(value);

  exchangeRateString = app.locals.exchange_rate_map[exchangeKey];
  if (!exchangeRateString) {
    return {
      error: 'Internal Server Error'
    };
  }
  console.log(exchangeRateString);
  exchangeRate = parseFloat(exchangeRateString);
  if (!exchangeRate) {
    return {
      error: 'Internal Service Error: ' + exchangeKey
    };
  }

  exchangeRateObj[to] = value;
  exchangeRateObj[from] = exchangeRate * value;
  return exchangeRateObj;
}

exports.attach = function(app) {
  app.locals.exchange_rate_map = {};

  app.get('/api/exchange_rate', function(req, res) {
    var fromExchangeRate = req.query.from,
        toExchangeRate = req.query.to || 'btc',
        valueExchangeRate = req.query.value || 1,
        isForced = req.query.force;

    if (isForced) {
      refreshExchangeRateCache(app, req, res);
    }
    else if (fromExchangeRate) {
      res.send(getExchangeRate(app, fromExchangeRate, toExchangeRate, valueExchangeRate));
    }
    else {
      res.send(getExchangeRate(app, 'btc', 'usd', '1'));
    }
  });
};

Add value conversions to the API


var codebrag = codebrag || {};

/**
 * Module for adding scripts not related with app features and Angular
 */
codebrag.addons = {};

codebrag.addons.betaRibbon = (function() {

    var ribbon = $('.ribbon');
    var infoPopup = $('.popup-page');
    var popupCloseBtn = infoPopup.find('.close-btn');
    var link = infoPopup.find('a[data-uv-lightbox="classic_widget"]');

    var openedPopupClass = 'opened';

    function togglePopupPage() {
        [ribbon, popupCloseBtn, link].forEach(function(el) {
            el.on('click', function() {
                infoPopup.toggleClass(openedPopupClass);
            });
        });
    }

    return {
        init: togglePopupPage
    }

}());

$(document).ready(function() {

    codebrag.addons.betaRibbon.init();

});

var codebrag = codebrag || {};

/**
 * Module for adding scripts not related with app features and Angular
 */
codebrag.addons = {};

codebrag.addons.betaRibbon = (function() {

    var ribbon = $('.ribbon');
    var infoPopup = $('#early-version-popup');
    var popupCloseBtn = infoPopup.find('.close-btn');
    var link = infoPopup.find('a');

    var openedPopupClass = 'opened';

    function togglePopupPage() {
        [ribbon, popupCloseBtn, link].forEach(function(el) {
            el.on('click', function() {
                infoPopup.toggleClass(openedPopupClass);
            });
        });
    }

    return {
        init: togglePopupPage
    }

}());

$(document).ready(function() {

    codebrag.addons.betaRibbon.init();

});

Fix - ribbon doesn't open contact form


const submenuItems = [
  {
    header: 'Health',
    items: [
      { title: 'Critical Illness Insurance', url: '/critical-illness-insurance/', },
      { title: 'Vision Insurance', url: '/vision-insurance/', },
      { title: 'Long-term Care Insurance', url: '/long-term-care-insurance/', },
      { title: 'Prescription Discount Cards', url: '/prescription-discount-cards/', },
    ],
  },
  {
    header: 'Other',
    items: [
      { title: 'Jewelry Insurance', url: '/jewelry-insurance/', },
      { title: 'Homeowners Insurance', url: '/homeowners-insurance/', },
      { title: 'Identity Theft Insurance', url: '/identity-theft-insurance/', },
      { title: 'Travel Insurance', url: '/travel-insurance/', },
    ],
  },
];

export default {
  menu: {
    header: 'More',
    activeName: 'more',
    link: '/blog/',
  },
  intro: {
    cta: 'Get Your Insurance Checkup™',
    imgSrc: 'https://res-3.cloudinary.com/policygenius/image/upload/v1/general/checkup-small',
    linkHref: '/insurance-checkup-and-advice/',
  },
  list: {
    key: 'more-submenu-list-',
    items: submenuItems,
    type: 'links',
  }
};

const submenuItems = [
  {
    header: 'Health',
    items: [
      { title: 'Critical Illness Insurance', url: '/critical-illness-insurance/', },
      { title: 'Vision Insurance', url: '/vision-insurance/', },
      { title: 'Long-term Care Insurance', url: '/long-term-care-insurance/', },
      { title: 'Prescription Discount Cards', url: '/prescription-discount-cards/', },
    ],
  },
  {
    header: 'Other',
    items: [
      { title: 'Jewelry Insurance', url: '/jewelry-insurance/', },
      { title: 'Homeowners Insurance', url: '/homeowners-insurance/', },
      { title: 'Identity Theft Insurance', url: '/identity-theft-insurance/', },
      { title: 'Travel Insurance', url: '/travel-insurance/', },
    ],
  },
];

export default {
  menu: {
    header: 'More',
    activeName: 'more',
  },
  intro: {
    cta: 'Get Your Insurance Checkup™',
    imgSrc: 'https://res-3.cloudinary.com/policygenius/image/upload/v1/general/checkup-small',
    linkHref: '/insurance-checkup-and-advice/',
  },
  list: {
    key: 'more-submenu-list-',
    items: submenuItems,
    type: 'links',
  }
};

Remove href for More submenu header


// @flow
import React from 'react'
import ReactDOM from 'react-dom'
import { Dispatcher, Context } from 'almin'
import AppStoreGroup from './store/AppStoreGroup'
import SearchApp from './SearchApp'
import LazyLoadApp from './LazyLoadApp'
import { registerWorkers } from './ServiceWorker'
import swPrecacheConfig from '../../sw-precache-config'

// --- Init SearchApp
const dispatcher = new Dispatcher()
const appContext = new Context({
  dispatcher,
  store: AppStoreGroup.create()
})

if (process.env.NODE_ENV !== 'production') {
  const { AlminLogger } = require('almin-logger')
  const logger = new AlminLogger()
  logger.startLogging(appContext)
}

ReactDOM.render(<SearchApp appContext={appContext} />, document.getElementById('search'))

// --- Init LazyLoadApp
LazyLoadApp(document.querySelectorAll('.markdown img'))

// --- Init ServiceWorker
registerWorkers([
  swPrecacheConfig.swFile
])

// @flow
import React from 'react'
import ReactDOM from 'react-dom'
import { Dispatcher, Context } from 'almin'
import AlminLogger from 'almin-logger'
import AppStoreGroup from './store/AppStoreGroup'
import SearchApp from './SearchApp'
import LazyLoadApp from './LazyLoadApp'
import { registerWorkers } from './ServiceWorker'
import swPrecacheConfig from '../../sw-precache-config'

// --- Init SearchApp
const dispatcher = new Dispatcher()
const appContext = new Context({
  dispatcher,
  store: AppStoreGroup.create()
})

if (process.env.NODE_ENV !== 'production') {
  const logger = new AlminLogger()
  logger.startLogging(appContext)
}

ReactDOM.render(<SearchApp appContext={appContext} />, document.getElementById('search'))

// --- Init LazyLoadApp
LazyLoadApp(document.querySelectorAll('.markdown img'))

// --- Init ServiceWorker
registerWorkers([
  swPrecacheConfig.swFile
])

Fix `AlminLogger is not constructor`


// Place all the behaviors and hooks related to the matching controller here.
// All this logic will automatically be available in application.js.
// You can use CoffeeScript in this file: http://coffeescript.org/
$("#micropost_picture").bind("change", function() {
  var size_in_megabytes = this.files[0].size/1024/1024;
  if (size_in_megabytes > 5) {
    alert("Maximum file size is 5MB. Please choose a smaller file.");
  }
});

$(document).ready(function() {
  if ($("#micropost_picture").length) {
    $("#micropost_picture").bind("change", function() {
      var size_in_megabytes = this.files[0].size / 1024 / 1024;

      if (size_in_megabytes > 5) {
        $(".new_micropost").prepend(
          $("<div>", { class: "error-explanation" }).append(
            $("<div>", {
              class: "alert alert-danger",
              text: "Maximum file size is 5MB. Please choose a smaller file."
            })
          )
        );
      }
    });
  }
});

Change javascript alert to a flash-like thing


Template.home.onCreated(function () {
  const instance = this;

  // Set current Home ID from router
  instance.homeId = Router.current().params.homeId;

  // Subscribe to current home
  instance.subscribe('singleHome', instance.homeId);

  // Subscribe to Home Residents
  instance.subscribe('homeCurrentResidents', instance.homeId);
});

Template.home.events({
  'click #edit-home': function () {
    // Get reference to template instance
    var instance = Template.instance();

    // Get Home ID
    var homeId = instance.homeId;

    // Get home
    var home = Homes.findOne(homeId);

    // Show the edit home modal
    Modal.show('editHome', home);
  }
});

Template.home.helpers({
  'home': function () {
    // Create reference to template instance
    var instance = Template.instance();

    // Get Home ID from template instance
    var homeId = instance.homeId;

    // Return current Home
    return Homes.findOne(homeId);
  },
  'residents': function () {
    // Create reference to template instance
    var instance = Template.instance();

    // Get Home ID from template instance
    var homeId = instance.homeId;

    // Return all residents for current home
    return Residents.find({'homeId': homeId});
  }
});

Template.home.onCreated(function () {
  const instance = this;

  // Set current Home ID from router
  instance.homeId = Router.current().params.homeId;

  // Subscribe to current home
  instance.subscribe('singleHome', instance.homeId);

  // Subscribe to Home Residents
  instance.subscribe('homeCurrentResidents', instance.homeId);
});

Template.home.events({
  'click #edit-home': function () {
    // Get reference to template instance
    var instance = Template.instance();

    // Get Home ID
    var homeId = instance.homeId;

    // Get home
    var home = Homes.findOne(homeId);

    // Show the edit home modal
    Modal.show('editHome', home);
  }
});

Template.home.helpers({
  'home': function () {
    // Create reference to template instance
    var instance = Template.instance();

    // Get Home ID from template instance
    var homeId = instance.homeId;

    // Return current Home
    return Homes.findOne(homeId);
  },
  'residents': function () {
    // Create reference to template instance
    var instance = Template.instance();

    // Get Home ID from template instance
    var homeId = instance.homeId;

    // Return all residents for current home, sorting by first name
    return Residents.find({'homeId': homeId}, {sort: {firstName: 1}});
  }
});

Sort residents by first name for consistency


import { Component } from 'substance'

/**
  ModalDialog component

  @class
  @component

  @prop {String} width 'small', 'medium', 'large' and 'full'

  @example

  ```js
  var form = $$(ModalDialog, {
    width: 'medium',
    textAlign: 'center'
  });
  ```
*/
export default class ModalDialog extends Component {

  render($$) {
    let el = $$('div').addClass('sc-modal-dialog')

    // TODO: don't think that this is good enough. Right the modal is closed by any unhandled click.
    // Need to be discussed.
    el.on('click', this._closeModal)

    if (this.props.width) {
      el.addClass('sm-width-'+this.props.width)
    }

    if (this.props.transparent) {
      el.addClass('sm-transparent-bg')
    }

    el.append(
      $$('div').addClass('se-body').append(
        this.props.children
      )
    )
    return el
  }

  _closeModal(e) {
    e.preventDefault()
    e.stopPropagation()
    let closeSurfaceClick = e.target.classList.contains('sc-modal-dialog')
    if (closeSurfaceClick) {
      this.send('closeModal')
    }
  }
}

import { Component } from 'substance'

/**
  ModalDialog component

  @class
  @component

  @prop {String} width 'small', 'medium', 'large' and 'full'

  @example

  ```js
  var form = $$(ModalDialog, {
    width: 'medium',
    textAlign: 'center'
  });
  ```
*/
export default class ModalDialog extends Component {

  render($$) {
    let el = $$('div').addClass('sc-modal-dialog')

    // TODO: don't think that this is good enough. Right the modal is closed by any unhandled click.
    // Need to be discussed.
    el.on('click', this._closeModal)
    el.on('keydown', this._onKeydown)

    if (this.props.width) {
      el.addClass('sm-width-'+this.props.width)
    }

    if (this.props.transparent) {
      el.addClass('sm-transparent-bg')
    }

    el.append(
      $$('div').addClass('se-body').append(
        this.props.children
      )
    )
    return el
  }

  _onKeydown(e) {
    e.stopPropagation()
  }

  _closeModal(e) {
    e.preventDefault()
    e.stopPropagation()
    let closeSurfaceClick = e.target.classList.contains('sc-modal-dialog')
    if (closeSurfaceClick) {
      this.send('closeModal')
    }
  }
}

Stop propagation of keydown events on modal level.


var modella = require('modella');
var test = require('tape');

var render = require('./');

var modelName = 'MyModel';

test('Model with no attributes', function(t) {
  var model = modella(modelName);
  t.equal(render(model), modelName);
  t.end();
});

test('Model with attributes', function(t) {
  var attributes = ['a', 'b', 'c'];
  var model = modella(modelName);

  attributes.forEach(function(attr) {
    model.attr(attr);
  });

  t.equal(
      render(model),
      'MyModel:\n- a\n- b\n- c\n');
  t.end();
});

test('Model with ID key', function(t) {
  var model = modella(modelName);

  model.attr('id');
  t.equal(
      render(model),
      'MyModel:\n- id (primaryKey)\n');
  t.end();
});

var modella = require('modella');
var test = require('tape');

var render = require('./');

var modelName = 'MyModel';

test('Model with no attributes', function(t) {
  var model = modella(modelName);
  t.equal(render(model), modelName);
  t.end();
});

test('Model with attributes', function(t) {
  var attributes = ['a', 'b', 'c'];
  var model = modella(modelName);

  attributes.forEach(function(attr) {
    model.attr(attr);
  });

  t.equal(
      render(model),
      modelName + ':\n- a\n- b\n- c\n');
  t.end();
});

test('Model with ID key', function(t) {
  var model = modella(modelName);

  model.attr('id');
  t.equal(
      render(model),
      modelName + ':\n- id (primaryKey)\n');
  t.end();
});

Refactor to avoid hardcoding model name


'use strict';
var assert = require('assert');
var atomName = require('./');

it('should return true when package name is available', function (cb) {
	var name = Math.random().toString(36).substring(7);
	atomName(name, function (err, available) {
		assert(!err, err);
		assert(available);
		cb();
	});
});

it('should return false when package name is taken', function (cb) {
	atomName('autocomplete-plus', function (err, available) {
		assert(!err, err);
		assert(!available);
		cb();
	});
});

'use strict';
var assert = require('assert');
var atomName = require('./');

it('should return true when package name is available', function (cb) {
	var name = Math.random().toString(36).substring(20);
	atomName(name, function (err, available) {
		assert(!err, err);
		assert(available);
		cb();
	});
});

it('should return false when package name is taken', function (cb) {
	atomName('autocomplete-plus', function (err, available) {
		assert(!err, err);
		assert(!available);
		cb();
	});
});

Increase entropy of available package


console.log("Connecting to https://luvit.io through proxy...");
connect("tls", "luvit.io", "443", function (write) {
  console.log("Connected to remote. Sending HTTP request...");
  write("GET / HTTP/1.1\r\nHost: luvit.io\r\n\r\n");
}, function (buffer) {
  console.log(buffer);
  console.log(toString(buffer));
});

function connect(protocol, host, port, onconnect, onmessage) {
  var socket = new WebSocket("wss://tedit.creationix.com/proxy/" + protocol + "/" + host + "/" + port);
  socket.binaryType = "arraybuffer";
  socket.onmessage = function (evt) {
    if (evt.data === "connect") {
      return onconnect(write);
    }
    if (typeof evt.data === "string") {
      console.log(evt.data);
    }
    else {
      onmessage(new Uint8Array(evt.data));
    }
  };
  function write(data) {
    return socket.send(data);
  }
  return write;
}

function toString(buffer) {
  var str = "";
  for (var i = 0, l = buffer.length; i < l; i++) {
    str += String.fromCharCode(buffer[i]);
  }
  // Decode UTF8
  return decodeURIComponent(escape(str));
}

console.log("Connecting to git repo through proxy...");
connect("tls", "github.com", "443", function (write) {
  console.log("Connected to remote. Sending HTTP request...");
  var path = "/creationix/conquest.git/info/refs?service=git-upload-pack";
  write("GET " + path + " HTTP/1.1\r\nHost: github.com\r\n\r\n");
}, function (buffer) {
  console.log(buffer);
  console.log(toString(buffer));
});

function connect(protocol, host, port, onconnect, onmessage) {
  var socket = new WebSocket("wss://tedit.creationix.com/proxy/" + protocol + "/" + host + "/" + port);
  socket.binaryType = "arraybuffer";
  socket.onmessage = function (evt) {
    if (evt.data === "connect") {
      return onconnect(write);
    }
    if (typeof evt.data === "string") {
      console.log(evt.data);
    }
    else {
      onmessage(new Uint8Array(evt.data));
    }
  };
  function write(data) {
    return socket.send(data);
  }
  return write;
}

function toString(buffer) {
  var str = "";
  for (var i = 0, l = buffer.length; i < l; i++) {
    str += String.fromCharCode(buffer[i]);
  }
  // Decode UTF8
  return decodeURIComponent(escape(str));
}

Implement primitive ls-remote over git proxy.


import gql from 'graphql-tag';

export default gql`
  fragment NotificationObject on DeedKind {
    __typename

    ... on Channel {
      id
      label: title
      href
      visibility
    }

    ... on Connectable {
      id
      label: to_s
      href
    }

    ... on User {
      id
      label: name
      href
      is_me
    }

    ... on Comment {
      id
      body(truncate: 100)
    }

    ... on Group {
      id
      label: name
    }
  }
`;

import gql from 'graphql-tag';

export default gql`
  fragment NotificationObject on DeedKind {
    __typename

    ... on Channel {
      id
      label: title
      href
      visibility
    }

    ... on Connectable {
      id
      label: to_s
      href
    }

    ... on User {
      id
      label: name
      href
      is_me
    }

    ... on Comment {
      id
      body(truncate: 100)
    }

    ... on Group {
      id
      label: name
      href
    }
  }
`;

Add href to group object in notifications


var weml = {
	allocateArray: function(size) {
		return new Float32Array(size);
	},
};

var weml = {
	allocateArray: function(size) {
		return new Float32Array(size);
	},

	selectTypeprototype: function(clazz) {
		const hasSIMD = window.SIMD !== undefined;
		clazz.typeprototype._current = (hasSIMD ? clazz.typeprototype.simd : null) || clazz.typeprototype.sisd;
	},
};

Add support for SIMD via selectTypeprototype()


'use strict';

var assert = require('assert');
var ipaMetadataParser = require('./');

describe('should parse .ipa file', function(){
  it('should contain the correct data', function(done) {
    ipaMetadataParser('tests/fixtures/testApp.ipa', function(err, data) {
      assert.equal(data.metadata['CFBundleDisplayName'], 'Test App');
      assert.equal(data.metadata['CFBundleIdentifier'], 'io.mts.testapp');
      assert.equal(data.metadata['CFBundleName'], 'Test App');
      assert.equal(data.metadata['CFBundleShortVersionString'], '1.0.0');
      assert.equal(data.metadata['CFBundleVersion'], '1337');

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

describe('should parse .ipa files with binary .plist files', function(){
  it('should contain the correct data', function(done) {
    ipaMetadataParser('tests/fixtures/testApp-binaryPlist.ipa', function(err, data) {
      assert.equal(data.metadata['CFBundleDisplayName'], 'Test App');
      assert.equal(data.metadata['CFBundleIdentifier'], 'io.mts.testapp');
      assert.equal(data.metadata['CFBundleName'], 'Test App');
      assert.equal(data.metadata['CFBundleShortVersionString'], '1.0.0');
      assert.equal(data.metadata['CFBundleVersion'], '1337');

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

'use strict';

var assert = require('assert');
var ipaMetadataParser = require('./');

describe('should parse .ipa file', function(){
  it('should contain the correct data', function(done) {
    ipaMetadataParser('tests/fixtures/testApp.ipa', function(err, data) {
      assert.equal(data.metadata['CFBundleDisplayName'], 'Test App');
      assert.equal(data.metadata['CFBundleIdentifier'], 'io.mts.testapp');
      assert.equal(data.metadata['CFBundleName'], 'Test App');
      assert.equal(data.metadata['CFBundleShortVersionString'], '1.0.0');
      assert.equal(data.metadata['CFBundleVersion'], '1337');

      // Property is only set on OS X
      assert.equal(data.entitlements, void 0);

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

describe('should parse .ipa files with binary .plist files', function(){
  it('should contain the correct data', function(done) {
    ipaMetadataParser('tests/fixtures/testApp-binaryPlist.ipa', function(err, data) {
      assert.equal(data.metadata['CFBundleDisplayName'], 'Test App');
      assert.equal(data.metadata['CFBundleIdentifier'], 'io.mts.testapp');
      assert.equal(data.metadata['CFBundleName'], 'Test App');
      assert.equal(data.metadata['CFBundleShortVersionString'], '1.0.0');
      assert.equal(data.metadata['CFBundleVersion'], '1337');

      // Property is only set on OS X
      assert.equal(data.entitlements, void 0);

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

Add assertion about undefined entitlements property


'use strict';

const debugFinder = require('./');
const assert = require('assert');
const File = require('vinyl');

describe('gulp-debug-finder', () => {
    it('should warn when there are debug statements present', (cb) => {
        const stream = debugFinder();

        stream.on('error', () => {
            assert(true);

            cb();
        });

        stream.write(new File({
            contents: new Buffer('if (true) { console.log("Hello world"); }'),
        }));

        stream.end();
    });
});

'use strict';

const debugFinder = require('./');
const assert = require('assert');
const File = require('vinyl');

describe('gulp-debug-finder', () => {
    it('should warn when there are debug statements present', (cb) => {
        const stream = debugFinder();

        stream.on('error', () => {
            assert(true);

            cb();
        });

        stream.write(new File({
            contents: new Buffer('if (true) { console.log("Hello world"); }'), // eslint-disable-line node/no-deprecated-api
        }));

        stream.end();
    });
});

Disable node/no-deprecated-api for Buffer constructor


function onDrop(event, data, state, editor) {
  if (data.type !== 'node') {
    return
  }
  let {target} = data
  const {isInternal, node} = data
  let {selection} = state
  if (isInternal) {
    data.type = 'fragment'
    data.isInternal = true
    data.fragment = state.document.getFragmentAtRange(selection)
  } else {
    selection = selection.moveToRangeOf(state.document.getChild(node.key))

    if (
      selection.endKey == target.endKey
        && selection.endOffset < target.endOffset
    ) {
      target = target.moveBackward(selection.startKey == selection.endKey
        ? selection.endOffset - selection.startOffset
        : selection.endOffset)
    }

    let transform = state.transform()
      .removeNodeByKey(node.key)
      .moveTo(target)

    if (node.kind === 'block') {
      transform = transform.insertBlock(node)
    } else if (node.kind === 'inline') {
      transform = transform.insertInline(node)
    } else {
      return
    }
    transform.apply()
  }
}

function FormBuilderNodeOnDrop(...args) {
  return {
    onDrop
  }
}

export default FormBuilderNodeOnDrop

// This plugin is responsible for dragging

function onDrop(event, data, state, editor) {

  if (data.type !== 'node') {
    return null
  }

  let {target} = data
  const {isInternal, node} = data
  let {selection} = state
  if (isInternal) {
    // Dragging of texts inernally in the document
    data.type = 'fragment'
    data.isInternal = true
    data.fragment = state.document.getFragmentAtRange(selection)
  } else {
    // Dragging of something from the outside
    selection = selection.moveToRangeOf(state.document.getChild(node.key))

    if (
      selection.endKey == target.endKey
        && selection.endOffset < target.endOffset
    ) {
      target = target.moveBackward(selection.startKey == selection.endKey
        ? selection.endOffset - selection.startOffset
        : selection.endOffset)
    }

    let transform = state.transform()
      .removeNodeByKey(node.key)
      .moveTo(target)

    if (node.kind === 'block') {
      transform = transform.insertBlock(node)
    } else if (node.kind === 'inline') {
      transform = transform.insertInline(node)
    } else {
      return null
    }
    return transform.apply()
  }
}

function FormBuilderNodeOnDrop(...args) {
  return {
    onDrop
  }
}

export default FormBuilderNodeOnDrop

Fix some issues with Slate drag plugin for outside content


exports.keys = [ "super duper secrets go here!" ]

exports.port = 15443

exports.cluster = { size : require("os").cpus().length }

// redis auth
exports.redis = { host: '127.0.0.1', port: 6379 }

exports.registryCouch = "https://isaacs-staging.iriscouch.net/"

// npm config settings
exports.npm =
  { loglevel: "warn"
  , registry: "http://staging.npmjs.org/"
  , "strict-ssl": false
  }

// bunyan config
exports.log =
  { name: 'npm-www'
  , level: 'trace'
  }

exports.package = require('./package.json')

exports.keys = [ "super duper secrets go here!" ]

exports.port = 15443

exports.cluster = { size : require("os").cpus().length }

// redis auth
exports.redis = { host: '127.0.0.1', port: 6379 }

exports.registryCouch = "https://isaacs.iriscouch.net/"

// npm config settings
exports.npm =
  { loglevel: "warn"
  , registry: "http://registry.npmjs.org/"
  , "strict-ssl": false
  }

// bunyan config
exports.log =
  { name: 'npm-www'
  , level: 'trace'
  }

exports.package = require('./package.json')

exports.errorPage = { debug: true }

exports.debug = true

Use the real registry, and set debug = true by default


// Handles the setup of the routes within the app.

// Sets the root the the 'body' tag
BlazeLayout.setRoot('body');

// Sets the index to automatically render either dashboard or the main page depending on logged in status
FlowRouter.route('/', {
  name: 'App_Content',
  action() {
	  if(Meteor.user() == undefined){
		BlazeLayout.render('content', {main: 'dashboard'});
	  }else{
		BlazeLayout.render('content', {main: 'index'});
	  }
  }
});

// Sets up the 404 page
FlowRouter.notFound = {
  action() {
    BlazeLayout.render('content', {main: 'App_notFound'});
  }
};

// Handles the setup of the routes within the app.

// Sets the root the the 'body' tag
BlazeLayout.setRoot('body');

// Sets the index to automatically render either dashboard or the main page depending on logged in status
FlowRouter.route('/', {
  name: 'App_Content',
  action() {
	  if(Meteor.user() === undefined){
		BlazeLayout.render('content', {main: 'dashboard'});
	  }else{
		BlazeLayout.render('content', {main: 'index'});
	  }
  }
});

// Sets up the 404 page
FlowRouter.notFound = {
  action() {
    BlazeLayout.render('content', {main: 'App_notFound'});
  }
};

Improve performance of if block


'use strict';

angular.module('mean.admin').controller('ThemesController', ['$scope', 'Global', '$rootScope', '$http',
    function($scope, Global, $rootScope, $http) {
        $scope.global = Global;
        $scope.themes = [];

        $scope.init = function() {
            $http({
                method: 'GET',
                url: 'http://api.bootswatch.com/3/'
            }).
            success(function(data, status, headers, config) {
                $scope.themes = data.themes;
            }).
            error(function(data, status, headers, config) {

            });
        };

        $scope.changeTheme = function(theme) {
            // Will add preview options soon
            // $('link').attr('href', theme.css);
            // $scope.selectedTheme = theme;
            $('.progress-striped').show();

            $http.get('/api/admin/themes?theme=' + theme.css).
            success(function(data, status, headers, config) {
                if (data)
                    window.location.reload();
            }).
            error(function(data, status, headers, config) {
                alert('error');
                $('.progress-striped').hide();

            });
        };

        $scope.defaultTheme = function() {
            $http.get('/api/admin/themes/defaultTheme').
            success(function(data, status, headers, config) {
                if (data)
                    window.location.reload();
            }).
            error(function(data, status, headers, config) {
                alert('error');
            });
        };
    }
]);

'use strict';

angular.module('mean.admin').controller('ThemesController', ['$scope', 'Global', '$rootScope', '$http',
    function($scope, Global, $rootScope, $http) {
        $scope.global = Global;
        $scope.themes = [];

        $scope.init = function() {
            $http({
                method: 'GET',
                url: 'http://api.bootswatch.com/3/',
                skipAuthorization: true
            }).
            success(function(data, status, headers, config) {
                $scope.themes = data.themes;
            }).
            error(function(data, status, headers, config) {

            });
        };

        $scope.changeTheme = function(theme) {
            // Will add preview options soon
            // $('link').attr('href', theme.css);
            // $scope.selectedTheme = theme;
            $('.progress-striped').show();

            $http.get('/api/admin/themes?theme=' + theme.css).
            success(function(data, status, headers, config) {
                if (data)
                    window.location.reload();
            }).
            error(function(data, status, headers, config) {
                alert('error');
                $('.progress-striped').hide();

            });
        };

        $scope.defaultTheme = function() {
            $http.get('/api/admin/themes/defaultTheme').
            success(function(data, status, headers, config) {
                if (data)
                    window.location.reload();
            }).
            error(function(data, status, headers, config) {
                alert('error');
            });
        };
    }
]);

Fix for angular-jwt injecting authorization header


( function ( $, mw ) {
	QUnit.module( 'Thanks mobilediff' );

	QUnit.test( 'render button for logged in users', 1, function ( assert ) {
		var $container = $( '<div>' ),
			$user = $( '<div>' ).data( 'user-name', 'jon' )
				.data( 'revision-id', 1 )
				.data( 'user-gender', 'male' );

		// eslint-disable-next-line no-underscore-dangle
		mw.thanks._mobileDiffInit( $user, $container );
		assert.strictEqual( $container.find( 'button' ).length, 1, 'Thanks button was created.' );
	} );

}( jQuery, mediaWiki ) );

( function ( $, mw ) {
	QUnit.module( 'Thanks mobilediff' );

	QUnit.test( 'render button for logged in users', function ( assert ) {
		var $container = $( '<div>' ),
			$user = $( '<div>' ).data( 'user-name', 'jon' )
				.data( 'revision-id', 1 )
				.data( 'user-gender', 'male' );

		// eslint-disable-next-line no-underscore-dangle
		mw.thanks._mobileDiffInit( $user, $container );
		assert.strictEqual( $container.find( 'button' ).length, 1, 'Thanks button was created.' );
	} );

}( jQuery, mediaWiki ) );

Make compartible with QUnit 2


import 'angular-mocks';

describe('Filter: eventAddressFormat', () => {
  var filter;

  beforeEach(() => {
    angular.mock.module('confRegistrationWebApp');

    inject($injector => {
      filter = $injector.get('$filter')('eventAddressFormat');
    });
  });

  it('should format address', () => {
    expect(filter('City', 'FL', '55555', 'US')).toEqual({
      addressLine3: 'City, FL 55555',
      addressCountry: 'United States',
    });
  });
});

import 'angular-mocks';

describe('Filter: eventAddressFormat', () => {
  var filter;

  beforeEach(() => {
    angular.mock.module('confRegistrationWebApp');

    inject($injector => {
      filter = $injector.get('$filter')('eventAddressFormat');
    });
  });

  it('should format address | City, State Zip', () => {
    expect(filter('City', 'FL', '55555', 'US')).toEqual({
      addressLine3: 'City, FL 55555',
      addressCountry: 'United States',
    });
  });

  it('should format address | City, State', () => {
    expect(filter('City', 'FL', null, 'US')).toEqual({
      addressLine3: 'City, FL',
      addressCountry: 'United States',
    });
  });

  it('should format address | City, Zip', () => {
    expect(filter('City', null, '55555', 'US')).toEqual({
      addressLine3: 'City 55555',
      addressCountry: 'United States',
    });
  });

  it('should format address | Just City', () => {
    expect(filter('City', null, null, 'US')).toEqual({
      addressLine3: 'City',
      addressCountry: 'United States',
    });
  });

  it('should format address | State Zip', () => {
    expect(filter(null, 'FL', '55555', 'US')).toEqual({
      addressLine3: 'FL 55555',
      addressCountry: 'United States',
    });
  });

  it('should format address | Just State', () => {
    expect(filter(null, 'FL', null, 'US')).toEqual({
      addressLine3: 'FL',
      addressCountry: 'United States',
    });
  });

  it('should format address | Just Zip', () => {
    expect(filter(null, null, '55555', 'US')).toEqual({
      addressLine3: '55555',
      addressCountry: 'United States',
    });
  });

  it('should format address | No address', () => {
    expect(filter(null, null, null, 'US')).toEqual({
      addressLine3: '',
      addressCountry: 'United States',
    });
  });
});

Add more tests for event address formatting


import EmberObject from '@ember/object';
import { A } from '@ember/array';
import validator from 'ember-legit-forms/validators/same-validator';
import validatorObject from 'ember-legit-forms/utils/validator-object';
import { module, test } from 'qunit';

module('Unit | Validators | same');

let subject = validator.create();

function argumentsObj(fieldName, value) {
  return validatorObject.create({
    arguments: ['address'],
    fields: A([
      EmberObject.create({ name: fieldName, value })
    ])
  });
}

test('it validates properly', function(assert) {
  assert.equal(
    subject.validate('foo', argumentsObj('address', 'foo')),
    undefined,
    'it returns true when both fields equal'
  );

  assert.equal(
    subject.validate('foo', argumentsObj('address', 'asd')).message,
    'mustBeSame'
  );
});

test('it allows empty values for chaining', function(assert) {
  assert.equal(subject.validate(''), undefined);

  assert.equal(subject.validate(null), undefined);
});

import EmberObject from '@ember/object';
import { A } from '@ember/array';
import validator from 'ember-legit-forms/validators/same-validator';
import validatorObject from 'ember-legit-forms/utils/validator-object';
import { module, test } from 'qunit';

module('Unit | Validators | same');

let subject = validator.create();

function argumentsObj(fieldName, value) {
  return validatorObject.create({
    arguments: ['address'],
    fields: A([
      EmberObject.create({ name: fieldName, value })
    ])
  });
}

test('it validates properly', function(assert) {
  assert.equal(
    subject.validate('foo', argumentsObj('address', 'foo')),
    undefined,
    'it returns true when both fields equal'
  );

  assert.equal(
    subject.validate('foo', argumentsObj('address', 'asd')).message,
    'mustBeSame'
  );

  assert.equal(
    subject.validate('foo', argumentsObj('address', '')).message,
    'mustBeSame'
  );
});

Update tests for same validation


vars = require('./vars');
dash_button = require('node-dash-button');
nest = require('unofficial-nest-api');

var dash = dash_button(vars.AMAZON_DASH_BUTTON_MAC);

console.log("Nest API Connecting...");
nest.login(vars.NEST_USER_NAME, vars.NEST_PASSWORD, function (err, data) {
    if (err) {
        console.log(err.message);
        process.exit(1);
        return;
    }
    console.log('Logged in!');
    listenForDash();
});

function fetchNestStatus(callback) {
    nest.fetchStatus(function (data) {
        var structure = data.structure[vars.NEST_STRUCTURE_ID];
        var device;
        for (var deviceId in data.device) {
            if (data.device.hasOwnProperty(deviceId)) {
                device = data.shared[deviceId];
            }
        }
        callback(structure, device);
    });
}

function listenForDash() {
    console.log("Listening For Dash Button...");
    dash.on("detected", function () {
        console.log("Button Pressed!");
        fetchNestStatus(function (device, structure) {
            console.log("Current Temp: " + nest.ctof(device.current_temperature));
            if (structure.away) {
                console.log("Nest is set to Away. Setting to home.");
                nest.setHome();
            } else {
                console.log("Nest is set to home.");
            }
        });

    });
}

vars = require('./vars');
dash_button = require('node-dash-button');
nest = require('unofficial-nest-api');
util = require('util');

var device;
var structure;
var dash = dash_button(vars.AMAZON_DASH_BUTTON_MAC);

console.log("Nest API Connecting...");
nest.login(vars.NEST_USER_NAME, vars.NEST_PASSWORD, function (err, data) {
    if (err) {
        console.log(err.message);
        process.exit(1);
        return;
    }
    console.log('Logged in!');
    listenForDash();
});

function fetchNestStatus(callback) {
    nest.fetchStatus(function (data) {
        structure = data.structure[vars.NEST_STRUCTURE_ID];
        for (var deviceId in data.device) {
            if (data.device.hasOwnProperty(deviceId)) {
                device = data.shared[deviceId];
            }
        }
        callback();
    });
}

function listenForDash() {
    console.log("Listening For Dash Button...");
    dash.on("detected", function () {
        console.log("Button Pressed!");
        fetchNestStatus(function () {
            console.log("Current Temp: " + nest.ctof(device.current_temperature));
            if (structure.away) {
                console.log("Nest is set to Away. Setting to home.");
                nest.setHome();
            } else {
                console.log("Nest is set to home.");
            }
        });

    });
}

Revert changes that broke everything


'use strict';

var pg = require('pg');

exports.newPool = function(config) {
    return function(onSuccess, onError) {
        onSuccess(new pg.Pool(config));
    };
};

exports.withConnection = function(pool) {
    return function(body) {
        return function(onSuccess, onError) {
            pool.connect(function(err, client, done) {
                if (err !== null) {
                    onError(err);
                    return;
                }
                body(client)(function(r) {
                    done();
                    onSuccess(r);
                }, function(e) {
                    done();
                    onError(e);
                });
            });
        };
    };
};

exports._query = function(client) {
    return function(sql) {
        return function(values) {
            return function(onSuccess, onError) {
                client.query({
                    text: sql,
                    values: values,
                    rowMode: 'array',
                }, function(err, result) {
                    if (err !== null) {
                        onError(err);
                        return;
                    }
                    onSuccess(result.rows);
                });
            };
        };
    };
};

exports.unsafeIsBuffer = function(x) {
    return x instanceof Buffer;
};

'use strict';

var Control_Monad_Aff = require('../Control.Monad.Aff');
var pg = require('pg');

exports.newPool = function(config) {
    return function(onSuccess, onError) {
        onSuccess(new pg.Pool(config));
        return Control_Monad_Aff.nonCanceler;
    };
};

exports.withConnection = function(pool) {
    return function(body) {
        return function(onSuccess, onError) {
            pool.connect(function(err, client, done) {
                if (err !== null) {
                    onError(err);
                    return;
                }
                body(client)(function(r) {
                    done();
                    onSuccess(r);
                }, function(e) {
                    done();
                    onError(e);
                });
            });
            return Control_Monad_Aff.nonCanceler;
        };
    };
};

exports._query = function(client) {
    return function(sql) {
        return function(values) {
            return function(onSuccess, onError) {
                client.query({
                    text: sql,
                    values: values,
                    rowMode: 'array',
                }, function(err, result) {
                    if (err !== null) {
                        onError(err);
                        return;
                    }
                    onSuccess(result.rows);
                });
                return Control_Monad_Aff.nonCanceler;
            };
        };
    };
};

exports.unsafeIsBuffer = function(x) {
    return x instanceof Buffer;
};

Return cancelers from Aff actions


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

module.exports = function(environment) {
  let ENV = {
    modulePrefix: 'dummy',
    environment,
    rootURL: '/',
    locationType: 'auto',
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      },
      EXTEND_PROTOTYPES: {
        // Prevent Ember Data from overriding Date.parse.
        Date: false
      }
    },

    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    }
  };

  if (environment === 'development') {
    // ENV.APP.LOG_RESOLVER = true;
    // ENV.APP.LOG_ACTIVE_GENERATION = true;
    // ENV.APP.LOG_TRANSITIONS = true;
    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
    // ENV.APP.LOG_VIEW_LOOKUPS = true;
  }

  if (environment === 'test') {
    // Testem prefers this...
    ENV.locationType = 'none';

    // keep test console output quieter
    ENV.APP.LOG_ACTIVE_GENERATION = false;
    ENV.APP.LOG_VIEW_LOOKUPS = false;

    ENV.APP.rootElement = '#ember-testing';
  }

  if (environment === 'production') {

  }

  return ENV;
};

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

module.exports = function(environment) {
  let ENV = {
    modulePrefix: 'dummy',
    environment,
    rootURL: '/',
    locationType: 'auto',
    EmberENV: {
      FEATURES: {
        // Here you can enable experimental features on an ember canary build
        // e.g. 'with-controller': true
      },
      EXTEND_PROTOTYPES: {
        // Prevent Ember Data from overriding Date.parse.
        Date: false
      }
    },

    APP: {
      // Here you can pass flags/options to your application instance
      // when it is created
    }
  };

  if (environment === 'development') {
    // ENV.APP.LOG_RESOLVER = true;
    // ENV.APP.LOG_ACTIVE_GENERATION = true;
    // ENV.APP.LOG_TRANSITIONS = true;
    // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
    // ENV.APP.LOG_VIEW_LOOKUPS = true;
  }

  if (environment === 'test') {
    // Testem prefers this...
    ENV.locationType = 'none';

    // keep test console output quieter
    ENV.APP.LOG_ACTIVE_GENERATION = false;
    ENV.APP.LOG_VIEW_LOOKUPS = false;

    ENV.APP.rootElement = '#ember-testing';
  }

  if (environment === 'production') {
    ENV.locationType = 'hash';
    ENV.rootURL = '/ember-polymer/';
  }

  return ENV;
};

Fix rootURL in production 🔨


import React from 'react';

class PickYourPoo extends React.Component {
  constructor(props) {
    super(props);
  }

  render () {
    return (

PoopVR

Coming Soon!

World War Two

Prehistoric Poo

    );
  }
};

module.exports = PickYourPoo;

import React from 'react';
import DinoScene from '../../aframe/components/DinoScene';
import WarScene from '../../aframe/components/WarScene';

const pooScenes = {
  war: WarScene,
  dino: DinoScene,
}

class PickYourPoo extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  loadPoo(poo) {
    this.setState({ scene: pooScenes[poo] });
  }

  loadWarPoo() {
    this.loadPoo('war');
  }

  loadDinoPoo() {
    this.loadPoo('dino');
  }

  render () {
    if (this.state.scene) {
      return <this.state.scene />;
    } else {
      return (

PoopVR

Coming Soon!

World War Two

Prehistoric Poo

      );
    }
  }
};

module.exports = PickYourPoo;

Add click handling for each poo event


if (typeof jQuery!=='undefined') {
	(function($) {
		var iframe = $('#Iframepage-iframe');
		var loading = $('#Iframepage-loading');

		// Add not-ready marker for third party use.
		iframe.addClass('iframepage-not-ready');
		// Show loading message
		iframe.hide();
		loading.show();

		$(function() {
			iframe.load(function() {
				// Iframe content has been loaded
				loading.hide();
				iframe.show();

				if (iframe.hasClass('iframepage-height-auto')===true) {
					// Try to set the height to the height of the iframe content (only possible if it is the same domain)
					try {
						var iframeInsideSize = $(iframe[0].contentWindow.document.body).height();
						iframe.css('height', (parseInt(iframeInsideSize)+100)+'px');
					}
					catch(e) {
						// Failed to set the height, we fall back to default css height.
					}
				}

				// Finally, remove the marker
				iframe.removeClass('iframepage-not-ready');
			});
		});
	})(jQuery);
}

if (typeof jQuery!=='undefined') {
	(function($) {
		var iframe = $('#Iframepage-iframe');
		var loading = $('#Iframepage-loading');

		// Add not-ready marker for third party use.
		iframe.addClass('iframepage-not-ready');
		// Show loading message
		iframe.hide();
		loading.show();

		$(function() {
			$( window ).load(function() {
				// Iframe content has been loaded
				loading.hide();
				iframe.show();

				if (iframe.hasClass('iframepage-height-auto')===true) {
					// Try to set the height to the height of the iframe content (only possible if it is the same domain)
					try {
						var iframeInsideSize = $(iframe[0].contentWindow.document.body).height();
						iframe.css('height', (parseInt(iframeInsideSize)+100)+'px');
					}
					catch(e) {
						// Failed to set the height, we fall back to default css height.
					}
				}

				// Finally, remove the marker
				iframe.removeClass('iframepage-not-ready');
			});
		});
	})(jQuery);
}

Add support for Firefox & IE9


jsio('from common.javascript import Singleton')

exports = Class(function() {

	this.init = function() {
		this._viewConstructors = {}
	}

	this.registerView = function(viewName, viewConstructor) {
		this._viewConstructors[viewName] = viewConstructor
	}

	this.getView = function(viewName, jsArgs, templateArgs) {
		return new this._viewConstructors[viewName](jsArgs, templateArgs)
	}
})

jsio('from common.javascript import Singleton')

exports = Class(function() {

	this.init = function() {
		this._viewConstructors = {}
	}

	this.registerView = function(viewName, viewConstructor) {
		this._viewConstructors[viewName] = viewConstructor
	}

	this.getView = function(viewName, jsArgs, templateArgs) {
		return new this._viewConstructors[viewName](jsArgs, templateArgs || [])
	}
})

Make sure the viewArgs array is never null


class CharCounter {
  constructor(input, counter, limit = 250) {
    this.input = input
    this.counter = counter
    this.limit = limit

    this.updateCounter()

    ;['keyup', 'change', 'input', 'paste'].forEach(event => {
        input.addEventListener(event, () => this.updateCounter)
    })
  }

  updateCounter() {
    let len = this.input.value.length
    this.counter.innerHTML = `${len} / ${this.limit}`
    let isFull = len >= this.limit
    this.counter.classList.toggle('full', isFull)
    this.input.classList.toggle('full', isFull)
  }
}

module.exports = function(input, counter, limit) {
  return new CharCounter(input, counter, limit)
}

class CharCounter {
  constructor(input, counter, limit = 250) {
    this.input = input
    this.counter = counter
    this.limit = limit

    this.updateCounter()

    ;['keyup', 'change', 'input', 'paste'].forEach(event => {
        input.addEventListener(event, () => this.updateCounter())
    })
  }

  updateCounter() {
    let len = this.input.value.length
    this.counter.innerHTML = `${len} / ${this.limit}`
    let isFull = len >= this.limit
    this.counter.classList.toggle('full', isFull)
    this.input.classList.toggle('full', isFull)
  }
}

module.exports = function(input, counter, limit) {
  return new CharCounter(input, counter, limit)
}

Use parentheses when you want to call a JS function.


/**
 * Node.js API Starter Kit (https://reactstarter.com/nodejs)
 *
 * Copyright © 2016-present Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

/* @flow */

// TODO: Log the error to Google Stackdriver, Rollbar etc.
function report(error: Error) {
  // eslint-disable-next-line no-console
  console.error(error);
}

export class ValidationError extends Error {
  code = 400;
  state: any;

  constructor(errors: []) {
    super('The request is invalid.');
    this.state = errors.reduce((result, error) => {
      if (Object.prototype.hasOwnProperty.call(result, error.key)) {
        result[error.key].push(error.message);
      } else {
        Object.defineProperty(result, error.key, {
          value: [error.message],
          enumerable: true,
        });
      }
      return result;
    }, {});
  }
}

export class UnauthorizedError extends Error {
  code = 401;
  message = this.message || 'Anonymous access is denied.';
}

export class ForbiddenError extends Error {
  code = 403;
  message = this.message || 'Access is denied.';
}

export default { report };

/**
 * Node.js API Starter Kit (https://reactstarter.com/nodejs)
 *
 * Copyright © 2016-present Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */

/* @flow */

// TODO: Log the error to Google Stackdriver, Rollbar etc.
function report(error: Error) {
  // eslint-disable-next-line no-console
  console.error(error);
}

export class ValidationError extends Error {
  code = 400;
  state: any;

  constructor(errors: Array<{ key: string, message: string }>) {
    super('The request is invalid.');
    this.state = errors.reduce((result, error) => {
      if (Object.prototype.hasOwnProperty.call(result, error.key)) {
        result[error.key].push(error.message);
      } else {
        Object.defineProperty(result, error.key, {
          value: [error.message],
          enumerable: true,
        });
      }
      return result;
    }, {});
  }
}

export class UnauthorizedError extends Error {
  code = 401;
  message = this.message || 'Anonymous access is denied.';
}

export class ForbiddenError extends Error {
  code = 403;
  message = this.message || 'Access is denied.';
}

export default { report };

Fix a Flow type annotation in ValidationError


export default function makeAuthActions (feathersClient) {
  return {
    authenticate (store, data) {
      const { commit, state, dispatch } = store

      commit('setAuthenticatePending')
      if (state.errorOnAuthenticate) {
        commit('clearAuthenticateError')
      }
      return feathersClient.authenticate(data)
        .then(response => {
          commit('setAccessToken', response.accessToken)

          // Decode the token and set the payload, but return the response
          return feathersClient.passport.verifyJWT(response.accessToken)
            .then(payload => {
              commit('setPayload', payload)

              // Populate the user if the userService was provided
              if (state.userService && payload.hasOwnProperty('userId')) {
                return dispatch('populateUser', payload.userId)
                  .then(() => {
                    commit('unsetAuthenticatePending')
                    return response
                  })
              } else {
                commit('unsetAuthenticatePending')
              }
              return response
            })
        })
        .catch(error => {
          commit('setAuthenticateError', error)
          commit('unsetAuthenticatePending')
          return Promise.reject(error)
        })
    },

    populateUser ({ commit, state }, userId) {
      return feathersClient.service(state.userService)
        .get(userId)
        .then(user => {
          commit('setUser', user)
          return user
        })
    },

    logout ({commit}) {
      commit('setLogoutPending')
      return feathersClient.logout()
        .then(response => {
          commit('logout')
          commit('unsetLogoutPending')
          return response
        })
        .catch(error => {
          return Promise.reject(error)
        })
    }
  }
}

export default function makeAuthActions (feathersClient) {
  return {
    authenticate (store, data) {
      const { commit, state, dispatch } = store

      commit('setAuthenticatePending')
      if (state.errorOnAuthenticate) {
        commit('clearAuthenticateError')
      }
      return feathersClient.authenticate(data)
        .then(response => {
          if (response.accessToken) {
            commit('setAccessToken', response.accessToken)

            // Decode the token and set the payload, but return the response
            return feathersClient.passport.verifyJWT(response.accessToken)
              .then(payload => {
                commit('setPayload', payload)

                // Populate the user if the userService was provided
                if (state.userService && payload.hasOwnProperty('userId')) {
                  return dispatch('populateUser', payload.userId)
                    .then(() => {
                      commit('unsetAuthenticatePending')
                      return response
                    })
                } else {
                  commit('unsetAuthenticatePending')
                }
                return response
              })
          // If there was not an accessToken in the response, allow the response to pass through to handle two-factor-auth
          } else {
            return response
          }
        })
        .catch(error => {
          commit('setAuthenticateError', error)
          commit('unsetAuthenticatePending')
          return Promise.reject(error)
        })
    },

    populateUser ({ commit, state }, userId) {
      return feathersClient.service(state.userService)
        .get(userId)
        .then(user => {
          commit('setUser', user)
          return user
        })
    },

    logout ({commit}) {
      commit('setLogoutPending')
      return feathersClient.logout()
        .then(response => {
          commit('logout')
          commit('unsetLogoutPending')
          return response
        })
        .catch(error => {
          return Promise.reject(error)
        })
    }
  }
}

Prepare for two-factor auth scenarios