/*
 * Simple middleware that saves res.locals as stringified JSON to
 * `res.locals.INITIAL_STATE`
 *
 * @module midwest/middleware/bootstrap
 */

'use strict'

const _ = require('lodash')

const defaultOmit = ['settings']

module.exports = function ({ pick, omit, property = 'INITIAL_STATE' }) {
  if (!pick) {
    omit = omit ? defaultOmit.concat(omit) : defaultOmit
  }

  return function bootstrap (req, res, next) {
    if (!req.xhr && req.accepts(['json', '*/*']) === '*/*') {
      let obj = Object.assign({}, res.app.locals, res.locals)

      if (pick) {
        obj = _.pick(obj, pick)
      } else {
        obj = _.omit(obj, omit)
      }

      res.locals[property] = JSON.stringify(obj)
    }

    next()
  }
}

/*
 * Simple middleware that saves res.locals as stringified JSON to
 * `res.locals.INITIAL_STATE`
 *
 * @module midwest/middleware/bootstrap
 */

'use strict'

const _ = require('lodash')

const defaultOmit = ['settings']

module.exports = function ({ pick, omit, property = 'INITIAL_STATE' } = {}) {
  if (!pick) {
    omit = omit ? defaultOmit.concat(omit) : defaultOmit
  }

  return function bootstrap (req, res, next) {
    if (!req.xhr && req.accepts(['json', '*/*']) === '*/*') {
      let obj = Object.assign({}, res.app.locals, res.locals)

      if (pick) {
        obj = _.pick(obj, pick)
      } else {
        obj = _.omit(obj, omit)
      }

      res.locals[property] = JSON.stringify(obj)
    }

    next()
  }
}

Set empty object as default arg


var RomoWordBoundaryFilter = function(filterString, setElems, getElemTextContentCallback) {
  this.boundaryCharsRegex = /[\s-_]+/;
  this.matchingNodes      = [];
  this.notMatchingNodes   = [];
  this.filters            = filterString
                              .trim()
                              .toLowerCase()
                              .split(this.boundaryCharsRegex);

  Romo.toArray(setElems).forEach($.proxy(function(node) {
    var contentStack = getElemTextContentCallback($(node))
                         .trim()
                         .toLowerCase()
                         .split(this.boundaryCharsRegex).reverse();

    var match = this.filters.reduce($.proxy(function(filterMatch, filter) {
      if (filterMatch === false) {
        // short-circuit the reduce
        return false;
      } else {
        var contentMatch = false;
        do {
          var content = contentStack.pop();
          if (content !== undefined && content.indexOf(filter) === 0) {
            contentMatch = true;
            // we found a match for this filter so we need to
            // break out of this do...while and go to next filter
            content = undefined;
          }
        } while(content !== undefined);
        return contentMatch;
      }
    }, this), true);

    if (match === true) {
      this.matchingNodes.push(node);
    } else {
      this.notMatchingNodes.push(node);
    }
  }, this));

  this.matchingElems    = $(this.matchingNodes);
  this.notMatchingElems = $(this.notMatchingNodes);
}

var RomoWordBoundaryFilter = function(filterString, setElems, getElemTextContentCallback) {
  this.boundaryCharsRegex = /[\s-_]+/;
  this.matchingElems      = [];
  this.notMatchingElems   = [];
  this.filters            = filterString
                              .trim()
                              .toLowerCase()
                              .split(this.boundaryCharsRegex);

  Romo.array(setElems).forEach(Romo.proxy(function(elem) {
    var contentStack = getElemTextContentCallback(elem)
                         .trim()
                         .toLowerCase()
                         .split(this.boundaryCharsRegex).reverse();

    var match = this.filters.reduce(Romo.proxy(function(filterMatch, filter) {
      if (filterMatch === false) {
        // short-circuit the reduce
        return false;
      } else {
        var contentMatch = false;
        do {
          var content = contentStack.pop();
          if (content !== undefined && content.indexOf(filter) === 0) {
            contentMatch = true;
            // we found a match for this filter so we need to
            // break out of this do...while and go to next filter
            content = undefined;
          }
        } while(content !== undefined);
        return contentMatch;
      }
    }, this), true);

    if (match === true) {
      this.matchingElems.push(elem);
    } else {
      this.notMatchingElems.push(elems);
    }
  }, this));
}

Update `RomoWordBoundaryFilter` component to not use jquery


import { defineComponent } from 'vue'
import { RouterLink as VueRouterLink } from 'vue-router'
import { getPreloadPath } from './lib/runtime-utils'

const prefetchedPaths = new Set()

export const RouterLink = defineComponent({
  ...VueRouterLink,

  name: 'RouterLink',

  async mounted() {
    if (import.meta.env.DEV) return

    const { matched, path } = this.$router.resolve(this.to)

    if (prefetchedPaths.has(path)) return

    const components = await Promise.all(
      matched.map((m) =>
        typeof m.components.default === 'function'
          ? m.components.default()
          : m.components.default
      )
    )
    for (const component of components) {
      if (component.$$staticPreload) {
        const url = getPreloadPath(path)
        fetch(url, { credentials: `include` })
        prefetchedPaths.add(path)
        break
      }
    }
  },
})

import { defineComponent } from 'vue'
import { RouterLink as VueRouterLink } from 'vue-router'
import { getPreloadPath } from './lib/runtime-utils'

const prefetchedPaths = new Set()

const link = import.meta.env.SSR ? null : document.createElement('link')
const hasPrefetch = import.meta.env.SSR
  ? false
  : link.relList && link.relList.supports && link.relList.supports('prefetch')

/**
 * Fetch URL using `<link rel="prefetch">` with fallback to `fetch` API
 * Safari doesn't have it enabled by default: https://caniuse.com/?search=prefetch
 * @param {string} url
 */
const prefetchURL = (url) => {
  if (hasPrefetch) {
    const link = document.createElement('link')
    link.rel = 'prefetch'
    link.href = url
    document.head.appendChild(link)
  } else {
    fetch(url, { credentials: `include` })
  }
}

export const RouterLink = defineComponent({
  ...VueRouterLink,

  name: 'RouterLink',

  async mounted() {
    if (import.meta.env.DEV) return

    const { matched, path } = this.$router.resolve(this.to)

    if (prefetchedPaths.has(path)) return

    const components = await Promise.all(
      matched.map((m) =>
        typeof m.components.default === 'function'
          ? m.components.default()
          : m.components.default
      )
    )
    for (const component of components) {
      if (component.$$staticPreload) {
        const url = getPreloadPath(path)
        prefetchURL(url)
        prefetchedPaths.add(path)
        break
      }
    }
  },
})

Revert "refactor: use fetch to prefetch url"


'use strict';

const passport = require('passport');
const main = require('../app/controllers/main');
const api = require('../app/controllers/api');
const auth = require('../app/controllers/auth');

/**
 * Expose routes
 */

module.exports = function applyRoutes(app) {
  app.get('/', main.index);
  app.post('/auth/token', auth.token);
  app.get('/:type(orders|accounts|portions|vendors)', passport.authenticate('jwt'), api);
  app.get('/:type(orders|accounts|portions|vendors)/:id', passport.authenticate('jwt'), api);
  app.post('/:type(orders|portions|vendors)', passport.authenticate('jwt'), api);
  app.post('/:type(accounts)', api);
};

'use strict';

const passport = require('passport');
const main = require('../app/controllers/main');
const api = require('../app/controllers/api');
const auth = require('../app/controllers/auth');

/**
 * Expose routes
 */

module.exports = function applyRoutes(app) {
  app.get('/', main.index);
  app.post('/auth/token', auth.token);
  app.get('/:type(orders|accounts|portions|vendors)', passport.authenticate('jwt'), api);
  app.get('/:type(orders|accounts|portions|vendors)/:id', passport.authenticate('jwt'), api);
  app.patch('/:type(orders)/:id', passport.authenticate('jwt'), api);
  app.post('/:type(orders|portions|vendors)', passport.authenticate('jwt'), api);
  app.post('/:type(accounts)', api);
};

Add server endpoint to update order via api


const fs = require('fs-extra')
const path = require('path')
const { ROOT_PATH } = require('./constants')

class ReleaseAssets {

  /**
  * Copies the assets into the releases folders
  * @param platforms: the platforms to build for
  * @return promise
  */
  static copyAssetsIntoReleases (platforms) {
    const platformsSet = new Set(platforms)
    if (platformsSet.has('darwin')) {
      fs.copySync(path.join(__dirname, 'dmg/First Run.html'), path.join(ROOT_PATH, 'WMail-darwin-x64/First Run.html'))
    } else if (platformsSet.has('linux')) {
      fs.copySync(path.join(ROOT_PATH, 'assets/icons/app.png'), path.join(ROOT_PATH, 'WMail-linux-ia32/icon.png'))
      fs.copySync(path.join(ROOT_PATH, 'assets/icons/app.png'), path.join(ROOT_PATH, 'WMail-linux-x64/icon.png'))
    }

    return Promise.resolve()
  }
}

module.exports = ReleaseAssets

const fs = require('fs-extra')
const path = require('path')
const { ROOT_PATH } = require('./constants')

class ReleaseAssets {

  /**
  * Copies the assets into the releases folders
  * @param platforms: the platforms to build for
  * @return promise
  */
  static copyAssetsIntoReleases (platforms) {
    const platformsSet = new Set(platforms)
    if (platformsSet.has('darwin')) {
      fs.copySync(path.join(__dirname, 'dmg/First Run.html'), path.join(ROOT_PATH, 'WMail-darwin-x64/First Run.html'))
    }
    if (platformsSet.has('linux')) {
      fs.copySync(path.join(ROOT_PATH, 'assets/icons/app.png'), path.join(ROOT_PATH, 'WMail-linux-ia32/icon.png'))
      fs.copySync(path.join(ROOT_PATH, 'assets/icons/app.png'), path.join(ROOT_PATH, 'WMail-linux-x64/icon.png'))
    }

    return Promise.resolve()
  }
}

module.exports = ReleaseAssets

Fix copying icon in packager


Object.defineProperty = function defineProperty(object, property, descriptor) {
	// handle object
	if (object === null || typeof object !== 'object') {
		throw new TypeError('Object must be an object');
	}

	// handle descriptor
	if (descriptor === null || typeof descriptor !== 'object') {
		throw new TypeError('Descriptor must be an object');
	}

	var
	propertyString = String(property),
	Element = window.Element || {};

	// handle descriptor.get
	if ('get' in descriptor) {
		object[propertyString] = object === Element.prototype ? Element.__defineGetter__(propertyString, descriptor.get) : descriptor.get.call(object);
	}
	// handle descriptor.value
	else {
		object[propertyString] = descriptor.value;
	}

	// handle descriptor.set
	if ('set' in descriptor && object.constructor === Element) {
		object.attachEvent('onpropertychange', function callee(event) {
			object.detachEvent('onpropertychange', callee);

			if (event.propertyName === propertyString) {
				object[propertyString] = descriptor.set.call(object, object[propertyString]);
			}

			object.attachEvent('onpropertychange', callee);
		});
	}

	// return object
	return object;
};

Object.defineProperty = function defineProperty(object, property, descriptor) {
	// handle object
	if (object === null || typeof object !== 'object') {
		throw new TypeError('Object must be an object');
	}

	// handle descriptor
	if (descriptor === null || typeof descriptor !== 'object') {
		throw new TypeError('Descriptor must be an object');
	}

	var
	propertyString = String(property),
	Element = window.Element || {};

	// handle descriptor.get
	if ('get' in descriptor) {
		object[propertyString] = object === Element.prototype ? new Element.__getter__(descriptor.get) : descriptor.get.call(object);
	}
	// handle descriptor.value
	else {
		object[propertyString] = descriptor.value;
	}

	// handle descriptor.set
	if ('set' in descriptor && object.constructor === Element) {
		object.attachEvent('onpropertychange', function callee(event) {
			object.detachEvent('onpropertychange', callee);

			if (event.propertyName === propertyString) {
				object[propertyString] = descriptor.set.call(object, object[propertyString]);
			}

			object.attachEvent('onpropertychange', callee);
		});
	}

	// return object
	return object;
};

