// @ngInject
export default class LanguageUtilsService {
  // @ngInject
  constructor($translate, $http, ENV) {
    this.$translate = $translate;
    this.$http = $http;
    this.ENV = ENV;
  }

  getCurrentLanguage() {
    return this.current;
  }

  setCurrentLanguage(language) {
    this.current = language;
    this.$translate.use(language.code);
    this.$http.defaults.headers.common['Accept-Language'] = language.code;
  }

  checkLanguage() {
    // Check if current language is listed in choices and
    // switch to default language if current choice is invalid.
    const key = this.$translate.storageKey();
    const storage = this.$translate.storage();
    const code = storage.get(key);
    const current = this.findLanguageByCode(code) || this.findLanguageByCode(this.ENV.defaultLanguage);
    this.setCurrentLanguage(current);
  }

  findLanguageByCode(code) {
    if (!code) {
      return;
    }
    return this.getChoices().filter(language => language.code === code)[0];
  }

  getChoices() {
    return this.ENV.languageChoices;
  }
}

// @ngInject
export default class LanguageUtilsService {
  // @ngInject
  constructor($translate, $http, ENV) {
    this.$translate = $translate;
    this.$http = $http;
    this.ENV = ENV;
  }

  getCurrentLanguage() {
    return this.current;
  }

  setCurrentLanguage(language) {
    this.current = language;
    this.$translate.use(language.code);
    moment.locale(language.code);
    this.$http.defaults.headers.common['Accept-Language'] = language.code;
  }

  checkLanguage() {
    // Check if current language is listed in choices and
    // switch to default language if current choice is invalid.
    const key = this.$translate.storageKey();
    const storage = this.$translate.storage();
    const code = storage.get(key);
    const current = this.findLanguageByCode(code) || this.findLanguageByCode(this.ENV.defaultLanguage);
    this.setCurrentLanguage(current);
  }

  findLanguageByCode(code) {
    if (!code) {
      return;
    }
    return this.getChoices().filter(language => language.code === code)[0];
  }

  getChoices() {
    return this.ENV.languageChoices;
  }
}

Update momentjs locale globally when language is switched.


/**
 * Module dependencies.
 */

var Base = require('./base')
  , cursor = Base.cursor
  , color = Base.color;

/**
 * Expose `TAP`.
 */

exports = module.exports = TAP;

/**
 * Initialize a new `TAP` reporter.
 *
 * @param {Runner} runner
 * @api public
 */

function TAP(runner) {
  Base.call(this, runner);

  var self = this
    , stats = this.stats
    , total = runner.total
    , n = 1;

  runner.on('start', function(){
    console.log('%d..%d', 1, total);
  });

  runner.on('test end', function(){
    ++n;
  });

  runner.on('pending', function(test){
    console.log('ok %d %s # SKIP -', n, title(test));
  });

  runner.on('pass', function(test){
    console.log('ok %d %s', n, title(test));
  });

  runner.on('fail', function(test, err){
    console.log('not ok %d %s', n, title(test));
    console.log(err.stack.replace(/^/gm, '  '));
  });
}

/**
 * Return a TAP-safe title of `test`
 *
 * @param {Object} test
 * @return {String}
 * @api private
 */

function title(test) {
  return test.fullTitle().replace(/#/g, '');
}

/**
 * Module dependencies.
 */

var Base = require('./base')
  , cursor = Base.cursor
  , color = Base.color;

/**
 * Expose `TAP`.
 */

exports = module.exports = TAP;

/**
 * Initialize a new `TAP` reporter.
 *
 * @param {Runner} runner
 * @api public
 */

function TAP(runner) {
  Base.call(this, runner);

  var self = this
    , stats = this.stats
    , n = 1;

  runner.on('start', function(){
    var total = runner.grepTotal(runner.suite);
    console.log('%d..%d', 1, total);
  });

  runner.on('test end', function(){
    ++n;
  });

  runner.on('pending', function(test){
    console.log('ok %d %s # SKIP -', n, title(test));
  });

  runner.on('pass', function(test){
    console.log('ok %d %s', n, title(test));
  });

  runner.on('fail', function(test, err){
    console.log('not ok %d %s', n, title(test));
    console.log(err.stack.replace(/^/gm, '  '));
  });
}

/**
 * Return a TAP-safe title of `test`
 *
 * @param {Object} test
 * @return {String}
 * @api private
 */

function title(test) {
  return test.fullTitle().replace(/#/g, '');
}

Fix to TAP output when grep is used to filter out tests: these tests should not contribute to the overall total in the TAP plan.


'use babel';

export default class AnnotationOverlayView {
  constructor({type, source, row, column, message}) {
    this.element = document.createElement('div');
    this.element.className = 'levels-view annotation-overlay';
    this.element.style.display = 'none';

    this.headerDiv = document.createElement('div');
    this.headerDiv.className = 'header';
    this.element.appendChild(this.headerDiv);

    this.typeSpan = document.createElement('span');
    this.typeSpan.className = `type badge badge-flexible badge-${type}`;
    this.typeSpan.textContent = type;
    this.headerDiv.appendChild(this.typeSpan);

    this.positionSpan = document.createElement('span');
    this.positionSpan.className = 'position';
    this.positionSpan.textContent = `at line ${row}${column ? `, column ${column}` : ''}`;
    this.headerDiv.appendChild(this.positionSpan);

    this.sourceSpan = document.createElement('span');
    this.sourceSpan.className = 'source';
    this.sourceSpan.textContent = source;
    this.headerDiv.appendChild(this.sourceSpan);

    this.element.appendChild(document.createElement('hr'));

    this.messageDiv = document.createElement('div');
    this.messageDiv.textContent = message;
    this.element.appendChild(this.messageDiv);
  }

  show() {
    this.element.style.display = '';
  }

  hide() {
    this.element.style.display = 'none';
  }
}

'use babel';

export default class AnnotationOverlayView {
  constructor({type, source, row, column, message}) {
    this.element = document.createElement('div');
    this.element.className = 'levels-view annotation-overlay';
    this.element.style.display = 'none';

    const headerDiv = document.createElement('div');
    headerDiv.className = 'header';
    this.element.appendChild(headerDiv);

    const typeSpan = document.createElement('span');
    typeSpan.className = `type badge badge-flexible badge-${type}`;
    typeSpan.textContent = type;
    headerDiv.appendChild(typeSpan);

    const positionSpan = document.createElement('span');
    positionSpan.className = 'position';
    positionSpan.textContent = `at line ${row}${column ? `, column ${column}` : ''}`;
    headerDiv.appendChild(positionSpan);

    const sourceSpan = document.createElement('span');
    sourceSpan.className = 'source';
    sourceSpan.textContent = source;
    headerDiv.appendChild(sourceSpan);

    this.element.appendChild(document.createElement('hr'));

    const messageDiv = document.createElement('div');
    messageDiv.textContent = message;
    this.element.appendChild(messageDiv);
  }

  show() {
    this.element.style.display = '';
  }

  hide() {
    this.element.style.display = 'none';
  }
}

Remove properties for HTML elements in the annotation overlay view


var gulp = require('gulp');
var react = require('gulp-react');
var git = require('gulp-git');
var fs = require('fs');
var shell = require('gulp-shell')

gulp.task('brew', function () {
  if (fs.existsSync('homebrew')) {
    git.pull('origin', 'master', { cwd: 'homebrew' }, function (err) {
      if (err) throw err;
    });
  } else {
    git.clone('https://github.com/Homebrew/homebrew', function (err) {
      if (err) throw err;
    });
  }
});

gulp.task('collect', shell.task([
  './bin/collect homebrew'
]));

gulp.task('info', shell.task([
  './bin/info homebrew'
]));

gulp.task('rank', shell.task([
  './bin/rank'
]));

gulp.task('dump', shell.task([
  './bin/dump > dist/formulae.json'
]));

gulp.task('copy', function () {
  return gulp.src('src/index.html')
    .pipe(gulp.dest('dist'))
})

gulp.task('default', [ 'brew', 'collect', 'info', 'rank', 'dump', 'copy' ], function () {
  return gulp.src('src/index.js')
    .pipe(react())
    .pipe(gulp.dest('dist'));
});

var gulp = require('gulp');
var react = require('gulp-react');
var git = require('gulp-git');
var fs = require('fs');
var shell = require('gulp-shell')

gulp.task('brew', function () {
  if (fs.existsSync('homebrew')) {
    git.pull('origin', 'master', { cwd: 'homebrew' }, function (err) {
      if (err) throw err;
    });
  } else {
    git.clone('https://github.com/Homebrew/homebrew', function (err) {
      if (err) throw err;
    });
  }
});

gulp.task('collect', shell.task([
  './bin/collect homebrew'
]));

gulp.task('info', shell.task([
  './bin/info homebrew'
]));

gulp.task('rank', shell.task([
  './bin/rank'
]));

gulp.task('dump', shell.task([
  './bin/dump > dist/formulae.json'
]));

gulp.task('copy', function () {
  return gulp.src('src/index.html')
    .pipe(gulp.dest('dist'))
})

gulp.task('react', function () {
  return gulp.src('src/index.js')
    .pipe(react())
    .pipe(gulp.dest('dist'));
})

gulp.task('default', [ 'brew', 'collect', 'info', 'rank', 'dump', 'copy', 'react' ], function () {
});

Refactor react gulp code into its own task


const gulp = require('gulp');
const babel = require('gulp-babel');

gulp.task('cp', function() {
  gulp.src([
      'app/**/!(*.js)',
      'MIT-LICENSE.txt',
      'README.md',
    ])
    .pipe(gulp.dest('dist'));
});

gulp.task('cp-lib', function() {
  gulp.src('app/lib/**')
    .pipe(gulp.dest('dist/lib'));
});

gulp.task('js', function() {
  var presets = ['es2015', 'react'];

  gulp.src('app/*.js')
    .pipe(babel({
      presets: presets,
    }))
    .pipe(gulp.dest('dist'));

  gulp.src('app/js/*.js')
    .pipe(babel({
      presets: presets,
    }))
    .pipe(gulp.dest('dist/js'));
});

gulp.task('default', ['cp', 'cp-lib', 'js']);

const gulp = require('gulp');
const babel = require('gulp-babel');

// Copy all files except for *.js ones
gulp.task('cp', function() {
  gulp.src([
      'app/**/!(*.js)',
      'MIT-LICENSE.txt',
      'README.md',
    ])
    .pipe(gulp.dest('dist'));
});

// Copy the entire lib/ directory; it's vendor, 3rd party stuff
gulp.task('cp-lib', function() {
  gulp.src('app/lib/**')
    .pipe(gulp.dest('dist/lib'));
});

// Compile app JS with babel
gulp.task('js', function() {
  var presets = ['es2015', 'react'];

  gulp.src('app/*.js')
    .pipe(babel({
      presets: presets,
    }))
    .pipe(gulp.dest('dist'));

  gulp.src('app/js/*.js')
    .pipe(babel({
      presets: presets,
    }))
    .pipe(gulp.dest('dist/js'));
});

gulp.task('default', ['cp', 'cp-lib', 'js']);

Annotate build steps for clarity


define([
	'Modernizr',
	'jQuery',
], function()
{

	function Header()
	{
		this._$body = $('body');
		this._$win = $(window);
		this._$header = $('.js-header');
	};

	Header.prototype.init = function()
	{
		//this.setStatus();
		this.formInit();
	}

	Header.prototype.setStatus = function()
	{
		var self = this;

		var checkIsXsmall = function()
		{
			if (self._$win.width() <= 768)
			{
				self._$header.removeClass('is-fixed');
				self._$body.removeAttr('style');
				return true;
			}
		}

		var update = function()
		{
			if(checkIsXsmall()) return;

			self._$header.addClass('is-fixed');
			self._$body.css({'padding-top': self._$header.height() + 'px'});
		}

		update();

		self._$win.on('resize.header', function(){
			update();
		});
	}

	Header.prototype.formInit = function()
	{
		console.log('form init');
	}

	return Header;
});

define([
	'jQuery',
], function()
{

	function Header()
	{
		this._$body = $('body');
		this._$win = $(window);
		this._$header = $('.js-header');
	};

	Header.prototype.init = function()
	{
		//this.setStatus();
		this.formInit();
	}

	Header.prototype.setStatus = function()
	{
		var self = this;

		var checkIsXsmall = function()
		{
			if (self._$win.width() <= 768)
			{
				self._$header.removeClass('is-fixed');
				self._$body.removeAttr('style');
				return true;
			}
		}

		var update = function()
		{
			if(checkIsXsmall()) return;

			self._$header.addClass('is-fixed');
			self._$body.css({'padding-top': self._$header.height() + 'px'});
		}

		update();

		self._$win.on('resize.header', function(){
			update();
		});
	}

	Header.prototype.formInit = function()
	{
		console.log('form init');
	}

	return Header;
});

Remove moderniz from the Block Header


_.extend(RouteController.prototype, {
  initialize: function (context, options) {
    this.request = context.options.request;
    this.response = context.options.response;
  },

  onRun: function () {
    this.response.end();
  }
});

_.extend(RouteController.prototype, {
  initialize: function (context, options) {
    this.request = context.options.request;
    this.response = context.options.response;
    this.next = context.options.next;
  },

  onRun: function () {
    this.response.end();
  }
});

Save the next() function to the controller too.


$(function() {
	var client = $("head").data("client");
	var setup = function() {
		$.getJSON("/getToken/", {client: client}, function(token) {
			var chan = new goog.appengine.Channel(token);
			var sock = chan.open();
			sock.onmessage = function(m) {
				var res = $.parseJSON(m.data);
				var button = $("button[data-index="+res.Ind+"]");
				var mark;
				if (res.Read) {
					mark = "unread";
				} else {
					mark = "read";
				}
				button.text("mark " + mark);
				button.data("mark", mark);
			};
			//sock.onclose = setup;
		});
	};
	setup();

	$(".ajax_link").click(function() {
		var button = $(this);
		var url;
		var mark = button.data("mark")
		if (mark === "read") {
			url = "/markRead/";
		} else {
			url = "/markUnread/";
		}
		$.get(url, button.data());
	});
});

$(function() {
	var connected = false;
	var client = $("head").data("client");
	var setup = function() {
		$.getJSON("/getToken/", {client: client}, function(token) {
			console.log("connecting");
			var chan = new goog.appengine.Channel(token);
			var sock = chan.open();
			sock.onopen = function() { connected = true; };
			sock.onmessage = function(m) {
				var res = $.parseJSON(m.data);
				var button = $("button[data-index="+res.Ind+"]");
				var mark;
				if (res.Read) {
					mark = "unread";
				} else {
					mark = "read";
				}
				button.text("mark " + mark);
				button.data("mark", mark);
			};
			sock.onclose = function() { connected = false; };
		});
	};
	setup();
	window.onfocus = function() {
		if (!connected) {
			setup();
		}
	};

	$(".ajax_link").click(function() {
		var button = $(this);
		var url;
		var mark = button.data("mark")
		if (mark === "read") {
			url = "/markRead/";
		} else {
			url = "/markUnread/";
		}
		$.get(url, button.data());
	});
});

Reconnect channel on window focus.


"use strict";

var Sphero = require("./lib/sphero");

/**
 * Creates a new Sphero instance with the provided options.
 *
 * @param {String} address port/UUID/address of the connected Sphero
 * @param {Object} [opts] options for sphero setup
 * @param {Object} [opts.adaptor] if provided, a custom adaptor used for Sphero
 * communication
 * @param {Number} [opts.sop2=0xFD] SOP2 value to be passed to commands
 * @param {Number} [opts.timeout=500] delay before a command is considered dead
 * @example var orb = sphero("/dev/rfcomm0");
 * @example var orb = sphero("/dev/tty.Sphero-OGB-AMP-SPP", { timeout: 300 });
 * @returns {Sphero} a new instance of Sphero
 */
module.exports = function sphero(address, opts) {
  return new Sphero(address, opts);
};

"use strict";

var Sphero = require("./lib/sphero");

/**
 * Creates a new Sphero instance with the provided options.
 *
 * @param {String} address port/UUID/address of the connected Sphero
 * @param {Object} [opts] options for sphero setup
 * @param {Object} [opts.adaptor] if provided, a custom adaptor used for Sphero
 * communication
 * @param {Number} [opts.sop2=0xFD] SOP2 value to be passed to commands
 * @param {Number} [opts.timeout=500] delay before a command is considered dead
 * @example var orb = sphero("/dev/rfcomm0"); // linux
 * @example var orb = sphero("COM4"); // windows
 * @example
 * var orb = sphero("/dev/tty.Sphero-OGB-AMP-SPP", { timeout: 300 }); // OS X
 * @returns {Sphero} a new instance of Sphero
 */
module.exports = function sphero(address, opts) {
  return new Sphero(address, opts);
};

Add Windows example for factory


'use strict';

var config = require('../');
var stylelint = require('stylelint');

function isObject(obj) {
  return typeof obj === 'object' && obj !== null;
}

it('test basic properties of config', function() {
  expect(isObject(config.plugins)).toBeTruthy();
  expect(isObject(config.rules)).toBeTruthy();
});

it('load config in stylelint to validate rule syntax', function() {
  return stylelint.lint({
    code: 'a { font-weight: bold; }\n',
    codeFilename: 'valid.css',
    config: config,
  }).then(function(data) {
    expect(data).toBeTruthy();
    expect(data.errored).toBeFalsy();
    expect(data.results[0].warnings.length).toBe(0);
  });
});

'use strict';

var path = require('path');
var stylelint = require('stylelint');

it('load config in stylelint to validate rule syntax', function() {
  return stylelint.lint({
    code: 'a { font-weight: bold; }\n',
    codeFilename: 'valid.css',
    configFile: path.join(__dirname, '../index.js'),
  }).then(function(data) {
    expect(data).toBeTruthy();
    expect(data.errored).toBeFalsy();
    expect(data.results[0].warnings.length).toBe(0);
  });
});

Change the way we reference the config in tests


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 self.store.config;
        }
    });

    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 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;

