//
//  redis_store.js
//
//  Redis data store backend for cb_server
//

var redis = require("redis");

var redisClient = redis.createClient(
  process.env.REDIS_PORT || 6379,
  process.env.REDIS_HOST || '127.0.0.1',
  {}
);

function redisUserKey(username) {
  return "SkynetUser:"+username;
}

module.exports = {
  getUser: function (username, callback) {
    redisClient.hgetall(redisUserKey(username), callback);
  },

  isExistingUser: function (username, callback) {
    redisClient.exists(redisUserKey(username), callback);
  },

  createUser: function (user, callback) {
    redisClient.hmset(redisUserKey(user.username), user, function (err) {
      callback(err, err != null ? null : user);
    });
  },

  updateUser: function (user, callback) {
    redisClient.hmset(redisUserKey(user.username), user, function (err) {
      callback(err, err != null ? null : user);
    });
  }
}

//
//  redis_store.js
//
//  Redis data store backend for cb_server
//

var redis = require("redis");

var redisClient = redis.createClient(
  process.env.REDIS_PORT || 6379,
  process.env.REDIS_HOST || '127.0.0.1',
  {}
);

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

function redisUserKey(username) {
  return "SkynetUser:"+username;
}

module.exports = {
  getUser: function (username, callback) {
    redisClient.hgetall(redisUserKey(username), callback);
  },

  isExistingUser: function (username, callback) {
    redisClient.exists(redisUserKey(username), callback);
  },

  createUser: function (user, callback) {
    redisClient.hmset(redisUserKey(user.username), user, function (err) {
      callback(err, err != null ? null : user);
    });
  },

  updateUser: function (user, callback) {
    redisClient.hmset(redisUserKey(user.username), user, function (err) {
      callback(err, err != null ? null : user);
    });
  }
}

Add an `error` listener to the Redis client.


var app = require('ridge');

module.exports = Backbone.Collection.extend({
	constructor: function(attributes, options) {
		var _collection = this;

		Backbone.Collection.apply(_collection, arguments);

		if(_.isString(_collection.model)) {
			_collection.modelName = _collection.model;
			if(app.models[_collection.model])
				_collection.model = app.models[_collection.model];
			else {
				_collection.model = require('ridge/model').extend({
					name: _collection.model
				});
			}
		}

		return _collection;
	},

	parse: function(resp) {
		if(resp.totalCount) {
			this.totalCount = resp.totalCount;

			return _.find(resp, _.isArray);
		}

		return resp;
	}

});

var app = require('ridge');

module.exports = Backbone.Collection.extend({
	constructor: function(attributes, options) {
		var _collection = this;

		Backbone.Collection.apply(_collection, arguments);

		if(_.isString(_collection.model)) {
			_collection.modelName = _collection.model;
			if(app.models[_collection.model])
				_collection.model = app.models[_collection.model];
			else {
				_collection.model = require('ridge/model').extend({
					name: _collection.model
				});
			}
		}

		return _collection;
	},

	parse: function(resp) {
		if(_.isNumber(resp.totalCount)) {
			this.totalCount = resp.totalCount;

			return _.find(resp, _.isArray);
		}

		return resp;
	}

});

Check if resp.totalCount is number instead of truthy.


/**
 * Animable.js
 *
 * https://github.com/JWhile/Animable.js
 *
 * Animable.js
 */

(function(){ // namespace

// class Animation
function Animation(from, to, time, update)
{
    this.from = from; // :int
    this.diff = to - from; // :int

    this.last = from; // :int

    this.start = Date.now(); // :long
    this.time = time; // :int

    this.update = update; // :function
}
// function next(long now):boolean
Animation.prototype.next = function(now)
{
    var n = (now - this.start) / this.time * this.diff + this.from;

    if(n > 1)
    {
        return true;
    }
    if(n !== this.last)
    {
        this.last = n;

        this.update(n);
    }

    return false;
};

var animations = []; // :Array<Animation>

// function next():void
var next = function()
{
    if(animations.length > 0)
    {
        newFrame(next);

        var now = Date.now();

        for(var i = 0; i < animations.length; ++i)
        {
            if(animations[i].next(now))
            {
                animations.splice(i, 1);

                --i;
            }
        }
    }
};

})();

/**
 * Animable.js
 *
 * https://github.com/JWhile/Animable.js
 *
 * Animable.js
 */

(function(){ // namespace

// class Animation
function Animation(from, to, time, update)
{
    this.from = from; // :int
    this.diff = to - from; // :int

    this.last = from; // :int

    this.start = Date.now(); // :long
    this.time = time; // :int

    this.update = update; // :function
}
// function next(long now):boolean
Animation.prototype.next = function(now)
{
    var n = (now - this.start) / this.time * this.diff + this.from;

    if(n > 1)
    {
        return true;
    }
    if(n !== this.last)
    {
        this.last = n;

        this.update(n);
    }

    return false;
};

var animations = []; // :Array<Animation>
var loop = false; // :boolean

// function next():void
var next = function()
{
    loop = (animations.length > 0);

    if(loop)
    {
        newFrame(next);

        var now = Date.now();

        for(var i = 0; i < animations.length; ++i)
        {
            if(animations[i].next(now))
            {
                animations.splice(i, 1);

                --i;
            }
        }
    }
};

// function addAnimation(int from, int to, int time, function update):void
var addAnimation = function(from, to, time, update)
{
    animations.push(new Animation(from, to, time, update));

    if(!loop)
    {
        newFrame(next);
    }
};

})();

Add function addAnimation() & restart loop on adding animation


var Trie = require('../app/trie');

describe('Trie', function() {
  var trie;

  beforeEach(function() {
    trie = new Trie();
  });

  it('should be an object', function() {
    expect(trie).to.be.ok;
  });

  it('should have a root', function() {
    expect(trie.root).to.be.ok;
  });

  it('should have add method', function() {
    expect(trie.add).to.be.an.instanceof(Function);
  });

  it('should add string to trie', function() {
    expect(trie.add('test')).to.be.undefined;
  });

  it('should have search method', function() {
    expect(trie.search).to.be.an.instanceof(Function);
  });

  it('should have find method', function() {
    expect(trie.find).to.be.an.instanceof(Function);
  });
});

var Trie = require('../app/trie');

describe('Trie', function() {
  var trie;

  beforeEach(function() {
    trie = new Trie();
  });

  it('should be an object', function() {
    expect(trie).to.be.ok;
  });

  it('should have a root', function() {
    expect(trie.root).to.be.ok;
  });

  it('should have add method', function() {
    expect(trie.add).to.be.an.instanceof(Function);
  });

  it('should have search method', function() {
    expect(trie.search).to.be.an.instanceof(Function);
  });

  it('should have find method', function() {
    expect(trie.find).to.be.an.instanceof(Function);
  });

  it('should add string to trie', function() {
    expect(trie.add('test')).to.be.undefined;
  });

  it('should correctly find an added string', function() {
    trie.add('test');
    expect(trie.find('test')).to.be.true;
  });

});

Add test for find string


var express = require('express');

module.exports = function(settings) {
  var app = express.Router();

  app.get('/settings.json',  settingsIndex);
  app.post('/settings.json', settingsUpdate);

  return app;

  function settingsIndex(req, res) {
    settings.get().then(function(fetched) {
      res.json(fetched);
    });
  }

  function settingsUpdate(req, res) {
    var newSettings = req.body;

    settings.set(newSettings).then(function() {
      res.json(newSettings);
    }, function(err) { res.json(500, {error: err.toString()}) });
  }
};

var express = require('express');

module.exports = function(settings) {
  var app = express.Router();

  app.get('/settings.json',  settingsIndex);
  app.post('/settings.json', settingsUpdate);

  return app;

  function settingsIndex(req, res) {
    settings.get().then(function(fetched) {
      res.json(fetched);
    });
  }

  function settingsUpdate(req, res) {
    var newSettings = req.body;

    settings.update(newSettings).then(function() {
      res.json(newSettings);
    }, function(err) { res.json(500, {error: err.toString()}) });
  }
};

Allow partial updates to settings


import stream from 'stream'

export function toObj () {

}

export default class Json2obj extends stream.Transform {

	constructor (
		{writableObjectMode, readableObjectMode} = {
			writableObjectMode: false,
			readableObjectMode: true
		}
	) {
		super({writableObjectMode, readableObjectMode})

		this.vertices = new Set
		this.textureCoordinates = new Set
		this.vertexNormals = new Set
		this.parameterVertices = new Set
		this.faces = new Set
	}

	_flush (done) {
		done()
	}

	_transform (chunk, encoding, done) {

		if (this.writableObjectMode){
			this.vertices.add chunk.vertices.add
		}

		this.push(chunk.toStrin
		this.push('\n')
		done()
	}
}

import stream from 'stream'

export function toObj () {

}

export default class Json2obj extends stream.Transform {

	constructor (
		{writableObjectMode, readableObjectMode} = {
			writableObjectMode: false,
			readableObjectMode: true
		}
	) {
		super({writableObjectMode, readableObjectMode})

		this.vertexMap = new Map
		this.textureCoordinateSet = new Set
		this.vertexNormalSet = new Set
		this.parameterVerticeSet = new Set
		this.faceSet = new Set
	}

	_transform (chunk, encoding, done) {

		let face = this.writableObjectMode ? chunk : JSON.parse(chunk)

		this.faceSet.add(face.vertices.map(
			vertex => {

				let vertexString = JSON.stringify(vertex)

				if (this.vertexMap.has(vertexString)) {
					return this.vertexMap.get(vertexString)
				}
				else {
					let vertexMapSize = this.vertexMap.size + 1
					this.vertexMap.set(vertexString, vertexMapSize)
					return vertexMapSize
				}

				this.vertexSet.add()
			}
		))

		done()
	}

	_flush (done) {

		this.push('o Solid Object\n\n')

		for (let vertexString of this.vertexMap.keys()) {
			//console.log('hjkl',vertexString)
			let vertex = JSON.parse(vertexString)
			this.push(`v ${vertex.x} ${vertex.y} ${vertex.z}\n`)
		}

		this.push(
			'\nf ' +
			[...this.faceSet]
				.map(face => face.join(' '))
				.join('\nf ') +
			'\n'
		)

		done()
	}
}

Implement first simple version of OBJ exporter


/**
 * Default model configuration
 * (sails.config.models)
 *
 * Unless you override them, the following properties will be included
 * in each of your models.
 *
 * For more info on Sails models, see:
 * http://sailsjs.org/#/documentation/concepts/ORM
 */

module.exports.models = {

  /***************************************************************************
  *                                                                          *
  * Your app's default connection. i.e. the name of one of your app's        *
  * connections (see `config/connections.js`)                                *
  *                                                                          *
  ***************************************************************************/
  // connection: 'localDiskDb',

  /***************************************************************************
  *                                                                          *
  * How and whether Sails will attempt to automatically rebuild the          *
  * tables/collections/etc. in your schema.                                  *
  *                                                                          *
  * See http://sailsjs.org/#/documentation/concepts/ORM/model-settings.html  *
  *                                                                          *
  ***************************************************************************/
  // migrate: 'alter'

};

/**
 * Default model configuration
 * (sails.config.models)
 *
 * Unless you override them, the following properties will be included
 * in each of your models.
 *
 * For more info on Sails models, see:
 * http://sailsjs.org/#/documentation/concepts/ORM
 */

module.exports.models = {

  /***************************************************************************
  *                                                                          *
  * Your app's default connection. i.e. the name of one of your app's        *
  * connections (see `config/connections.js`)                                *
  *                                                                          *
  ***************************************************************************/
  // connection: 'localDiskDb',

  /***************************************************************************
  *                                                                          *
  * How and whether Sails will attempt to automatically rebuild the          *
  * tables/collections/etc. in your schema.                                  *
  *                                                                          *
  * See http://sailsjs.org/#/documentation/concepts/ORM/model-settings.html  *
  *                                                                          *
  ***************************************************************************/
  migrate: 'safe'

};

Set migrate to default safe to skip a step when running 'sails lift' in terminal


'use strict';

var assign        = require('es5-ext/object/assign')
  , mixin         = require('es5-ext/object/mixin')
  , d             = require('d')
  , autoBind      = require('d/auto-bind')
  , DOMRadio      = require('dbjs-dom/input/enum').Radio
  , RadioBtnGroup = require('./inline-button-group')