Update defineProperty for old IE


var _ = require('lodash');

var resources = require('../dummy/resources');

var DummyResource = resources.DummyResource;

var DummyConfigResource = DummyResource.extend(function(self, name, store) {
    /**class:DummyConfigResource(name)

    Handles api requests to the config resource from :class:`DummyApi`.

    :param string name:
        The name of the resource. Should match the name given in api requests.
    */
    DummyResource.call(self, name);

    /**attribute:DummyConfigResource.store
    An object containing sandbox's config data. Properties do not need to be
    JSON-stringified, this is done when the config is retrieved using a
    ``'config.get'`` api request.
    */
    self.store = store || {config: {}};

    /**attribute:DummyConfigResource.app
    A shortcut to DummyConfigResource.store.config (the app's config).
    */
    Object.defineProperty(self, 'app', {
        get: function() {
            return self.store.config;
        },
        set: function(v) {
            self.store.config = v;
            return v;
        }
    });

    self.handlers.get = function(cmd) {
        var value = self.store[cmd.key];

        if (_.isUndefined(value)) {
            value = null;
        }

        return {
            success: true,
            value: JSON.stringify(value)
        };
    };
});

this.DummyConfigResource = DummyConfigResource;

var _ = require('lodash');

var resources = require('../dummy/resources');

var DummyResource = resources.DummyResource;

var DummyConfigResource = DummyResource.extend(function(self, name, store) {
    /**class:DummyConfigResource(name)

    Handles api requests to the config resource from :class:`DummyApi`.

    :param string name:
        The name of the resource. Should match the name given in api requests.
    */
    DummyResource.call(self, name);

    /**attribute:DummyConfigResource.store
    An object containing the sandbox's config data. Properties do not need to be
    JSON-stringified, this is done when the config is retrieved using a
    ``'config.get'`` api request.
    */
    self.store = store || {config: {}};

    /**attribute:DummyConfigResource.app
    A shortcut to DummyConfigResource.store.config (the app's config).
    */
    Object.defineProperty(self, 'app', {
        get: function() {
            return self.store.config;
        },
        set: function(v) {
            self.store.config = v;
            return v;
        }
    });

    self.handlers.get = function(cmd) {
        var value = self.store[cmd.key];

        if (_.isUndefined(value)) {
            value = null;
        }

        return {
            success: true,
            value: JSON.stringify(value)
        };
    };
});

this.DummyConfigResource = DummyConfigResource;

Add missing article to DummyConfigResource docstring


/* eslint global-require: 0 */

if (process.env.NODE_ENV === 'production') {
  module.exports = require('./configureStore.prod');
} else {
  module.exports = require('./configureStore.dev');
}

/* eslint global-require: 0 */

if (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') {
  module.exports = require('./configureStore.prod');
} else {
  module.exports = require('./configureStore.dev');
}

Disable actions logger when testing


module.exports = function(grunt) {

  grunt.initConfig({
    qunit: ['./test/index.html'],
    lint: ['supermodel.js', './test/*.js'],
    jshint: {
      options: {
        eqnull: true,
        undef: true
      },
      globals: {
        // QUnit
        ok: true,
        test: true,
        module: true,
        deepEqual: true,
        strictEqual: true,

        // Dependencies
        _: true,
        jQuery: true,
        Backbone: true,
        Supermodel: true
      }
    }
  });

};

module.exports = function(grunt) {

  grunt.initConfig({
    qunit: ['./test/index.html'],
    lint: ['supermodel.js', './test/*.js'],
    jshint: {
      options: {
        eqnull: true,
        undef: true
      },
      globals: {
        // QUnit
        ok: true,
        test: true,
        module: true,
        raises: true,
        deepEqual: true,
        strictEqual: true,

        // Dependencies
        _: true,
        jQuery: true,
        Backbone: true,
        Supermodel: true
      }
    }
  });

};

Add `raises` to linter globals.


'use strict';

/**
 * Run package tests.
 * (C) 2014 Alex Fernández.
 */

// requires
var testing = require('testing');
var Log = require('log');

// globals
var log = new Log('info');

/**
 * Run all module tests.
 */
exports.test = function(callback)
{
	log.debug('Running tests');
	var tests = {};
	var libs = ['estimation', 'db', 'badges', 'packages'];
    var factors = ['issues', 'versions', 'downloads'];
    var bin = ['all', 'update'];
	libs.forEach(function(lib)
	{
		tests[lib] = require('./lib/' + lib + '.js').test;
	});
    factors.forEach(function(factor)
    {
        tests[factors] = require('./lib/factors/' + factor + '.js').test;
    });
    bin.forEach(function(bin)
    {
        tests[bin] = require('./bin/' + bin + '.js').test;
    });
	testing.run(tests, 4200, callback);
};

// run tests if invoked directly
if (__filename == process.argv[1])
{
	exports.test(testing.show);
}

'use strict';

/**
 * Run package tests.
 * (C) 2014 Alex Fernández.
 */

// requires
var testing = require('testing');
var Log = require('log');

// globals
var log = new Log('info');

/**
 * Run all module tests.
 */
exports.test = function(callback)
{
	log.debug('Running tests');
	var tests = {};
	var libs = ['estimation', 'db', 'badges', 'packages', 'update'];
    var factors = ['issues', 'versions', 'downloads'];
    var bin = ['all'];
	libs.forEach(function(lib)
	{
		tests[lib] = require('./lib/' + lib + '.js').test;
	});
    factors.forEach(function(factor)
    {
        tests[factors] = require('./lib/factors/' + factor + '.js').test;
    });
    bin.forEach(function(bin)
    {
        tests[bin] = require('./bin/' + bin + '.js').test;
    });
	testing.run(tests, 4200, callback);
};

// run tests if invoked directly
if (__filename == process.argv[1])
{
	exports.test(testing.show);
}

Test update as lib, not in bin.


var Office365ConnectorHook = require("./index.js"),
    winston = require('winston');

var Logger = winston.Logger;
var Console = winston.transports.Console;

var logger = new Logger({
    transports: [
        new Console({}),
        new Office365ConnectorHook({
            hookUrl: 'https://outlook.office.com/webhook/28b98568-d712-43b0-acab-dc662d48af10@6afcd346-7cb2-46db-897d-c639dbdf0449/IncomingWebhook/292e9954dc38453ab8dca787d4dae2c3/26b2f7b0-a26a-43d3-b4e8-ccd198b6b3ef'
        })
    ]
});

logger.info('I am being logged here'); // will be sent to both console and Slack

var Office365ConnectorHook = require("./index.js"),
    winston = require('winston');

var Logger = winston.Logger;
var Console = winston.transports.Console;

var logger = new Logger({
    transports: [
        new Console({}),
        new Office365ConnectorHook({
            hookUrl: 'https://outlook.office.com/webhook/28b98568-d712-43b0-acab-dc662d48af10@6afcd346-7cb2-46db-897d-c639dbdf0449/IncomingWebhook/292e9954dc38453ab8dca787d4dae2c3/26b2f7b0-a26a-43d3-b4e8-ccd198b6b3ef'
        })
    ]
});

logger.info('I am being logged here'); // will be sent to both console and channel

Change comment reference to Team channel


/* global describe, it */

require('es6-promise').polyfill()

var popsicle = require('popsicle')
var nock = require('nock')
var expect = require('chai').expect
var prefix = require('./')

describe('popsicle prefix', function () {
  nock('http://prefix.com')
    .get('/http://example.com/')
    .reply(200)

  it('should prefix all urls', function () {
    return popsicle.get('http://example.com')
      .use(prefix('http://prefix.com'))
      .then(res => {
        expect(res.url).to.equal('http://prefix.com/http://example.com/')
      })
  })
})

/* global describe, it */

require('es6-promise').polyfill()

var popsicle = require('popsicle')
var nock = require('nock')
var expect = require('chai').expect
var prefix = require('./')

describe('popsicle prefix', function () {
  nock('http://prefix.com')
    .get('/http://example.com/')
    .reply(200)

  it('should prefix all urls', function () {
    return popsicle.get('http://example.com')
      .use(prefix('http://prefix.com'))
      .then(function (res) {
        expect(res.url).to.equal('http://prefix.com/http://example.com/')
      })
  })
})

Correct old node version usage


const Koa = require('koa');
const fetch = require('node-fetch');
const app = new Koa();
const port = process.env.PORT || 3000;

app.use(async (ctx, next) => {
    var response = await fetch('http://inspirobot.me/api?generate=true');
    var url = await response.text();
    ctx.body = url;
});

if (!module.parent) app.listen(port);

const Koa = require('koa');
const fetch = require('node-fetch');
const app = new Koa();
const port = process.env.PORT || 3000;

app.use(async (ctx, next) => {
    const response = await fetch('http://inspirobot.me/api?generate=true');
    const url = await response.text();
    const result = {
        parse: "full",
        response_type: "in_channel",
        text: url,
        attachments: [{ image_url: url }],
        unfurl_media: true,
        unfurl_links: true
    };
    ctx.set('Content-Type', 'application/json');
    ctx.body = result;
});

if (!module.parent) app.listen(port);

Format response to slack json


define([
    "underscore",
    "parse",
], function( _, Parse  ) {

    var process_single = function (error) {
        if (_.has(error, "message")) {
            console.log("Error in promise:" + error.message);
            if (Parse.Error.INVALID_LINKED_SESSION == error.code) {
                Parse.User.logOut();
            }
        } else {
            console.log("Error in promise:" + error);
        }
    };

    var func = function (error) {
        if (_.isArray(error)) {
            console.log("Error in a multi-promise follows {");
            _.each(error, function (e, i) {
                if (_.has(e, "success")) {
                    console.log("" + i + " succeeded at " + e.updatedAt);
                } else {
                    process_single(e);
                }
            });
            console.log("}");
        } else {
            process_single(error);
        }
    };

    return func;
} );

define([
    "underscore",
    "parse",
], function( _, Parse  ) {

    var process_single = function (error) {
        if (_.has(error, "message")) {
            console.log("Error in promise: " + error.message);
            trackJs.track("Error in promise: " + error.message);
            if (Parse.Error.INVALID_LINKED_SESSION == error.code) {
                Parse.User.logOut();
            }
        } else {
            console.log("Error in promise: " + error);
            trackJs.track("Error in promise: " + error);
        }
    };

    var func = function (error) {
        if (_.isArray(error)) {
            console.log("Error in a multi-promise follows {");
            _.each(error, function (e, i) {
                if (_.has(e, "success")) {
                    console.log("" + i + " succeeded at " + e.updatedAt);
                } else {
                    process_single(e);
                }
            });
            console.log("}");
        } else {
            process_single(error);
        }
    };

    return func;
} );

Send promise failures to trackJs


'use strict';

var redis = require('redis'),
    coRedis = require('co-redis'),
    koa = require('koa');

var app = koa(),
    client  = redis.createClient(
      process.env.REDIS_PORT,
      process.env.REDIS_HOST
    ),
    dbCo = coRedis(client);

if (process.env.REDIS_SECRET) {
  client.auth(process.env.REDIS_SECRET);
}

client.on('error', function (err) {
  console.log('Redis client error: ' + err);
});