Return the assignment value instead of for DummyConfigResource.app's setter


var gcEmitter,
	util = require('util'),
	gcstats = require('./build/Release/gcstats'),
	EventEmitter = require('events').EventEmitter;

function GCStats() {
	if(!gcEmitter) {
		gcEmitter = new EventEmitter();
		gcstats.afterGC(function(stats) {
			gcEmitter.emit('data', stats);
		});
	}

	EventEmitter.call(this);

	gcEmitter.on('data', this.emit.bind(this, 'stats'));
}

util.inherits(GCStats, EventEmitter);

module.exports = GCStats;

var gcEmitter,
	gcstats = require('./build/Release/gcstats'),
	EventEmitter = require('events').EventEmitter;

function gcStats() {
	if (this instanceof gcStats){
        	throw Error('gc-stats no longer exports a constructor. Call without the `new` keyword');
   	}
	if(!gcEmitter) {
		gcEmitter = new EventEmitter();
		gcstats.afterGC(function(stats) {
			gcEmitter.emit('data', stats);
			gcEmitter.emit('stats', stats);
		});
	}
	return gcEmitter;
}

module.exports = gcStats;

Replace constructor with singleton. Emit 'stats' on singleton


#!/usr/bin/env node

"use strict";

const moment = require("moment");
const sugar = require("sugar");
const chalk = require("chalk");
const exec = require("child_process").exec;

let fatal = err => {
    console.error(`fatal: ${err}`);
    process.exit(1);
};

process.argv.splice(0, 2);
if (process.argv.length === 0) {
    fatal("No date string given");
};

let date = process.argv.join(" ");
let parsedDate = new sugar.Date.create(date);

if (/Invalid Date/.test(parsedDate)) {
    fatal(`Could not parse ${date} into a valid date`);
};

let dateString = moment(parsedDate).format("ddd MMM DD HH:mm:ss YYYY ZZ");

let command = `GIT_COMMITTER_DATE="${dateString}" git commit --amend --date="${dateString}" --no-edit`;
exec(command, (err, stdout, stderr) => {
    if (err) fatal("Could not modify the date of the previous commit");
    console.log("\nModified previous commit:\n    AUTHOR_DATE " + chalk.grey(dateString) + "\n    COMMITTER_DATE " + chalk.grey(dateString) + "\n\nCommand executed:\n    " + chalk.bgWhite.black(command) + "\n");
});

#!/usr/bin/env node

"use strict";

const moment = require("moment");
const sugar = require("sugar");
const chalk = require("chalk");
const exec = require("child_process").exec;

let fatal = err => {
    console.error(`fatal: ${err}`);
    process.exit(1);
};

process.argv.splice(0, 2);
if (process.argv.length === 0) {
    fatal("No date string given");
};

let date = process.argv.join(" ");
let parsedDate = new sugar.Date.create(date);

if (/Invalid Date/.test(parsedDate)) {
    fatal(`Could not parse ${date} into a valid date`);
};

let dateString = moment(parsedDate).format("ddd MMM DD HH:mm:ss YYYY ZZ");

let command = `GIT_COMMITTER_DATE="${dateString}" git commit --amend --date="${dateString}" --no-edit`;
exec(command, (err, stdout, stderr) => {
    if (err) fatal("Could not modify the date of the previous commit");
    console.log(chalk`\nModified previous commit:\n    AUTHOR_DATE {grey ${dateString}}\n    COMMITTER_DATE {grey ${dateString}}\n\nCommand executed:\n    {bgWhite.black ${command}}\n`);
});

Use tagged template literal for chalk


'use strict'

module.exports = function (filename, encoding) {
  var filename = filename || '.env'
  var encoding = encoding || 'utf8'
}

'use strict'

module.exports = function (filename, encoding) {
  filename = filename || '.env'
  encoding = encoding || 'utf8'
}

Remove the unnecessary variable declarations.


Template.nav.onRendered(function() {
  this.$('.button-collapse').sideNav({
    closeOnClick: true
  });
});

Template.nav.events({
  'click .logout': function(e){
    e.preventDefault();
    Meteor.logout(function(){
      sAlert.info('Logged out succesfully');
      Router.go('initiatives');
    });
  }
})

Template.userDropdown.onRendered(function() {
  this.$('.dropdown-button').dropdown({
    beloworigin: true
  });
});

Template.userDropdown.helpers({
  avatar: function(){
    return Meteor.user().profile.avatarImg;
  }
});

Template.nav.onRendered(function() {
  this.$('.button-collapse').sideNav({
    closeOnClick: true
  });
});

Template.nav.events({
  'click .logout': function(e){
    e.preventDefault();
    Meteor.logout(function(){
      sAlert.info('Logged out succesfully');
    });
  }
})

Template.userDropdown.onRendered(function() {
  this.$('.dropdown-button').dropdown({
    beloworigin: true
  });
});

Template.userDropdown.helpers({
  avatar: function(){
    return Meteor.user().profile.avatarImg;
  }
});

Fix router redirect on logout


/**
 * The default controller for your page structure.
 */
var turnpike = require('turnpike');
var util     = require('util');
var _        = turnpike.imports.underscore;

/**
 * Controller that composes page elements and prepares a complete HTML document for delivery.
 *
 * @param connection
 * @constructor
 */
function Page(connection) {
  turnpike.classes.base.controller.PageComposerController.call(this, connection);
  this.view = turnpike.application.views.Page;

  this.prepare = function(next) {
    this.mode = 'main';

    this.data = {
      'body':  this.connection.response(),
      'title': this.connection.controller.title + ' | ' + turnpike.config.sitename
    };

    process.nextTick(next);
  };
}
util.inherits(Page, turnpike.classes.base.controller.PageComposerController);

module.exports = Page;

/**
 * The default controller for your page structure.
 */