  , createOption = DOMRadio.prototype.createOption
  , reload =  DOMRadio.prototype.reload
  , Radio;

module.exports = Radio = function (document, type/*, options*/) {
	var options = Object(arguments[2]);
	this.controlsOptions = Object(options.controls);
	DOMRadio.call(this, document, type, options);
};

Radio.prototype = Object.create(DOMRadio.prototype);
mixin(Radio.prototype, RadioBtnGroup.prototype);
Object.defineProperties(Radio.prototype, assign({
	constructor: d(Radio),
	createOption: d(function (name) {
		var dom = createOption.call(this, name);
		this.listItems[name] = dom = dom.firstChild;
		return dom;
	})
}, autoBind({
	reload: d(function () {
		reload.apply(this, arguments);
		this.dom.insertBefore(this.classHandlerScript, this.dom.firstChild);
	})
})));

'use strict';

var assign        = require('es5-ext/object/assign')
  , mixin         = require('es5-ext/object/mixin')
  , d             = require('d')
  , autoBind      = require('d/auto-bind')
  , DOMRadio      = require('dbjs-dom/input/enum').Radio
  , RadioBtnGroup = require('./inline-button-group')

  , createOption = DOMRadio.prototype.createOption
  , reload =  DOMRadio.prototype.reload
  , Radio;

module.exports = Radio = function (document, type/*, options*/) {
	var options = Object(arguments[2]);
	this.controlsOptions = Object(options.controls);
	type.meta.forEach(function (itemMeta, name) {
		if (itemMeta.htmlClass) this[name] = itemMeta.htmlClass;
	}, this.classMap = {});
	DOMRadio.call(this, document, type, options);
};

Radio.prototype = Object.create(DOMRadio.prototype);
mixin(Radio.prototype, RadioBtnGroup.prototype);
Object.defineProperties(Radio.prototype, assign({
	constructor: d(Radio),
	createOption: d(function (name) {
		var dom = createOption.call(this, name);
		this.listItems[name] = dom = dom.firstChild;
		return dom;
	})
}, autoBind({
	reload: d(function () {
		reload.apply(this, arguments);
		this.dom.insertBefore(this.classHandlerScript, this.dom.firstChild);
	})
})));

Support html classes through meta


'use strict';

var Backbone = require('backbone');
var $ = require('jquery');
Backbone.$ = $;

var BusinessModel = require('../models/business-model');

module.exports = Backbone.Collection.extend({
	model: BusinessModel,
	url: function(location, params) {
		var URLparams = JSON.stringify(this.params);
		console.log('params in the model: ' + URLparams);
		return 'api/0_0_1/' + this.location + '/' + URLparams;
	},
	initialize: function(location, params) {
		this.location = location;
		this.params = params;
	},
	parse: function(response) {
		// console.log(JSON.parse(response).businesses);
		return JSON.parse(response).businesses;
	}
});

'use strict';

var Backbone = require('backbone');
var $ = require('jquery');
Backbone.$ = $;

var _ = require('../../bower_components/underscore');

var BusinessModel = require('../models/business-model');

module.exports = Backbone.Collection.extend({

	model: BusinessModel,

	url: function(location, params) {
		var URLstring = 'api/0_0_1/' +
			JSON.stringify(this.location) +
			'/' +
			JSON.stringify(this.params);
		return URLstring;
	},

	initialize: function(location, params) {
		this.location = location; // No longer necessary, b/c overwritten on search
		this.params = params; // Eventually necessary?
	},

	parse: function(response) {
		/* The data comes in as an array of objects (one for each lat/long point),
		each of which has a "businesses" attribute that holds an array of businesses.
		We parse the businesses out of each object and return a master businesses array.
		*/
		var allBusinesses = [];
		for (var i = 0; i < response.length; i++) {
			var innerArray = JSON.parse(response[i]).businesses;
			for (var j = 0; j < innerArray.length; j++) {
				allBusinesses.push(innerArray[j]);
			}
		}
		// Filter out duplicate businesses with Underscore
		allBusinesses = _.uniq(allBusinesses, function(business) {
			return business.id;
		});
		return allBusinesses;
	},

	search: function(latLongArray) {
		this.location = latLongArray;
		this.fetch();
	}
});

Rewrite parse() to process an array of businesses objects, each of which holds an array of individual businesses. (Also, filter out duplicate businesses)


var assert = require('chai').assert;
var ColorUtils = require('../lib/ColorUtils');

function assertRGBEqualsHSL(rgb, hsl) {
    var actualHsl = ColorUtils.RGBtoHSL(rgb);
    assert.closeTo(actualHsl[0], hsl[0], 0.01);
    assert.closeTo(actualHsl[1], hsl[1], 0.01);
    assert.closeTo(actualHsl[2], hsl[2], 0.01);
}

describe('ColorUtils', function() {
    it('should convert rgb to hsl', function() {
        assertRGBEqualsHSL([224, 60, 70], [-3.6585376, 0.7256638, 0.5568628]);
        assertRGBEqualsHSL([218, 219, 208], [65.454544, 0.1325301, 0.8372549]);
    });
});

var assert = require('chai').assert;
var ColorUtils = require('../lib/ColorUtils');

function assertRGBEqualsHSL(rgb, hsl) {
    var actualHsl = ColorUtils.RGBtoHSL(rgb);
    assert.closeTo(actualHsl[0], hsl[0], 0.01);
    assert.closeTo(actualHsl[1], hsl[1], 0.01);
    assert.closeTo(actualHsl[2], hsl[2], 0.01);
}

describe('ColorUtils', function() {
    it('should convert rgb to hsl', function() {
        assertRGBEqualsHSL([224, 60, 70], [356, 0.7256638, 0.5568628]);
        assertRGBEqualsHSL([218, 219, 208], [65, 0.1325301, 0.8372549]);
    });
});

Fix test to match ColorUtils behavior


/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

require('ass')
var dbServer = require('fxa-auth-db-server')
var backendTests = require('fxa-auth-db-server/test/backend')
var config = require('../../config')
var log = { trace: console.log, error: console.log, stat: console.log, info: console.log }
var DB = require('../../db/mysql')(log, dbServer.errors)
var P = require('bluebird')

var server

// defer to allow ass code coverage results to complete processing
if (process.env.ASS_CODE_COVERAGE) {
  process.on('SIGINT', function() {
    process.nextTick(process.exit)
  })
}

var db

DB.connect(config)
  .then(function (newDb) {
    db = newDb
    server = dbServer.createServer(db)
    var d = P.defer()
    server.listen(config.port, config.hostname, function() {
      d.resolve()
    })
    server.on('failure', function (d) { console.error(d.err.code, d.url)})
    server.on('success', function (d) { console.log(d.method, d.url)})
    return d.promise
  })
  .then(function() {
    return backendTests.remote(config)
  })
  .then(function() {
    server.close()
    db.close()
  })

/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

require('ass')
var dbServer = require('fxa-auth-db-server')
var backendTests = require('fxa-auth-db-server/test/backend')
var config = require('../../config')
var noop = function () {}
var log = { trace: noop, error: noop, stat: noop, info: noop }
var DB = require('../../db/mysql')(log, dbServer.errors)
var P = require('bluebird')

var server

// defer to allow ass code coverage results to complete processing
if (process.env.ASS_CODE_COVERAGE) {
  process.on('SIGINT', function() {
    process.nextTick(process.exit)
  })
}

var db

DB.connect(config)
  .then(function (newDb) {
    db = newDb
    server = dbServer.createServer(db)
    var d = P.defer()
    server.listen(config.port, config.hostname, function() {
      d.resolve()
    })
    return d.promise
  })
  .then(function() {
    return backendTests.remote(config)
  })
  .then(function() {
    server.close()
    db.close()
  })

Remove console logging during test run.


var Path    = require("path"),
    futil   = require("sake/file-utils"),
    files   = new FileList("**/*", "some/non-existing/file.js")
;

require("sake/clean");
require("sake/clobber");

desc("Sample task one.");
task("one", ["two"], function (t) {
    // task actions
    t.done();
});

desc("Sample task two.");
task("two", function (t) {
    t.done();
});

desc("Test text file.");
file("test.txt", ["LICENSE"], function (t) {
    console.log(t.name);
    write(t.name, read(t.prerequisites[0], "utf8"), "utf8");
    t.done();
});

desc("Default task.");
task("default", ["test.txt"]);

taskSync("testSync", function (t) {
    console.log(t.name);
});

taskSync("testSync2", ["testSync"], function (t) {
    console.log(t.name);
});

// console.log(task("one").prerequisites.length);
// console.log(Object.keys(Task.tasks));
// console.log(Task.getAll());
// console.log(Task.get("one") + "");

var Path    = require("path"),
    futil   = require("sake/file-utils"),
    files   = new FileList("**/*", "some/non-existing/file.js")
;

require("sake/clean");
require("sake/clobber");

desc("Sample task one.");
task("one", ["two"], function (t) {
    // task actions
    t.done();
});

desc("Sample task two.");
task("two", function (t) {
    t.done();
});

directory("tmp/path/directory");
CLOBBER.include("tmp");

desc("Test text file.");
fileSync("tmp/path/directory/test.txt", ["tmp/path/directory", "LICENSE"], function (t) {
    console.log(t.name);
    write(t.name, read(t.prerequisites[1], "utf8"), "utf8");
});
CLEAN.include("tmp/path/directory/test.txt");

desc("Default task.");
task("default", ["tmp/path/directory/test.txt"]);

taskSync("testSync", function (t) {
    console.log(t.name);
});

taskSync("testSync2", ["testSync"], function (t) {
    console.log(t.name);
});

// console.log(task("one").prerequisites.length);
// console.log(Object.keys(Task.tasks));
// console.log(Task.getAll());
// console.log(Task.get("one") + "");

Add a directory and clean and clobber tasks.


var gulp = require('gulp');
var tslint = require('gulp-tslint');
var shell = require('gulp-shell')

var serverFiles = {
    src: 'server/src/**/*.ts'
};

var clientFiles = {
    src: 'client/src/**/*.ts'
}

gulp.task('compileClient', shell.task([
    'cd client && tsc -p .'
]));

gulp.task('compileServer', shell.task([
    'cd server && tsc -p .'
]));

gulp.task('tslint', function () {
    return gulp.src([serverFiles.src, clientFiles.src])
        .pipe(tslint({
            formatter: "verbose"
        }))
        .pipe(tslint.report())
});

gulp.task('default', ['compileServer', 'compileClient', 'tslint']);

var gulp = require('gulp');
var tslint = require('gulp-tslint');
var shell = require('gulp-shell')

var serverFiles = {
    src: 'server/src/**/*.ts'
};

var clientFiles = {
    src: 'client/src/**/*.ts'
}

gulp.task('compileClient', shell.task([
    'cd client && npm install && tsc -p .'
]));

gulp.task('compileServer', shell.task([
    'cd server && npm install && tsc -p .'
]));

gulp.task('tslint', function () {
    return gulp.src([serverFiles.src, clientFiles.src])
        .pipe(tslint({
            formatter: "verbose"
        }))
        .pipe(tslint.report())
});

gulp.task('default', ['compileServer', 'compileClient', 'tslint']);

Install npm packages for client/server before building


import { addNpmScriptMemory } from '../utils';

export default async function generateDeployment(params) {
  switch (params.deployment) {
    case 'heroku':
      if (params.testing) {
        addNpmScriptMemory('deploy', 'npm test && git push heroku master');
      } else {
        addNpmScriptMemory('deploy', 'git push heroku master');
      }
      addNpmScriptMemory('postdeploy', 'echo Succesfully deployed to Heroku!');
      break;
    case 'azure':
      break;
    case 'bluemix':
      break;
    case 'digitalOcean':
      break;
    default:
  }
}

import { addNpmScriptMemory } from '../utils';

export default async function generateDeployment(params) {
  switch (params.deployment) {
    case 'heroku':
      if (params.testing) {
        addNpmScriptMemory('deploy', 'npm test && git push heroku master', params);
      } else {
        addNpmScriptMemory('deploy', 'git push heroku master', params);
      }
      addNpmScriptMemory('postdeploy', 'echo Succesfully deployed to Heroku!', params);
      break;
    case 'azure':
      break;
    case 'bluemix':
      break;
    case 'digitalOcean':
      break;
    default:
  }
}

Add missing params argument to addNpmScript deploy


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

describe('banner', function() {

  var FILEPATH = 'test-target.js';

  context('without options (using defaults)', function() {
    var expectation = '/*!\n * add-banner <https://github.com/jonschlinkert/add-banner>\n *\n * Copyright (c) 2018 Jon Schlinkert, contributors.\n * Licensed under the MIT license.\n */\n\n';
    it('expected to populate banner', function() {
      expect(banner(FILEPATH)).to.eql(expectation);
    });
  });

  context('with specific options', function() {

    var options = {
      name: 'addbanner',
      author: 'J. Schlinkert (https://github.com/jonschlinkert)',
      homepage: 'https://github.com/jonschlinkert/addbanner',
      banner: 'banner.tmpl',
      year: '2017',
      license: 'GPL-3'
    };

    var expectation = '/*!\n * addbanner <https://github.com/jonschlinkert/addbanner>\n *\n * Copyright (c) 2017 J. Schlinkert, contributors.\n * Licensed under the GPL-3 license.\n */\n\n';

    it('expected to populate banner', function() {
      expect(banner(FILEPATH, options)).to.eql(expectation);
    });
  });

});

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

describe('banner', function() {

  var FILEPATH = 'test-target.js';

  context('without options (using defaults)', function() {
    var year =  new Date().getFullYear();
    var expectation = '/*!\n * add-banner <https://github.com/jonschlinkert/add-banner>\n *\n * Copyright (c) ' + year + ' Jon Schlinkert, contributors.\n * Licensed under the MIT license.\n */\n\n';
    it('expected to populate banner', function() {
      expect(banner(FILEPATH)).to.eql(expectation);
    });
  });

  context('with specific options', function() {

    var options = {
      name: 'addbanner',
      author: 'J. Schlinkert (https://github.com/jonschlinkert)',
      homepage: 'https://github.com/jonschlinkert/addbanner',
      banner: 'banner.tmpl',
      year: '2017',
      license: 'GPL-3'
    };

    var expectation = '/*!\n * addbanner <https://github.com/jonschlinkert/addbanner>\n *\n * Copyright (c) 2017 J. Schlinkert, contributors.\n * Licensed under the GPL-3 license.\n */\n\n';

    it('expected to populate banner', function() {
      expect(banner(FILEPATH, options)).to.eql(expectation);
    });
  });

});

Fix spec failing over year with default options


/// <reference path="../Services/AccountService.js" />
/// <reference path="../Services/PollService.js" />
(function () {
    angular
        .module('GVA.Creation')
        .controller('DashboardController', DashboardController);

    DashboardController.$inject = ['$scope', 'AccountService', 'PollService'];

    function DashboardController($scope, AccountService, PollService) {

        $scope.account = AccountService.account;
        $scope.createPoll = createNewPoll;
        $scope.getUserPolls = getUserPolls;
        $scope.navigateToManagePage = navigateToManagePage;

        $scope.userPolls = {};

        activate();

        function activate() {
            AccountService.registerAccountObserver(function () {
                $scope.account = AccountService.account;
            });

            getUserPolls();
        }

        function createNewPoll(question) {
            PollService.createPoll(question, createPollSuccessCallback);
        }

        function createPollSuccessCallback(data) {
            navigateToManagePage(data.manageId);
        }

        function getUserPolls() {
            PollService.getUserPolls()
                .success(function (data) {
                    $scope.userPolls = data;
                });
        }

        function navigateToManagePage(manageId) {
            window.location.href = '/#/Manage/' + manageId;
        };
    }

})();

/// <reference path="../Services/AccountService.js" />
/// <reference path="../Services/PollService.js" />
(function () {
    angular
        .module('GVA.Creation')
        .controller('DashboardController', DashboardController);

    DashboardController.$inject = ['$scope', 'AccountService', 'PollService'];

    function DashboardController($scope, AccountService, PollService) {

        $scope.account = AccountService.account;
        $scope.createPoll = createNewPoll;
        $scope.getUserPolls = getUserPolls;
        $scope.navigateToManagePage = navigateToManagePage;

        $scope.userPolls = {};

        activate();

        function activate() {
            AccountService.registerAccountObserver(function () {
                $scope.account = AccountService.account;
            });

            getUserPolls();
        }

        function createNewPoll(question) {
            PollService.createPoll(question, createPollSuccessCallback);
        }

        function createPollSuccessCallback(data) {
            navigateToManagePage(data.ManageId);
        }

        function getUserPolls() {
            PollService.getUserPolls()
                .success(function (data) {
                    $scope.userPolls = data;
                });
        }

        function navigateToManagePage(manageId) {
            window.location.href = '/#/Manage/' + manageId;
        };
    }

})();

Fix manageId typo causing error when creating on dashboard


var i2c = require('i2c');
var _ = require('lodash');
var utils = require('./utils');

var BH1750 = function (opts) {
    this.options = _.extend({}, {
        address: 0x23,
        device: '/dev/i2c-1',
        command: 0x10,
        length: 2
    }, opts);
    this.wire = new i2c(this.options.address, {device: this.options.device});
};

BH1750.prototype.readLight = function (cb) {
    var self = this;
    if (!utils.exists(cb)) {
        console.error("missing callback");
        return;
    }
    this.wire.writeByte(this.options.command, function (err) {
        if (utils.exists(err)) {
            console.error("error write byte to BH1750 - command: ", self.options.command);
        }
    });
    this.wire.readBytes(this.options.command, this.options.length, function (err, res) {
        var hi = res.readUInt8(0);
        var lo = res.readUInt8(1);
        cb.call(self, ((hi << 8) + lo));
    });
};

module.exports = BH1750;

var i2c = require('i2c');
var _ = require('lodash');
var utils = require('./utils');

var BH1750 = function (opts) {
    this.options = _.extend({}, {
        address: 0x23,
        device: '/dev/i2c-1',
        command: 0x10,
        length: 2
    }, opts);
    this.wire = new i2c(this.options.address, {device: this.options.device});
};

BH1750.prototype.readLight = function (cb) {
    var self = this;
    if (!utils.exists(cb)) {
        console.error("missing callback");
        return;
    }
    this.wire.writeByte(this.options.command, function (err) {
        if (utils.exists(err)) {
            console.error("error write byte to BH1750 - command: ", self.options.command);
        }
    });
    this.wire.readBytes(this.options.command, this.options.length, function (err, res) {
        var hi = res.readUInt8(0);
        var lo = res.readUInt8(1);
        var lux = ((hi << 8) + lo)/1.2;
        if (self.options.command = 0x11) {
            lux = lux/2;
        }
        cb.call(self, lux);
    });
};

module.exports = BH1750;

Divide by 1.2 as described in BH1750 datasheet. Additionally divide by 2 when in high-resolution mode2


var defaultConfig = {
  amqpUrl: process.env.AMQP_URL || 'amqp://localhost',
  prefetch: process.env.AMQP_PREFETCH || 1,
  isRequeueEnabled: true
};

module.exports = function(config) {
  require('./lib/boot/logger');

  return {
    producer: require('./lib/producer')(config || defaultConfig),
    consumer: require('./lib/consumer')(config || defaultConfig)
  };
};

var defaultConfig = {
  amqpUrl: process.env.BUNNYMQ_URL || 'amqp://localhost',
  prefetch: process.env.BUNNYMQ_PREFETCH || 1,
  isRequeueEnabled: true
};

module.exports = function(config) {
  require('./lib/boot/logger');

  return {
    producer: require('./lib/producer')(config || defaultConfig),
    consumer: require('./lib/consumer')(config || defaultConfig)
  };
};

Rename env var to BUNNYMQ_URL instead of AMQP_URL


'use strict';
require('coffee-script/register');

var devServer = require('./devServer.coffee');
var build = require('./build.coffee');

exports.develop = function(config) {
	console.log('Starting dev server…');
	build.buildDevIndex(config);
	devServer.dev('build');
};

exports.build = function(config) {
	console.log('Building site…');
	build.build(config);
};

'use strict';
require('coffee-script/register');

var devServer = require('./devServer.coffee');
var build = require('./build.coffee');

exports.develop = function(config) {
	build.buildDevIndex(config);
	devServer.dev('build');
};

exports.build = function(config) {
	build.build(config);
};

Drop console.logs from entry point


const Discord = require('discord.js');
const client = new Discord.Client();

const config = require('./config.json');
const commands = require('./commands.js')

client.on('ready', () => {
    console.log("I am ready!");
    client.owner = client.users.get(config.ids.soupmaster);

    let spam = client.channels.get(config.ids.spam_channel);

    spam.send(`${client.owner}, soup is ready!`);
});

client.on('message', (message) => {
  // Ignore bot messages or messages not starting with prefix
    if (!message.content.startsWith(config.prefix) || message.author.bot) return;

    let _content = message.content.slice(config.prefix.length),
        params   = _content.split(/ +/),
        command  = params.shift(),
        msg      = _content.slice(command.length + 1);

    if (commands[command] !== undefined) {
        commands[command](message, config, msg, ...params);
    } else {
        message.reply("that's not a valid command.");
    }
});

client.on('disconnected', () => {
    console.log("Disconnected!");
    process.exit(0);
});

client.login(config.token);

const Discord = require('discord.js');
const client = new Discord.Client();

const config = require('./config.json');
const commands = require('./commands.js')

client.on('ready', () => {
    console.log("I am ready!");
    client.owner = client.users.get(config.ids.soupmaster);

    let spam = client.channels.get(config.ids.spam_channel);

    spam.send(`${client.owner}, soup is ready!`);
});

client.on('message', (message) => {
  // Ignore bot messages or messages not starting with prefix
    if (!message.content.startsWith(config.prefix) || message.author.bot) return;

    let _content = message.content.slice(config.prefix.length),
        [command, ...params] = _content.split(/ +/),
        msg      = _content.slice(command.length + 1);

    if (commands[command] !== undefined) {
        commands[command](message, config, msg, ...params);
    } else {
        message.reply("that's not a valid command.");
    }
});

client.on('disconnected', () => {
    console.log("Disconnected!");
    process.exit(0);
});

client.login(config.token);

Use destructuring for command and params


import * as React from "react";

/**
  * Main List Element to be displayed on the Navigation.
 */
const NavigationItem = props =>
{props.innerText}

/**
 * Main Navigation List.
 * Instance will be embedded in ./Header component.
 * @param {options: [option]} props arbituary object containing an array of
 * options, where each option = {href:String, text: String}.
 */
export const Navigation = (props) => {
  return (

      {props.options.map(option => <NavigationItem href={option.link} innerText={option.text}/>)}

  );
}

import * as React from "react";

/**
  * Main List Element to be displayed on the Navigation.
  * @param {href:String, innerText:String}
 */
const NavigationItem = props =>
{props.innerText}

/**
 * Main Navigation List.
 * Instance will be embedded in ./Header component.
 * @param {options: [option]} props arbituary object containing an array of
 * options, where each option = {link:String, text: String}.
 */
export const Navigation = (props) => {
  return (

      {props.options.map(option => <NavigationItem href={option.link} innerText={option.text}/>)}

  );
}

Update comments to be more descriptive of the props


// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

var localStrings = new LocalStrings();

/**
 * Window onload handler, sets up the page.
 */
function load() {
  chrome.send('getPrinters');
};

/**
 * Fill the printer list drop down.
 */
function setPrinters(printers) {
  if (printers.length > 0) {
    for (var i = 0; i < printers.length; ++i) {
      var option = document.createElement('option');
      option.textContent = printers[i];
      $('printer-list').add(option);
    }
  } else {
    var option = document.createElement('option');
    option.textContent = localStrings.getString('no-printer');
    $('printer-list').add(option);
    $('printer-list').disabled = true;
    $('print-button').disabled = true;
  }
}

window.addEventListener('DOMContentLoaded', load);

// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

var localStrings = new LocalStrings();

/**
 * Window onload handler, sets up the page.
 */
function load() {
  $('cancel-button').addEventListener('click', function(e) {
    window.close();
  });

  chrome.send('getPrinters');
};

/**
 * Fill the printer list drop down.
 */
function setPrinters(printers) {
  if (printers.length > 0) {
    for (var i = 0; i < printers.length; ++i) {
      var option = document.createElement('option');
      option.textContent = printers[i];
      $('printer-list').add(option);
    }
  } else {
    var option = document.createElement('option');
    option.textContent = localStrings.getString('no-printer');
    $('printer-list').add(option);
    $('printer-list').disabled = true;
    $('print-button').disabled = true;
  }
}

window.addEventListener('DOMContentLoaded', load);

Print Preview: Hook up the cancel button.


'use strict';

var selector = document.location.pathname.startsWith('/m/') ? '.fgG0' : '.record';
var elements = document.querySelectorAll(selector);
if (elements.length) {
  var line = Array.prototype.filter.call(elements, function (record) {
    return record.textContent.indexOf('文章網址') !== -1;
  })[0].textContent;
  var matched = line.match(/(https*:\/\/[\w\.\/]+)/);
  if (matched) {
    window.location.href = matched[1];
  }
}

if (window.location.hostname == "webptt.com") {
  var search = window.location.search.split("n=");
  if (search.length > 1) {
    window.location.href = "https://www.ptt.cc/" + search[1];
  }
}

if (window.location.hostname == "moptt.tw") {
  let regexp = /\/p\/([^\.]+)\.(.*)/;
  if (regexp.test(window.location.pathname)) {
    window.location.href = window.location.pathname.replace(regexp, "https://www.ptt.cc/bbs/$1/$2.html");
  }
}

'use strict';

var selector = document.location.pathname.startsWith('/m/') ? '.fgG0' : '.record';
var elements = document.querySelectorAll(selector);
if (elements.length) {
  var line = Array.prototype.filter.call(elements, function (record) {
    return record.textContent.indexOf('文章網址') !== -1;
  })[0].textContent;
  var matched = line.match(/(https*:\/\/[\w\.\/-]+)/);
  if (matched) {
    window.location.href = matched[1];
  }
}

if (window.location.hostname == "webptt.com") {
  var search = window.location.search.split("n=");
  if (search.length > 1) {
    window.location.href = "https://www.ptt.cc/" + search[1];
  }
}

if (window.location.hostname == "moptt.tw") {
  let regexp = /\/p\/([^\.]+)\.(.+)/;
  if (regexp.test(window.location.pathname)) {
    window.location.href = window.location.pathname.replace(regexp, "https://www.ptt.cc/bbs/$1/$2.html");
  }
}

Fix regular expressions for disp.cc and moptt.tw


describe("Support Functions for Unit Testing", function() {
	describe("resetDB", function() {
		it("should restore the DB to factory settings", function* () {
			this.timeout(30000);
			yield Zotero.Items.erase(1);
			assert.isFalse(yield Zotero.Items.getAsync(1));
			yield resetDB();
			var item = yield Zotero.Items.getAsync(1);
			assert.isObject(item);
			yield item.loadItemData();
			assert.equal(item.getField("url"), "https://www.zotero.org/support/quick_start_guide");
		});
	});
});

describe("Support Functions for Unit Testing", function() {
	describe("resetDB", function() {
		it("should restore the DB to factory settings", function* () {
			this.timeout(45000);
			yield Zotero.Items.erase(1);
			assert.isFalse(yield Zotero.Items.getAsync(1));
			yield resetDB();
			var item = yield Zotero.Items.getAsync(1);
			assert.isObject(item);
			yield item.loadItemData();
			assert.equal(item.getField("url"), "https://www.zotero.org/support/quick_start_guide");
		});
	});
});

Increase resetDB test timeout to 45 seconds


// BusinessProcess processing steps resolution

'use strict';

var memoize                  = require('memoizee/plain')
  , defineMultipleProcess    = require('../lib/multiple-process')
  , defineProcessingStepBase = require('../processing-step-base')
  , defineBusinessProcess    = require('./base');

module.exports = memoize(function (db/* options */) {
	var BusinessProcess    = defineBusinessProcess(db, arguments[1])
	  , MultipleProcess    = defineMultipleProcess(db)
	  , ProcessingStepBase = defineProcessingStepBase(db);

	BusinessProcess.prototype.defineProperties({
		processingSteps: { type: MultipleProcess, nested: true }
	});

	BusinessProcess.prototype.processingSteps.map._descriptorPrototype_.type = ProcessingStepBase;
	BusinessProcess.prototype.processingSteps.defineProperties({
		// Applicable procesing steps
		applicable: { type: ProcessingStepBase },
		// Revision processing steps
		revisions: { type: ProcessingStepBase, multiple: true, value: function (_observe) {
			var result   = []
			  , revision = this.map.revision;

			if (revision) {
				var isApplicable = revision._get ? _observe(revision._isApplicable) : revision.isApplicable;
				if (isApplicable) result.push(revision);
			}

			return result;
		} }
	});

	return BusinessProcess;
}, { normalizer: require('memoizee/normalizers/get-1')() });

// BusinessProcess processing steps resolution

'use strict';

var memoize                  = require('memoizee/plain')
  , defineMultipleProcess    = require('../lib/multiple-process')
  , defineProcessingStepBase = require('../processing-step-base')
  , defineBusinessProcess    = require('./base');

module.exports = memoize(function (db/* options */) {
	var BusinessProcess    = defineBusinessProcess(db, arguments[1])
	  , MultipleProcess    = defineMultipleProcess(db)
	  , ProcessingStepBase = defineProcessingStepBase(db);

	BusinessProcess.prototype.defineProperties({
		processingSteps: { type: MultipleProcess, nested: true }
	});

	BusinessProcess.prototype.processingSteps.map._descriptorPrototype_.type = ProcessingStepBase;
	BusinessProcess.prototype.processingSteps.defineProperties({
		// Applicable procesing steps
		applicable: { type: ProcessingStepBase },
		// Revision processing steps
		revisions: { type: ProcessingStepBase, multiple: true, value: function (_observe) {
			var result = [], type = this.database.RevisionProcessingStep;

			if (!type) return;
			this.applicable.forEach(function (step) {
				if (step instanceof type) result.push(step);
			});

			return result;
		} }
	});

	return BusinessProcess;
}, { normalizer: require('memoizee/normalizers/get-1')() });

Fix resolution of revision steps


var Good = require("good");
var goodConsole = require("good-console");
var moment = require("moment");

var LOG_PATH = process.env.LOG_PATH ||
  "./logs/log-" + moment().format("YYYYMMDD");

// Log to file FALSE by default
var LOG_FILE = process.env.LOG_FILE || "false";

if (LOG_FILE !== "false" && LOG_FILE !== "true") {
  throw new Error("LOG_FILE env variable must be \"true\" or \"false\"");
}

var reporters = [];

if (LOG_FILE) {
  reporters.push(new Good.GoodFile(LOG_PATH, {
    ops: "*",
    request: "*",
    log: "*",
    error: "*"
  }));
}

// Setup console reporter
// Default args
var consoleArgs = [{
  log: "*",
  error: "*",
  request: "*"
}];

// Set args for test env
if (process.env.NODE_ENV === "test") {
  consoleArgs = [{
    log: ["error"],
    error: "*"
  }];
}

reporters.push({
  reporter: goodConsole,
  args: consoleArgs
});

module.exports = reporters;

var Good = require("good");
var goodConsole = require("good-console");
var moment = require("moment");

var LOG_PATH = process.env.LOG_PATH ||
  "./logs/log-" + moment().format("YYYYMMDD");

// Log to file FALSE by default
var LOG_FILE = process.env.LOG_FILE || "false";

if (LOG_FILE !== "false" && LOG_FILE !== "true") {
  throw new Error("LOG_FILE env variable must be \"true\" or \"false\"");
}

var reporters = [];

if (LOG_FILE === "true") {
  reporters.push(new Good.GoodFile(LOG_PATH, {
    ops: "*",
    request: "*",
    log: "*",
    error: "*"
  }));
}

// Setup console reporter
// Default args
var consoleArgs = [{
  log: "*",
  error: "*",
  request: "*"
}];

// Set args for test env
if (process.env.NODE_ENV === "test") {
  consoleArgs = [{
    log: ["error"],
    error: "*"
  }];
}

reporters.push({
  reporter: goodConsole,
  args: consoleArgs
});

module.exports = reporters;

Fix if statement in logger config


'use babel';

import _ from 'lodash';

function addProgressNotification(message) {
  atom.notifications.addInfo(message, {
    dismissable: true,
  });

  // This is a little icky but is the only method I can see in the atom
  // api for retrieving the notification that was created.
  const newNotification = _.head(
    atom.notifications
      .getNotifications()
      .filter(notification => notification.getMessage() === message),
  );

  return newNotification;
}

export default addProgressNotification;

'use babel';

import _ from 'lodash';

function addProgressNotification(message) {
  atom.notifications.addInfo(message, {
    dismissable: true,
  });

  // This is a little icky but is the only method I can see in the atom
  // api for retrieving the notification that was created.
  const newNotification = _.head(
    atom.notifications
      .getNotifications()
      .filter(
        notification =>
          notification.getMessage() === message && !notification.isDismissed(),
      ),
  );

  return newNotification;
}

export default addProgressNotification;

Fix an issue which caused notifications to persist after being dismissed


import babel from 'rollup-plugin-babel';
import babelrc from 'babelrc-rollup';

const pkg = require('./package.json');
const external = Object.keys(pkg.dependencies);

export default {
  entry: 'src/parser.js',
  plugins: [babel(babelrc())],
  external: external,
  targets: [
    {
      dest: pkg['main'],
      format: 'umd',
      moduleName: 'decaffeinate.parser',
      globals: {
        'coffee-script': 'CoffeeScript'
      }
    },
    {
      dest: pkg['jsnext:main'],
      format: 'es6'
    }
  ]
};

import babel from 'rollup-plugin-babel';
import babelrc from 'babelrc-rollup';

var pkg = require('./package.json');
var external = Object.keys(pkg.dependencies);

export default {
  entry: 'src/parser.js',
  plugins: [babel(babelrc())],
  external: external,
  targets: [
    {
      dest: pkg['main'],
      format: 'umd',
      moduleName: 'decaffeinate.parser',
      globals: {
        'coffee-script': 'CoffeeScript'
      }
    },
    {
      dest: pkg['jsnext:main'],
      format: 'es6'
    }
  ]
};

Use var so that we can build in node v0.12.


var VOWELS   = /[aeiou]/gi;
var VOWELS_AND_SPACE = /[aeiou\s]/g;

exports.parse = function parse(string, join){
  if(typeof string !== 'string'){
    throw new TypeError('Expected a string as the first option');
  }

  join = join || false;
  var replacer = VOWELS;
  if (join) {
      replacer = VOWELS_AND_SPACE;
  }

  return string.replace(replacer, '');
};

var VOWELS   = /[aeiou]/gi;
var VOWELS_AND_SPACE = /[aeiou\s]/g;

exports.parse = function parse(string, join){
  if(typeof string !== 'string'){
    throw new TypeError('Expected a string as the first option');
  }

  var replacer = VOWELS;
  if (join) {
      replacer = VOWELS_AND_SPACE;
  }

  return string.replace(replacer, '');
};

Remove casting in favor of implicit boolean value


app.directive('film', function() {
    return{
        restrict: 'E',
        controller: function($scope){
            $scope.genres = [];

            this.addGenres = function(genres){
                $scope.genres = $scope.genres.concat(genres);
            };
        },
        link: function(scope, element){
            element.addClass('info');
            element.bind('mouseenter', function(){
                console.log(scope.genres);
            });
        }
    };
});

app.directive('genre', function(){
    return {
      require: "film",
      link: function(scope, element, attrs, filmCtrl){
       var genres = attrs.genre.split(" ");
       filmCtrl.addGenres(genres);
      }
    };
});

app.directive('mouseenter', function(){
   return function(scope, element){
       element.bind('mouseenter', function(){
           element.addClass('active');
       });
   }
});

app.directive('mouseleave', function(){
   return function(scope, element){
       element.bind('mouseleave', function(){
           element.removeClass('active');
       });
   }
});

app.directive('load', function(){
    return function(scope, element, attrs){
       element.bind("click", function(){
          scope.$apply(attrs.load);
       });
    };
});

app.directive('film', function() {
    return{
        restrict: 'E',
        scope: {},
        controller: function($scope){
            $scope.genres = [];

            this.addGenres = function(genres){
                $scope.genres = $scope.genres.concat(genres);
            };
        },
        link: function(scope, element){
            element.addClass('info');
            element.bind('mouseenter', function(){
                console.log(scope.genres);
            });
        }
    };
});

app.directive('genre', function(){
    return {
      require: "film",
      link: function(scope, element, attrs, filmCtrl){
       var genres = attrs.genre.split(" ");
       filmCtrl.addGenres(genres);
      }
    };
});

app.directive('mouseenter', function(){
   return function(scope, element){
       element.bind('mouseenter', function(){
           element.addClass('active');
       });
   }
});

app.directive('mouseleave', function(){
   return function(scope, element){
       element.bind('mouseleave', function(){
           element.removeClass('active');
       });
   }
});

app.directive('load', function(){
    return function(scope, element, attrs){
       element.bind("click", function(){
          scope.$apply(attrs.load);
       });
    };
});

Add new scope for every film element, to avoid rewriting genre massive.


import emojione from 'emojione';

const newEmojiListWithOutPriorityList = (priorityList) => {
  const list = {};
  for (const key in emojione.emojioneList) { // eslint-disable-line no-restricted-syntax
    if (priorityList.hasOwnProperty(key)) { // eslint-disable-line no-prototype-builtins
      continue; // eslint-disable-line no-continue
    }
    list[key] = emojione.emojioneList[key].unicode;
  }

  return { ...priorityList, ...list };
};

const emojiList = {};

emojiList.setPriorityList = (newPriorityList) => {
  // re-generate emojiList when set PriorityList
  emojiList.list = newEmojiListWithOutPriorityList(newPriorityList);
};

// init emojiList
const priorityList = {
  ':thumbsup:': ['1f44d'],
  ':smile:': ['1f604'],
  ':heart:': ['2764-fe0f', '2764'],
  ':ok_hand:': ['1f44c'],
  ':joy:': ['1f602'],
  ':tada:': ['1f389'],
  ':see_no_evil:': ['1f648'],
  ':raised_hands:': ['1f64c'],
  ':100:': ['1f4af'],
};
emojiList.setPriorityList(priorityList);

export default emojiList;

import emojione from 'emojione';

const newEmojiListWithOutPriorityList = (priorityList) => {
  const list = {};
  for (const key in emojione.emojioneList) { // eslint-disable-line no-restricted-syntax
    if (priorityList.hasOwnProperty(key)) { // eslint-disable-line no-prototype-builtins
      continue; // eslint-disable-line no-continue
    }
    list[key] = emojione.emojioneList[key].unicode;
  }

  return { ...priorityList, ...list };
};

const emojiList = {};

emojiList.setPriorityList = (newPriorityList) => {
  // re-generate emojiList when set PriorityList
  emojiList.list = newEmojiListWithOutPriorityList(newPriorityList);
};

// init emojiList
const priorityList = {
  ':thumbsup:': ['1f44d'],
  ':smile:': ['1f604'],
  ':heart:': ['2764'],
  ':ok_hand:': ['1f44c'],
  ':joy:': ['1f602'],
  ':tada:': ['1f389'],
  ':see_no_evil:': ['1f648'],
  ':raised_hands:': ['1f64c'],
  ':100:': ['1f4af'],
};
emojiList.setPriorityList(priorityList);

export default emojiList;

Fix Unicode of emoji 'heart'


/*
 * Copyright (c) 2014-2020 Bjoern Kimminich.
 * SPDX-License-Identifier: MIT
 */

const chai = require('chai')
const sinonChai = require('sinon-chai')
const expect = chai.expect
chai.use(sinonChai)

const fs = require('fs')
const { safeLoad } = require('js-yaml')
const { promisify } = require('util')
const readFile = promisify(fs.readFile)
const path = require('path')

const loadYamlFile = async (filename) => {
  const contents = await readFile(filename, { encoding: 'utf8' })
  return safeLoad(contents)
}

describe('challengeTutorialSequence', () => {
  let challenges
  before(async () => {
    challenges = await loadYamlFile(path.join(__dirname, '../../data/static/challenges.yml'))
  })

  it('should have unique tutorial orders', async () => {
    const tutorialOrderCounts = {}

    for (const { name, tutorial } of challenges) {

      if (tutorial) {
        const order = tutorial.order
        if (!Object.prototype.hasOwnProperty.call(tutorialOrderCounts, order)) {
          tutorialOrderCounts[order] = 0
        }
        tutorialOrderCounts[order]++
      }
    }

    for (const order of Object.keys(tutorialOrderCounts)) {
      const count = tutorialOrderCounts[order]

      expect(count, `Tutorial order "${order}" is used for multiple challenges.`).to.equal(1)
    }
  })
})

/*
 * Copyright (c) 2014-2020 Bjoern Kimminich.
 * SPDX-License-Identifier: MIT
 */

const chai = require('chai')
const sinonChai = require('sinon-chai')
const expect = chai.expect
chai.use(sinonChai)

const fs = require('fs')
const { safeLoad } = require('js-yaml')
const { promisify } = require('util')
const readFile = promisify(fs.readFile)
const path = require('path')

const loadYamlFile = async (filename) => {
  const contents = await readFile(filename, { encoding: 'utf8' })
  return safeLoad(contents)
}

describe('challengeTutorialSequence', () => {
  let challenges
  before(async () => {
    challenges = await loadYamlFile(path.join(__dirname, '../../data/static/challenges.yml'))
  })

  it('should have unique tutorial orders', async () => {
    const tutorialOrderCounts = {}

    for (const { tutorial } of challenges) {
      if (tutorial) {
        const order = tutorial.order
        if (!Object.prototype.hasOwnProperty.call(tutorialOrderCounts, order)) {
          tutorialOrderCounts[order] = 0
        }
        tutorialOrderCounts[order]++
      }
    }

    for (const order of Object.keys(tutorialOrderCounts)) {
      const count = tutorialOrderCounts[order]

      expect(count, `Tutorial order "${order}" is used for multiple challenges.`).to.equal(1)
    }
  })
})

Remove unused variable for challenge name


#!/usr/bin/env node

var Http = require('http'),
    Stack = require('stack'),
    Creationix = require('creationix'),
    WebApp = require('topcube');

var PORT = 7569;
var handler = Stack(
  Creationix.log(),
  Creationix.static("/", __dirname + "/www")
);
(function listen() {
  try {
    Http.createServer(handler).listen(PORT);
  } catch (err) {
    if (err.code === "EADDRINUSE") {
      PORT++;
      listen();
      return;
    }
    throw err;
  }
  var child = WebApp("http://127.0.0.1:" + PORT + "/index.html");
}());

#!/usr/bin/env node

var Http = require('http'),
    Stack = require('stack'),
    Creationix = require('creationix'),
    TopCube = require('topcube');

var PORT = 7569;
var handler = Stack(
  Creationix.log(),
  Creationix.static("/", __dirname + "/www")
);
(function listen() {
  try {
    Http.createServer(handler).listen(PORT);
  } catch (err) {
    if (err.code === "EADDRINUSE") {
      PORT++;
      listen();
      return;
    }
    throw err;
  }
  TopCube("http://127.0.0.1:" + PORT + "/index.html");
}());

Complete rename from WebApp to TopCube


import commander from 'commander';
import containerInit from './init';
import fs from 'fs';
import path from 'path';

commander
  .command('serve')
  .alias('s')
  .description('serve ap-npm')
  .option('--config', "config file to use")
  .action(function(config) {
    let container;
    if (fs.existsSync(config)) {
      console.log("using config: " + config + '\n');
      container = containerInit(config);
    }
    else if (typeof config === 'string') {
      if (fs.existsSync(path.join(__dirname, '../', config))) {
        let configLocation = path.join(__dirname, '../', config);
        console.log("using config: " + configLocation + '\n');
        container = containerInit(configLocation);
      }
    }
    else {
      console.log("using default config\n");
      container = containerInit("../config.json");
    }

    let command = container.get('command-serve');
    command.run();
  });

commander.parse(process.argv);

if (!process.argv.length) {
  commander.outputHelp();
}

import commander from 'commander';
import containerInit from './init';
import fs from 'fs';
import path from 'path';

commander
  .command('serve')
  .alias('s')
  .description('serve ap-npm')
  .option('--config', "config file to use")
  .action(function(config) {
    let container;
    if (fs.existsSync(config)) {
      console.log("using config: " + config + '\n');
      container = containerInit(config);
    }
    else if (typeof config === 'string') {
      if (fs.existsSync(path.join(__dirname, '../', config))) {
        let configLocation = path.join(__dirname, '../', config);
        console.log("using config: " + configLocation + '\n');
        container = containerInit(configLocation);
      }
    }
    else {
      console.log("using default config\n");
      container = containerInit("../config.json");
    }

    let command = container.get('command-serve');
    command.run();
  });

commander.parse(process.argv);

if (!process.argv.slice(2).length) {
  commander.outputHelp();
}

Fix help information not showing up


'use strict';

const path = require('path');
const rimraf = require('rimraf');
const configuration = require('../config');
const ROOT = require('../config/path-helper').ROOT;

const removeDirectory = glob => {
  rimraf(glob, err => {
    if (err) {
      console.error(err);
    } else {
      console.log(`Deleted files/folders: ${glob}`);
    }
  });
};

removeDirectory(path.join(ROOT, configuration.path.build));
removeDirectory(path.join(ROOT, configuration.path.publicAssets));

const path = require("path");
const rimraf = require("rimraf");
const configuration = require("../config");
const ROOT = require("../config/path-helper").ROOT;

const removeDirectory = glob => {
  rimraf(glob, err => {
    if (err) {
      console.error(err);
    } else {
      console.log(`Deleted files/folders: ${glob}`);
    }
  });
};

removeDirectory(path.join(ROOT, configuration.path.build));
removeDirectory(path.join(ROOT, configuration.path.publicAssets));
removeDirectory(path.join(ROOT, configuration.path.publicAssets, "../sw.js"));

Remove sw.js from public folder


'use strict';

/**
 * @ngdoc directive
 * @name hmdaPilotApp.directive:FileMetadata
 * @description
 * # File Metadata directive
 * Directive for displaying metadata relevent to the current HMDA data file
 */
module.exports = /*@ngInject*/ function () {

    return {
        restrict: 'E',
        templateUrl: 'partials/fileMetadata.html',
        controller: function($scope, FileMetadata) {
            // Initialize $scope variables
            $scope.metadata = {};
            $scope.showMetadata = false;

            // Refresh the fileMetadata if it changes (usually only once on page load)
            $scope.$watch(function() {
                return FileMetadata.fileMetadata;
            }, function() {
                angular.copy(FileMetadata.fileMetadata, $scope.metadata);

                // Check to see if some of the data from the parsed file is available since
                // this information may not be immediately available like the filename
                if (FileMetadata.fileMetadata.activityYear) {
                    $scope.showMetadata = true;
                }
            }, true);
        }
    };
};

'use strict';

/**
 * @ngdoc directive
 * @name hmdaPilotApp.directive:FileMetadata
 * @description
 * # File Metadata directive
 * Directive for displaying metadata relevent to the current HMDA data file
 */
module.exports = /*@ngInject*/ function () {

    return {
        restrict: 'E',
        templateUrl: 'partials/fileMetadata.html',
        controller: /*@ngInject*/ function ($scope, FileMetadata) {
            // Initialize $scope variables
            $scope.metadata = {};
            $scope.showMetadata = false;

            // Refresh the fileMetadata if it changes (usually only once on page load)
            $scope.$watch(function() {
                return FileMetadata.fileMetadata;
            }, function() {
                angular.copy(FileMetadata.fileMetadata, $scope.metadata);

                // Check to see if some of the data from the parsed file is available since
                // this information may not be immediately available like the filename
                if (FileMetadata.fileMetadata.activityYear) {
                    $scope.showMetadata = true;
                }
            }, true);
        }
    };
};

Add missing @ngInject annotation that's required so that we don't have to use the long-form strict-di.


"use strict";

var sha1 = require('git-sha1');
var pathJoin = require('pathjoin');
var parallel = require('carallel');
var binary = require('bodec');

var mime = "text/cache-manifest";

var cache = {};

module.exports = appcache;

function appcache(servePath, req, callback) {
  // If the file is external load and cache it for speed.
  if (req.target) {
    var cached = cache[req.target.hash];
    if (!cached) {
      return req.target.fetch(function (err, input) {
        if (input === undefined) return callback(err);
        input = binary.toUnicode(input);
        cache[req.target.hash] = input.split("\n").filter(Boolean);
        return appcache(servePath, req, callback);
      });
    }
    req.args = req.args.concat(cached);
  }

  var actions = req.args.map(function (file) {
    return function (callback) {
      servePath(pathJoin(req.path, "..", file), null, callback);
    };
  });

  parallel(actions, function (err, entries) {
    if (err) return callback(err);
    var manifest = "CACHE MANIFEST\n";
    entries.forEach(function(entry, i) {
      if (entry) {
        manifest += req.args[i] + "#" + entry.etag + "\n";
      }
      else {
        manifest += req.args[i] + "\n";
      }
    });
    var etag = sha1(manifest);
    callback(null, {etag: etag, mime: mime, fetch:function (callback) {
      callback(null, manifest);
    }});
  });

}

"use strict";

var sha1 = require('git-sha1');
var pathJoin = require('pathjoin');
var parallel = require('carallel');

var mime = "text/cache-manifest";

module.exports = appcache;

function appcache(servePath, req, callback) {

  var actions = req.cache.map(function (file) {
    return function (callback) {
      servePath(pathJoin(req.paths.rule, "..", file), callback);
    };
  });

  parallel(actions, function (err, entries) {
    if (err) return callback(err);
    var manifest = "CACHE MANIFEST\n";
    entries.forEach(function(entry, i) {
      if (entry.hash) {
        manifest += req.cache[i] + "#" + entry.hash + "\n";
      }
      else {
        manifest += req.cache[i] + "\n";
      }
    });
    // TODO: output data from "fallback" and "network" in the rule
    var hash = sha1(manifest);
    callback(null, {hash: hash, mime: mime, fetch:function (callback) {
      callback(null, manifest);
    }});
  });

}

Update amd filter to new .rule format


/*
Copyright 2016 Mozilla

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.
*/

/**
 * React doesn't support non-HTML attributes unless the element is a custom
 * element which must have a hyphen in the tag name. This means React can't
 * render most of the attributes of a <webview> so we use this <web-view> as a
 * wrapper.
 */
(function() {
  class WebView extends HTMLElement {
    createdCallback() {
      const shadow = this.createShadowRoot();

      this.webview = document.createElement('webview');
      for (let i = 0; i < this.attributes.length; i++) {
        const attr = this.attributes[i];
        this.webview.setAttribute(attr.name, attr.value);
      }

      shadow.appendChild(this.webview);
    }

    attributeChangedCallback(name, oldValue, newValue) {
      if (newValue) {
        this.webview.setAttribute(name, newValue);
      } else {
        this.webview.removeAttribute(name);
      }
    }
  }

  document.registerElement('web-view', WebView);
}());

/*
Copyright 2016 Mozilla

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.
*/

/**
 * React doesn't support non-HTML attributes unless the element is a custom
 * element which must have a hyphen in the tag name. This means React can't
 * render most of the attributes of a <webview> so we use this <web-view> as a
 * wrapper.
 */
class WebView extends HTMLElement {
  createdCallback() {
    this.webview = document.createElement('webview');

    for (const { name, value } of Array.from(this.attributes)) {
      this.webview.setAttribute(name, value);
    }

    const shadow = this.createShadowRoot();
    shadow.appendChild(this.webview);
  }