app.use(function* () {

  var indexkey;

  if (this.request.query.index_key) {
    indexkey = process.env.APP_NAME +':'+ this.request.query.index_key;
  } else {
    indexkey = yield dbCo.get(process.env.APP_NAME +':current');
  }
  var index = yield dbCo.get(indexkey);

  if (index) {
    this.body = index;
  } else {
    this.status = 404;
  }
});

app.listen(process.env.PORT || 3000);

'use strict';

var redis = require('redis'),
    coRedis = require('co-redis'),
    koa = require('koa');

var app = koa(),
    client  = redis.createClient(
      process.env.REDIS_PORT,
      process.env.REDIS_HOST
    ),
    dbCo = coRedis(client);

if (process.env.REDIS_SECRET) {
  client.auth(process.env.REDIS_SECRET);
}

client.on('error', function (err) {
  console.log('Redis client error: ' + err);
});

app.use(function* () {

  var indexkey;

  if (this.request.query.index_key) {
    indexkey = process.env.APP_NAME +':'+ this.request.query.index_key;
  } else {
    indexkey = yield dbCo.get(process.env.APP_NAME +':current');
    indexkey = process.env.APP_NAME + ':' + indexkey;
  }
  var index = yield dbCo.get(indexkey);

  if (index) {
    this.body = index;
  } else {
    this.status = 404;
  }
});

app.listen(process.env.PORT || 3000);

Fix ember-cli-deploy-redis v 0.5 bug


function vargs(fn) {
    var slice = Array.prototype.slice,
        paramsCount = fn.length

    return function () {
        var argsCount = arguments.length,
            lastArg = arguments[argsCount - 1]

        if (argsCount >= paramsCount || typeof lastArg !== 'function')
            return fn.apply(this, arguments)

        var args = slice.call(arguments, 0, argsCount - 1)
        for (var i = paramsCount - argsCount; i; i -= 1)
            args.push(undefined)
        args.push(lastArg)

        return fn.apply(this, args)
    }
}

module.exports = vargs

function vargs(fn, requiredCallback) {
    var slice = Array.prototype.slice,
        paramsCount = fn.length

    return function () {
        var argsCount = arguments.length,
            lastArg = arguments[argsCount - 1]
        if (requiredCallback && typeof lastArg !== 'function')
            throw new Error('callback is required')

        if (argsCount >= paramsCount || typeof lastArg !== 'function')
            return fn.apply(this, arguments)

        var args = slice.call(arguments, 0, argsCount - 1)
        for (var i = paramsCount - argsCount; i; i -= 1)
            args.push(undefined)
        args.push(lastArg)

        return fn.apply(this, args)
    }
}

module.exports = vargs

Add optional argument to vargs to be able to force existing callback


'use strict';
var postcssCached = require('postcss-cached');
var through = require('through2');
var applySourceMap = require('vinyl-sourcemaps-apply');
var gulpUtil = require('gulp-util');

module.exports = function(options) {
  options = Object(options);
  var postcssCachedInstance = postcssCached();

  var gulpPlugin = through.obj(function(file, encoding, callback) {
    var fileOptions = Object.create(options);
    fileOptions.from = file.path;
    fileOptions.map = fileOptions.map || file.sourceMap;

    try {
      var result = postcssCachedInstance
        .process(file.contents.toString('utf8'), fileOptions);
      file.contents = new Buffer(result.css);
      if (fileOptions.map) {
        applySourceMap(file, result.map);
      }
      this.push(file);
      callback();
    } catch(e) {
      callback(new gulpUtil.PluginError('gulp-postcss-cached', e));
    }
  });

  gulpPlugin.use = function(postcssPlugin) {
    postcssCachedInstance.use(postcssPlugin);
    return this;
  };

  return gulpPlugin;
};

'use strict';
var postcssCached = require('postcss-cached');
var through = require('through2');
var applySourceMap = require('vinyl-sourcemaps-apply');
var gulpUtil = require('gulp-util');

module.exports = function(options) {
  options = Object(options);
  var postcssCachedInstance = postcssCached();

  var gulpPlugin = through.obj(function(file, encoding, callback) {
    var fileOptions = Object.create(options);
    fileOptions.from = file.path;
    fileOptions.map = fileOptions.map || file.sourceMap;

    try {
      var result = postcssCachedInstance
        .process(file.contents.toString('utf8'), fileOptions);
      file.contents = new Buffer(result.css);
      if (fileOptions.map) {
        applySourceMap(file, result.map);
      }
      callback(null, file);
    } catch(e) {
      callback(new gulpUtil.PluginError('gulp-postcss-cached', e));
    }
  });

  gulpPlugin.use = function(postcssPlugin) {
    postcssCachedInstance.use(postcssPlugin);
    return this;
  };

  return gulpPlugin;
};

Use callback(null, file) when pushing only one file


var siteKey = process.env.HORSELIGHTS_SITEKEY
var lightId = process.argv[2]

if(siteKey == null || lightId == null) {
  throw "Please provide site key in environment variable and light id as command line parameter"
}

console.log("Controlling lightId", lightId);

var midi = require('midi')
var request = require('request')

function putLightState(message) {
  var url = "https://houm.herokuapp.com/api/site/" + siteKey + "/light/state"
  request({ method: 'PUT', url: url, body: message, json: true}, function (error, response, body) {
    if(error) {
      console.log('Failed setting light state to', message)
    }
  });
}

var input = new midi.input()

input.on('message', function(deltaTime, message) {
  var isControlMessage = (message[0] >> 4) === 0xb;
  if(isControlMessage) {
    var controllerNumber = message[1];
    var controllerValue = message[2];
    console.log('Set brightness to', controllerValue);
    putLightState({
      _id: lightId,
      on: controllerValue > 0,
      bri: controllerValue
    });
  }
});

input.openPort(0);

var siteKey = process.env.HORSELIGHTS_SITEKEY
var lightId = process.argv[2]

if(siteKey == null || lightId == null) {
  throw "Please provide site key in environment variable and light id as command line parameter"
}

console.log("Controlling lightId", lightId);

var midi = require('midi')
var request = require('request')

function putLightState(message) {
  var url = "https://houm.herokuapp.com/api/site/" + siteKey + "/light/state"
  request({ method: 'PUT', url: url, body: message, json: true}, function (error, response, body) {
    if(error) {
      console.log('Failed setting light state to', message)
    }
  });
}

var input = new midi.input()

input.on('message', function(deltaTime, message) {
  var isControlMessage = (message[0] >> 4) === 0xb;
  if(isControlMessage) {
    var controllerValue = message[2];
    var brightness = controllerValue * 2;
    console.log('Set brightness to', brightness);
    putLightState({
      _id: lightId,
      on: brightness > 0,
      bri: brightness
    });
  }
});

input.openPort(0);

Scale MIDI value (0-127) to expected scale (0-255)


var Promise = require('bluebird')
var request = require('got')
var extend = require('xtend')

var BASE_URL = 'http://www.broadbandmap.gov/broadbandmap/broadband/jun2014/'

module.exports = function broadbandMap (lat, long, options) {
  options = extend({
    types: ['wireline', 'wireless']
  }, options || {})

  return Promise.map(options.type, buildRequest)
  .spread(function (res1, res2) {
    var results = []

    if (res1 && res1.body) results.push(JSON.parse(res1.body).Results)
    if (res2 && res2.body) results.push(JSON.parse(res2.body).Results)

    return results
  })

  function buildRequest (type) {
    return request.get(BASE_URL + type + '?latitude=' +
      lat + '&longitude=' + long + '&format=json')
  }
}

var Promise = require('bluebird')
var request = require('got')
var extend = require('xtend')

var BASE_URL = 'http://www.broadbandmap.gov/broadbandmap/broadband/jun2014/'

module.exports = function broadbandMap (lat, long, options) {
  options = extend({
    types: ['wireline', 'wireless']
  }, options || {})

  return Promise.resolve(options.types).map(sendRequest).map(parseResults)

  function sendRequest (type) {
    return request.get(BASE_URL + type + '?latitude=' +
      lat + '&longitude=' + long + '&format=json')
  }

  function parseResults (response) {
  	return JSON.parse(response.body).Results
  }
}

Use mapping to parse results


'use strict'
const most = require('most')
const request = require('request')
const EventEmitter = require('events')
const emitter = new EventEmitter()
const jsdom = require('jsdom')
const Promise = require('bluebird')
const _ = require('lodash/fp')

const startUrl = 'https://google.com'

const stream = most
  .fromEvent('html', emitter)
  .chain(x => {
    return most.fromPromise(new Promise((resolve, reject) => {
      jsdom.env(x, ['http://code.jquery.com/jquery.js'], (err, window) => {
        if (err) {
          reject(err)
        } else {
          let $ = window.$
          let links = $('a').toArray()

          let hrefs = _.reduce((acc, x) => {
            acc.push($(x).attr('href'))
            return acc
          }, [], links)

          resolve(hrefs)
        }
      })
    }))
  })

stream
  .subscribe({
    next: x => console.log('Data', x),
    complete: x => console.log('Finished', x),
    error: x => console.error('Error', x)
  })

request(startUrl, (err, response, body) => {
  emitter.emit('html', body)
})

'use strict'
const most = require('most')
const request = require('request')
const EventEmitter = require('events')
const emitter = new EventEmitter()
const jsdom = require('jsdom')
const Promise = require('bluebird')
const _ = require('lodash/fp')

const startUrl = 'https://google.com'

function stripUrls($, selector, prop) {
  let urls = $(selector)
    .toArray()
    .map(x => $(x).attr(prop))

  return urls
}

const stream = most
  .fromEvent('html', emitter)
  .chain(x => {
    return most.fromPromise(new Promise((resolve, reject) => {
      jsdom.env(x, ['http://code.jquery.com/jquery.js'], (err, window) => {
        if (err) {
          reject(err)
        } else {
          let $ = window.$
          let aHrefs = stripUrls($, 'a', 'href')
          let imgSrc = stripUrls($, 'img', 'src')

          let result = {
            aHrefs,
            imgSrc
          }

          resolve(result)
        }
      })
    }))
  })

stream
  .subscribe({
    next: x => console.log('Data', x),
    complete: x => console.log('Finished', x),
    error: x => console.error('Error', x)
  })

request(startUrl, (err, response, body) => {
  emitter.emit('html', body)
})

Add stripUrls fn. Stream now returns A hrefs and IMG src props.


// Load the node-ffi extensions
require('./ffi-extend')

// The main exports is the casting function
module.exports = $
$._ = $ // legacy. TODO: remove by 0.1.0

// export the exports from the 'import' module
var Import = require('./import')
$.import  = Import.import
$.resolve = Import.resolve

// This function accepts native JS types (String, Number, Date) and converts them
// to the proper Objective-C type (NSString, NSNumber, NSDate).
//   Syntax Sugar...
function $ (str) {
  $.import('Foundation')
  return module.exports.NSString('stringWithUTF8String', String(str))
}

// Load the node-ffi extensions
require('./ffi-extend')

// The main exports is the casting function
module.exports = $
$._ = $ // legacy. TODO: remove by 0.1.0

// export the exports from the 'import' module
var Import = require('./import')
$.import  = Import.import
$.resolve = Import.resolve

// This function accepts native JS types (String, Number, Date) and converts them
// to the proper Objective-C type (NSString, NSNumber, NSDate).
//   Syntax Sugar...
function $ (o) {
  var t = typeof o
  if (t == 'string') {
    return $.NSString('stringWithUTF8String', String(o))
  } else if (t == 'number') {
    return $.NSNumber('numberWithDouble', Number(o))
  } else if (isDate(o)) {
    return $.NSDate('dateWithTimeIntervalSince1970', o / 1000)
  }
  throw new Error('Unsupported object passed in to convert')
}