var turnpike = require('turnpike');
var util     = require('util');
var _        = turnpike.imports.underscore;

/**
 * Controller that composes page elements and prepares a complete HTML document for delivery.
 *
 * @param connection
 * @constructor
 */
function Page(connection) {
  turnpike.classes.base.controller.PageComposerController.call(this, connection);

  this.prepare = function(next) {
    this.mode = 'main';

    this.data = {
      'body':  this.connection.response(),
      'title': this.connection.controller.title + ' | ' + turnpike.config.sitename
    };

    process.nextTick(next);
  };
}
util.inherits(Page, turnpike.classes.base.controller.PageComposerController);

module.exports = Page;

Add a method to EndpointController to get back the Renderable representation of its data.


import React from 'react';

const styles = {
  display: 'table-cell',
  boxSizing: 'border-box',
  verticalAlign: 'top',
  height: 21,
  width: '100%',
  outline: 'none',
  border: '1px solid #ececec',
  fontSize: '12px',
  color: '#555',
};

class BooleanType extends React.Component {
  render() {
    const { knob, onChange } = this.props;

    return (
      <input
        id={knob.name}
        ref="input"
        style={styles}
        type="checkbox"
        onChange={() => onChange(this.refs.input.checked)}
        checked={knob.value}
      />
    );
  }
}

BooleanType.propTypes = {
  knob: React.PropTypes.object,
  onChange: React.PropTypes.func,
};

BooleanType.serialize = function (value) {
  return String(value);
};

BooleanType.deserialize = function (value) {
  if (!value) return false;
  return value.trim() === 'true';
};

export default BooleanType;

import React from 'react';

const styles = {
  display: 'table-cell',
  boxSizing: 'border-box',
  verticalAlign: 'top',
  height: 21,
  outline: 'none',
  border: '1px solid #ececec',
  fontSize: '12px',
  color: '#555',
};

class BooleanType extends React.Component {
  render() {
    const { knob, onChange } = this.props;

    return (
      <input
        id={knob.name}
        ref="input"
        style={styles}
        type="checkbox"
        onChange={() => onChange(this.refs.input.checked)}
        checked={knob.value}
      />
    );
  }
}

BooleanType.propTypes = {
  knob: React.PropTypes.object,
  onChange: React.PropTypes.func,
};

BooleanType.serialize = function (value) {
  return String(value);
};

BooleanType.deserialize = function (value) {
  if (!value) return false;
  return value.trim() === 'true';
};

export default BooleanType;

Make boolean checkbox looks great on Firefox.


module.exports = function (dest, src) {
  Object.getOwnPropertyNames(src).forEach(function (name) {
    var descriptor = Object.getOwnPropertyDescriptor(src, name)
    Object.defineProperty(dest, name, descriptor)
  })

  return dest
}

/*!
 * merge-descriptors
 * Copyright(c) 2014 Jonathan Ong
 * MIT Licensed
 */

/**
 * Module exports.
 * @public
 */

module.exports = merge

/**
 * Merge the property descriptors of `src` into `dest`
 *
 * @param {object} dest Object to add descriptors to
 * @param {object} src Object to clone descriptors from
 * @returns {object} Reference to dest
 * @public
 */

function merge(dest, src) {
  Object.getOwnPropertyNames(src).forEach(function forEachOwnPropertyName(name) {
    var descriptor = Object.getOwnPropertyDescriptor(src, name)
    Object.defineProperty(dest, name, descriptor)
  })

  return dest
}

Add jsdoc to source file


/**
 * Presets of sentenceSeparator, clauseRegExp, wordRegExp, wordBoundaryRegExp, abbrRegExp
 * for each languages.
 */