  attributeChangedCallback(name, oldValue, newValue) {
    if (newValue) {
      this.webview.setAttribute(name, newValue);
    } else {
      this.webview.removeAttribute(name);
    }
  }
}

document.registerElement('web-view', WebView);

Remove useless wrapper function and cleanup WebView a bit


var fs = require('fs');
var sys = require('sys');
var exec = require('child_process').exec;

var generateParser = function(logger) {
  var log = logger.create('generate-parser');
  log.info('hello');
  return function(content, file, done) {
    log.info('Generating parser for parser test: %s', file.originalPath);

    fs.readFile(file.originalPath, function(err, data) {
      if (err) throw err;

      exec(
          'dart --checked bin/parser_generator_for_spec.dart getter-setter',
          function(err, stdout, stderr) {
            if (err) throw err;
            done(data + '\n\n' + stdout);
          });
    });
  }
}

module.exports = generateParser;

var fs = require('fs');
var sys = require('sys');
var exec = require('child_process').exec;

var generateParser = function(logger) {
  var log = logger.create('generate-parser');
  return function(content, file, done) {
    log.info('Generating parser for parser test: %s', file.originalPath);

    fs.readFile(file.originalPath, function(err, data) {
      if (err) throw err;

      exec(
          'dart --checked bin/parser_generator_for_spec.dart getter-setter',
          function(err, stdout, stderr) {
            if (err) throw err;
            done(data + '\n\n' + stdout);
          });
    });
  }
}

module.exports = generateParser;

Remove hello from the karma output


module.exports = {
  semi: false,
  trailingComma: "all",
  singleQuote: true,
  printWidth: 120,
  tabWidth: 4
};

module.exports = {
    semi: false,
    trailingComma: 'all',
    singleQuote: true,
    printWidth: 100,
    tabWidth: 4,
}

Change max width to 100


"use strict";

module.exports = (converter) => {
    return () => {
        var args = Array.from(arguments);

        return require('through2').obj(function(file, enc, cb) {

            if (file.isNull()) {
                this.push(file);
                return cb();
            }

            if (file.isStream()) {
                let gutil = require('gulp-util');
                this.emit('error', new gutil.PluginError('change-buffer', 'Streaming not supported'));
                return cb();
            }

            let data = file.contents;
            let isBuffer = Buffer.isBuffer(data);

            if (isBuffer) {
                data = data.toString('utf-8');
            }
            let arg = [data, file];
            arg.push.apply(arg, args);

            let result = converter.apply(null, arg);

            file.contents = isBuffer ? new Buffer(result) : result;

            this.push(file);
            cb();
        });
    }
};

"use strict";

module.exports = (converter) => {
    return (flags) => {
        var args = [flags];

        return require('through2').obj(function(file, enc, cb) {

            if (file.isNull()) {
                this.push(file);
                return cb();
            }

            if (file.isStream()) {
                let gutil = require('gulp-util');
                this.emit('error', new gutil.PluginError('change-buffer', 'Streaming not supported'));
                return cb();
            }

            let data = file.contents;
            let isBuffer = Buffer.isBuffer(data);

            if (isBuffer) {
                data = data.toString('utf-8');
            }
            let arg = [data, file];
            arg.push.apply(arg, args);

            let result = converter.apply(null, arg);

            file.contents = isBuffer ? new Buffer(result) : result;

            this.push(file);
            cb();
        });
    }
};

Fix the frontend production build


import styled from 'styled-components';

const Avatar = styled.a`
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: relative;
  width: ${x => x.size}px;
  height: ${x => x.size}px;
  background-color: ${x => x.theme.colors.gray.semiLight};
`;

Avatar.defaultProps = {
  size: 40,
};

export default Avatar;

import styled from 'styled-components';
import { space } from 'styled-system';

const Avatar = styled.a`
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: relative;
  width: ${x => x.size}px;
  height: ${x => x.size}px;
  background-color: ${x => x.theme.colors.gray.semiLight};
  ${space}
`;

Avatar.defaultProps = {
  size: 40,
};

export default Avatar;

Add space parameters to avatar


"use strict";

let session = require('express-session'),
    auth = module.exports = {
        passport: require('passport'),
        init: function(app, db) {
            app.use(session({secret: 'super-secret'}));
            app.use(auth.passport.initialize());
            app.use(auth.passport.session());

            auth.passport.use(db.models.user.createStrategy());
            auth.passport.serializeUser(db.models.user.serializeUser());
            auth.passport.deserializeUser(db.models.user.deserializeUser());
        }
    };

"use strict";

let session = require('express-session'),
    auth = module.exports = {
        passport: require('passport'),
        init: function(app, db) {
            app.use(session({
                secret: 'super-secret',
                resave: false,
                saveUninitialized: false
            }));

            app.use(auth.passport.initialize());
            app.use(auth.passport.session());

            auth.passport.use(db.models.user.createStrategy());
            auth.passport.serializeUser(db.models.user.serializeUser());
            auth.passport.deserializeUser(db.models.user.deserializeUser());
        }
    };

Add resave and saveUninitialized settings for express-session


const merge = require('webpack-merge');
const config = require('./webpack.config');

const host = process.env.SDF_HOST || 'localhost';
const port = process.env.SDF_PORT || '8080';

const backendHost = process.env.SDF_BACKEND_HOST || 'backend';
const backendPort = process.env.SDF_BACKEND_PORT || '3000';

module.exports = merge.smart(config, {
  devtool: 'eval-source-map',
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: 'css-loader',
            options: {
              sourceMap: true,
              modules: true,
              localIdentName: '[name]__[local]___[hash:base64:5]',
            },
          },
          {
            loader: 'postcss-loader',
          },
        ],
      },
    ],
  },
  devServer: {
    host,
    port,
    proxy: [
      {
        context: ['/socket.io/', '/login', '/auth', '/logout', '/openid-login', '/openid-auth'],
        target: `http://${backendHost}:${backendPort}`,
      },
    ],
    historyApiFallback: {
      index: '/',
    },
  },
});