function isDate (d) {
  return d instanceof Date
      || Object.prototype.toString.call(d) == '[object Date]'
}

Add support for converting Numbers to NSNumbers and Dates to NSDates in the $ function.


var fs = require('fs'),
    path = require('path'),
    extend = require('extend');

function load() {
    // Recursively load one or more directories passed as arguments.

    var dir, result = {};

    function processFilename(fn) {
        var fp = path.join(dir, fn),
            // If the given filename is a directory, recursively load it.
            extra = fs.statSync(fp).isDirectory() ? load(fp) : require(fp);

        // The JSON data is independent of the actual file
        // hierarchy, so it is essential to extend "deeply".
        result = extend(true, result, extra);
    }

    for (dir of arguments) {
        dir = path.resolve(__dirname, dir);
        fs.readdirSync(dir).forEach(processFilename);
    }

    return result;
}

module.exports = load(
    // 'api',
    'css',
    // 'http',
    // 'javascript',
    'webextensions'
);

var fs = require('fs'),
    path = require('path'),
    extend = require('extend');

function load() {
    // Recursively load one or more directories passed as arguments.

    var dir, result = {};

    function processFilename(fn) {
        var fp = path.join(dir, fn),
            // If the given filename is a directory, recursively load it.
            extra = fs.statSync(fp).isDirectory() ? load(fp) : require(fp);

        // The JSON data is independent of the actual file
        // hierarchy, so it is essential to extend "deeply".
        result = extend(true, result, extra);
    }

    for (dir of arguments) {
        dir = path.resolve(__dirname, dir);
        fs.readdirSync(dir).forEach(processFilename);
    }

    return result;
}

module.exports = load(
    'api',
    'css',
    // 'http',
    // 'javascript',
    'webextensions'
);

Add 'api' folder to exports


'use strict';

module.exports = function (template, args) {
  if (!args) {
    return interpolate.bind(null, template);
  }

  return interpolate(template, args);
};

function interpolate(template, args) {
  return template.replace(/{([^}]*)}/g, function (match, key) {
    return key in args ? args[key] : match;
  });
}

'use strict';

module.exports = function (template, args) {
  if (!args) {
    return interpolate.bind(null, template);
  }

  return interpolate(template, args);
};

function interpolate(template, args) {
  if (typeof args === 'undefined') {
    args = {};
  }
  return template.replace(/{([^}]*)}/g, function (match, key) {
    return key in args ? args[key] : match;
  });
}

Set the args variable value as an empty object if the argument in missing


/* jshint node: true */
'use strict';

module.exports = {
  name: 'ember-cli-nouislider',

  included: function(app) {
    this._super.included(app);
    if (!process.env.EMBER_CLI_FASTBOOT) {
      var bowerDirectory = app.bowerDirectory || 'bower_components';

      this.import({
        development: bowerDirectory + '/nouislider/distribute/nouislider.js',
        production: bowerDirectory + '/nouislider/distribute/nouislider.min.js'
      });
      this.import(bowerDirectory + '/nouislider/distribute/nouislider.min.css');

      this.import('vendor/nouislider/shim.js', {
        exports: { 'noUiSlider': ['default'] }
      });
    }
  }
};

/* jshint node: true */
'use strict';

module.exports = {
  name: 'ember-cli-nouislider',

  included: function(app) {
    this._super.included(app);
    if (!process.env.EMBER_CLI_FASTBOOT) {
      // In nested addons, app.bowerDirectory might not be available
      var bowerDirectory = app.bowerDirectory || 'bower_components';
      // In ember-cli < 2.7, this.import is not available, so fall back to use app.import
      var importShim = typeof this.import !== 'undefined' ? this : app;

      importShim.import({
        development: bowerDirectory + '/nouislider/distribute/nouislider.js',
        production: bowerDirectory + '/nouislider/distribute/nouislider.min.js'
      });
      importShim.import(bowerDirectory + '/nouislider/distribute/nouislider.min.css');

      importShim.import('vendor/nouislider/shim.js', {
        exports: { 'noUiSlider': ['default'] }
      });
    }
  }
};

Fix asset import in ember-cli < 2.7


// Used to detect for Cygwin
var os = require('os');

// Required for Windows/Cygwin support
var root = [__dirname, '/vendor/libgit2/build/shared'].join(''),
  path = process.env.PATH;

if (~os.type().indexOf('CYGWIN') && !~path.indexOf(root)) {
  process.env.PATH = root + ':' + path;
}

// Assign raw api to module
var rawApi;
try {
  rawApi = require('./build/Release/nodegit');
} catch (e) {
  rawApi = require('./build/Debug/nodegit');
}
for (var key in rawApi) {
  exports[key] = rawApi[key];
}

// Import extensions
require('./lib/commit.js');
require('./lib/blob.js');
require('./lib/object.js');
require('./lib/signature.js');
require('./lib/odb.js');
require('./lib/oid.js');
require('./lib/index.js');
require('./lib/repo.js');
require('./lib/reference.js');
require('./lib/revwalk.js');
require('./lib/tree.js');
require('./lib/diff_list.js');
require('./lib/tree_entry.js');
require('./lib/tree_builder.js');

// Set version
exports.version = require('./package').version;

// Initialize threads
exports.Threads.init();

// Used to detect for Cygwin
var os = require('os');

// Required for Windows/Cygwin support
var root = [__dirname, '/vendor/libgit2/build/shared'].join(''),
  path = process.env.PATH;

if (~os.type().indexOf('CYGWIN') && !~path.indexOf(root)) {
  process.env.PATH = root + ':' + path;
}

// Assign raw api to module
var rawApi;
try {
  rawApi = require('./build/Release/nodegit');
} catch (e) {
  rawApi = require('./build/Debug/nodegit');
}

// Set the exports prototype to the raw API.
exports.__proto__ = rawApi;

// Import extensions
require('./lib/commit.js');
require('./lib/blob.js');
require('./lib/object.js');
require('./lib/signature.js');
require('./lib/odb.js');
require('./lib/oid.js');
require('./lib/index.js');
require('./lib/repo.js');
require('./lib/reference.js');
require('./lib/revwalk.js');
require('./lib/tree.js');
require('./lib/diff_list.js');
require('./lib/tree_entry.js');
require('./lib/tree_builder.js');

// Set version
exports.version = require('./package').version;

// Initialize threads
exports.Threads.init();

Set the raw API directly.


'use strict'; // jshint ignore:line
let _ = require('lodash');

let JoiSequelize = function(model) {
  this._joi = {};
  this._model = model;
  this.sequelize = {
    define: require('./lib/define').bind(this)
  };
  this.datatypes = require('./lib/datatypes');
  model(this.sequelize, this.datatypes);
};

JoiSequelize.prototype.joi = function () {
  return this._joi;
};
JoiSequelize.prototype.omit = function () {
  if(!arguments.length) throw new Error('Omit must have params (arguments)');
  return _.omit(this._joi, arguments);
};
JoiSequelize.prototype.pick = function () {
  if(!arguments.length) throw new Error('Pick must have params (arguments)');
  return _.pick(this._joi, arguments);
};

module.exports = JoiSequelize;

'use strict'; // jshint ignore:line
let _ = require('lodash');

let JoiSequelize = function(model) {
  this._joi = {};
  this._model = model;
  this.sequelize = {
    define: require('./lib/define').bind(this)
  };
  this.datatypes = require('./lib/datatypes');
  model(this.sequelize, this.datatypes);
};

JoiSequelize.prototype.joi = function () {
  return this._joi;
};
JoiSequelize.prototype.omit = function () {
  if(!arguments.length) throw new Error('Omit must have params (arguments)');
  if(arguments[0] instanceof Array) return _.omit(this._joi, arguments[0]);
  else return _.omit(this._joi, arguments);
};
JoiSequelize.prototype.pick = function () {
  if(!arguments.length) throw new Error('Pick must have params (arguments)');
  if(arguments[0] instanceof Array) return _.pick(this._joi, arguments[0]);
  else return _.pick(this._joi, arguments);
};
JoiSequelize.prototype.include = function (o) {
  if(!o || !(o instanceof Object) )
    throw new Error('Pick must have params (arguments)');
  return _.merge(this._joi, o);
};

module.exports = JoiSequelize;

Change pick, omit to allow both array and list of params. Create function include.


/**
 * Randomly generate a boolean based on an input probability.
 * @private
 * @param {Number} chance Value between 0 and 1 representing 0% and 100%
 *   probabilities of a true value, respectively
 * @return {Boolean} Random value
 */
var pass = function (chance) {
	return Math.random() < chance;
};

/**
 * Generate an error to return to the client.
 * @private
 * @return {Error} Error object
 */
var getRandomError = function () {
	return new Error('Server error');
};

/**
 * Returns middleware which will simulate server errors being generated and sent
 *   back to the client.
 * @function express-simulate-errors
 * @param {Object=} options
 * @param {Number} [options.chance=1] Chance of a random error occurring
 * @return {Function} Middleware function
 */
module.exports = function (options) {
	if (!options) {
		options = {};
	}
	var chance = options.chance || 1;
	return function (req, res, next) {
		var passed = pass(chance);
		if (passed) {
			var err = getRandomError();
			next(err);
		}
		else {
			next();
		}
	};
};

/**
 * Randomly generate a boolean based on an input probability.
 * @private
 * @param {Number} chance Value between 0 and 1 representing 0% and 100%
 *   probabilities of a true value, respectively
 * @return {Boolean} Random value
 */
var pass = function (chance) {
	return Math.random() < chance;
};

/**
 * Generate an error to return to the client.
 * @private
 * @return {Error} Error object
 */
var getRandomError = function () {
	return new Error('Server error');
};

/**
 * Returns middleware which will simulate server errors being generated and sent
 *   back to the client.
 * @function express-simulate-errors
 * @param {Object=} options
 * @param {Number} [options.chance=1] Chance of a random error occurring
 * @return {Function} Middleware function
 */
module.exports = function (options) {
	if (!options) {
		options = {};
	}
	var chance = options.chance;
	if (chance !== 0 && !(chance > 0)) {
		chance = 1;
	}
	return function (req, res, next) {
		var passed = pass(chance);
		if (passed) {
			var err = getRandomError();
			next(err);
		}
		else {
			next();
		}
	};
};

Allow error chance of 0


'use strict';

const PluginError = require('plugin-error');
const rocambole = require('rocambole');
const through = require('through2');

module.exports = () => {
    let out = '';

    return through.obj((file, enc, cb) => {
        if (file.isNull()) {
            return cb(null, file);
        }

        if (file.isStream()) {
            return cb(new PluginError('gulp-debug-finder', 'Streaming not supported.'));
        }

        try {
            const contents = file.contents.toString();

            rocambole.moonwalk(contents, (node) => {
                const expression = node.callee || {};

                if (node.type !== 'CallExpression' || expression.type !== 'MemberExpression') {
                    return;
                }

                const maybeIdent = expression.object || {};

                if (maybeIdent.type === 'Identifier' && maybeIdent.name === 'console' && expression.property) {
                    out = 'There are JavaScript debug statements present.';
                }
            });
        } catch (e) {
            this.emit('error', new PluginError('gulp-debug-finder', e, {
                fileName: file.path
            }));
        }

        return cb();
    }, function (cb) {
        if (out) {
            this.emit('error', new PluginError('gulp-debug-finder', out, {
                showStack: false
            }));
        }

        return cb();
    });
};

'use strict';

const PluginError = require('plugin-error');
const rocambole = require('rocambole');
const through = require('through2');