export default {
  en: {
    sentenceSeparator: /\n/g,
    clauseRegExp: /[^,:"?!.]+/g,
    wordRegExp: /[\w'\-\.]+/g,
    wordBoundaryRegExp: /\b/,
    abbrRegExp: /\.\.\./g,
  },

  ojp: {
    sentenceSeparator: /(?:。|[\n\r]+|「|」|『|』)(?:\s+)?/g,
    clauseRegExp: /[^、。「」『』]+/g,
    wordRegExp: /\S+/g,
    wordBoundaryRegExp: / /,
    abbrRegExp: /〜/g,
  },
};

/**
 * Presets of sentenceSeparator, clauseRegExp, wordRegExp, wordBoundaryRegExp, abbrRegExp
 * for each languages.
 */

export default {
  en: {
    sentenceSeparator: /\n/g,
    clauseRegExp: /[^,:"?!.]+/g,
    wordRegExp: /[\w'\-\.]+(?!\s*\.\s*$)/g,
    wordBoundaryRegExp: /\b/,
    abbrRegExp: /\.\.\./g,
  },

  ojp: {
    sentenceSeparator: /(?:。|[\n\r]+|「|」|『|』)(?:\s+)?/g,
    clauseRegExp: /[^、。「」『』]+/g,
    wordRegExp: /\S+/g,
    wordBoundaryRegExp: / /,
    abbrRegExp: /〜/g,
  },
};

Improve the accuracy of dividing English words


var rimraf = require('rimraf')

function removeSync (dir) {
  return rimraf.sync(dir)
}

function remove (dir, callback) {
  return callback ? rimraf(dir, callback) : rimraf(dir, function () {})
}

module.exports = {
  remove: remove,
  removeSync: removeSync
}

var rimraf = require('rimraf')

function removeSync (dir) {
  return rimraf.sync(dir, {disableGlob: true})
}

function remove (dir, callback) {
  var options = {disableGlob: true}
  return callback ? rimraf(dir, options, callback) : rimraf(dir, options, function () {})
}

module.exports = {
  remove: remove,
  removeSync: removeSync
}

Disable rimraf globbing since its not supported anywhere else within fs-extra


import { Iterable, fromJS } from 'immutable';

const defaultArgs = {
  initialState: {}
};

/**
 * Create reducer
 * @description Shorthand function to create a new reducer. By default, always returns the state
 * in case it was not modified by any action.
 * @param {Object} initialState - Initial state of the reducer.
 * @param {Object} actions - Subscribed actions (format: { actionType: fn() } ).
 * @return {Function} Reducer function.
 */
export function createReducer(args = defaultArgs) {
  let { initialState, actions } = args;

  if (!actions) {
    throw Error(`Shorthand reducer should have {actions} property specified, but received: ${actions}.`);
  }

  return (state, action) => {
    /* Duplicate the state for further changes */
    let newState = state || initialState;

    /* Enforce immutable state */
    if (!Iterable.isIterable(newState)) {
      newState = fromJS(newState);
    }

    /* Iterate through each specified action */
    actions.forEach((expectedAction) => {
      const expectedType = expectedAction.type;
      const isRegExp = (expectedType instanceof RegExp);

      /* Determine if dispatched action type is expected */
      const shouldActionPass = isRegExp ? expectedType.test(action.type) : expectedType.includes(action.type);

      /* Mutate the state once dispatched action type is expected */
      if (shouldActionPass) {
        newState = expectedAction.reducer(newState, fromJS(action));
      }
    });

    /* Return a new state */
    return newState;
  };
}

import { Iterable, fromJS } from 'immutable';

const defaultArgs = {
  initialState: {}
};

/**
 * Create reducer
 * @description Shorthand function to create a new reducer. By default, always returns the state
 * in case it was not modified by any action.
 * @param {Object} initialState - Initial state of the reducer.
 * @param {Object} actions - Subscribed actions (format: { actionType: fn() } ).
 * @return {Function} Reducer function.
 */
export function createReducer(args = defaultArgs) {
  let { initialState, actions } = args;

  if (!actions) {
    throw Error(`Shorthand reducer should have {actions} property specified, but received: ${actions}.`);
  }

  return (state, action) => {
    const dispatchedType = action.type;

    /* Duplicate the state for further changes */
    let newState = state || initialState;

    /* Enforce immutable state */
    if (!Iterable.isIterable(newState)) {
      newState = fromJS(newState);
    }

    /* Iterate through each specified action */
    actions.forEach((expectedAction) => {
      const expectedType = expectedAction.type;
      const isRegExp = (expectedType instanceof RegExp);

      /* Determine if dispatched action type is expected */
      const shouldActionPass = isRegExp ? expectedType.test(dispatchedType) : expectedType.includes(dispatchedType);

      /* Mutate the state once dispatched action type is expected */
      if (shouldActionPass) {
        newState = expectedAction.reducer(newState, fromJS(action));
      }
    });

    /* Return a new state */
    return newState;
  };
}

Create reducer: Move dispatched action type into the dedicated variable


import Monospaced from './Monospaced';
import { textFactory } from './Text';

const Heading1 = textFactory('heading', 'heading-1', 'h1');
const Heading2 = textFactory('heading', 'heading-2', 'h2');
const Heading3 = textFactory('heading', 'heading-3', 'h3');
const Heading4 = textFactory('heading', 'heading-4', 'h4');

const TextDisplay = textFactory('text', 'text-display', 'p');
const TextBody = textFactory('text', 'text-body', 'p');
const TextSmall = textFactory('text', 'text-small', 'p');

export { Heading1, Heading2, Heading3, Heading4, Monospaced, TextBody, TextDisplay, TextSmall };

import Monospaced from './Monospaced';
import { textFactory } from './Text';

const Heading1 = textFactory('heading', 'heading-1', 'h1');
const Heading2 = textFactory('heading', 'heading-2', 'h2');
const Heading3 = textFactory('heading', 'heading-3', 'h3');
const Heading4 = textFactory('heading', 'heading-4', 'h4');

const TextDisplay = textFactory('text', 'text-display', 'p');
const TextBody = textFactory('text', 'text-body', 'p');
const TextSmall = textFactory('text', 'text-small', 'p');

Heading1.displayName = 'Heading1';
Heading2.displayName = 'Heading2';
Heading3.displayName = 'Heading3';
Heading4.displayName = 'Heading4';
TextDisplay.displayName = 'TextDisplay';
TextBody.displayName = 'TextBody';
TextSmall.displayName = 'TextSmall';

export { Heading1, Heading2, Heading3, Heading4, Monospaced, TextBody, TextDisplay, TextSmall };

Add displayName to all the Typography components


module.exports = function(session, options) {
    function testBrowser(name, version) {
        var config = {
            'browserName': name,
            'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER || 'test',
            'build': process.env.TRAVIS_BUILD_NUMBER,
            'name': session
        };

        if (version) {
            config.version = version;
        }

        return config;
    }

    var browsers = [
        testBrowser('chrome'),
        testBrowser('firefox'),
        testBrowser('safari', 5),
        testBrowser('safari', 6),
        testBrowser('safari', 7),
        testBrowser('internet explorer', 9),
        testBrowser('internet explorer', 10),
        testBrowser('internet explorer', 11),
        testBrowser('iphone', '6.1'),
        testBrowser('iphone', '7.1'),
        testBrowser('android', '4.0'),
        testBrowser('android', '4.3'),
    ];

    if (options && options.legacy) {
        browsers.push(testBrowser('internet explorer', 8));
    }

    return browsers;
};

module.exports = function(session, options) {
    function testBrowser(name, version) {
        var config = {
            'browserName': name,
            'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER || 'test',
            'build': process.env.TRAVIS_BUILD_NUMBER,
            'name': session
        };

        if (version) {
            config.version = version;
        }

        return config;
    }

    var browsers = [
        testBrowser('android', '4.3'),
        testBrowser('chrome'),
        testBrowser('firefox'),
        testBrowser('internet explorer', 11),
        testBrowser('iphone', '7.1'),
        testBrowser('safari', 7),
    ];

    if (options && options.legacy) {
        browsers.push(testBrowser('android', '4.0'));
        browsers.push(testBrowser('internet explorer', 10));
        browsers.push(testBrowser('internet explorer', 8));
        browsers.push(testBrowser('internet explorer', 9));
        browsers.push(testBrowser('iphone', '6.1'));
        browsers.push(testBrowser('safari', 5));
        browsers.push(testBrowser('safari', 6));
    }

    return browsers;
};

Move a lot of things to legacy.


// ----------------------------------------------------------------
// Content Class

class Content {
  static get CONTENT() {
    return 'content';
  }

  static get ITEM() {
    return 'content-item';
  }

  static get ITEM_HEADER() {
    return 'content-item-header';
  }

  static get ITEM_NAME() {
    return 'content-item-name';
  }

  static get ITEM_KEYS() {
    return 'content-item-keys';
  }

  static get ITEM_KEY() {
    return 'content-item-key';
  }
}

Add some .class in Content


module.exports = function(grunt) {
	require("time-grunt")(grunt);
	grunt.initConfig({
		pkg: grunt.file.readJSON("package.json"),
		eslint: {
			options: {
				configFile: ".eslintrc.json"
			},
			target: ["js/*.js", "modules/default/*.js", "serveronly/*.js", "*.js"]
		},
		stylelint: {
			simple: {
				options: {
					configFile: ".stylelintrc"
				},
				src: "**/**/**/**/**/**/**/**/*.css"
			}
		}
	});
	grunt.loadNpmTasks("grunt-eslint");
	grunt.loadNpmTasks("grunt-stylelint");
	grunt.registerTask("default", ["eslint", "stylelint"]);
};

module.exports = function(grunt) {
	require("time-grunt")(grunt);
	grunt.initConfig({
		pkg: grunt.file.readJSON("package.json"),
		eslint: {
			options: {
				configFile: ".eslintrc.json"
			},
			target: ["js/*.js", "modules/default/*.js", "serveronly/*.js", "*.js"]
		},
		stylelint: {
			simple: {
				options: {
					configFile: ".stylelintrc"
				},
				src: ["css/custom.css", "css/main.css", "modules/default/calendar/calendar.css", "modules/default/clock/clock_styles.css", "modules/default/currentweather/currentweather.css", "modules/default/weatherforcast/weatherforcast.css"]
			}
		}
	});
	grunt.loadNpmTasks("grunt-eslint");
	grunt.loadNpmTasks("grunt-stylelint");
	grunt.registerTask("default", ["eslint", "stylelint"]);
};

Clean Up the Contributing Documentation and Process: Part VII


var webpack = require('webpack');

module.exports = function(grunt) {
  require('load-grunt-tasks')(grunt);

  grunt.initConfig({
    secret: grunt.file.readJSON('secret.json'),

    clean: {
      build: ['build/']
    },

    webpack: {
      production: require('./webpack/production')
    },

    sftp: {
      options: {
        path: '<%= secret.path %>',
        host: '<%= secret.host %>',
        username: '<%= secret.username %>',
        agent: process.env.SSH_AUTH_SOCK,
        showProgress: true,
        srcBasePath: 'build/',
        createDirectories: true
      },

      code: {
        files: {
          './': ['build/**', '!build/assets/**']
        }
      },

      assets: {
        files: {
          './': ['build/assets/**']
        }
      }
    },

    copy: {
      index: {
        src: 'index.html',
        dest: 'build/index.html'
      }
    },

    zip: {
      itch: {
        cwd: 'build/',
        src: ['build/**/*'],
        dest: 'build/itch' + Date.now() + '.zip'
      }
    }
  });

  grunt.registerTask('dist', ['clean:build', 'webpack:production', 'copy:index']);

  grunt.registerTask('deploy:code', ['dist', 'sftp:code']);
  grunt.registerTask('deploy', ['dist', 'sftp']);

  grunt.registerTask('itch', ['dist', 'zip']);
};

Remove unused import from gruntfile


const Sequelize = require('sequelize');

if (process.env.DATABASE_URL) {
  let db = new Sequelize(process.env.DATABASE_URL, {
    dialect:  'postgres',
    protocol: 'postgres',
    port:     match[4],
    host:     match[3],
    logging:  true
  });
} else {
  let db = new Sequelize('postgres', 'postgres', ' ', {
    dialect: 'postgres',
    port: 5432,
  });
}

module.exports = { database: db, Sequelize };

const Sequelize = require('sequelize');

let db;

if (process.env.DATABASE_URL) {
  db = new Sequelize(process.env.DATABASE_URL, {
    dialect: 'postgres',
    protocol:'postgres',
    port: match[4],
    host: match[3],
    logging: true
  });
} else {
  db = new Sequelize('postgres', 'postgres', ' ', {
    dialect: 'postgres',
    port: 5432,
  });
}

module.exports = { database: db, Sequelize };

Use heroku db in production


export default function(state, ctx, model, helpers) {

	const ConcretizeIfNative = helpers.ConcretizeIfNative;

	//TODO: Test IsNative for apply, bind & call
	model.add(Function.prototype.apply, ConcretizeIfNative(Function.prototype.apply));
	model.add(Function.prototype.call, ConcretizeIfNative(Function.prototype.call));
	model.add(Function.prototype.bind, ConcretizeIfNative(Function.prototype.bind));
  model.add(console.log, function(base, args) {
		console.log.apply(base, args);
	});
}

export default function(state, ctx, model, helpers) {

	const ConcretizeIfNative = helpers.ConcretizeIfNative;

	//TODO: Test IsNative for apply, bind & call
	model.add(Function.prototype.apply, ConcretizeIfNative(Function.prototype.apply));
	model.add(Function.prototype.call, ConcretizeIfNative(Function.prototype.call));
	model.add(Function.prototype.bind, ConcretizeIfNative(Function.prototype.bind));
  model.add(Function.prototype.hasOwnProperty, function(base, args) {

    for (let i = 0; i < args.length; i++) {
      args[i] = state.getConcrete(args[i]);
    }

    Function.prototype.hasOwnProperty.apply(state.getConcrete(base), args);
  });

  model.add(console.log, function(base, args) {

    for (let i = 0; i < args.length; i++) {
      args[i] = state.getConcrete(args[i]);
    }

		console.log.apply(base, args);
	});
}

Add a shallow concretizing hasOwnProperty prototyp


document.addEventListener("DOMContentLoaded", function (event) {
    // redirect to 7777 port
    // ping golang counter
    httpGetAsync("http://www.aracki.me:7777", function (res) {
        alert(res);
    })
});

function httpGetAsync(theUrl, callback) {
    var xmlHttp = new XMLHttpRequest();
    xmlHttp.onreadystatechange = function () {
        if (xmlHttp.readyState === 4 && xmlHttp.status === 200)
            callback(xmlHttp.responseText);
    };
    xmlHttp.open("GET", theUrl, true); // true for asynchronous
    xmlHttp.setRequestHeader("Access-Control-Allow-Origin", '*');
    xmlHttp.setRequestHeader("Access-Control-Allow-Methods", 'GET, POST, PATCH, PUT, DELETE, OPTIONS');
    xmlHttp.setRequestHeader("Access-Control-Allow-Headers", 'Origin, Content-Type, X-Auth-Token');
    xmlHttp.send("ivan");
}

document.addEventListener("DOMContentLoaded", function (event) {
    // redirect to 7777 port
    // ping golang counter
    httpGetAsync("http://www.aracki.me:7777/count", function (res) {
        alert(res);
    })
});

function httpGetAsync(theUrl, callback) {
    var xmlHttp = new XMLHttpRequest();
    xmlHttp.onreadystatechange = function () {
        if (xmlHttp.readyState === 4 && xmlHttp.status === 200)
            callback(xmlHttp.responseText);
    };
    xmlHttp.open("GET", theUrl, true); // true for asynchronous
    xmlHttp.setRequestHeader("Access-Control-Allow-Origin", '*');
    xmlHttp.setRequestHeader("Access-Control-Allow-Methods", 'GET, POST, PATCH, PUT, DELETE, OPTIONS');
    xmlHttp.setRequestHeader("Access-Control-Allow-Headers", 'Origin, Content-Type, X-Auth-Token');
    xmlHttp.send("ivan");
}

Add new url to get request


/**
 * Created by sandeshkumar on 4/5/16.
 */
function flattenArray(arr) {
    if(Array.isArray(arr)) {
        //return flatIterative(arr);
        //return flatRecursive(arr, []);
        return flatFunctional(arr);
    }
    return [];
}

function flatRecursive(arr, arr2) {
    var flattenedArr = arr2;
    for( var i = 0;  i < arr.length; i++) {
        var el = arr[i];
        var items = Array.isArray(el) ? flatRecursive(el, flattenedArr): el;
        if(!Array.isArray(el)) {
            flattenedArr.push(el);
        }
    }
    return flattenedArr;
}

function flatIterative(arr) {
    var flat = [];
    for (var i = 0; i < arr.length; i++) {
        var el = arr[i];
        if(Array.isArray(el)) {
            for(var j = 0; j < el.length; j++) {
                var elm = el[j];
                flat.push(elm);
            }
        } else {
            flat.push(el);
        }
    }
    return flat;
}

function flatFunctional(arr) {
    var flattened = arr.reduce(function(memo, el) {
        return memo.concat(el);
    }, []);
    return flattened;
}

/**
 * Created by sandeshkumar on 4/5/16.
 */
function flattenArray(arr) {
    if(Array.isArray(arr)) {
        //return flatIterative(arr);
        //return flatRecursive(arr, []);
        return flatFunctional(arr);
    }
    return false;
}

function flatRecursive(arr, arr2) {
    var flattenedArr = arr2;
    for( var i = 0;  i < arr.length; i++) {
        var el = arr[i];
        var items = Array.isArray(el) ? flatRecursive(el, flattenedArr): el;
        if(!Array.isArray(el)) {
            flattenedArr.push(el);
        }
    }
    return flattenedArr;
}

function flatIterative(arr) {
    var flat = [];
    for (var i = 0; i < arr.length; i++) {
        var el = arr[i];
        if(Array.isArray(el)) {
            for(var j = 0; j < el.length; j++) {
                var elm = el[j];
                flat.push(elm);
            }
        } else {
            flat.push(el);
        }
    }
    return flat;
}

function flatFunctional(arr) {
    var flattened = arr.reduce(function(memo, el) {
        return memo.concat(el);
    }, []);
    return flattened;
}

Check for input to be array


const redux = require('redux');
const reducers = require('../reducers');

function reduxStore(initialState) {
  const store = redux.createStore(reducers, initialState);

  if (module.hot) {
    // Enable Webpack hot module replacement for reducers
    module.hot.accept('../reducers', () => {
      // We need to require for hot reloadign to work properly.
      const nextReducer = require('../reducers');  // eslint-disable-line global-require

      store.replaceReducer(nextReducer);
    });
  }

  return store;
}

export default reduxStore;

const redux = require('redux');
const reducers = require('../reducers');

function reduxStore(initialState) {
  const store = redux.createStore(reducers, initialState,
    window.devToolsExtension && window.devToolsExtension());

  if (module.hot) {
    // Enable Webpack hot module replacement for reducers
    module.hot.accept('../reducers', () => {
      // We need to require for hot reloadign to work properly.
      const nextReducer = require('../reducers');  // eslint-disable-line global-require

      store.replaceReducer(nextReducer);
    });
  }

  return store;
}

export default reduxStore;

Enable debugging with redux chrome plugin


Template.HostCreate.events({
  'form submit' : function (e, tmpl) {
    e.preventDefault();

    // Define form field variables.
    var hostname = e.target.hostname.value;
    var type = e.target.type.value;
    var version = e.target.version.value;

    // Need more validation here.
    if (hostname.val().length) {

      // Create sort field sequence value.
      var total = hosts.find().count();
      if (total == 0) {
        var seq = total;
      } else {
        var seq = total++;
      }

      // Insert data into document.
      hosts.insert({
        hostname: hostname,
        type: type,
        version: version,
        host_created: new Date,
        sort: seq
      });
      // Reset form.
      tmpl.find('form').reset();
    }
  }
});

Add event handler for inserting new Host document.


'use strict';

angular.module('visualizerApp')
  .controller('ExploreController', ['$scope', '$upload', '$location', '$http', '$timeout', '$filter', 'jsonFactory', function ($scope, $upload, $location, $http, $timeout, $filter, jsonFactory) {
    $scope.fileApiSupport = jsonFactory.fileApiSupport;
    $scope.repos = [];
    $scope.filteredRepos = [];
    $scope.perColumn = 0;
    $scope.searchText = '';

    jsonFactory.init
      .success(function() {
        $scope.repos = jsonFactory.repositories.map(function(r) { return { owner: r.owner, repo: r.repo }; });
        filterRepos();
      });

    track();

    /* Filter */
    $scope.$watch('searchText', filterRepos);

    $scope.onFileClick = function onFileClick(repo) {
      $location.path('/display/' + repo.owner + '/' + repo.repo);
    };

    $scope.openProject = function openProject() {
      if ($scope.filteredRepos.length > 0) {
        $scope.onFileClick($scope.filteredRepos[0]);
      }
    };

    function filterRepos(value) {
      $scope.filteredRepos = $filter('filter')($scope.repos, {$:value});
      $scope.perColumn = Math.ceil($scope.filteredRepos.length / 3);
    }

    function track() {
      ga('send', 'pageview', {'page': $location.path()});
    }
  }]);

'use strict';

angular.module('visualizerApp')
  .controller('ExploreController', ['$scope', '$upload', '$location', '$http', '$timeout', '$filter', 'jsonFactory', function ($scope, $upload, $location, $http, $timeout, $filter, jsonFactory) {
    $scope.fileApiSupport = jsonFactory.fileApiSupport;
    $scope.repos = [];
    $scope.filteredRepos = [];
    $scope.perColumn = 0;
    $scope.searchText = '';

    jsonFactory.init
      .success(function() {
        $scope.repos = jsonFactory.repositories.map(function(r) { return { owner: r.owner, repo: r.repo, name: r.owner + '/' + r.repo }; });
        filterRepos();
      });

    track();

    /* Filter */
    $scope.$watch('searchText', filterRepos);

    $scope.onFileClick = function onFileClick(repo) {
      $location.path('/display/' + repo.owner + '/' + repo.repo);
    };

    $scope.openProject = function openProject() {
      if ($scope.filteredRepos.length > 0) {
        $scope.onFileClick($scope.filteredRepos[0]);
      }
    };

    function filterRepos(value) {
      $scope.filteredRepos = $filter('filter')($scope.repos, {$:value});
      $scope.perColumn = Math.ceil($scope.filteredRepos.length / 3);
    }

    function track() {
      ga('send', 'pageview', {'page': $location.path()});
    }
  }]);

Enable full repo name search


var path = require('path');

// strip the last dir...  essentially the same as __dirname/..
var root = path.dirname(__dirname); //.replace(/\/[^\/]+\/?$/,''),
deploy_root = path.normalize(root + '/..');

module.exports = {
	  port: 8001
	, public_dir: root + '/public'
	, db_name: 'wompt_dev'
	, root: root
	, deploy_root: deploy_root
	, app_host:  '127.0.0.1'
	, db_host:   '127.0.0.1'
	, auth_host: '127.0.0.1'

	, minify_assets: false

	, constants: {
		messages: {
				max_length: 256 * 1,
				max_shown: 500
		}
	}

	, hoptoad: {
		reportErrors: false
	}

	, logs: {
		root: root,
		channels: {
			root: root + '/logs/channels'
		}
	}

	/* google analytics */
	, ga: {
		use: true
	}

	, redirectWww: false
	, redirectWwwToPort: 16867
}

var path = require('path');

// strip the last dir...  essentially the same as __dirname/..
var root = path.dirname(__dirname); //.replace(/\/[^\/]+\/?$/,''),
deploy_root = path.normalize(root + '/..');

module.exports = {
	  port: 8001
	, public_dir: root + '/public'
	, db_name: 'wompt_dev'
	, root: root
	, deploy_root: deploy_root
	, app_host:  '127.0.0.1'
	, db_host:   '127.0.0.1'
	, auth_host: '127.0.0.1'

	, minify_assets: false

	, constants: {
		messages: {
				max_length: 256 * 1,
				max_shown: 500
		}
	}

	, hoptoad: {
		reportErrors: false
	}

	, logs: {
		root: root,
		channels: {
			root: root + '/logs/channels'
		}
	}

	/* google analytics */
	, ga: {
		use: true
	}

	, socketIO: {
		serverOptions: {
		}
	}

	, redirectWww: false
	, redirectWwwToPort: 16867
}

Add SocketIO server options to "default" nodejs environment so development mode isn't broken


// Redux
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
// import createLogger from 'redux-logger';
// import Immutable from 'immutable';
import rootReducer from '../reducers';

// const __DEV__ = process.env.NODE_ENV === 'production' ? false : true;
const finalCreateStore = compose(
	applyMiddleware(thunk),
    window.devToolsExtension() || (f => f),
    // applyMiddleware(createLogger({
    //     // Only logs in development mode
    //     predicate: () => __DEV__,
    //     // Transform Immutable object to plain json for better debuggin experience
    //     transformer: (state) => {
    //         var newState = {};
    //         for (var i of Object.keys(state)) {
    //             if (Immutable.Iterable.isIterable(state[i])) {
    //                 newState[i] = state[i].toJS();
    //             } else {
    //                 newState[i] = state[i];
    //             }
    //         };
    //         return newState;
    //     }
    // }))
)(createStore);

export default function configureStore(initialState) {
	const store = finalCreateStore(rootReducer, initialState);

	if (module.hot) {
        // Enable Webpack hot module replacement for reducers
        module.hot.accept('../reducers', () => {
            const nextRootReducer = require('../reducers');
            store.replaceReducer(nextRootReducer);
        });
    }

    return store;
};

// Redux
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
// import createLogger from 'redux-logger';
// import Immutable from 'immutable';
import rootReducer from '../reducers';

// const __DEV__ = process.env.NODE_ENV === 'production' ? false : true;
const finalCreateStore = compose(
	applyMiddleware(thunk),
    window.devToolsExtension ? window.devToolsExtension() : f => f
    // applyMiddleware(createLogger({
    //     // Only logs in development mode
    //     predicate: () => __DEV__,
    //     // Transform Immutable object to plain json for better debuggin experience
    //     transformer: (state) => {
    //         var newState = {};
    //         for (var i of Object.keys(state)) {
    //             if (Immutable.Iterable.isIterable(state[i])) {
    //                 newState[i] = state[i].toJS();
    //             } else {
    //                 newState[i] = state[i];
    //             }
    //         };
    //         return newState;
    //     }
    // }))
)(createStore);

export default function configureStore(initialState) {
	const store = finalCreateStore(rootReducer, initialState);

	if (module.hot) {
        // Enable Webpack hot module replacement for reducers
        module.hot.accept('../reducers', () => {
            const nextRootReducer = require('../reducers');
            store.replaceReducer(nextRootReducer);
        });
    }

    return store;
};

Update redux dev tool configuration


import Service from 'ember-service';
import get, { getProperties } from 'ember-metal/get';
import set from 'ember-metal/set';
import computed from 'ember-computed';
import getOwner from 'ember-owner/get';
import isMobile from 'ismobilejs';

export default Service.extend({

  fastboot: computed(function() {
    return getOwner(this).lookup('service:fastboot');
  }),

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

    let queries;
    if (get(this, 'fastboot.isFastBoot')) {
      const headers = get(this, 'fastboot.request.headers');
      const userAgent = headers.get('user-agent');
      queries = getProperties(isMobile(userAgent), ['any', 'phone', 'tablet', 'apple', 'android', 'amazon', 'windows', 'seven_inch', 'other']);
    } else {
      queries = isMobile;
    }

    for (let media in queries) {
      set(this, media, queries[media]);
    }
  }
});

import Service from 'ember-service';
import get, { getProperties } from 'ember-metal/get';
import set from 'ember-metal/set';
import computed from 'ember-computed';
import getOwner from 'ember-owner/get';
import isMobile from 'ismobilejs';

export default Service.extend({

  fastboot: computed(function() {
    return getOwner(this).lookup('service:fastboot');
  }),

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

    let queries = [];
    if (get(this, 'fastboot.isFastBoot')) {
      const headers = get(this, 'fastboot.request.headers');
      const userAgent = headers.get('user-agent');
      if (userAgent) {
        queries = getProperties(
          isMobile(userAgent),
          ['any', 'phone', 'tablet', 'apple', 'android', 'amazon', 'windows', 'seven_inch', 'other']
        );
      }
    } else {
      queries = isMobile;
    }

    for (let media in queries) {
      set(this, media, queries[media]);
    }
  }
});

Fix errors in FastBoot if user-agent is blank


var path = require('path');
var webpack = require('webpack');

module.exports = {
    entry: './main.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'main.js'
    },
    module: {
        loaders: [
            {
                test: /\.jsx?$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                query: {
                    presets: ['es2015'],
                    cacheDirectory: true
                }
            }
        ]
    },
    stats: {
        colors: true
    },
    devtool: 'source-map'
};

var path = require('path');
var webpack = require('webpack');

module.exports = {
    entry: {
        main: ['./main.js']
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    },
    module: {
        loaders: [
            {
                test: /\.jsx?$/,
                exclude: /node_modules/,
                loader: 'babel-loader',
                query: {
                    presets: ['es2015'],
                    cacheDirectory: true
                }
            }
        ]
    },
    stats: {
        colors: true
    },
    devtool: 'source-map'
};

Resolve client dist output js name from entry point


import React from 'react'
import {combineReducers, createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import { Provider } from 'react-redux'
import { render } from 'react-dom'
import createHistory from 'history/createBrowserHistory'
import Breezy from '@jho406/breezy'

// This mapping can be auto populate through
// Breezy generators, for example:
// Run `rails g breezy:view Post index`
const mapping = {
}

const history = createHistory({})
const initialPage = window.BREEZY_INITIAL_PAGE_STATE,
const baseUrl = ''

//The Nav is pretty bare bones
//Feel free to replace the implementation
const {reducer, initialState, Nav, connect} = Breezy.start(
  window,
  initialPage,
  baseUrl,
  history
)

const store = createStore(
  reducer,
  initialState,
  applyMiddleware(thunk)
)

connect(store)

class App extends React.Component {
  render() {
    return <Provider store={store}>
      <Nav mapping={mapping}/>
    </Provider>
  }
}

document.addEventListener("DOMContentLoaded", function() {
  render(<App mapping={mapping}/>, document.getElementById('app'))
})

import React from 'react'
import {combineReducers, createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
import { Provider } from 'react-redux'
import { render } from 'react-dom'
import createHistory from 'history/createBrowserHistory'
import Breezy from '@jho406/breezy'

// This mapping can be auto populate through
// Breezy generators, for example:
// Run `rails g breezy:view Post index`
const mapping = {
}

const history = createHistory({})
const initialPage = window.BREEZY_INITIAL_PAGE_STATE
const baseUrl = ''

//The Nav is pretty bare bones
//Feel free to replace the implementation
const {reducer, initialState, Nav, connect} = Breezy.start(
  window,
  initialPage,
  baseUrl,
  history
)

const store = createStore(
  reducer,
  initialState,
  applyMiddleware(thunk)
)

connect(store)

class App extends React.Component {
  render() {
    return <Provider store={store}>
      <Nav mapping={mapping}/>
    </Provider>
  }
}

document.addEventListener("DOMContentLoaded", function() {
  render(<App mapping={mapping}/>, document.getElementById('app'))
})

Fix issue with template, extra comma


$(document).ready(function() {
    var editor = ace.edit("json");
    editor.setTheme("ace/theme/github");
    editor.session.setMode("ace/mode/json");
    editor.setFontSize(14);
    editor.$blockScrolling = "Infinity";
    $("#submit_json").click(function() {
        try {
            var json = $.parseJSON(editor.getValue());
            $.ajax({
                method: "POST",
                url: "/" + $("#conn_name").val() + "/" + $("#db_name").val() + "/" + $("#coll_name").val() + "/save",
                data: json
            })
            .success(function(msg) {
                show_notification(msg,"success");
            })
            .error(function(msg) {
                show_notification(msg.responseText,"danger");
            });
        }
        catch (err) {
            show_notification(err,"danger");
        }
    });

    // prettify the json
    var jsonString = editor.getValue();
    var jsonPretty = JSON.stringify(JSON.parse(jsonString),null,2);
    editor.setValue(jsonPretty);
});

$(document).ready(function() {
    var editor = ace.edit("json");
    editor.setTheme("ace/theme/github");
    editor.session.setMode("ace/mode/json");
    editor.setFontSize(14);
    editor.$blockScrolling = "Infinity";
    $("#submit_json").click(function() {
        try {
            $.ajax({
                method: "POST",
                contentType: 'application/json',
                url: "/" + $("#conn_name").val() + "/" + $("#db_name").val() + "/" + $("#coll_name").val() + "/save",
                data: editor.getValue()
            })
            .success(function(msg) {
                show_notification(msg,"success");
            })
            .error(function(msg) {
                show_notification(msg.responseText,"danger");
            });
        }
        catch (err) {
            show_notification(err,"danger");
        }
    });

    // prettify the json
    var jsonString = editor.getValue();
    var jsonPretty = JSON.stringify(JSON.parse(jsonString),null,2);
    editor.setValue(jsonPretty);
});

Set "contentType" on AJAX call.


"use strict";
const gh = require("simple-github");
const jwt = require("jsonwebtoken");

const CERT = process.env.GITHUB_INTEGRATION_KEY;  // get private key
const ID = process.env.GITHUB_INTEGRATION_ID;
const GH_TOKEN = process.env.GITHUB_TOKEN;

let sign = (exp) => {
    return jwt.sign({ iss: ID }, CERT, { algorithm: 'RS256', expiresIn: exp });
};

let headers = token => {
    return {
        "Authorization": `Bearer ${ token }`,
        "Accept": "application/vnd.github.machine-man-preview+json"
    };
};

let getAuthHeaders = (installation, options) => {
    if (installation) {
        return gh({
            debug: options && options.debug,
            headers: headers(sign("5m"))
        }).request("POST /installations/:installation_id/access_tokens", {
            installation_id: installation.id
        }).then(installation => headers(installation.token));
    }
    return Promise.resolve({ "Authorization": `token ${ GH_TOKEN }` });
};

module.exports = getAuthHeaders;

"use strict";
const gh = require("simple-github");
const jwt = require("jsonwebtoken");

const CERT = process.env.GITHUB_INTEGRATION_KEY;  // get private key
const ID = process.env.GITHUB_INTEGRATION_ID;
const GH_TOKEN = process.env.GITHUB_TOKEN;

let sign = (exp) => {
    return jwt.sign({ iss: ID }, CERT, { algorithm: 'RS256', expiresIn: exp });
};

let headers = token => {
    return {
        "Authorization": `Bearer ${ token }`,
        "Accept": "application/vnd.github.machine-man-preview+json"
    };
};

let getAuthHeaders = (installation, options) => {
    if (installation) {
        return gh({
            debug: options && options.debug,
            headers: headers(sign("5m"))
        }).request("POST /app/installations/:installation_id/access_tokens", {
            installation_id: installation.id
        }).then(installation => headers(installation.token));
    }
    return Promise.resolve({ "Authorization": `token ${ GH_TOKEN }` });
};

module.exports = getAuthHeaders;

Update deprecated access token endpoint


"use strict";

module.exports = function (grunt) {
    grunt.registerTask("make", [].concat([
        "exec:version",
        "default",
        "concurrent:source",
        "exec:buildDebug",
        "exec:buildRelease",
        "uglify:buildRelease",
        "concurrent:debug",
        "concurrent:release",
        "uglify:buildTests",
        "copy:publishVersionPlato",
        "copy:publishVersion",
        "copy:publishTest"
    ]));
};

"use strict";

module.exports = function (grunt) {
    grunt.registerTask("make", [].concat([
        "exec:version",
        "default",
        "connect:server",
        "concurrent:source",
        "exec:buildDebug",
        "exec:buildRelease",
        "uglify:buildRelease",
        "concurrent:debug",
        "concurrent:release",
        "uglify:buildTests",
        "copy:publishVersionPlato",
        "copy:publishVersion",
        "copy:publishTest"
    ]));
};

Connect is required for selenium tests


require('babel-register');
require('babel-polyfill');

const Bluebird = require('bluebird');
const WebpackIsomorphicTools = require('webpack-isomorphic-tools');
const config = require('../common/config');
const rootDir = require('path').resolve(__dirname, '..', '..');
const webpackIsomorphicAssets = require('../../webpack/assets');

if (!process.env.NODE_ENV) {
  throw new Error(
    'Environment variable NODE_ENV must be set to development or production.'
  );
}

// http://bluebirdjs.com/docs/why-bluebird.html
global.Promise = Bluebird;

// http://formatjs.io/guides/runtime-environments/#polyfill-node
if (global.Intl) {
  // We don't have to check whether Node runtime supports specific language,
  // because without special build it does support only english anyway.
  require('intl');
  global.Intl.NumberFormat = global.IntlPolyfill.NumberFormat;
  global.Intl.DateTimeFormat = global.IntlPolyfill.DateTimeFormat;
} else {
  global.Intl = require('intl');
}

global.webpackIsomorphicTools = new WebpackIsomorphicTools(webpackIsomorphicAssets)
  .development(!config.isProduction)
  .server(rootDir, () => {
    require('./main');
  });

require('babel-register');
require('babel-polyfill');

const Bluebird = require('bluebird');
const WebpackIsomorphicTools = require('webpack-isomorphic-tools');
const config = require('../common/config');
const rootDir = require('path').resolve(__dirname, '..', '..');
const webpackIsomorphicAssets = require('../../webpack/assets');

if (!process.env.NODE_ENV) {
  throw new Error(
    'Environment variable NODE_ENV must be set to development or production.'
  );
}

// http://bluebirdjs.com/docs/why-bluebird.html
global.Promise = Bluebird;

Promise.config({
  warnings: {
    // Disables forgotten return statements warnings when await is used.
    // "Warning: a promise was created in a handler but was not returned from it"
    wForgottenReturn: false
  }
});

// http://formatjs.io/guides/runtime-environments/#polyfill-node
if (global.Intl) {
  // We don't have to check whether Node runtime supports specific language,
  // because without special build it does support only english anyway.
  require('intl');
  global.Intl.NumberFormat = global.IntlPolyfill.NumberFormat;
  global.Intl.DateTimeFormat = global.IntlPolyfill.DateTimeFormat;
} else {
  global.Intl = require('intl');
}

global.webpackIsomorphicTools = new WebpackIsomorphicTools(webpackIsomorphicAssets)
  .development(!config.isProduction)
  .server(rootDir, () => {
    require('./main');
  });

Disable forgotten returns for server promises


'use strict';

const EasyObjectValue = require('../values/EasyObjectValue');
const ObjectValue = require('../values/ObjectValue');
const ArrayValue = require('../values/ArrayValue');

class ArrayObject extends EasyObjectValue {
	*call(thiz, args, s) {
		if ( args.length === 1 && args[0].jsTypeName === 'number' ) {
			let result = ArrayValue.make([], s.realm);
			yield * result.set('length', args[0]);
			return result;
		}
		return ArrayValue.make(args, s.realm);
	}

	callPrototype(realm) { return realm.ArrayPrototype; }
	constructorFor(realm) { return realm.ArrayPrototype; }
	//objPrototype(realm) { return realm.Function; }

	static *isArray(thiz, args) {
		if ( args.length < 1 ) return EasyObjectValue.false;
		return EasyObjectValue.fromNative(args[0] instanceof ArrayValue);
	}
}

module.exports = ArrayObject;

'use strict';

const EasyObjectValue = require('../values/EasyObjectValue');
const ObjectValue = require('../values/ObjectValue');
const ArrayValue = require('../values/ArrayValue');
const CompletionRecord = require('../CompletionRecord');

class ArrayObject extends EasyObjectValue {
	*call(thiz, args, s) {
		if ( args.length === 1 && args[0].jsTypeName === 'number' ) {
			let len = args[0].toNative();
			if ( len != len>>0 ) {
				return yield CompletionRecord.makeRangeError(this.realm, "Invalid array length");
			}
			let result = ArrayValue.make([], s.realm);
			yield * result.set('length', args[0]);
			return result;
		}
		return ArrayValue.make(args, s.realm);
	}

	callPrototype(realm) { return realm.ArrayPrototype; }
	constructorFor(realm) { return realm.ArrayPrototype; }
	//objPrototype(realm) { return realm.Function; }

	static *isArray(thiz, args) {
		if ( args.length < 1 ) return EasyObjectValue.false;
		return EasyObjectValue.fromNative(args[0] instanceof ArrayValue);
	}
}

module.exports = ArrayObject;

Throw on non-int array lenghts


import { createContainer } from 'meteor/react-meteor-data';

import { Posts, Comments } from '../../api';
import PostList from '../components/posts/post-list';

const PostListContainer = createContainer(() => {
  const postsSub = Meteor.subscribe('posts');
  const postsReady = postsSub.ready();

  let posts = Posts.find().fetch();

  posts = _.map(posts, post => {
    const comments = Comments.find({
      postId: post._id,
    }).fetch();

    return {
      ...post,
      comments,
    };
  });

  return {
    postsReady,
    posts,
  };
}, PostList);

export default PostListContainer;

import { createContainer } from 'meteor/react-meteor-data';

import { Posts, Comments } from '../../api';
import PostList from '../components/posts/post-list';

const limit = new ReactiveVar(10);

const PostListContainer = createContainer(() => {
  const postsSub = Meteor.subscribe('posts', limit.get());
  const postsReady = postsSub.ready();

  let posts = Posts.find().fetch();

  posts = _.map(posts, post => {
    const comments = Comments.find({
      postId: post._id,
    }).fetch();

    return {
      ...post,
      comments,
    };
  });

  return {
    limit,
    postsReady,
    posts,
  };
}, PostList);

export default PostListContainer;

Set feeds post limit as Reactive Var


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

'use strict';
// Define each block's generated code

Blockly.JavaScript['simple_input_output'] = function(block) {
  return ['simple_input_output', Blockly.JavaScript.ORDER_ATOMIC];
};

Blockly.JavaScript['multiple_input_output'] = function(block) {
  return ['multiple_input_output', Blockly.JavaScript.ORDER_ATOMIC];
};

Blockly.JavaScript['statement_no_input'] = function(block) {
  return 'do something;\n';
};

Blockly.JavaScript['statement_value_input'] = function(block) {
  return 'statement_value_input';
};

Blockly.JavaScript['statement_multiple_value_input'] = function(block) {
  return 'statement_multiple_value_input';
};

Blockly.JavaScript['statement_statement_input'] = function(block) {
  return 'statement_statement_input';
};

Blockly.JavaScript['output_no_input'] = function(block) {
  return ['output_no_input', Blockly.JavaScript.ORDER_ATOMIC];
};

Blockly.JavaScript['statement_no_next'] = function(block) {
  return 'statement_no_next';
};

Add missing license header to js files


// Fill in the regular expressions

verify(/ca[rt]/,
       ["my car", "bad cats"],
       ["camper", "high art"]);

verify(/pr?op/,
       ["pop culture", "mad props"],
       ["plop"]);

verify(/ferr(et|y|ari)/,
       ["ferret", "ferry", "ferrari"],
       ["ferrum", "transfer A"]);

verify(/ious\b/,
       ["how delicious", "spacious room"],
       ["ruinous", "consciousness"]);

verify(/\s[.,:;]/,
       ["bad punctuation ."],
       ["escape the period"]);

verify(/\w{7,}/,
       ["hottentottententen"],
       ["no", "hotten totten tenten"]);

verify(/\b[^e\s]+\b/,
       ["red platypus", "wobbling nest"],
       ["earth bed", "learning ape"]);

verify(/ca[rt]/,
	["my car", "bad cats"],
	["camper", "high art"]);

verify(/pr?op/,
	["pop culture", "mad props"],
	["plop", "prrrop"]);

verify(/ferr(et|y|ari)/,
	["ferret", "ferry", "ferrari"],
	["ferrum", "transfer A"]);

verify(/ious\b/,
	["how delicious", "spacious room"],
	["ruinous", "consciousness"]);

verify(/\s[.,:;]/,
	["bad punctuation ."],
	["escape the period"]);

verify(/\w{7,}/,
	["Siebentausenddreihundertzweiundzwanzig"],
	["no", "three small words"]);

verify(/\b[^e]+\b/i,
	["red platypus", "wobbling nest"],
	["earth bed", "learning ape", "BEET"]);

Add chapter 9, exercise 1


"use strict";

import React from "react";

import SearchActions from "../actions/SearchActions.js";

function dedupArray(arr) {
  var t = {};
  var result = [];
  arr.forEach(function(item) {
    if (t.hasOwnProperty(item)) {
      return;
    }
    t[item] = true;
    result.push(item);
  });
  return result;
}

function attributeLink(_this, x) {
  return [
    {x},
    •,
  ];
}

export default class GroupAttributes extends React.Component {
  render() {
    var _this = this;
    var list = dedupArray(this.props.list);
    list = list.map(function(attr) {
      if (Array.isArray(attr)) {
        return attr.map(function(x) {
          return attributeLink(_this, x);
        });
      }
      return attributeLink(_this, attr);
    });

    if (list.length > 0) {
      list[list.length - 1].pop();
    }

    return (

        {list}

    );
  }

  _onClickAttribute(attributeValue, evt) {
    evt.stopPropagation();
    SearchActions.search(attributeValue);
  }
}

"use strict";

import React from "react";

import SearchActions from "../actions/SearchActions.js";

function _dedupArray(arr, t, result) {
  arr.forEach(function(item) {
    if (Array.isArray(item)) {
      _dedupArray(item, t, result);
      return;
    }
    if (t.hasOwnProperty(item)) {
      return;
    }
    t[item] = true;
    result.push(item);
  });
  return result;
}

function dedupArray(arr) {
  let t = {};
  let result = [];
  _dedupArray(arr, t, result);
  return result;
}

function attributeLink(_this, x) {
  return [
    {x},
    •,
  ];
}

export default class GroupAttributes extends React.Component {
  render() {
    var _this = this;
    var list = dedupArray(this.props.list);
    list = list.map(function(attr) {
      return attributeLink(_this, attr);
    });

    if (list.length > 0) {
      list[list.length - 1].pop();
    }

    return (

        {list}

    );
  }

  _onClickAttribute(attributeValue, evt) {
    evt.stopPropagation();
    SearchActions.search(attributeValue);
  }
}

Update GroupAttribute deduplication code to handle multi-value attributes.


const _ = require('lodash');
const router = require('express').Router();

router.get('/cookie', (req, res) => {
  const data = JSON.stringify(req.headers);

  let response = '';
  response += 'HTTP/1.1 200 OK\r\n';
  response += 'Connection: close\r\n';
  response += `Content-Length: ${data.length}\r\n`;
  response += 'Content-Type: application/json\r\n';
  response += `Cookie: ${_.repeat('a', req.query.length)}\r\n`;
  response += 'Date: Wed, 30 Nov 2016 20:19:57 GMT\r\n\r\n';
  response += data;

  res.socket.end(response);
  res.socket.destroy();
});

router.get('/content-length', (req, res) => {
  let response = '';
  response += 'HTTP/1.1 200 OK\r\n';
  response += 'Connection: close\r\n';
  response += 'Content-Length: 3\r\n';
  response += 'Content-Type: text/plain\r\n';
  response += 'Date: Wed, 30 Nov 2016 20:19:57 GMT\r\n\r\n';
  response += 'abcdefghijklmnopqrstuvwxyz';

  res.socket.end(response);
  res.socket.destroy();
});

module.exports = router;

const _ = require('lodash');
const router = require('express').Router();

router.get('/cookie', (req, res) => {
  const data = JSON.stringify(req.headers);

  let response = '';
  response += 'HTTP/1.1 200 OK\r\n';
  response += 'Connection: close\r\n';
  response += `Content-Length: ${data.length}\r\n`;
  response += 'Content-Type: application/json\r\n';
  response += `Cookie: ${_.repeat('a', req.query.length)}\r\n`;
  response += 'Date: Wed, 30 Nov 2016 20:19:57 GMT\r\n\r\n';
  response += data;

  res.socket.end(response);
  res.socket.destroy();
});

router.get('/content-length', (req, res) => {
  let response = '';
  response += 'HTTP/1.1 200 OK\r\n';
  response += 'Connection: close\r\n';
  response += 'Content-Length: 3\r\n';
  response += 'Access-Control-Allow-Origin: *\r\n';
  response += 'Content-Type: text/plain\r\n';
  response += 'Date: Wed, 30 Nov 2016 20:19:57 GMT\r\n\r\n';
  response += 'abcdefghijklmnopqrstuvwxyz';

  res.socket.end(response);
  res.socket.destroy();
});

module.exports = router;

Add cors header for browser testing


import React from 'react';
import { View, Button } from 'react-native';

/*
Developer screen.
Provides access to components during development.
*/
export default class DeveloperScreen extends React.Component {
  static navigationOptions = {
    tabBar: {
      label: 'Developer',
    },
    title: 'Developer',
  };

  render() {
    const { navigate } = this.props.navigation;
    return (
      <View>
        <Button onPress={() => navigate('SampleScreen')} title="Go to the sample screen" />
      </View>
    );
  }
}

import React from 'react';
import { View, Button } from 'react-native';

/*
Developer screen.
Provides access to components during development.
*/
export default class DeveloperScreen extends React.Component {
  static navigationOptions = {
    tabBar: {
      label: 'Developer',
    },
    title: 'Developer',
  };

  render() {
    const { navigate } = this.props.navigation;
    return (
      <View>
        <Button onPress={() => navigate('SampleScreen')} title="Go to the sample screen" />
        <Button onPress={() => navigate('LoginScreen')} title="Go to the login screen" />
      </View>
    );
  }
}

Add navigation to login screen from developer screen


var gateway = require(__dirname+'/../../../../');

module.exports = function(req, res){

  gateway.config.set('DOMAIN', req.body.domain);
  gateway.config.save(function(){
    res.send({ 'DOMAIN': gateway.config.get('DOMAIN') });
  });

};

var gateway = require(__dirname+'/../../../../');

/*
* @requires Config
* @function setDomain
* @description Set the domain via http for use in the
* ripple.txt file and also as the email address for
* admin basic authentication.
* @param {String} domain
*/

module.exports = function(req, res){

  gateway.config.set('DOMAIN', req.body.domain);
  gateway.config.save(function(){
    res.send({ 'DOMAIN': gateway.config.get('DOMAIN') });
  });

};

Add jsdoc to getDomain http call.


if (typeof globalThis === 'undefined') {
  // define `globalThis` for IE
  self.globalThis = this;
}

self.importScripts('opal.min.js');

// disable network access
self.importScripts = null;
self.XMLHttpRequest = null;
self.fetch = null;

var Opal = self.Opal;

var input = null;
var output = '';
var error = '';

Opal.global.console.log = function(s) {
  output += s;
};

Opal.global.console.warn = function(s) {
  error += s;
};

Opal.STDIN.$read = function() {
  if (arguments.length !== 0) {
    throw 'read: not implemented';
  }
  return (input !== null ? input : Opal.nil);
};

self.addEventListener('message', function(event) {
  var data = event.data;

  input = data.input;
  output = '';
  error = '';

  try {
    Opal.eval(data.code);
  } catch (e) {
    self.postMessage({
      output: null,
      error: null,
      exception: e.toString() || 'undefined exception',
    });
    return;
  }

  self.postMessage({
    output: output,
    error: error,
    exception: '',
  });
}, false);

if (typeof globalThis === 'undefined') {
  // define `globalThis` for IE
  self.globalThis = this;
}

self.importScripts('opal.min.js');

// disable network access
self.importScripts = null;
self.XMLHttpRequest = null;
self.fetch = null;
self.WebSocket = null;

var Opal = self.Opal;

var input = null;
var output = '';
var error = '';

Opal.global.console.log = function(s) {
  output += s;
};

Opal.global.console.warn = function(s) {
  error += s;
};

Opal.STDIN.$read = function() {
  if (arguments.length !== 0) {
    throw 'read: not implemented';
  }
  return (input !== null ? input : Opal.nil);
};

self.addEventListener('message', function(event) {
  var data = event.data;

  input = data.input;
  output = '';
  error = '';

  try {
    Opal.eval(data.code);
  } catch (e) {
    self.postMessage({
      output: null,
      error: null,
      exception: e.toString() || 'undefined exception',
    });
    return;
  }

  self.postMessage({
    output: output,
    error: error,
    exception: '',
  });
}, false);

Disable WebSocket in 'ruby-runtime-opal' modular-module


angular.module('myApp')
.directive('googleMaps', [function () {
  return {
    controller: 'MapCtrl',

    link: function (scope, elem, attrs, ctrl) {
      var mapOptions,
        latitude = attrs.latitude,
        longitude = attrs.longitude,
        map;

      mapOptions = {
        zoom: 8,
        center: new google.maps.LatLng(latitude, longitude)
      };

      map = new google.maps.Map(elem[0], mapOptions);

      // pass instance of map to controller to allow maniuplation of google map
      // objects within controller.
      ctrl.registerMap(map);
    }
  }

}]);

angular.module('myApp')
.directive('googleMaps', [function () {
  return {
    controller: 'MapCtrl',

    link: function (scope, elem, attrs, ctrl) {
      var mapOptions;
      var latitude = attrs.latitude;
      var longitude = attrs.longitude;
      var map;

      mapOptions = {
        zoom: 8,
        center: new google.maps.LatLng(latitude, longitude)
      };

      map = new google.maps.Map(elem[0], mapOptions);

      // pass instance of map to controller to allow maniuplation of google map
      // objects within controller.
      ctrl.registerMap(map);
    }
  }

}]);

Use one var declaration per variable


var ResponseParser = require('../lib/responseparser'),
	winston = require('winston'),
	fs = require('fs'),
	path = require('path');

describe('responseparser', function() {
	var parser = new ResponseParser({
		format: 'xml',
		logger: new winston.Logger({ transports: [] })
	});

	it('should return xml when format is xml', function() {
		var callbackSpy = jasmine.createSpy(),
			xml = fs.readFileSync(
				path.join(__dirname +
					'/responses/release-tracks-singletrack.xml'),
					'utf8');
		parser.parse(callbackSpy, null, xml);
		expect(callbackSpy).toHaveBeenCalled();
		expect(callbackSpy).toHaveBeenCalledWith(null, xml);
	});

	it('should return xml when format is XML', function() {
		var callbackSpy = jasmine.createSpy(),
			xml = fs.readFileSync(
				path.join(__dirname +
					'/responses/release-tracks-singletrack.xml'),
					'utf8');

		parser.format = 'XML';
		parser.parse(callbackSpy, null, xml);
		expect(callbackSpy).toHaveBeenCalled();
		expect(callbackSpy).toHaveBeenCalledWith(null, xml);
	});
});

var ResponseParser = require('../lib/responseparser'),
	winston = require('winston'),
	fs = require('fs'),
	path = require('path');

describe('responseparser', function() {
	var parser = new ResponseParser({
		format: 'xml',
		logger: new winston.Logger({ transports: [] })
	});

	it('should return xml when format is xml', function() {
		var callbackSpy = jasmine.createSpy(),
			xml = fs.readFileSync(
				path.join(__dirname +
					'/responses/release-tracks-singletrack.xml'),
					'utf8');
		parser.parse(callbackSpy, null, xml);
		expect(callbackSpy).toHaveBeenCalled();
		expect(callbackSpy).toHaveBeenCalledWith(null, xml);
	});

	it('should return xml when format is XML', function() {
		var callbackSpy = jasmine.createSpy(),
			xml = fs.readFileSync(
				path.join(__dirname +
					'/responses/release-tracks-singletrack.xml'),
					'utf8');

		parser.format = 'XML';
		parser.parse(callbackSpy, null, xml);
		expect(callbackSpy).toHaveBeenCalled();
		expect(callbackSpy).toHaveBeenCalledWith(null, xml);
	});

	it('should return javascript object when format is not xml', function() {
		var callbackSpy = jasmine.createSpy(),
			xml = fs.readFileSync(
				path.join(__dirname +
					'/responses/release-tracks-singletrack.xml'),
					'utf8');

		parser.format = 'js';
		parser.parse(callbackSpy, null, xml);
		expect(callbackSpy).toHaveBeenCalled();
		expect(typeof callbackSpy.mostRecentCall.args[1]).toEqual('object');
	});
});

Add test for xml parsing


/* global Chart */
import Ember from 'ember';

export default Ember.Component.extend({
  tagName: 'canvas',
  attributeBindings: ['width', 'height'],

  didInsertElement: function(){
    var context = this.get('element');
    var data    = this.get('data');
    var type    = this.get('type');
    var options = this.get('options');

    var chart = new Chart(context, {
      type: type,
      data: data,
      options: options
    });
    this.set('chart', chart);
    this.addObserver('data', this, this.updateChart);
    this.addObserver('data.[]', this, this.updateChart);
    this.addObserver('options', this, this.updateChart);
  },

  willDestroyElement: function(){
    this.get('chart').destroy();
    this.removeObserver('data', this, this.updateChart);
    this.removeObserver('data.[]', this, this.updateChart);
    this.removeObserver('options', this, this.updateChart);
  },

  updateChart: function(){
    var chart   = this.get('chart');
    var data    = this.get('data');
    var options = this.get('options');
    chart.config.data = data;
    chart.config.options = options;
    chart.update();
  }
});

/* global Chart */
import Ember from 'ember';

export default Ember.Component.extend({
  tagName: 'canvas',
  attributeBindings: ['width', 'height'],

  didInsertElement() {
    this._super(...arguments);
    let context = this.get('element');
    let data    = this.get('data');
    let type    = this.get('type');
    let options = this.get('options');

    let chart = new Chart(context, {
      type: type,
      data: data,
      options: options
    });
    this.set('chart', chart);
  },

  willDestroyElement() {
    this._super(...arguments);
    this.get('chart').destroy();
  },

  didUpdateAttrs() {
    this._super(...arguments);
    let chart   = this.get('chart');
    let data    = this.get('data');
    let options = this.get('options');
    let animate = this.get('animate');

    chart.config.data = data;
    chart.config.options = options;
    if (animate) {
      chart.update();
    } else {
      chart.update(0);
    }
  }
});

Change to didUpdateAttrs() from observers


var chai = require('chai');
var chaihttp = require('chai-http');
chai.use(chaihttp);
var expect = chai.expect;
var fs = require('fs');
var server = require(__dirname + '/../index.js');

describe('our NLP sever', function() {

  before(function(done) {
    fs.readFile(__dirname + '/../public/index.html', function(err, data) {
      if(err) throw err;
      this.html = data.toString();

      done();

    }.bind(this));
  });

  it('should respond to a get request with our index page', function(done) {
    chai.request('localhost:3000')
      .get('/')
      .end(function(err, res) {
        expect(err).to.eql(null);
        expect(res.status).to.eql(200);
        expect(res.text).to.eql(this.html);
        done();
      }.bind(this));
  });

});

var chai = require('chai');
var chaihttp = require('chai-http');
chai.use(chaihttp);
var expect = chai.expect;
var fs = require('fs');
var server = require(__dirname + '/../index.js');

describe('our NLP sever', function() {

  before(function(done) {
    fs.readFile(__dirname + '/../public/index.html', function(err, data) {
      if(err) throw err;
      this.html = data.toString();

      done();

    }.bind(this));
  });

  it('should respond to a GET request with our index page', function(done) {
    chai.request('localhost:3000')
      .get('/')
      .end(function(err, res) {
        expect(err).to.eql(null);
        expect(res.status).to.eql(200);
        expect(res.text).to.eql(this.html);
        done();
      }.bind(this));
  });

  it('should respond to a POST request to the process URL', function(done){
    chai.request('localhost:3000')
    .post('/process')
    .type('form')
    .send('text=You talking to me?')
    .end(function(err, res){
      expect(err).to.eql(null);
      expect(res.text).to.eql('{"you":1,"talking":1,"to":1,"me":1}')
      done();
    })
  })
});

Add test for POST request


(function($) {
  $(function () {
    window.cxVariation = cxApi.chooseVariation();
    if(window.cxVariation === 1){
      var $donate = $('.sidebar__donate-cta');
      var $link = $('.sidebar__donate-cta__button', $donate);
      var experimentHref = URI($link.attr('href')).removeSearch('utm_content').
        addSearch('utm_content', 'experiment_no_' + window.cxVariation).
        toString();
      $link.attr('href', experimentHref)

      $('.asktheeu-promo').replaceWith($donate)
    }

    $('.sidebar__donate-cta__button').on('click', function(e){
      if( typeof ga !== 'undefined' && ga.loaded ){
        e.preventDefault();
        var $link = $(this);
        ga('send', {
          hitType: 'event',
          eventCategory: 'sidebar__donate-cta',
          eventAction: 'click',
          eventLabel: document.title,
          hitCallback: function() {
            window.location.href = $link;
          }
        });
      }
    });
  });
})(window.jQuery);

(function($) {
  $(function () {
    window.cxVariation = cxApi.chooseVariation();
    if(window.cxVariation === 1){
      var $donate = $('.sidebar__donate-cta');
      var $link = $('.sidebar__donate-cta__button', $donate);
      var experimentHref = URI($link.attr('href')).removeSearch('utm_content').
        addSearch('utm_content', 'experiment_no_' + window.cxVariation).
        toString();
      $link.attr('href', experimentHref)

      $('.asktheeu-promo').replaceWith($donate)
    }

    $('.sidebar__donate-cta__button').on('click', function(e){
      if( typeof ga !== 'undefined' && ga.loaded ){
        e.preventDefault();
        var $link = $(this);
        ga('send', {
          hitType: 'event',
          eventCategory: 'sidebar__donate-cta',
          eventAction: 'click',
          eventLabel: document.title,
          hitCallback: function() {
            window.location.href = $link.attr('href');
          }
        });
      }
    });
  });
})(window.jQuery);

Fix A/B test donate button link


/**
 * Emulates stream.setTimeout() behaviour, but respects outgoing data
 * too.
 *
 * @param {Number} timeout Milliseconds
 */
exports.attach = function(stream, timeout) {
    var timer;
    var emitTimeout = function() {
        timer = undefined;
        stream.emit('timeout');
    };
    var updateTimer = function() {
        if (timer)
            clearTimeout(timer);
        timer = setTimeout(emitTimeout, timeout);
    };

    var oldWrite = stream.write;
    stream.write = function() {
        updateTimer();
        oldWrite.apply(this, arguments);
    };
    stream.addListener('data', updateTimer);
    stream.addListener('close', function() {
	if (timer)
	    clearTimeout(timer);
    });
};

/**
 * Emulates stream.setTimeout() behaviour, but respects outgoing data
 * too.
 *
 * @param {Number} timeout Milliseconds
 */
exports.attach = function(stream, timeout) {
    var timer;
    var emitTimeout = function() {
        timer = undefined;
        stream.emit('timeout');
    };
    var updateTimer = function() {
        if (timer)
            clearTimeout(timer);
        timer = setTimeout(emitTimeout, timeout);
    };

    var oldWrite = stream.write;
    stream.write = function() {
        updateTimer();
        oldWrite.apply(this, arguments);
    };
    stream.addListener('data', updateTimer);
    stream.addListener('close', function() {
		if (timer)
		    clearTimeout(timer);
	    stream.write = oldWrite;
    });
    stream.addListener('end', function() {
    	if (timer)
    	    clearTimeout(timer);
        stream.write = oldWrite;
    });
};

Fix the memory leak issue on idle timeout


const path = require('path')
const presetDefault = require('pundle-preset-default')

module.exports = {
  entry: ['./src', './index.html'],
  components: presetDefault({
    transform: {
      json5: true,
    },
  }),
  rootDirectory: __dirname,
  output: {
    rootDirectory: path.join(__dirname, 'dist'),
  },
}

const path = require('path')
const presetDefault = require('pundle-preset-default')

module.exports = {
  entry: ['./src', './index.html'],
  components: presetDefault({
    transform: {
      json5: true,
      babel: 7,
    },
  }),
  rootDirectory: __dirname,
  output: {
    rootDirectory: path.join(__dirname, 'dist'),
  },
}

Enable babel 7 in test package


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

(function() {
    "use strict";
    var Stream = require("stream");

    var root = exports || this;

    root.getDuplexStream = function() {
        var duplex = new Stream.Duplex();
        duplex.data = "";
        duplex._write = function(chunk, enc, next) {
            this.data += chunk.toString();
            next();
        };
        duplex._read = function() {
            return this.data;
        };
        return duplex;
    };

    root.getReadableStream = function() {
        var readable = new Stream.Readable();
        readable.data = "";
        readable._read = function() {
            return this.data;
        };
        return readable;
    };

})();

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

(function() {
    "use strict";
    var Stream = require("readable-stream");

    var root = exports || this;

    root.getDuplexStream = function() {
        var duplex = new Stream.Duplex();
        duplex.data = "";
        duplex._write = function(chunk, enc, next) {
            this.data += chunk.toString();
            next();
        };
        duplex._read = function() {
            return this.data;
        };
        return duplex;
    };

    root.getReadableStream = function() {
        var readable = new Stream.Readable();
        readable.data = "";
        readable._read = function() {
            return this.data;
        };
        return readable;
    };

})();

Make streams work w/ node v0.8.x+


'use strict';

var lint = require('./_lint');

//////////////////////////////
// SCSS syntax tests
//////////////////////////////
describe('no trailing zero - scss', function () {
  var file = lint.file('no-trailing-zero.scss');

  it('enforce', function (done) {
    lint.test(file, {
      'trailing-zero': 1
    }, function (data) {
      lint.assert.equal(6, data.warningCount);
      done();
    });
  });
});

//////////////////////////////
// Sass syntax tests
//////////////////////////////
describe('no trailing zero - sass', function () {
  var file = lint.file('no-trailing-zero.sass');

  it('enforce', function (done) {
    lint.test(file, {
      'trailing-zero': 1
    }, function (data) {
      lint.assert.equal(6, data.warningCount);
      done();
    });
  });
});

'use strict';

var lint = require('./_lint');

//////////////////////////////
// SCSS syntax tests
//////////////////////////////
describe('no trailing zero - scss', function () {
  var file = lint.file('no-trailing-zero.scss');

  it('enforce', function (done) {
    lint.test(file, {
      'trailing-zero': 1
    }, function (data) {
      lint.assert.equal(8, data.warningCount);
      done();
    });
  });
});

//////////////////////////////
// Sass syntax tests
//////////////////////////////
describe('no trailing zero - sass', function () {
  var file = lint.file('no-trailing-zero.sass');

  it('enforce', function (done) {
    lint.test(file, {
      'trailing-zero': 1
    }, function (data) {
      lint.assert.equal(8, data.warningCount);
      done();
    });
  });
});

Fix test file after an update to the test cases.