const merge = require('webpack-merge');
const config = require('./webpack.config');

const host = process.env.SDF_HOST || 'localhost';
const port = process.env.SDF_PORT || '8080';

const backendHost = process.env.SDF_BACKEND_HOST || 'backend';
const backendPort = process.env.SDF_BACKEND_PORT || '3000';

module.exports = merge.smart(config, {
  devtool: 'eval-source-map',
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          {
            loader: 'style-loader',
          },
          {
            loader: 'css-loader',
            options: {
              sourceMap: true,
              modules: true,
              localIdentName: '[name]__[local]___[hash:base64:5]',
            },
          },
          {
            loader: 'postcss-loader',
          },
        ],
      },
    ],
  },
  devServer: {
    host,
    port,
    proxy: [
      {
        context: ['/login', '/auth', '/logout', '/openid-login', '/openid-auth'],
        target: `http://${backendHost}:${backendPort}`,
      },
      {
        context: ['/socket.io/'],
        target: `http://${backendHost}:${backendPort}`,
        ws: true,
      },
    ],
    historyApiFallback: {
      index: '/',
    },
  },
});

Enable proper websocket support in webpack proxy


'use strict';

(function() {
  angular.module('ncsaas')
    .controller('AuthController', ['$location', '$auth', 'authService', AuthController]);

  function AuthController($location, $auth, authService) {
    var vm = this;
    vm.isSignupFormVisible = false;
    vm.signin = signin;
    vm.user = {};
    vm.hasErrors = hasErrors;
    vm.getErrors = getErrors;
    vm.authenticate = authenticate;

    function signin() {
      authService.signin(vm.user.username, vm.user.password).then(success, error);

      function success() {
        $location.path('/projects/');
      }

      function error(response) {
        vm.errors = response.data;
      }
    }

    function hasErrors() {
      return vm.errors;
    }

    function getErrors() {
      if (vm.errors !== undefined) {
        var prettyErrors = [];
        for (var key in vm.errors) {
          if (vm.errors.hasOwnProperty(key)) {
            if (Object.prototype.toString.call(vm.errors[key]) === '[object Array]') {
              prettyErrors.push(key + ': ' + vm.errors[key].join(', '));
            } else {
              prettyErrors.push(key + ': ' + vm.errors[key]);
            }
          }
        }
        return prettyErrors;
      } else {
        return '';
      }
    }

    function authenticate(provider) {
      $auth.authenticate(provider);
    }

  }

})();