module.exports = () => {
    let out = '';

    return through.obj(function (file, enc, cb) {
        if (file.isNull()) {
            return cb(null, file);
        }

        if (file.isStream()) {
            return cb(new PluginError('gulp-debug-finder', 'Streaming not supported.'));
        }

        try {
            const contents = file.contents.toString();

            rocambole.moonwalk(contents, (node) => {
                const expression = node.callee || {};

                if (node.type !== 'CallExpression' || expression.type !== 'MemberExpression') {
                    return;
                }

                const maybeIdent = expression.object || {};

                if (maybeIdent.type === 'Identifier' && maybeIdent.name === 'console' && expression.property) {
                    out = 'There are JavaScript debug statements present.';
                }
            });
        } catch (e) {
            this.emit('error', new PluginError('gulp-debug-finder', e, {
                fileName: file.path
            }));
        }

        return cb();
    }, function (cb) {
        if (out) {
            this.emit('error', new PluginError('gulp-debug-finder', out, {
                showStack: false
            }));
        }

        return cb();
    });
};

Fix this issue with arrow function


var connect = require('connect');
var faker = require('faker');
var serveStatic = require('serve-static');
var WebSocketServer = require('ws').Server;

connect().use(serveStatic(__dirname)).listen(8080);

var wss = new WebSocketServer({port: 6639});
wss.broadcast = function (data) {
  wss.clients.forEach(function (client) {
    client.send(data);
  });
};

wss.on('connection', function (ws) {
  var username = faker.internet.userName();
  wss.broadcast(username + ' joined!');
  ws.on('message', function (message) {
    wss.broadcast(username + ': ' + message);
  });
});

process.on('SIGINT', function() {
  wss.broadcast('Server going down NOW!');
  setTimeout(function () {
    process.exit();
  }, 100);
});

Handle Ctrl+C cleanly in the server.


"use strict";

var request = require('request');

module.exports = function herokuSelfPing(url, options) {
  if(!options) {
    options = {};
  }

  options.interval = options.interval || 45 * 1000 * 60;
  options.logger = options.logger || console.log;
  options.verbose = options.verbose || false;

  var isHeroku = require("is-heroku");

  if(!url) {
    options.verbose && options.logger("heroku-self-ping: no url provided. Exiting.");
    return false;
  }
  if(!isHeroku) {
    options.verbose && options.logger("heroku-self-ping: heroku not detected. Exiting.");

    return false;
  }

  options.verbose && options.logger("heroku-self-ping: Setting up hearbeat to " + url + " every " + options.interval + "ms.");

  return setInterval(function() {
    options.logger("heroku-self-ping: Sending hearbeat to " + url);
    request(url, function () {});
  }, options.interval);
};

"use strict";

var request = require('request');

module.exports = function herokuSelfPing(url, options) {
  if(!options) {
    options = {};
  }

  options.interval = options.interval || 20 * 1000 * 60;
  options.logger = options.logger || console.log;
  options.verbose = options.verbose || false;

  var isHeroku = require("is-heroku");

  if(!url) {
    options.verbose && options.logger("heroku-self-ping: no url provided. Exiting.");
    return false;
  }
  if(!isHeroku) {
    options.verbose && options.logger("heroku-self-ping: heroku not detected. Exiting.");

    return false;
  }

  options.verbose && options.logger("heroku-self-ping: Setting up hearbeat to " + url + " every " + options.interval + "ms.");

  return setInterval(function() {
    options.logger("heroku-self-ping: Sending hearbeat to " + url);
    request(url, function () {});
  }, options.interval);
};

Set defaut interval to 20 minutes


var path = require('path');

var escape = function (str) {
    return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
};

var regexp = ['lib', 'test'].map(function (i) {
    return '^' + escape(path.join(__dirname, i) + path.sep);
}).join('|');

require('6to5/register')({
    only:   new RegExp('(' + regexp + ')'),
    ignore: false
});
module.exports = require('./lib/postcss');

var path = require('path');

var escape = function (str) {
    return str.replace(/[\[\]\/{}()*+?.\\^$|-]/g, "\\$&");
};

var regexp = ['lib', 'test'].map(function (i) {
    return '^' + escape(path.join(__dirname, i) + path.sep);
}).join('|');

require('6to5/register')({
    only:   new RegExp('(' + regexp + ')'),
    ignore: false
});
module.exports = require('./lib/postcss');

Remove redundant escaping from regexp char class


//
// lambdash - AWS Lambda function to run shell commands
//
// See also: https://alestic.com/2014/11/aws-lambda-shell/
//
var AWS = require('aws-sdk');
var exec = require('child_process').exec;
var MAX_OUTPUT = 1024 * 1024 * 1024; // 1 GB
exports.handler = function(event, context) {
    var child = exec(event.command, {encoding: 'binary', maxBuffer: MAX_OUTPUT},
        function (error, stdout, stderr) {
            var result = {
                "stdout": new Buffer(stdout, 'binary').toString('base64'),
                "stderr": new Buffer(stderr, 'binary').toString('base64'),
                "error": error
            };
            context.succeed(result);
        }
    );
}

//
// lambdash - AWS Lambda function to run shell commands
//
// See also: https://alestic.com/2014/11/aws-lambda-shell/
//
process.env['PATH'] = process.env['PATH'] + ':' + process.cwd()
var AWS = require('aws-sdk');
var exec = require('child_process').exec;
var MAX_OUTPUT = 1024 * 1024 * 1024; // 1 GB
exports.handler = function(event, context) {
    var child = exec(event.command, {encoding: 'binary', maxBuffer: MAX_OUTPUT},
        function (error, stdout, stderr) {
            var result = {
                "stdout": new Buffer(stdout, 'binary').toString('base64'),
                "stderr": new Buffer(stderr, 'binary').toString('base64'),
                "error": error
            };
            context.succeed(result);
        }
    );
}

Add /var/task to $PATH for "aws" command.


'use strict';

/**
 * Admit One
 *
 */

import Auth from 'ember-tiny-auth';
import Authenticator from 'ember-admit-one/authenticator';
import Authorizer from 'ember-admit-one/authorizer';
import setup from 'ember-admit-one/setup';

var AdmitOne = {};

// expose everything in tiny-auth to admit-one users. we'll end up overwriting
// some of the pieces in here, but they'll serve the same purposes, and
// advanced uses should directly access tiny-auth.
Ember.$.extend(AdmitOne, Auth);

AdmitOne.Authenticator = Authenticator;
AdmitOne.Authorizer = Authorizer;
AdmitOne.setup = setup;

export default AdmitOne;

'use strict';

import Auth from 'ember-tiny-auth';
import Authenticator from 'ember-admit-one/authenticator';
import Authorizer from 'ember-admit-one/authorizer';
import setup from 'ember-admit-one/setup';

var AdmitOne = {};

// expose everything in tiny-auth to admit-one users. we'll end up overwriting
// some of the pieces in here, but they'll serve the same purposes, and
// advanced uses should directly access tiny-auth.
Ember.$.extend(AdmitOne, Auth);

AdmitOne.Authenticator = Authenticator;
AdmitOne.Authorizer = Authorizer;
AdmitOne.setup = setup;

export default AdmitOne;

Comment no longer really needed.


// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery3
//= require popper
//= require bootstrap-sprockets
//= require turbolinks
//= require select2
//= require_tree .

$(function () {
  $(document).on("mouseover", ".previewable_card_name", function() {
    var preview_link = $(this).data("preview-link");
    $(this).closest(".decklist").find(".card_picture_cell").hide();
    $(this).closest(".decklist").find(".card_picture_cell[data-preview='"+preview_link+"']").show();
  })

  $(".pack_selection select").select2();

  if (!('ontouchstart' in document.documentElement) && (document.location.hash === "")) {
    document.getElementById("q").focus();
  }
})

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery3
//= require popper
//= require bootstrap-sprockets
//= require turbolinks
//= require select2
//= require_tree .

$(function () {
  $(document).on("mouseover", ".previewable_card_name", function() {
    var preview_link = $(this).data("preview-link");
    $(this).closest(".decklist").find(".card_picture_cell").hide();
    $(this).closest(".decklist").find(".card_picture_cell[data-preview='"+preview_link+"']").show();
  })

  $(".pack_selection select").select2();

  if (!('ontouchstart' in document.documentElement) && (document.location.hash === "")) {
    document.getElementById("q").focus();
  }

  $('#deck_upload').on('change', function(){
    var fileName = $(this).val().split("\\").pop();
    $(this).next('.custom-file-label').html(fileName);
    $(this).closest("form").submit();
  })
})

Improve UX of deck upload button


// flow-typed signature: 3193712ae4ddd5385f359c7b985ac6fd
// flow-typed version: 1517003b23/react-hot-loader_v4.x.x/flow_>=v0.53.0

// @flow
declare module "react-hot-loader/root" {
    declare type Module = {
        id: string,
    };

    declare type AppContainerProps = {|
        children: React$Element<any>,
        errorBoundary?: boolean,
        errorReporter?: React$ComponentType<{
            error: Error,
            errorInfo: { componentStack: string },
        }>,
    |};

    declare export class AppContainer extends React$Component<AppContainerProps> {}

    declare export function hot(
        someModule: Module
    ): <T, W: React$ComponentType<T>>(
        wrappedComponent: W,
        props?: $Diff<AppContainerProps, { children: React$Element<any> }>
    ) => React$ComponentType<T>;
}

// flow-typed signature: 3193712ae4ddd5385f359c7b985ac6fd
// flow-typed version: 1517003b23/react-hot-loader_v4.x.x/flow_>=v0.53.0

declare module "react-hot-loader/root" {
    declare type AppContainerProps = {
        errorBoundary?: boolean,
        errorReporter?: React$ComponentType<{
            error: Error,
            errorInfo: { componentStack: string },
        }>,
    };

    declare export function hot(
        Component: React$ComponentType<any>,
        props?: AppContainerProps
    ): React$ComponentType<any>;
}

Add types for react-hot-loader package


var loadTemplate = require('../loadTemplate')

var config = require('config')

var federate = require('../federate')
var collateObjects = require('../collateObjects')

function PrefixesEndpoint(req, callback) {

    callback(null, 200, JSON.stringify(config.get('prefixes')))
}

module.exports = federate(PrefixesEndpoint, collateObjects)

var loadTemplate = require('../loadTemplate')

var config = require('config')

var federate = require('../federate')
var collateObjects = require('../collateObjects')

function PrefixesEndpoint(req, callback) {

    callback(null, 200, {
        mimeType: 'application/json',
        body: JSON.stringify(config.get('prefixes'))
    })

}

module.exports = federate(PrefixesEndpoint, collateObjects)

Update prefixes endpoint for federation


// need to subtract 2 from source maps
import 'source-map-support/register';

import chai from 'chai';
import fs from 'fs';
import os from 'os';
import path from 'path';
import pngToIcns from './../../lib/pngToIcns';

let assert = chai.assert;

// Prerequisite for test: to use OSX with sips, iconutil and imagemagick convert

function testConvertPng(pngName, done) {
    if (os.platform() !== 'darwin') {
        console.warn('Skipping png conversion tests, OSX is required');
        done();
    }
    pngToIcns(path.join(__dirname, '../../', 'test-resources', pngName), (error, icnsPath) => {
        if (error) {
            done(error);
            return;
        }

        let stat = fs.statSync(icnsPath);
        assert.isTrue(stat.isFile(), 'Output icns file should be a path');
        done();
    });
}

describe('Get Icon Module', function() {
    it('Can convert a rgb png to icns', function(done) {
        testConvertPng('iconSample.png', done);
    });

    it('Can convert a grey png to icns', function(done) {
        testConvertPng('iconSampleGrey.png', done);
    });
});

// need to subtract 2 from source maps
import 'source-map-support/register';

import chai from 'chai';
import fs from 'fs';
import os from 'os';
import path from 'path';
import pngToIcns from './../../lib/pngToIcns';

let assert = chai.assert;

// Prerequisite for test: to use OSX with sips, iconutil and imagemagick convert

function testConvertPng(pngName, done) {
    pngToIcns(path.join(__dirname, '../../', 'test-resources', pngName), (error, icnsPath) => {
        if (error) {
            done(error);
            return;
        }

        let stat = fs.statSync(icnsPath);
        assert.isTrue(stat.isFile(), 'Output icns file should be a path');
        done();
    });
}

describe('Get Icon Module', function() {
    it('Can convert icons', function() {
        if (os.platform() !== 'darwin') {
            console.warn('Skipping png conversion tests, OSX is required');
            return;
        }

        it('Can convert a rgb png to icns', function(done) {
            testConvertPng('iconSample.png', done);
        });

        it('Can convert a grey png to icns', function(done) {
            testConvertPng('iconSampleGrey.png', done);
        });
    });
});

Fix test for non darwin platforms


'use strict';

var bunyan = require('bunyan');

var logShim = require('thehelp-log-shim');

var logger = logShim('test:scenarios:bunyan');

logger.verbose('this is the default verbose text');

logger.info('this is the info text', {
  data: {
    yes: 'no'
  }
});

logger.warn('this is the warn text', function(arg1, arg2, arg3, arg4) {
  /*jshint unused: false*/
  logger.warn('warn callback!');
});

logger.error('this is the error %s', 'interpolation');

logShim.loadLogger = function(moduleName) {
  var logger = bunyan.createLogger({
    name: moduleName,
    level: 'debug'
  });

  logger.verbose = logger.debug;
  return logger;
};

var second = logShim('test:scenarios:bunyan:custom');

second.verbose('this is the custom verbose text');

'use strict';

var bunyan = require('bunyan');

var logShim = require('thehelp-log-shim');

var logger = logShim('test:scenarios:bunyan');

// this should not show up; the default level is info
logger.verbose('this is the default verbose text');

logger.info('this is the info text', {
  data: {
    yes: 'no'
  }
});

logger.warn('this is the warn text', function(arg1, arg2, arg3, arg4) {
  /*jshint unused: false*/
  logger.warn('warn callback!');
});

logger.error('this is the error %s', 'interpolation');

// monkey-patching to approximate global configuration
var create = bunyan.createLogger;
bunyan.createLogger = function(options) {
  options.level = 'debug';
  return create(options);
};

// this will show up
var second = logShim('test:scenarios:bunyan:custom');

second.verbose('this is the custom verbose text');

Update bunyan integration test: show monkey-patch


import Ember from 'ember';
import Move from 'ember-animated/motions/move';
import Scale from 'ember-animated/motions/scale';

export function * transition() {
  this.insertedSprites.forEach(sprite => {
    sprite.reveal();
  });

  this.keptSprites.forEach(sprite => {
    sprite.scale(sprite.initialBounds.width / sprite.finalBounds.width, sprite.initialBounds.height / sprite.finalBounds.height);
    this.animate(new Move(sprite));
    this.animate(new Scale(sprite));
  });
}

export default Ember.Controller.extend({
  transition
});

import Ember from 'ember';
import Move from 'ember-animated/motions/move';
import Scale from 'ember-animated/motions/scale';
import Opacity from 'ember-animated/motions/opacity';

export function * transition() {
  this.insertedSprites.forEach(sprite => {
    this.animate(new Opacity(sprite));
  });

  this.keptSprites.forEach(sprite => {
    sprite.scale(sprite.initialBounds.width / sprite.finalBounds.width, sprite.initialBounds.height / sprite.finalBounds.height);
    sprite.applyStyles({
      'z-index': 1
    });
    this.animate(new Move(sprite));
    this.animate(new Scale(sprite));
  });
}

export default Ember.Controller.extend({
  transition
});

Update Hero demo to support Opacity transition


class ModelController {
  constructor($rootScope, $parse) {
    this.$parse = $parse;
    this.listener = $rootScope.$on('render', (event, component) => {
      this.render(event, component);
    });
    this.settings = {
      mode: 'json',
      useWrapMode : true,
      // showGutter: false,
      onLoad: this.onEditorChange.bind(this)
    }
    this.$rootScope = $rootScope;
    this.inFirst = true;
  }

  $onDestroy() {
    this.listener();
  }

  onEditorChange(editor) {
    let session = editor.getSession();
    session.on("change", (e) => {
      this.broadcastModel(session.getValue());
    });
  }

  broadcastModel(model) {
    if (this.inFirst) {
      this.inFirst = false;
      return;
    }
    try {
      let v = JSON.parse(model);
      this.$rootScope.$broadcast('model', v);
    } catch(e) {}
  }

  render(event, component) {
    this.model = JSON.stringify(component.model, null, Number(4));
  }

}

export default ModelController;

class ModelController {
  constructor($rootScope, $parse) {
    this.$parse = $parse;
    this.listener = $rootScope.$on('render', (event, component) => {
      this.render(event, component);
    });
    this.settings = {
      mode: 'json',
      useWrapMode : true,
      // showGutter: false,
      onLoad: this.onEditorChange.bind(this),
      $blockScrolling: Infinity
    }
    this.$rootScope = $rootScope;
    this.inFirst = true;
  }

  $onDestroy() {
    this.listener();
  }

  onEditorChange(editor) {
    let session = editor.getSession();
    session.on("change", (e) => {
      this.broadcastModel(session.getValue());
    });
  }

  broadcastModel(model) {
    if (this.inFirst) {
      this.inFirst = false;
      return;
    }
    try {
      let v = JSON.parse(model);
      this.$rootScope.$broadcast('model', v);
    } catch(e) {}
  }

  render(event, component) {
    this.model = JSON.stringify(component.model, null, Number(4));
  }

}

export default ModelController;

Use Infinity blockScrolling for ace editor


import express from 'express'
import graphQLHTTP from 'express-graphql'
import { maskErrors } from 'graphql-errors'
import cors from 'cors'
import cookieParser from 'cookie-parser'
import dotenv from 'dotenv'
import debug from 'debug'

import sessionMiddleware from './sessionMiddleware'
import Schema from './schema'
import intlMiddleware from './intlMiddleware'
import Database from './data/Database'

dotenv.config()
const log = debug('graphql')
const PORT = process.env.PORT_GRAPHQL

const app = express()

const corsOptions = {
  origin: process.env.APP_ENDPOINT,
  methods: ['POST'],
  allowedHeaders: ['X-Requested-With', 'content-type'],
  credentials: true,
  optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
}

app.use(cors(corsOptions))
app.use(cookieParser())
app.use(intlMiddleware)
app.use(sessionMiddleware)

maskErrors(Schema)
app.use('/', graphQLHTTP(({ session, tokenData }, res) => ({
  graphiql: true,
  pretty: true,
  schema: Schema,
  context: { db: new Database() },
  rootValue: { session, tokenData, res },
})))

app.listen(PORT, () =>
  log(`GraphQL Server is now running on http://localhost:${PORT}`),
)

import express from 'express'
import graphQLHTTP from 'express-graphql'
import { maskErrors } from 'graphql-errors'
import cors from 'cors'
import helmet from 'helmet'
import cookieParser from 'cookie-parser'
import dotenv from 'dotenv'
import debug from 'debug'

import sessionMiddleware from './sessionMiddleware'
import Schema from './schema'
import intlMiddleware from './intlMiddleware'
import Database from './data/Database'

dotenv.config()
const log = debug('graphql')
const PORT = process.env.PORT_GRAPHQL

const app = express()

const corsOptions = {
  origin: process.env.APP_ENDPOINT,
  methods: ['POST'],
  allowedHeaders: ['X-Requested-With', 'content-type'],
  credentials: true,
  optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
}

app.use(helmet())
app.use(cors(corsOptions))
app.use(cookieParser())
app.use(intlMiddleware)
app.use(sessionMiddleware)

maskErrors(Schema)
app.use('/', graphQLHTTP(({ session, tokenData }, res) => ({
  graphiql: true,
  pretty: true,
  schema: Schema,
  context: { db: new Database() },
  rootValue: { session, tokenData, res },
})))

app.listen(PORT, () =>
  log(`GraphQL Server is now running on http://localhost:${PORT}`),
)

Use helmet on graphql server


'use strict';

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

class Slot extends Node {
    constructor(def) {
        super('Slot');

        this.generatorSlot = null;
    }

    generateCode(generator) {
        // At the time the code for this node is to be generated we instead
        // create a slot. A slot is just a marker in the output code stream
        // that we can later inject code into. The injection happens after
        // the entire tree has been walked.
        this.generatorSlot = generator.createSlot();
    }

    setContent(content) {
        this.generatorSlot.setContent(content, {
            statement: this.statement
        });
    }

    toJSON() {
        return {
            type: this.type
        };
    }
}

module.exports = Slot;

'use strict';

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

class Slot extends Node {
    constructor(def) {
        super('Slot');
        this.onDone = def.onDone;
        this.generatorSlot = null;
    }

    generateCode(generator) {
        if (this.onDone) {
            generator.onDone((generator) => {
                this.onDone(this, generator);
            });
        }
        // At the time the code for this node is to be generated we instead
        // create a slot. A slot is just a marker in the output code stream
        // that we can later inject code into. The injection happens after
        // the entire tree has been walked.
        this.generatorSlot = generator.beginSlot(this);
    }

    setContent(content) {
        this.generatorSlot.setContent(content);
    }

    toJSON() {
        return {
            type: this.type
        };
    }
}

module.exports = Slot;

Allow an onDone listener to be associated with a slot


/* eslint-disable no-undef */
import { fork, call, select } from 'redux-saga/effects';
import { isIos } from 'worona-deps';
import * as deps from '../deps';

function* redirectHome() {
  const contentType = yield select(deps.selectors.getContentType);
  if (contentType === 'Home') {
    const { type, category, page } = yield select(
      deps.selectorCreators.getSetting('theme', 'frontPage'),
    );
    if (type === 'category') {
      yield call(deps.libs.push, `?cat=${category}`);
    } else if (type === 'page') {
      yield call(deps.libs.push, `?page_id=${page}`);
    }
  }
}

function hideIosStatusBar() {
  console.log(isIos);
  console.log(StatusBar);
  if (isIos && StatusBar) {
    StatusBar.hide();
    console.log('hiden!');
  }
}

export default function* starterProSagas() {
  yield [
    fork(redirectHome),
    fork(hideIosStatusBar),
  ];
}

/* eslint-disable no-undef */
import { fork, call, select } from 'redux-saga/effects';
import { isIos } from 'worona-deps';
import * as deps from '../deps';

function* redirectHome() {
  const contentType = yield select(deps.selectors.getContentType);
  if (contentType === 'Home') {
    const { type, category, page } = yield select(
      deps.selectorCreators.getSetting('theme', 'frontPage'),
    );
    if (type === 'category') {
      yield call(deps.libs.push, `?cat=${category}`);
    } else if (type === 'page') {
      yield call(deps.libs.push, `?page_id=${page}`);
    }
  }
}

function hideIosStatusBar() {
  console.log(isIos);
  debugger;
  if (isIos && StatusBar) {
    StatusBar.hide();
    console.log('hiden!');
  }
}

export default function* starterProSagas() {
  yield [
    fork(redirectHome),
    fork(hideIosStatusBar),
  ];
}

Test if debugger work in safari