'use strict';

(function() {
  angular.module('ncsaas')
    .controller('AuthController', ['$location', '$state', '$auth', 'authService', AuthController]);

  function AuthController($location, $state, $auth, authService) {
    var vm = this;
    vm.isSignupFormVisible = false;
    vm.signin = signin;
    vm.user = {};
    vm.hasErrors = hasErrors;
    vm.getErrors = getErrors;
    vm.authenticate = authenticate;

    function signin() {
      authService.signin(vm.user.username, vm.user.password).then(success, error);

      function success() {
        $state.go('dashboard');
      }

      function error(response) {
        vm.errors = response.data;
      }
    }

    function hasErrors() {
      return vm.errors;
    }

    function getErrors() {
      if (vm.errors !== undefined) {
        var prettyErrors = [];
        for (var key in vm.errors) {
          if (vm.errors.hasOwnProperty(key)) {
            if (Object.prototype.toString.call(vm.errors[key]) === '[object Array]') {
              prettyErrors.push(key + ': ' + vm.errors[key].join(', '));
            } else {
              prettyErrors.push(key + ': ' + vm.errors[key]);
            }
          }
        }
        return prettyErrors;
      } else {
        return '';
      }
    }

    function authenticate(provider) {
      $auth.authenticate(provider);
    }

  }

})();