import React from 'react'
import { StackNavigator } from 'react-navigation'
import Home from './stateless/screens/Home'
import ReposList from './stateless/screens/ReposList'
// import SEARCH_QUERY from './stateless/screens/Home'

const Router = StackNavigator({
  Home: { screen: Home },
  ReposList: { screen: props => <ReposList {...props} searchQuery="react" /> }
  // ContributorsList: { ContributorsList }
})

export default Router

import React from 'react'
import { StackNavigator } from 'react-navigation'
import Home from './stateless/screens/Home'
import ReposList from './stateless/screens/ReposList'
import { SEARCH_QUERY } from './stateless/screens/Home'

const Router = StackNavigator({
  Home: { screen: Home },
  ReposList: { screen: props => <ReposList {...props} searchQuery={SEARCH_QUERY} /> }
  // ContributorsList: { ContributorsList }
})

export default Router

Fix prop returning class instead of value of prop


/* jshint node:true, strict:false */

var
  requirejs = require('requirejs'),

  jasmine = (function () {
    var Jasmine = require('jasmine');
    return new Jasmine({ projectBaseDir: require('path').resolve() });
  }()),

  $ = (function () {
    var
      doc = require('jsdom').jsdom(),
      window = doc.parentWindow;
    return require('jquery')(window);
  }()),

  Backbone = require('backbone'),

  specs = [
    'spec/bbctrl-button.spec.js',
    'spec/bbctrl-switch.spec.js',
    'spec/bbctrl-text-field.spec.js'
  ];

Backbone.$ = $;

requirejs.config({
  nodeRequire: require,
  baseUrl: 'lib',
  paths: {}
});

requirejs(specs, function () {
  jasmine.configureDefaultReporter({});
  jasmine.env.execute();
});

/* jshint node:true, strict:false */

var
  path = require('path'),

  requirejs = require('requirejs'),

  jasmine = (function () {
    var Jasmine = require('jasmine');
    return new Jasmine({ projectBaseDir: path.resolve() });
  }()),

  $ = (function () {
    var
      doc = require('jsdom').jsdom(),
      window = doc.parentWindow;
    return require('jquery')(window);
  }()),

  Backbone = require('backbone'),

  specs = [
    '../spec/bbctrl-button.spec',
    '../spec/bbctrl-switch.spec',
    '../spec/bbctrl-text-field.spec'
  ];

Backbone.$ = $;

requirejs.config({
  nodeRequire: require,
  baseUrl: path.resolve(__dirname, '../lib'),
  paths: {}
});

requirejs(specs, function () {
  jasmine.configureDefaultReporter({});
  jasmine.env.execute();
});

Make cli spec runner work regardless of cur. dir


import React, { Component } from 'react'
import { isArray, forEach } from 'lodash'
import qs from 'qs'
import Datasets from './Datasets'

const DISABLED_FILTERS = ['q', 'page', ]

export function _extractFilters(query) {
  let filters = []
  forEach(query, function(value, key) {
    if (DISABLED_FILTERS.includes(key)) {
      return
    }

    if (isArray(value)) {
      forEach(value, function(current) {
        filters.push({name: key, value: current})
      })
    } else {
      filters.push({name: key, value})
    }
  })

  return filters
}

export function parseQuery(query) {
  const parse = qs.parse(query)

  return {
    textInput: parse.q,
    page: parse.page,
    filters: _extractFilters(parse),
  }
}

class WrappedDatasets extends Component {
  constructor(props) {
    super(props)
    this.state = { query: parseQuery(this.props.location.query) }
  }

  render() {
    return <Datasets query={this.state.query}/>
  }
}

export default WrappedDatasets

import React, { Component } from 'react'
import { isArray, forEach } from 'lodash'
import qs from 'qs'
import Datasets from './Datasets'

const DISABLED_FILTERS = [ 'q', 'offset', 'limit' ]

export function _extractFilters(query) {
  let filters = []
  forEach(query, function(value, key) {
    if (DISABLED_FILTERS.includes(key)) {
      return
    }

    if (isArray(value)) {
      forEach(value, function(current) {
        filters.push({name: key, value: current})
      })
    } else {
      filters.push({name: key, value})
    }
  })

  return filters
}

export function parseQuery(query) {
  const parse = qs.parse(query)

  return {
    textInput: parse.q,
    page: parse.page,
    filters: _extractFilters(parse),
  }
}

class WrappedDatasets extends Component {
  constructor(props) {
    super(props)
    this.state = { query: parseQuery(this.props.location.query) }
  }

  render() {
    return <Datasets query={this.state.query}/>
  }
}

export default WrappedDatasets

Remove page, add offset and limit to filtered filters


import React from 'react';
import Markdown from 'react-markdown';
import styles from 'components/Paragraph/paragraph.scss';

const MarkdownParagraph = ({ text, noMargin }) => (
  <Markdown
    className={styles[noMargin ? 'no-margin' : 'root']}
    disallowedTypes={['HtmlInline', 'HtmlBlock', 'Image']}
    source={text} />
);

export default MarkdownParagraph;

import React from 'react';
import Markdown from 'react-markdown';
import styles from 'components/Paragraph/paragraph.scss';

const MarkdownParagraph = ({ text, noMargin }) => (
  <Markdown
    className={styles[noMargin ? 'no-margin' : 'root']}
    disallowedTypes={['HtmlBlock', 'Image']}
    source={text} />
);

export default MarkdownParagraph;

Allow inline HTML links for markdown


export const colors = {
  blue: "#3B7A9E",
  lightBlue: "#61BDE0",
  darkBlue: "#5F7682",
  grey: "#999999",
  darkGrey: "#666666",
  lightGrey: "#c3c3c3",
  lighterGrey: "#f5f5f5",
  red: "#D0021B",
  orange: "#FDBD56",
  black: "#333333",
  white: "#FFFFFF"
};

colors.primary = colors.blue;
colors.secondary = colors.darkBlue;
colors.tertiary = colors.orange;
colors.text = colors.black;
colors.textLight = colors.darkGrey;
colors.borders = colors.grey;
colors.bordersLight = colors.lightGrey;

export const radius = "4px";
export const shadow = "0 2px 4px rgba(0,0,0,0.1)";

export default {
  colors,
  radius,
  shadow
};

export const colors = {
  blue: "#3B7A9E",
  lightBlue: "#61BDE0",
  darkBlue: "#5F7682",
  grey: "#999999",
  darkGrey: "#666666",
  lightGrey: "#c3c3c3",
  lightMediumGrey: "#E4E8EB",
  lighterGrey: "#f5f5f5",
  red: "#D0021B",
  orange: "#FDBD56",
  black: "#333333",
  white: "#FFFFFF"
};

colors.primary = colors.blue;
colors.secondary = colors.darkBlue;
colors.tertiary = colors.orange;
colors.text = colors.black;
colors.textLight = colors.darkGrey;
colors.borders = colors.grey;
colors.bordersLight = colors.lightGrey;

export const radius = "4px";
export const shadow = "0 2px 4px rgba(0,0,0,0.1)";

export default {
  colors,
  radius,
  shadow
};

Add light medium grey colour to constants


// Mocha Specification Suite

// Imports
import { assertDeepStrictEqual } from 'assert-deep-strict-equal';
import { readdirSync } from 'fs';

////////////////////////////////////////////////////////////////////////////////////////////////////
describe('The current files', () => {

   it('for the "dist" folder are correct', () => {
      const actual = readdirSync('dist').sort();
      const expected = [
         'hamburger-menu.css',
         'hamburger-menu.js',
         'hamburger-menu.min.css',
         'hamburger-menu.min.js',
         ];
      assertDeepStrictEqual(actual, expected);
      });

   it('for the "docs" folder are correct', () => {
      const actual = readdirSync('docs').sort();
      const expected = [
         'CNAME',
         'app.js',
         'hamburger-menu.css',
         'hamburger-menu.js',
         'index.html',
         'multipage',
         'single-page-app',
         'style.css',
         ];
      assertDeepStrictEqual(actual, expected);
      });

   });

// Mocha Specification Suite

// Imports
import { assertDeepStrictEqual } from 'assert-deep-strict-equal';
import fs from 'fs';

////////////////////////////////////////////////////////////////////////////////////////////////////
describe('The current files', () => {

   it('for the "dist" folder are correct', () => {
      const actual = fs.readdirSync('dist').sort();
      const expected = [
         'hamburger-menu.css',
         'hamburger-menu.js',
         'hamburger-menu.min.css',
         'hamburger-menu.min.js',
         ];
      assertDeepStrictEqual(actual, expected);
      });

   it('for the "docs" folder are correct', () => {
      const actual = fs.readdirSync('docs').sort();
      const expected = [
         'CNAME',
         'app.js',
         'hamburger-menu.css',
         'hamburger-menu.js',
         'index.html',
         'multipage',
         'single-page-app',
         'style.css',
         ];
      assertDeepStrictEqual(actual, expected);
      });

   });

Switch import for better auto completion


'use strict';

var questionView = {
  $getOptions: $( '.get-options' ),
  $followupNoNotSure: $( '.followup__no-not-sure' ),
  $followupYes: $( '.followup__yes' ),
  $nextSteps: $( '.next-steps' ),
  $feedback: $( '.feedback' ),

  init: function() {
    this.$followupNoNotSure.hide();
    this.$followupYes.hide();
    this.$getOptions.hide();
    this.$nextSteps.hide();
    this.$feedback.hide();
    this.bigQuestionListener();
  },

  bigQuestionListener: function() {
    $( '.question_answers > .btn' ).on( 'click', function() {
      if ( $( this ).attr( 'id' ) === 'question_answer-yes' ) {
        questionView.$followupYes.show();
        questionView.$followupNoNotSure.hide();
      } else {
        questionView.$followupNoNotSure.show();
        questionView.$followupYes.hide();
      }
      // Show the rest of the page with a 600 millisecond animation.
      questionView.$getOptions.show( 'slow' );
      questionView.$nextSteps.show( 'slow' );
      questionView.$feedback.show( 'slow' );
    } );
  }

};

module.exports = questionView;

'use strict';

var questionView = {
  $getOptions: $( '.get-options' ),
  $followupNoNotSure: $( '.followup__no-not-sure' ),
  $followupYes: $( '.followup__yes' ),
  $nextSteps: $( '.next-steps' ),
  $feedback: $( '.feedback' ),

  init: function() {
    this.bigQuestionListener();
  },

  bigQuestionListener: function() {
    $( '.question_answers > .btn' ).on( 'click', function() {
      if ( $( this ).attr( 'id' ) === 'question_answer-yes' ) {
        questionView.$followupYes.show();
        questionView.$followupNoNotSure.hide();
      } else {
        questionView.$followupNoNotSure.show();
        questionView.$followupYes.hide();
      }
      // Show the rest of the page with a 600 millisecond animation.
      questionView.$getOptions.show( 'slow' );
      questionView.$nextSteps.show( 'slow' );
      questionView.$feedback.show( 'slow' );
    } );
  }

};

module.exports = questionView;

Remove hide()s on page load (already done with CSS)


require('colors');

module.exports = {
    plugins: [
        require('postcss-preset-env')({
            warnForDuplicates: false,
            features: {
                rem: {
                    html: false
                },
                calc: false
            }
        }),
        require('postcss-fixes')({ preset: 'safe' }),
        require('list-selectors').plugin((list) => {
            console.log('\n\n');

            list.simpleSelectors.ids.forEach((id) => {
                console.log(`${'ID'.red}:\n\n    ${id} {\n        . . .\n    }`);
            });

            console.log('\n');
        }),
        require('doiuse')(require('./doiuse.config.js')),
        require('cssnano')({
            discardComments: {
                removeAll: true
            }
        })
    ]
};

require('colors');

module.exports = {
    plugins: [
        require('postcss-preset-env')({
            warnForDuplicates: false,
            features: {
                rem: {
                    html: false
                },
                calc: false
            }
        }),
        require('postcss-fixes')({ preset: 'safe' }),
        require('list-selectors').plugin((list) => {
            console.log('\n\n');

            list.simpleSelectors.ids.forEach((id) => {
                console.log(`${'ID'.red}:\n\n    ${id} {\n        . . .\n    }`);
            });

            console.log('\n');
        }),
        require('doiuse')(require('./doiuse.config.js')),
        require('cssnano')({
            preset: ['default', {
                discardComments: {
                    removeAll: true
                }
            }]
        })
    ]
};

Fix preset options for cssnano


function noop() {}

export default class TransientManager {
	constructor(name, onTransChange=noop) {
		this.name = name;
		this.onTransChange = onTransChange;
	}

	lock(trans) {
		if (trans === this.trans) { return this.transId }

		// unlock old transient (if exists)
		this.release();

		if (trans) {
			this.trans = trans;
			this.transLock = trans.lock(this.name);
			this.transId = trans.id;

			this.onTransChange(this.transId, trans);
		}
	}

	object() {
		const transObj = this.trans && this.trans.isActive() && this.trans._();

		return transObj && transObj.data;
	}

	release() {
		if (this.trans) {
			this.transLock.release();

			this.trans = null;
			this.transLock = null;
			this.transId = null;

			this.onTransChange(null, null);
		}
	}
}

function noop() {}

export default class TransientManager {
	constructor(name, onTransChange=noop) {
		this.name = name;
		this.onTransChange = onTransChange;
	}

	lock(trans) {
		if (trans === this.trans) { return this.transId }

		// unlock old transient (if exists)
		this.release();

		if (trans) {
			this.trans = trans;
			this.transLock = trans.lock(this.name);
			this.transId = trans.id;

			// give react and f.lux a chance to settle
			setTimeout( () => this.onTransChange(this.transId, trans) );
		}
	}

	object() {
		const transObj = this.trans && this.trans.isActive() && this.trans._();

		return transObj && transObj.data;
	}

	release() {
		if (this.trans) {
			this.transLock.release();

			this.trans = null;
			this.transLock = null;
			this.transId = null;

			// give react and f.lux a chance to settle
			setTimeout( () => this.onTransChange(null, null) );
		}
	}
}

Add delay in notifying about transient change


import React from 'react';
import SearchBar from 'react-search-bar';

export default React.createClass({
  getInitialState: function () {
    return {
      users: [
        'leo',
        'bill',
        'barbara',
        'christine',
        'allie',
        'lucille',
        'glenn',
        'jeanette',
        'madge',
        'winifred']
    };
  },
  onChange: function (input, resolve) {
    resolve(this.state.users.filter((suggestion) => {
      return suggestion.toLowerCase().startsWith(input.toLowerCase());
    }));
  },
  onSubmit: function (input) {
    this.props.history.push('/' + input);
  },

  render: function () {
    return (
      <SearchBar
        placeholder="Search for a user"
        onChange={this.onChange}
        onSubmit={this.onSubmit} />
    );
  }
});

import React from 'react';
import R from 'ramda';
import SearchBar from 'react-search-bar';

export default React.createClass({
  getInitialState: function () {
    return {
      users: [],
      names: []
    };
  },
  componentDidMount: function () {
    let component = this;
    if (process.env.NODE_ENV === 'development') {
      fetch('http://104.236.25.175:3000/users')
      .then(function (response) {
        if (response.status >= 400) {
          throw new Error('Bad response');
        }
        return response.json();
      })
      .then(function (users) {
        var names = R.map(R.prop('name'), users);
        if (component.isMounted()) {
          component.setState({
            users: users,
            names: names
          });
        }
      });
    }
  },
  onChange: function (input, resolve) {
    resolve(this.state.names.filter((suggestion) => {
      return suggestion.toLowerCase().startsWith(input.toLowerCase());
    }));
  },
  onSubmit: function (input) {
    var user = R.find(R.propEq('name', input))(this.state.users);
    this.props.history.push('/' + user.id);
  },

  render: function () {
    return (
      <SearchBar
        placeholder="Search for a user"
        onChange={this.onChange}
        onSubmit={this.onSubmit} />
    );
  }
});

Add API to users page


import Actions from '../actions/actions';
import mapStore from '../stores/mapStore';
import { ModeConstants } from '../constants/constants';

export default class Marker {
  constructor(latLng, icon) {
    this.icon = icon;
    let options = {
      icon,
      draggable: true
    }
    let marker = new L.Marker(latLng, options);
    return this.withHooks(marker);
  }

  withHooks(marker) {
    let iconMargin = 0;
    let icon = this.icon;

    marker.on('click', (e) => {
      e.target.getPopup()._isOpen = false;
      $(e.target.getPopup()._container).css('display', 'block');
    })

    marker.on('add', () => {
      $('.leaflet-popup').css('display', 'none')
    });

    marker.on('dragstart', (e) => {
      let icon = e.target._icon;

      if (!iconMargin) {
        iconMargin = parseInt(L.DomUtil.getStyle(icon, 'marginTop'));
      }
      icon.style.marginTop = (iconMargin - 15)  + 'px';
    });

    marker.on('dragend', (e) => {
      e.target._icon.style.marginTop = iconMargin + 'px';
    });

    return marker;
  }
}

import L from 'leaflet';
import Actions from '../actions/actions';
import mapStore from '../stores/mapStore';
import { ModeConstants } from '../constants/constants';

export default class Marker {
  constructor(latLng, icon) {
    this.icon = icon;
    let options = {
      icon,
      draggable: true
    }
    let marker = new L.Marker(latLng, options);
    return this.withHooks(marker);
  }

  withHooks(marker) {
    let iconMargin = 0;
    let icon = this.icon;
    let mode = null;
    let map = mapStore.getState().map;

    marker.on('click', (e) => {
      e.target.getPopup()._isOpen = false;
      $(e.target.getPopup()._container).css('display', 'block');
    })

    marker.on('add', () => {
      $('.leaflet-popup').css('display', 'none')
    });

    marker.on('dragstart', (e) => {
      let icon = e.target._icon;

      mode = mapStore.getState().mode;
      Actions.changeMode(ModeConstants);

      if (!iconMargin) {
        iconMargin = parseInt(L.DomUtil.getStyle(icon, 'marginTop'));
      }
      icon.style.marginTop = `${iconMargin - 15}px`;
    });

    marker.on('dragend', (e) => {
      let map = mapStore.getState().map;
      e.target._icon.style.marginTop = `${iconMargin}px`;

      map.once('click', () => {
        Actions.changeMode(mode);
      })
    });

    return marker;
  }
}

Fix icon bounding box drag.


import Actions from '../actions/actions';
import mapStore from '../stores/mapStore';
import { ModeConstants } from '../constants/constants';

export default class Marker {
  constructor(latLng, icon) {
    this.icon = icon;
    let options = {
      icon,
      draggable: true
    }
    let marker = new L.Marker(latLng, options);
    return this.withHooks(marker);
  }

  withHooks(marker) {
    let iconMargin = 0;
    let icon = this.icon;

    marker.on('click', (e) => {
      e.target.getPopup()._isOpen = false;
      $(e.target.getPopup()._container).css('display', 'block');
    })

    marker.on('add', () => {
      $('.leaflet-popup').css('display', 'none')
    });

    marker.on('dragstart', (e) => {
      let icon = e.target._icon;

      if (!iconMargin) {
        iconMargin = parseInt(L.DomUtil.getStyle(icon, 'marginTop'));
      }

      icon.style.marginTop = (iconMargin - 15)  + 'px';
    });

    marker.on('dragend', (e) => {
      e.target._icon.style.marginTop = iconMargin + 'px';
    });

    return marker;
  }
}

import L from 'leaflet';
import Actions from '../actions/actions';
import mapStore from '../stores/mapStore';
import { ModeConstants } from '../constants/constants';

export default class Marker {
  constructor(latLng, icon) {
    this.icon = icon;
    let options = {
      icon,
      draggable: true
    }
    let marker = new L.Marker(latLng, options);
    return this.withHooks(marker);
  }

  withHooks(marker) {
    let iconMargin = 0;
    let icon = this.icon;
    let mode = null;
    let map = mapStore.getState().map;

    marker.on('click', (e) => {
      e.target.getPopup()._isOpen = false;
      $(e.target.getPopup()._container).css('display', 'block');
    })

    marker.on('add', () => {
      $('.leaflet-popup').css('display', 'none')
    });

    marker.on('dragstart', (e) => {
      let icon = e.target._icon;

      mode = mapStore.getState().mode;
      Actions.changeMode(ModeConstants);

      if (!iconMargin) {
        iconMargin = parseInt(L.DomUtil.getStyle(icon, 'marginTop'));
      }

      icon.style.marginTop = `${iconMargin - 15}px`;
    });

    marker.on('dragend', (e) => {
      let map = mapStore.getState().map;
      e.target._icon.style.marginTop = `${iconMargin}px`;

      map.once('click', () => {
        Actions.changeMode(mode);
      })
    });

    return marker;
  }
}

Fix dragging on icon bounding box.


define(['leaflet'], function(L) {
    L.Map.prototype._originalGetBounds = L.Map.prototype.getBounds;
    return L;
});

define(['leaflet'], function(L) {
  L.Map.prototype._originalGetBounds = L.Map.prototype.getBounds

  L.Path.prototype.onRemove = function(map) {
    map
      .off('viewreset', this.projectLatlngs, this)
      .off('moveend', this._updatePath, this)

    if (this.options.clickable) {
      // This override removes the following line from original method:
      // this._map.off('click', this._onClick, this);
      this._map.off('mousemove', this._onMouseMove, this)
    }

    this._requestUpdate()

    this.fire('remove')
    this._map = null
  }

  return L
})

Add override for Leaflet Path::onRemove to work around click handlers being removed


const express = require('express')
const router = express.Router()
const api = require('./api')

const { FACEBOOK_APP_ID, APP_URL, APP_TITLE, APP_DESCRIPTION, APP_IMAGE } = process.env

/* GET Home page. */
router.get('/', function (req, res) {
  res.render('index', { FACEBOOK_APP_ID, APP_URL, APP_TITLE, APP_DESCRIPTION, APP_IMAGE })
})

router.use('/api', api)

module.exports = router

const express = require('express')
const router = express.Router()
const api = require('./api')

const { FACEBOOK_APP_ID, APP_URL, APP_TITLE, APP_DESCRIPTION, APP_IMAGE } = process.env

/* GET Home page. */
router.get('/', function (req, res) {
  const name = req.query.name
  if (name) {
    return res.render('index', {
      FACEBOOK_APP_ID,
      APP_URL  : `${APP_URL}${req.url}`,
      APP_TITLE: `${name} on ${APP_TITLE}`,
      APP_DESCRIPTION,
      APP_IMAGE
    })
  }

  res.render('index', { FACEBOOK_APP_ID, APP_URL, APP_TITLE, APP_DESCRIPTION, APP_IMAGE })
})

router.use('/api', api)

module.exports = router

Update meta data for query a name


import express from 'express';
const router  = express.Router();

//home
router.get('/', function(req, res) {
    res.render('index', {
      header: 'express-boilerplate',
      description: 'Your basic web-app structure.'
    });
});

module.exports = router;

import express from 'express';
const router  = express.Router();

//home
router.get('/', function(req, res) {
    res.render('index', {
      title: 'express-boilerplate',
      header: 'express-boilerplate',
      description: 'Your basic web-app structure.'
    });
});

module.exports = router;

Add title to home route