Change redirect after success login from projects to dashboard.


// Extension to track errors using google analytics as a data store.
(function() {

    "use strict";
    var trackJavaScriptError = function (e) {
      var errorSource = e.filename + ': ' + e.lineno;
      _gaq.push([
        '_trackEvent',
        'JavaScript Error',
        e.message,
        errorSource,
        true //nonInteractive so bounce rate isn't affected
      ]);
    };

    if (window.addEventListener) {
      window.addEventListener('error', trackJavaScriptError, false);
    } else if (window.attachEvent) {
      window.attachEvent('onerror', trackJavaScriptError);
    } else {
      window.onerror = trackJavaScriptError;
    }

}());

// Extension to track errors using google analytics as a data store.
(function() {

    "use strict";
    var trackJavaScriptError = function (e) {
      var errorSource = e.filename + ': ' + e.lineno;
      _gaq.push([
        '_trackEvent',
        'JavaScript Error',
        e.message,
        errorSource,
        1, // Set our value to 1, though we could look to tally session errors here
        true // nonInteractive so bounce rate isn't affected
      ]);
    };

    if (window.addEventListener) {
      window.addEventListener('error', trackJavaScriptError, false);
    } else if (window.attachEvent) {
      window.attachEvent('onerror', trackJavaScriptError);
    } else {
      window.onerror = trackJavaScriptError;
    }

}());

Add in `value` count, leaving non-interactive as the final parameter


import { versionOutOfDate } from '../../utils/version';

const initUpgradeNotification = () => {
  const container = document.querySelector('[data-upgrade]');

  if (!container) {
    return;
  }

  /*
  *   Expected JSON payload:
  *   {
  *       "version" : "1.2.3",    // Version number. Can only contain numbers and decimal point.
  *       "url" : "https://wagtail.io"  // Absolute URL to page/file containing release notes or actual package. It's up to you.
  *   }
  */
  const releasesUrl = 'https://releases.wagtail.io/latest.txt';
  const currentVersion = container.dataset.wagtailVersion;

  fetch(releasesUrl).then(response => {
    if (response.status !== 200) {
      // eslint-disable-next-line no-console
      console.log(`Unexpected response from ${releasesUrl}. Status: ${response.status}`);
      return false;
    }
    return response.json();
  }).then(data => {
    if (data && data.version && versionOutOfDate(data.version, currentVersion)) {
      container.querySelector('[data-upgrade-version]').innerText = data.version;
      container.querySelector('[data-upgrade-link]').setAttribute('href', data.url);
      container.style.display = '';
    }
  })
    .catch(err => {
      // eslint-disable-next-line no-console
      console.log(`Error fetching ${releasesUrl}. Error: ${err}`);
    });
};

export { initUpgradeNotification };

import { versionOutOfDate } from '../../utils/version';

const initUpgradeNotification = () => {
  const container = document.querySelector('[data-upgrade]');

  if (!container) {
    return;
  }

  /*
  *   Expected JSON payload:
  *   {
  *       "version" : "1.2.3",    // Version number. Can only contain numbers and decimal point.
  *       "url" : "https://wagtail.io"  // Absolute URL to page/file containing release notes or actual package. It's up to you.
  *   }
  */
  const releasesUrl = 'https://releases.wagtail.io/latest.txt';
  const currentVersion = container.dataset.wagtailVersion;

  fetch(releasesUrl, {
    referrerPolicy: 'strict-origin-when-cross-origin',
  }).then(response => {
    if (response.status !== 200) {
      // eslint-disable-next-line no-console
      console.log(`Unexpected response from ${releasesUrl}. Status: ${response.status}`);
      return false;
    }
    return response.json();
  }).then(data => {
    if (data && data.version && versionOutOfDate(data.version, currentVersion)) {
      container.querySelector('[data-upgrade-version]').innerText = data.version;
      container.querySelector('[data-upgrade-link]').setAttribute('href', data.url);
      container.style.display = '';
    }
  })
    .catch(err => {
      // eslint-disable-next-line no-console
      console.log(`Error fetching ${releasesUrl}. Error: ${err}`);
    });
};

export { initUpgradeNotification };

Set referer policy of upgrade notifcation to "strict-origin-when-cross-origin"


'use strict';

angular.module('cloudnode.service.history', [
])

.factory('HistoryService', function ($rootScope) {
  var history         = [];
  var historyObserver = {};

  $rootScope.$on('history.add', function (event, track){
    History.addTrack(track);
  });

  function notifyObserver(track) {
    historyObserver(track);
  }

  var History = {
    addTrack: function addHistoryItem(track) {
      var index = history.indexOf(track);
      if (index !== -1) {
        delete history[index];
      }
      history.push(track);
      notifyObserver(track);
    },

    getHistory: function getHistory() {
      return history;
    },

    onHistoryUpdate: function onHistoryUpdate(observer) {
      if (angular.isFunction(observer)) {
        historyObserver = observer;
      }
    }
  };

  return History;
});

'use strict';

angular.module('cloudnode.service.history', [
])

.factory('HistoryService', function ($rootScope) {
  var history         = [];
  var historyObserver = {};

  $rootScope.$on('history.add', function (event, track){
    History.addTrack(track);
  });

  function notifyObserver(track) {
    if (angular.isFunction(historyObserver)) {
      historyObserver(track);
    }
  }

  var History = {
    addTrack: function addHistoryItem(track) {
      var index = history.indexOf(track);
      if (index !== -1) {
        delete history[index];
      }
      history.push(track);
      notifyObserver(track);
    },

    getHistory: function getHistory() {
      return history;
    },

    onHistoryUpdate: function onHistoryUpdate(observer) {
      if (angular.isFunction(observer)) {
        historyObserver = observer;
      }
    }
  };

  return History;
});

Check if observer is actually a function


var fs = require('fs')
  , markdox = require('markdox');

var sources = [
    '../src/index.js',
    '../src/apis/users.js',
    '../src/apis/organizations.js',
    '../src/apis/memberships.js',
    '../src/apis/sourceimages.js',
    '../src/apis/operations.js',
    '../src/apis/stacks.js',
    '../src/apis/render.js'
  ]
  , readme = '../README.md'
  , tmpFile = './API.md';

var options = {
  template: './template.md.ejs',
  output: tmpFile
};

markdox.process(sources, options, function() {
  var docsStr = fs.readFileSync(tmpFile, 'utf8')
    , readmeStr = fs.readFileSync(readme, 'utf8');

  docsStr = docsStr
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/'/g, "'")
    .replace(/"/g, "'")
    .replace(/&/g, '&');

  readmeStr = readmeStr
    .replace(/(<!-- DOCS -->)(?:\r|\n|.)+(<!-- ENDDOCS -->)/gm,
              '$1' + docsStr + '$2');

  fs.writeFileSync(readme, readmeStr);
  fs.unlinkSync(tmpFile);

  process.stdout.write('Documentation generated.\n');

});

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

const sources = [
  '../src/index.js',
  '../src/apis/users.js',
  '../src/apis/organizations.js',
  '../src/apis/memberships.js',
  '../src/apis/sourceimages.js',
  '../src/apis/operations.js',
  '../src/apis/stacks.js',
  '../src/apis/render.js'
];
const readme = '../README.md';
const tmpFile = './API.md';

const options = {
  template: './template.md.ejs',
  output: tmpFile
};

markdox.process(sources, options, function() {
  let docsStr = fs.readFileSync(tmpFile, 'utf8');
  let readmeStr = fs.readFileSync(readme, 'utf8');

  docsStr = docsStr
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/'/g, "'")
    .replace(/"/g, "'")
    .replace(/&/g, '&');

  readmeStr = readmeStr
    .replace(/(<!-- DOCS -->)(?:\r|\n|.)+(<!-- ENDDOCS -->)/gm,
              '$1' + docsStr + '$2');

  fs.writeFileSync(readme, readmeStr);
  fs.unlinkSync(tmpFile);

  process.stdout.write('Documentation generated.\n');
});

Update code style of doc generator


const Benchmark = require('benchmark');
const benchmarks = require('beautify-benchmark');
const fs = require('fs-extra');

const Suite = new Benchmark.Suite('index');
Suite.on('start', () => fs.emptyDirSync('data'));
Suite.on('cycle', (event) => benchmarks.add(event.target));
Suite.on('complete', () => benchmarks.log());
Suite.on('error', (e) => console.log(e.target.error));

const Stable = require('event-storage');
const Latest = require('../index');

const WRITES = 1000;

function bench(index) {
	index.open();
	for (let i = 1; i<=WRITES; i++) {
		index.add(new Stable.Index.Entry(i,2,4,8));
	}
	index.close();

	let number;
	index.open();
	for (let entry of index.all()) {
		number = entry.number;
	}
	index.close();
	if (number < WRITES) throw new Error('Not all entries were written! Last entry was '+number);
}

Suite.add('index [stable]', function() {
	bench(new Stable.Index(this.cycles + '.index', { dataDirectory: 'data/stable' }));
});

Suite.add('index [latest]', function() {
	bench(new Latest.Index(this.cycles + '.index', { dataDirectory: 'data/latest' }));
});

Suite.run();

const Benchmark = require('benchmark');
const benchmarks = require('beautify-benchmark');
const fs = require('fs-extra');

const Suite = new Benchmark.Suite('index');
Suite.on('start', () => fs.emptyDirSync('data'));
Suite.on('cycle', (event) => benchmarks.add(event.target));
Suite.on('complete', () => benchmarks.log());
Suite.on('error', (e) => console.log(e.target.error));

const Stable = require('event-storage');
const Latest = require('../index');

const WRITES = 1000;

function bench(index) {
	index.open();
	for (let i = 1; i<=WRITES; i++) {
		index.add(new Stable.Index.Entry(i,2,4,8));
	}
	index.close();

	let number;
	index.open();
	for (let entry of index.range(-WRITES + 1)) {
		number = entry.number;
	}
	index.close();
	if (number < WRITES) throw new Error('Not all entries were written! Last entry was '+number);
}

Suite.add('index [stable]', function() {
	bench(new Stable.Index(this.cycles + '.index', { dataDirectory: 'data/stable' }));
});

Suite.add('index [latest]', function() {
	bench(new Latest.Index(this.cycles + '.index', { dataDirectory: 'data/latest' }));
});

Suite.run();

Fix index bench read range


module.exports = function(config) {
  config.set({
    files: [
      {
        pattern: "src/**/*.js",
        mutated: true,
        included: false
      },
      "test/**/*.js"
    ],
    testRunner: "mocha",
    mochaOptions: {
      // Optional mocha options
      require: [ 'babel-register' ,'jsdom-global/register']
    },
    mutator: "javascript",
    transpilers: ["babel"],
    reporter: ["html", "clear-text", "progress"],
    testFramework: "mocha",
    coverageAnalysis: "off",
    babelrcFile: ".babelrc"
  });
};

module.exports = function(config) {
  config.set({
    files: [
      {
        pattern: "src/**/*.js",
        mutated: true,
        included: false
      },
      '!src/index.js',
      "test/**/*.js"
    ],
    testRunner: "mocha",
    mochaOptions: {
      // Optional mocha options
      require: [ 'babel-register' ,'jsdom-global/register']
    },
    mutator: "javascript",
    transpilers: ["babel"],
    reporter: ["html", "progress"],
    testFramework: "mocha",
    coverageAnalysis: "off",
    babelrcFile: ".babelrc"
  });
};

Exclude main index.js from mutation testing


import express from 'express';
import logger from 'morgan';
import bodyParser from 'body-parser';

import routes from './routes';

const app = express();
const port = process.env.PORT || 3001;

app.use(logger('dev'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.use('/api-docs', express.static('docs'));

app.use('/api/v1', routes);

if (process.env.NODE_ENV === 'production') {
  app.use('/', express.static('client/build'));
  app.use('/dashboard', express.static('client/build'));
}

if (process.env.NODE_ENV === 'production') {
  app.use('*', express.static('client/build'));
}

app.get('/', (req, res) => {
  res.status(201).json({
    title: 'More-Recipes',
    message: 'Please navigate this API via \'/api/v1/\' url prefix'
  });
});

app.get('*', (req, res) => {
  res.status(404).send({
    success: false,
    message: 'invalid link'
  });
});

app.post('*', (req, res) => {
  res.status(404).send({
    success: false,
    message: 'invalid link'
  });
});

app.listen(port, () => {
});

export default app;

import express from 'express';
import logger from 'morgan';
import bodyParser from 'body-parser';

import routes from './routes';

const app = express();
const port = process.env.PORT || 3001;

app.use(logger('dev'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.use('/api-docs', express.static('docs'));

app.use('/api/v1/', routes);

if (process.env.NODE_ENV === 'production') {
  app.use('/', express.static('client/build'));
  app.use('/dashboard', express.static('client/build'));
}

if (process.env.NODE_ENV === 'production') {
  app.use('*', express.static('client/build'));
}

app.get('/', (req, res) => {
  res.status(201).json({
    title: 'More-Recipes',
    message: 'Please navigate this API via \'/api/v1/\' url prefix'
  });
});

app.get('*', (req, res) => {
  res.status(404).send({
    success: false,
    message: 'invalid link'
  });
});

app.post('*', (req, res) => {
  res.status(404).send({
    success: false,
    message: 'invalid link'
  });
});

app.listen(port, () => {
});

export default app;

Fix bug in endpoint test opn heroku


'use strict';

angular.module('QSD.Tardis')

  .directive('tardisTrafficLight', function() {

    console.log( "Initializing the traffic light");

    return {
      restrict: 'AEC',
      scope: {
          light: '='
      },
      templateUrl: "views/directives/traffic-light.html"
    };

  });

'use strict';

angular.module('QSD.Tardis')

  .directive('tardisTrafficLight', function() {

    return {
      restrict: 'AEC',
      scope: {
          light: '='
      },
      link: function( $scope ) {
        $scope.$watch( 'light', function() {
            $scope.light.state = $scope.light.state.toLowerCase();
        } );
      },
      templateUrl: "views/directives/traffic-light.html"
    };

  });

Put watch on the lights var


module.exports = {
  componentDefaults: {
    homeOfficeCountries: [
      'nationality',
      'former-nationality',
      'dual-nationality',
      'nominated-nationality',
      'nationality-error',
      'country',
      'someone-else-nationality'
    ]
  }
};

module.exports = {
  componentDefaults: {
    homeOfficeCountries: [
      'nationality',
      'former-nationality',
      'dual-nationality',
      'nominated-nationality',
      'nationality-error',
      'country',
      'country-of-birth',
      'someone-else-nationality'
    ]
  }
};

Add config for country of birth for homeoffice countries component


/*global global*/
var argv = require('minimist')(process.argv.slice(2));

global.require = require;
var unexpected = require('unexpected').clone().installPlugin(require('./lib/unexpectedStream'));
var generator = require('unexpected-documentation-site-generator');
argv.unexpected = unexpected;
generator(argv);

/*global global*/
var argv = require('minimist')(process.argv.slice(2));

global.require = require;
var unexpected = require('unexpected').clone().installPlugin(require('./lib/unexpectedStream'));
unexpected.output.preferredWidth = 80;
var generator = require('unexpected-documentation-site-generator');
argv.unexpected = unexpected;
generator(argv);

Use a fixed width for the output snippets on the documentation site.


/*
 * Localisation Manager
 *
 * @author: Nils Hörrmann, post@nilshoerrmann.de
 * @source: http://github.com/symphonists/localisationmanager
 */
(function($) {
	$(document).ready(function() {

		// Language strings
		Symphony.Language.add({
			'Sort strings alphabetically': false
		});

		// Append sort option
		var context = $('#context'),
			table = $('table'),
			sort = $('
<input type="checkbox" name="sortit" /> ' + Symphony.Language.get('Sort strings alphabetically') + '
').appendTo(context);

		// Events
		sort.click(function(event) {

			// Get status
			var checked = $(event.target).attr('checked');

			// Add or remove sort option
			table.find('a').each(function(index, link) {
				link = $(link);
				var href = link.attr('href');

				// Is checked?
				if(checked) {
					link.attr('href', href + '?sort');
				}

				// Is not checked?
				else {
					link.attr('href', href.replace('?sort', ''));
				}
			});
		});
	});
})(jQuery.noConflict());

/*
 * Localisation Manager
 *
 * @author: Nils Hörrmann, post@nilshoerrmann.de
 * @source: http://github.com/symphonists/localisationmanager
 */
(function($) {
	$(document).ready(function() {

		// Language strings
		Symphony.Language.add({
			'Sort strings alphabetically': false
		});

		// Append sort option
		var context = $('#context'),
			table = $('table'),
			sort = $('<label style="float: right;"><input type="checkbox" name="sortit" /> ' + Symphony.Language.get('Sort strings alphabetically') + '</label>').appendTo(context);

		// Events
		sort.click(function(event) {

			// Get status
			var checked = $(event.target).attr('checked');

			// Add or remove sort option
			table.find('a').each(function(index, link) {
				link = $(link);
				var href = link.attr('href');

				// Is checked?
				if(checked) {
					link.attr('href', href + '?sort');
				}

				// Is not checked?
				else {
					link.attr('href', href.replace('?sort', ''));
				}
			});
		});
	});
})(jQuery.noConflict());

Use <label /> for a label


// Source: http://stackoverflow.com/a/1830844
function isNumber(n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

function AppViewModel() {
    this.statStrength = ko.observable();
    this.statEndurance = ko.observable();
    this.statAgility = ko.observable();
    this.statSpeed = ko.observable();

    this.statWillpower = ko.observable();
    this.statInsight = ko.observable();
    this.statReasoning = ko.observable();
    this.statPerception = ko.observable();

    this.statPresence = ko.observable();
    this.statComposure = ko.observable();
    this.statManipulation = ko.observable();
    this.statBeauty = ko.observable();

    this.statBonusStrength = ko.computed(function() {
        return this.statStrength();
    }, this);
}

// Activate knockout.js
ko.applyBindings(new AppViewModel());

Add test to check whether a variable is a number.


'use strict';

var assign = require('es5-ext/object/assign');

// Common
assign(exports, require('../user/server'));

// Add user
exports['user-add'] = require('mano-auth/controller/server-master/register');

'use strict';

var assign           = require('es5-ext/object/assign')
  , registerSubmit   = require('mano-auth/controller/server-master/register')
  , sendNotification = require('../../server/email-notifications/create-account');

// Common
assign(exports, require('../user/server'));

// Add user
exports['user-add'] = {
	submit: function (data) {
		return registerSubmit.apply(this, arguments)(function (result) {
			sendNotification(data).done();
			return result;
		});
	}
};

Send create account notification on account create