<commit_before>var DEFINES = exports;
DEFINES.INVALID = 0;
DEFINES.ALLOCATE_POST = 1;
DEFINES.INSERT_POST = 2;
DEFINES.UPDATE_POST = 3;
DEFINES.FINISH_POST = 4;
DEFINES.DELETE_POSTS = 5;
DEFINES.DELETE_THREAD = 6;
DEFINES.INSERT_IMAGE = 7;
DEFINES.IMAGE_STATUS = 8;
DEFINES.SYNCHRONIZE = 9;
DEFINES.CATCH_UP = 10;

DEFINES.ANON = 'Anonymous';
DEFINES.INPUT_MIN_SIZE = 300;
DEFINES.INPUT_ROOM = 20;
DEFINES.MAX_POST_LINES = 30;
DEFINES.MAX_POST_CHARS = 2000;
DEFINES.WORD_LENGTH_LIMIT = 120;

DEFINES.BKSP_KEY = 8;
DEFINES.DEL_KEY = 46;
DEFINES.RETURN_KEY = 13;
DEFINES.SPACE_KEY = 32;

function is_pubsub(t) {
	return t >= INSERT_POST && t <= DELETE_THREAD;
}

var entities = {'&' : '&', '<': '<', '>': '>', '"': '"'};
function escape_html(html) {
	return html.replace(/[&<>"]/g, function (c) {
		return entities[c];
	});
}
exports.escape_html = escape_html;

function escape_fragment(frag) {
	var t = typeof(frag);
	if (t == 'object' && frag && typeof(frag.safe) == 'string')
		return frag.safe;
	else if (t == 'string')
		return escape_html(frag);
	else if (t == 'number')
		return frag.toString();
	else
		return '???';

}
exports.escape_fragment = escape_fragment;

function flatten(frags) {
	var out = [];
	for (var i = 0; i < frags.length; i++) {
		var frag = frags[i];
		if (frag.constructor == Array)
			out = out.concat(flatten(frag));
		else
			out.push(escape_fragment(frag));
	}
	return out;
}

function safe(frag) {
	return {safe: frag};
}
exports.safe = safe;

function map_unsafe(frags, func) {
	for (var i = 0; i < frags.length; i++) {
		if (typeof(frags[i]) == 'string')
			frags[i] = func(frags[i]);
		else if (frags[i].constructor == Array)
			frags[i] = map_unsafe(frags[i], func);
	}
	return frags;
}

function is_empty(o) {
	if (!o)
		return true;
	for (k in o)
		if (o.hasOwnProperty(k))
			return false;
	return true;
}
exports.is_empty = is_empty;

function is_noko(email) {
	return email && email.indexOf('@') == -1 && email.match(/noko/i);
}
exports.is_noko = is_noko;
function is_sage(email) {
	return email && email.indexOf('@') == -1 && email.match(/sage/i);
}
exports.is_sage = is_sage;

var OneeSama = function (t) {
	this.tamashii = t;
};
exports.OneeSama = OneeSama;
var OS = OneeSama.prototype;

var break_re = new RegExp("(\\S{" + DEFINES.WORD_LENGTH_LIMIT + "})");
/* internal refs and youtube videos */
var ref_re = />>(\d+|>\/?(?:watch\?)?v[=\/][\w-]{11}(?:#t=[\dhms]{1,9})?)/;
var youtube_re = /^>>>\/?(?:watch\?)?v[=\/]([\w-]{11})(#t=[\dhms]{1,9})?$/;
var youtube_time_re = /^#t=(?:(\d\d?)h)?(?:(\d\d?)m)?(?:(\d\d?)s)?$/;
var youtube_url_re = /(?:>>>*?)?(?:http:\/\/)?(?:www\.)?youtube\.com\/watch\?((?:[^\s#&=]+=[^\s#&]*&)*)?v=([\w-]{11})((?:&[^\s#&=]+=[^\s#&]*)*)&?(#t=[\dhms]{1,9})?/;

OS.break_heart = function (frag) {
	if (frag.safe)
		return this.callback(frag);
	var bits = frag.split(break_re);
	for (var i = 0; i < bits.length; i++) {
		/* anchor refs */
		var morsels = bits[i].split(ref_re);
		for (var j = 0; j < morsels.length; j++) {
			var m = morsels[j];
			if (j % 2) {
				if (m[0] == '>') {
					/* This is alright since it's always
					   a single word */
					this.callback(safe('' +
							escape_html('>>' + m) +
							''));
				}
				else
					this.tamashii(parseInt(m, 10));
			}
			else if (i % 2)
				this.callback(m + ' ');
			else
				this.callback(m);
		}
	}
};

OS.iku = function (token, to) {
	var state = this.state;
	if (state[0] == 1 && to != 1)
		this.callback(safe(''));
	switch (to) {
	case 1:
		if (state[0] != 1) {
			this.callback(safe(''));
			state[0] = 1;
		}
		this.break_heart(token);
		break;
	case 3:
		if (token[1] == '/') {
			state[1]--;
			this.callback(safe(''));
		}
		else {
			this.callback(safe(''));
			state[1]++;
		}
		break;
	default:
		this.break_heart(token);
		break;
	}
	state[0] = to;
}

OS.fragment = function (frag) {
	var chunks = frag.split(/(\[\/?spoiler\])/i);
	var state = this.state;
	for (var i = 0; i < chunks.length; i++) {
		var chunk = chunks[i];
		if (i % 2) {
			var to = 3;
			if (chunk[1] == '/' && state[1] < 1)
				to = (state[0] == 1) ? 1 : 2;
			this.iku(chunk, to);
			continue;
		}
		lines = chunk.split(/(\n)/);
		for (var l = 0; l < lines.length; l++) {
			var line = lines[l];
			if (l % 2)
				this.iku(safe('
'), 0);
			else if (state[0] === 0 && line[0] == '>')
				this.iku(line, 1);
			else if (line)
				this.iku(line, state[0]==1 ? 1 : 2);
		}
	}
};

OS.karada = function (body) {
	var output = [];
	this.state = [0, 0];
	this.callback = function (frag) { output.push(frag); }
	this.fragment(body);
	this.callback = null;
	if (this.state[0] == 1)
		output.push(safe(''));
	for (var i = 0; i < this.state[1]; i++)
		output.push(safe(''));
	return output;
}

function chibi(text) {
	var m = text.match(/^(.{40}).{8,}(\.\w{3,4})$/);
	/* Comma inlined for convience in OS.gazou (beware of concatenating
	 * lists with strings) */
	if (!m)
		return ', ' + text;
	return [safe(', '), m[1],
		safe('(…)'), m[2], safe('')];
}

OS.gazou = function (info, toppu) {
	var src = encodeURI(this.media + 'src/' + info.src);
	var thumb = src, d = info.dims;
	var w = d[0], h = d[1], tw = d[2], th = d[3];
	if (info.spoiler) {
		thumb = encodeURI(this.media + 'spoiler' + (toppu ? '' : 's')
				+ info.spoiler + '.png');
		var sp = toppu ? [250, 250] : [125, 125]; /* TEMP */
		tw = sp[0];
		th = sp[1];
	}
	else if (info.thumb)
		thumb = encodeURI(this.media + 'thumb/' + info.thumb);
	else {
		tw = w;
		th = h;
	}
	return [safe('
' +
		'
Image ' +
		info.src + ' (' + readable_filesize(info.size) + ', ' +
		w + 'x' + h), this.full ? chibi(info.imgnm) : '',
		safe(')
' +
		'' + '
\n\t')];
};

function readable_filesize(size) {
       /* Metric. Deal with it. */
       if (size < 1000)
               return size + ' B';
       if (size < 1000000)
               return Math.round(size / 1000) + ' KB';
       size = Math.round(size / 100000).toString();
       return size.slice(0, -1) + '.' + size.slice(-1) + ' MB';
}

function pad(n) {
	return (n < 10 ? '0' : '') + n;
}

function readable_time(time) {
	var d = new Date(time - new Date().getTimezoneOffset() * 60000);
	return (d.getUTCFullYear() + '/' + pad(d.getUTCMonth()+1) + '/' +
		pad(d.getUTCDate()) + ' ' + pad(d.getUTCHours()) + ':' +
		pad(d.getUTCMinutes()));
}
exports.readable_time = readable_time;

function datetime(time) {
	var d = new Date(time);
	return (d.getUTCFullYear() + '-' + pad(d.getUTCMonth()+1) + '-' +
		pad(d.getUTCDate()) + 'T' + pad(d.getUTCHours()) + ':' +
		pad(d.getUTCMinutes()) + ':' + pad(d.getUTCSeconds()) + 'Z');
}

function post_url(post, quote) {
	return (post.op || post.num) + (quote ? '#q' : '#') + post.num;
}
exports.post_url = post_url;

function num_html(post) {
	return ('No.' + post.num + '');
}

function expand_html(num) {
	return '   [Expand]';
}

OS.monogatari = function (data, t) {
	var auth = data.auth;
	var header = auth ? [safe('')]
			: [safe('')];
	header.push(data.name || DEFINES.ANON);
	if (data.trip)
		header.push(safe(' ' + data.trip + ''));
	if (auth)
		header.push(' ## ' + auth);
	header.push(safe(''));
	if (data.email) {
		header.unshift(safe(''));
		header.push(safe(''));
	}
	header.unshift(safe(''));
	header.push(safe(' <time pubdate datetime="' + datetime(data.time) +
			'">' + readable_time(data.time) + '</time> ' +
			num_html(data)));
	if (!this.full && !data.op)
		header.push(safe(expand_html(data.num)));
	header.push(safe('\n\t'));
	var body = [safe('
'), this.karada(data.body),
			safe('
')];
	if (!data.image)
		return {header: header, body: body};
	return {header: header, image: this.gazou(data.image, t), body: body};
};

OS.mono = function (data) {
	var o = safe(data.editing
			? '\t
'
			: '\t
'),
	    c = safe('
\n'),
	    gen = this.monogatari(data, false);
	return flatten([o, gen.header, gen.image || '', gen.body, c]).join('');
};

OS.monomono = function (data) {
	var o = safe('
'),
	    c = safe('
\n'),
	    gen = this.monogatari(data, true);
	return flatten([o, gen.image || '', gen.header, gen.body, '\n', c]);
};

exports.abbrev_msg = function (omit, img_omit) {
	return omit + (omit==1 ? ' reply' : ' replies') + (img_omit
		? ' and ' + img_omit + ' image' + (img_omit==1 ? '' : 's')
		: '') + ' omitted.';
};

exports.parse_name = function (name) {
	var tripcode = '', secure = '';
	var hash = name.indexOf('#');
	if (hash >= 0) {
		tripcode = name.substr(hash+1);
		name = name.substr(0, hash);
		hash = tripcode.indexOf('#');
		if (hash >= 0) {
			secure = escape_html(tripcode.substr(hash+1));
			tripcode = tripcode.substr(0, hash);
		}
		tripcode = escape_html(tripcode);
	}
	return [name.trim().substr(0, 100), tripcode.substr(0, 128),
			secure.substr(0, 128)];
};
<commit_msg>Add missing DEFINES.<commit_after>var DEFINES = exports;
DEFINES.INVALID = 0;
DEFINES.ALLOCATE_POST = 1;
DEFINES.INSERT_POST = 2;
DEFINES.UPDATE_POST = 3;
DEFINES.FINISH_POST = 4;
DEFINES.DELETE_POSTS = 5;
DEFINES.DELETE_THREAD = 6;
DEFINES.INSERT_IMAGE = 7;
DEFINES.IMAGE_STATUS = 8;
DEFINES.SYNCHRONIZE = 9;
DEFINES.CATCH_UP = 10;

DEFINES.ANON = 'Anonymous';
DEFINES.INPUT_MIN_SIZE = 300;
DEFINES.INPUT_ROOM = 20;
DEFINES.MAX_POST_LINES = 30;
DEFINES.MAX_POST_CHARS = 2000;
DEFINES.WORD_LENGTH_LIMIT = 120;

DEFINES.BKSP_KEY = 8;
DEFINES.DEL_KEY = 46;
DEFINES.RETURN_KEY = 13;
DEFINES.SPACE_KEY = 32;

function is_pubsub(t) {
	return t >= DEFINES.INSERT_POST && t <= DEFINES.DELETE_THREAD;
}

var entities = {'&' : '&', '<': '<', '>': '>', '"': '"'};
function escape_html(html) {
	return html.replace(/[&<>"]/g, function (c) {
		return entities[c];
	});
}
exports.escape_html = escape_html;

function escape_fragment(frag) {
	var t = typeof(frag);
	if (t == 'object' && frag && typeof(frag.safe) == 'string')
		return frag.safe;
	else if (t == 'string')
		return escape_html(frag);
	else if (t == 'number')
		return frag.toString();
	else
		return '???';

}
exports.escape_fragment = escape_fragment;

function flatten(frags) {
	var out = [];
	for (var i = 0; i < frags.length; i++) {
		var frag = frags[i];
		if (frag.constructor == Array)
			out = out.concat(flatten(frag));
		else
			out.push(escape_fragment(frag));
	}
	return out;
}

function safe(frag) {
	return {safe: frag};
}
exports.safe = safe;

function map_unsafe(frags, func) {
	for (var i = 0; i < frags.length; i++) {
		if (typeof(frags[i]) == 'string')
			frags[i] = func(frags[i]);
		else if (frags[i].constructor == Array)
			frags[i] = map_unsafe(frags[i], func);
	}
	return frags;
}

function is_empty(o) {
	if (!o)
		return true;
	for (k in o)
		if (o.hasOwnProperty(k))
			return false;
	return true;
}
exports.is_empty = is_empty;

function is_noko(email) {
	return email && email.indexOf('@') == -1 && email.match(/noko/i);
}
exports.is_noko = is_noko;
function is_sage(email) {
	return email && email.indexOf('@') == -1 && email.match(/sage/i);
}
exports.is_sage = is_sage;

var OneeSama = function (t) {
	this.tamashii = t;
};
exports.OneeSama = OneeSama;
var OS = OneeSama.prototype;

var break_re = new RegExp("(\\S{" + DEFINES.WORD_LENGTH_LIMIT + "})");
/* internal refs and youtube videos */
var ref_re = />>(\d+|>\/?(?:watch\?)?v[=\/][\w-]{11}(?:#t=[\dhms]{1,9})?)/;
var youtube_re = /^>>>\/?(?:watch\?)?v[=\/]([\w-]{11})(#t=[\dhms]{1,9})?$/;
var youtube_time_re = /^#t=(?:(\d\d?)h)?(?:(\d\d?)m)?(?:(\d\d?)s)?$/;
var youtube_url_re = /(?:>>>*?)?(?:http:\/\/)?(?:www\.)?youtube\.com\/watch\?((?:[^\s#&=]+=[^\s#&]*&)*)?v=([\w-]{11})((?:&[^\s#&=]+=[^\s#&]*)*)&?(#t=[\dhms]{1,9})?/;

OS.break_heart = function (frag) {
	if (frag.safe)
		return this.callback(frag);
	var bits = frag.split(break_re);
	for (var i = 0; i < bits.length; i++) {
		/* anchor refs */
		var morsels = bits[i].split(ref_re);
		for (var j = 0; j < morsels.length; j++) {
			var m = morsels[j];
			if (j % 2) {
				if (m[0] == '>') {
					/* This is alright since it's always
					   a single word */
					this.callback(safe('' +
							escape_html('>>' + m) +
							''));
				}
				else
					this.tamashii(parseInt(m, 10));
			}
			else if (i % 2)
				this.callback(m + ' ');
			else
				this.callback(m);
		}
	}
};

OS.iku = function (token, to) {
	var state = this.state;
	if (state[0] == 1 && to != 1)
		this.callback(safe(''));
	switch (to) {
	case 1:
		if (state[0] != 1) {
			this.callback(safe(''));
			state[0] = 1;
		}
		this.break_heart(token);
		break;
	case 3:
		if (token[1] == '/') {
			state[1]--;
			this.callback(safe(''));
		}
		else {
			this.callback(safe(''));
			state[1]++;
		}
		break;
	default:
		this.break_heart(token);
		break;
	}
	state[0] = to;
}

OS.fragment = function (frag) {
	var chunks = frag.split(/(\[\/?spoiler\])/i);
	var state = this.state;
	for (var i = 0; i < chunks.length; i++) {
		var chunk = chunks[i];
		if (i % 2) {
			var to = 3;
			if (chunk[1] == '/' && state[1] < 1)
				to = (state[0] == 1) ? 1 : 2;
			this.iku(chunk, to);
			continue;
		}
		lines = chunk.split(/(\n)/);
		for (var l = 0; l < lines.length; l++) {
			var line = lines[l];
			if (l % 2)
				this.iku(safe('
'), 0);
			else if (state[0] === 0 && line[0] == '>')
				this.iku(line, 1);
			else if (line)
				this.iku(line, state[0]==1 ? 1 : 2);
		}
	}
};

OS.karada = function (body) {
	var output = [];
	this.state = [0, 0];
	this.callback = function (frag) { output.push(frag); }
	this.fragment(body);
	this.callback = null;
	if (this.state[0] == 1)
		output.push(safe(''));
	for (var i = 0; i < this.state[1]; i++)
		output.push(safe(''));
	return output;
}

function chibi(text) {
	var m = text.match(/^(.{40}).{8,}(\.\w{3,4})$/);
	/* Comma inlined for convience in OS.gazou (beware of concatenating
	 * lists with strings) */
	if (!m)
		return ', ' + text;
	return [safe(', '), m[1],
		safe('(…)'), m[2], safe('')];
}

OS.gazou = function (info, toppu) {
	var src = encodeURI(this.media + 'src/' + info.src);
	var thumb = src, d = info.dims;
	var w = d[0], h = d[1], tw = d[2], th = d[3];
	if (info.spoiler) {
		thumb = encodeURI(this.media + 'spoiler' + (toppu ? '' : 's')
				+ info.spoiler + '.png');
		var sp = toppu ? [250, 250] : [125, 125]; /* TEMP */
		tw = sp[0];
		th = sp[1];
	}
	else if (info.thumb)
		thumb = encodeURI(this.media + 'thumb/' + info.thumb);
	else {
		tw = w;
		th = h;
	}
	return [safe('
' +
		'
Image ' +
		info.src + ' (' + readable_filesize(info.size) + ', ' +
		w + 'x' + h), this.full ? chibi(info.imgnm) : '',
		safe(')
' +
		'' + '
\n\t')];
};

function readable_filesize(size) {
       /* Metric. Deal with it. */
       if (size < 1000)
               return size + ' B';
       if (size < 1000000)
               return Math.round(size / 1000) + ' KB';
       size = Math.round(size / 100000).toString();
       return size.slice(0, -1) + '.' + size.slice(-1) + ' MB';
}

function pad(n) {
	return (n < 10 ? '0' : '') + n;
}

function readable_time(time) {
	var d = new Date(time - new Date().getTimezoneOffset() * 60000);
	return (d.getUTCFullYear() + '/' + pad(d.getUTCMonth()+1) + '/' +
		pad(d.getUTCDate()) + ' ' + pad(d.getUTCHours()) + ':' +
		pad(d.getUTCMinutes()));
}
exports.readable_time = readable_time;

function datetime(time) {
	var d = new Date(time);
	return (d.getUTCFullYear() + '-' + pad(d.getUTCMonth()+1) + '-' +
		pad(d.getUTCDate()) + 'T' + pad(d.getUTCHours()) + ':' +
		pad(d.getUTCMinutes()) + ':' + pad(d.getUTCSeconds()) + 'Z');
}

function post_url(post, quote) {
	return (post.op || post.num) + (quote ? '#q' : '#') + post.num;
}
exports.post_url = post_url;

function num_html(post) {
	return ('No.' + post.num + '');
}

function expand_html(num) {
	return '   [Expand]';
}

OS.monogatari = function (data, t) {
	var auth = data.auth;
	var header = auth ? [safe('')]
			: [safe('')];
	header.push(data.name || DEFINES.ANON);
	if (data.trip)
		header.push(safe(' ' + data.trip + ''));
	if (auth)
		header.push(' ## ' + auth);
	header.push(safe(''));
	if (data.email) {
		header.unshift(safe(''));
		header.push(safe(''));
	}
	header.unshift(safe(''));
	header.push(safe(' <time pubdate datetime="' + datetime(data.time) +
			'">' + readable_time(data.time) + '</time> ' +
			num_html(data)));
	if (!this.full && !data.op)
		header.push(safe(expand_html(data.num)));
	header.push(safe('\n\t'));
	var body = [safe('
'), this.karada(data.body),
			safe('
')];
	if (!data.image)
		return {header: header, body: body};
	return {header: header, image: this.gazou(data.image, t), body: body};
};

OS.mono = function (data) {
	var o = safe(data.editing
			? '\t
'
			: '\t
'),
	    c = safe('
\n'),
	    gen = this.monogatari(data, false);
	return flatten([o, gen.header, gen.image || '', gen.body, c]).join('');
};

OS.monomono = function (data) {
	var o = safe('
'),
	    c = safe('
\n'),
	    gen = this.monogatari(data, true);
	return flatten([o, gen.image || '', gen.header, gen.body, '\n', c]);
};

exports.abbrev_msg = function (omit, img_omit) {
	return omit + (omit==1 ? ' reply' : ' replies') + (img_omit
		? ' and ' + img_omit + ' image' + (img_omit==1 ? '' : 's')
		: '') + ' omitted.';
};

exports.parse_name = function (name) {
	var tripcode = '', secure = '';
	var hash = name.indexOf('#');
	if (hash >= 0) {
		tripcode = name.substr(hash+1);
		name = name.substr(0, hash);
		hash = tripcode.indexOf('#');
		if (hash >= 0) {
			secure = escape_html(tripcode.substr(hash+1));
			tripcode = tripcode.substr(0, hash);
		}
		tripcode = escape_html(tripcode);
	}
	return [name.trim().substr(0, 100), tripcode.substr(0, 128),
			secure.substr(0, 128)];
};
<|endoftext|>


<commit_before>var fs = require('fs')
var log = require('better-console')
var path = require('path')

var Mango = function(){

}

Mango.prototype.init = function(folder, name, forkTemplate) {
	var git = require('gift')

	this._forkRepo(git, forkTemplate, folder, function(repo){

	})
}
Mango.prototype._forkRepo = function(git, remote, folder, callback){
	log.info('Forking a git repository ' + remote + ' to ' + folder)

	git.clone(remote, folder, function(err, repo){
		if(err) return log.error('Failed to clone a template repository', err)

		repo.remote_remove('origin', function(err){
			if(err) return log.error('Failed to remove the remote origin', err)
			log.log('Repository forked')
			callback(repo)
		})
	})
}
Mango.prototype._initRepo = function(git, folder, callback){
	log.info('Initializing an empty git repository in ' + folder)

	if(!fs.existsSync(folder)){
		log.log('Creating folder first')
		fs.mkdirSync(folder)
	}

	git.init(folder, function(err, repo){
		if(err) return log.error('Failed to initialize repository', err)
		log.log('Repository initialized')
		callback(repo)
	})
}

Mango.prototype.install = function(folder, config, packages) {
	log.info('Installing packages via NPM')

	var npm = require('npm')

	npm.load(function(err) {
		if(err) return log.error('Failed to load NPM to install packages', err)

		var pkgs = config.dependencies.concat(packages)

		npm.on("log", function (msg) {
			console.log(msg) // log the progress of the installation
		})
		npm.commands.install(folder, pkgs, function(err, data) {
			if(err) return log.error('Failed to install packages', err)
		})
	})
}

Mango.prototype.build = function(folder, config, tasks) {
	var dist = path.resolve(folder, config.dist_folder)
	var gulp = require('gulp')

	log.info('Building project assets for production to ' + dist)

	tasks = tasks.length ? tasks : ['styles', 'scripts', 'templates', 'images']

	tasks.forEach(function(task) {
		switch (task) {
			case 'styles':
				this._buildStyles(gulp, config, dist, false)
				break
			case 'scripts':
				this._buildScripts(gulp, config, dist, false)
				break
			case 'templates':
				this._buildTemplates(gulp, config, dist, false)
				break
			case 'images':
				this._buildImages(gulp, config, dist, false)
				break
		}
	}, this)
}
Mango.prototype._buildStyles = function(gulp, config, dist, development) {
	log.log('Compiling Stylus into minimized CSS')

	var cssmin = require('gulp-cssmin')
	var nib = require('nib')()
	var stylus = require('gulp-stylus')

	gulp.src(config.styles)
		.pipe(stylus({
			use: nib
		}))
		.pipe(cssmin())
		.pipe(gulp.dest(dist))
}
Mango.prototype._buildScripts = function(gulp, config, dist, development) {
	log.log('Compiling and minimizing scripts')

	var browserify = require('browserify')
	// var rename = require('gulp-rename')
	var transform = require('vinyl-transform')
	var uglify = require('gulp-uglify')
	var coffeeify = require('coffeeify')
	var reactify = require('reactify')

	var browserified = transform(function(filename) {
		var b = browserify(filename)
		b.transform(coffeeify)
		b.transform(reactify)
		return b.bundle()
	})

	gulp.src(config.scripts)
		.pipe(browserified)
		.pipe(uglify())
		.pipe(gulp.dest(dist))

}
Mango.prototype._buildTemplates = function(gulp, config, dist, development) {
	log.log('Compiling templates into HTML')

}
Mango.prototype._buildImages = function(gulp, config, dist, development) {
	log.log('Minimizing images')

	var imagemin = require('gulp-imagemin')
	var pngquant = require('imagemin-pngquant')

	gulp.src(config.images)
		.pipe(imagemin({
			progressive: true,
			svgoPlugins: [{removeViewBox: false}],
			use: [pngquant()]
		}))
		.pipe(gulp.dest(path.resolve(dist, 'img')))
}

Mango.prototype.dev = function(folder, config, proxy) {
	var browsersync = require('browser-sync')
	var dist = path.resolve(folder, config.dist_folder)
	var options = {}
	var gulp = require('gulp')
	var filterDist = '!' + config.dist_folder + '/**'

	log.info('Starting development mode and watching for assets change')

	if(proxy) {
		options.proxy = proxy
	} else {
		options.server = { baseDir: dist }
	}
	// Start browserSync
	browsersync(options)

	// Events and file watching
	gulp.watch(['**/*.styl', filterDist], { cwd: folder }, function(e) {
		this._buildStyles(gulp, config, dist, true)
	}.bind(this))

	gulp.watch(['**/*.js', filterDist], { cwd: folder }, function(e) {
		this._buildScripts(gulp, config, dist, true)
	}.bind(this))

	gulp.watch(['**/*.hbs', filterDist], { cwd: folder }, function(e) {
		this._buildScripts(gulp, config, dist, true)
	}.bind(this))

	gulp.watch(['**/*.{jpg,png,svg}', filterDist], { cwd: folder }, function(e) {
		this._buildScripts(gulp, config, dist, true)
	}.bind(this))

	// Reload
	gulp.watch('**', { cwd: dist }, function(e) {
		browsersync.reload(e.path)
		log.log('Reloading ' + e.path)
	})
}

module.exports = Mango
<commit_msg>Dist folder now replicates the source folder structure<commit_after>var fs = require('fs')
var log = require('better-console')
var path = require('path')

var Mango = function(){

}

Mango.prototype.init = function(folder, name, forkTemplate) {
	var git = require('gift')

	this._forkRepo(git, forkTemplate, folder, function(repo){

	})
}
Mango.prototype._forkRepo = function(git, remote, folder, callback){
	log.info('Forking a git repository ' + remote + ' to ' + folder)

	git.clone(remote, folder, function(err, repo){
		if(err) return log.error('Failed to clone a template repository', err)

		repo.remote_remove('origin', function(err){
			if(err) return log.error('Failed to remove the remote origin', err)
			log.log('Repository forked')
			callback(repo)
		})
	})
}
Mango.prototype._initRepo = function(git, folder, callback){
	log.info('Initializing an empty git repository in ' + folder)

	if(!fs.existsSync(folder)){
		log.log('Creating folder first')
		fs.mkdirSync(folder)
	}

	git.init(folder, function(err, repo){
		if(err) return log.error('Failed to initialize repository', err)
		log.log('Repository initialized')
		callback(repo)
	})
}

Mango.prototype.install = function(folder, config, packages) {
	log.info('Installing packages via NPM')

	var npm = require('npm')

	npm.load(function(err) {
		if(err) return log.error('Failed to load NPM to install packages', err)

		var pkgs = config.dependencies.concat(packages)

		npm.on("log", function (msg) {
			console.log(msg) // log the progress of the installation
		})
		npm.commands.install(folder, pkgs, function(err, data) {
			if(err) return log.error('Failed to install packages', err)
		})
	})
}

Mango.prototype.build = function(folder, config, tasks) {
	var dist = path.resolve(folder, config.dist_folder)
	var gulp = require('gulp')

	log.info('Building project assets for production to ' + dist)

	tasks = tasks.length ? tasks : ['styles', 'scripts', 'templates', 'images']

	tasks.forEach(function(task) {
		switch (task) {
			case 'styles':
				this._buildStyles(gulp, config, folder, dist, false)
				break
			case 'scripts':
				this._buildScripts(gulp, config, folder, dist, false)
				break
			case 'templates':
				this._buildTemplates(gulp, config, folder, dist, false)
				break
			case 'images':
				this._buildImages(gulp, config, folder, dist, false)
				break
		}
	}, this)
}
Mango.prototype._buildStyles = function(gulp, config, base, dist, development) {
	log.log('Compiling Stylus into minimized CSS')

	var cssmin = require('gulp-cssmin')
	var nib = require('nib')()
	var stylus = require('gulp-stylus')

	gulp.src(config.styles, { base: base })
		.pipe(stylus({
			use: nib
		}))
		.pipe(cssmin())
		.pipe(gulp.dest(dist))
}
Mango.prototype._buildScripts = function(gulp, config, base, dist, development) {
	log.log('Compiling and minimizing scripts')

	var browserify = require('browserify')
	// var rename = require('gulp-rename')
	var transform = require('vinyl-transform')
	var uglify = require('gulp-uglify')
	var coffeeify = require('coffeeify')
	var reactify = require('reactify')

	var browserified = transform(function(filename) {
		var b = browserify(filename)
		b.transform(coffeeify)
		b.transform(reactify)
		return b.bundle()
	})

	gulp.src(config.scripts, { base: base })
		.pipe(browserified)
		.pipe(uglify())
		.pipe(gulp.dest(dist))

}
Mango.prototype._buildTemplates = function(gulp, config, base, dist, development) {
	log.log('Compiling templates into HTML')

}
Mango.prototype._buildImages = function(gulp, config, base, dist, development) {
	log.log('Minimizing images')

	var imagemin = require('gulp-imagemin')
	var pngquant = require('imagemin-pngquant')

	gulp.src(config.images, { base: base })
		.pipe(imagemin({
			progressive: true,
			svgoPlugins: [{ removeViewBox: false }],
			use: [pngquant()]
		}))
		.pipe(gulp.dest(dist))
}

Mango.prototype.dev = function(folder, config, proxy) {
	var browsersync = require('browser-sync')
	var dist = path.resolve(folder, config.dist_folder)
	var options = {}
	var gulp = require('gulp')
	var filterDist = '!' + config.dist_folder + '/**'

	log.info('Starting development mode and watching for assets change')

	if(proxy) {
		options.proxy = proxy
	} else {
		options.server = { baseDir: dist }
	}
	// Start browserSync
	browsersync(options)

	// Events and file watching
	gulp.watch(['**/*.styl', filterDist], { cwd: folder }, function(e) {
		this._buildStyles(gulp, config, folder, dist, true)
	}.bind(this))

	gulp.watch(['**/*.js', filterDist], { cwd: folder }, function(e) {
		this._buildScripts(gulp, config, folder, dist, true)
	}.bind(this))

	gulp.watch(['**/*.hbs', filterDist], { cwd: folder }, function(e) {
		this._buildScripts(gulp, config, folder, dist, true)
	}.bind(this))

	gulp.watch(['**/*.{jpg,png,svg}', filterDist], { cwd: folder }, function(e) {
		this._buildScripts(gulp, config, folder, dist, true)
	}.bind(this))

	// Reload
	gulp.watch('**', { cwd: dist }, function(e) {
		browsersync.reload(e.path)
		log.log('Reloading ' + e.path)
	})
}

module.exports = Mango
<|endoftext|>


<commit_before>const request = require('request');
const express = require('express');
const router = express.Router();

// Map routes to controller functions

// get the stock api
router.get('/api/:query', function (req, res) {
  console.log(req.params.query);
  const url = 'https://www.alphavantage.co/query?function=' + req.params.query;

  request({
    url: url,
    json: true,
    rejectUnauthorized: false
  }, function (error, response, body) {

    if (!error && response.statusCode === 200) {
      //console.log(body) // Print the json response
      res.send(body);
    }
    if (error) {
      console.log('error: ', error);
      console.log('unable to get data!!!!!!!!');
    }
  });

});

router.get('/error', function (req, resp) {
  throw new Error('Derp. An error occurred.');
});

// For all GET requests, send back index.html
// so that PathLocationStrategy can be used
router.get('**', function (req, res) {
  res.sendFile(path.join(__dirname + './../dist/index.html'));
});

module.exports = router;
<commit_msg>minor fix<commit_after>const path = require('path');
const request = require('request');
const express = require('express');
const router = express.Router();

// Map routes to controller functions

// get the stock api
router.get('/api/:query', function (req, res) {
  console.log(req.params.query);
  const url = 'https://www.alphavantage.co/query?function=' + req.params.query;

  request({
    url: url,
    json: true,
    rejectUnauthorized: false
  }, function (error, response, body) {

    if (!error && response.statusCode === 200) {
      //console.log(body) // Print the json response
      res.send(body);
    }
    if (error) {
      console.log('error: ', error);
      console.log('unable to get data!!!!!!!!');
    }
  });

});

router.get('/error', function (req, resp) {
  throw new Error('Derp. An error occurred.');
});

// For all GET requests, send back index.html
// so that PathLocationStrategy can be used
router.get('**', function (req, res) {
  res.sendFile(path.join(__dirname + './../dist/index.html'));
});

module.exports = router;
<|endoftext|>


<commit_before>/*!
 * mocha
 * Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var path = require('path')
  , utils = require('./utils');

/**
 * Expose `Mocha`.
 */

exports = module.exports = Mocha;

/**
 * Expose internals.
 */

exports.utils = utils;
exports.interfaces = require('./interfaces');
exports.reporters = require('./reporters');
exports.Runnable = require('./runnable');
exports.Context = require('./context');
exports.Runner = require('./runner');
exports.Suite = require('./suite');
exports.Hook = require('./hook');
exports.Test = require('./test');

/**
 * Return image `name` path.
 *
 * @param {String} name
 * @return {String}
 * @api private
 */

function image(name) {
  return __dirname + '/../images/' + name + '.png';
}

/**
 * Setup mocha with `options`.
 *
 * Options:
 *
 *   - `ui` name "bdd", "tdd", "exports" etc
 *   - `reporter` reporter instance, defaults to `mocha.reporters.Dot`
 *   - `globals` array of accepted globals
 *   - `timeout` timeout in milliseconds
 *   - `bail` bail on the first test failure
 *   - `slow` milliseconds to wait before considering a test slow
 *   - `ignoreLeaks` ignore global leaks
 *   - `grep` string or regexp to filter tests with
 *
 * @param {Object} options
 * @api public
 */

function Mocha(options) {
  options = options || {};
  this.files = [];
  this.options = options;
  this.grep(options.grep);
  this.suite = new exports.Suite('', new exports.Context);
  this.ui(options.ui);
  this.bail(options.bail);
  this.reporter(options.reporter);
  if (null != options.timeout) this.timeout(options.timeout);
  if (options.slow) this.slow(options.slow);
}

/**
 * Enable or disable bailing on the first failure.
 *
 * @param {Boolean} [bail]
 * @api public
 */

Mocha.prototype.bail = function(bail){
  if (0 == arguments.length) bail = true;
  this.suite.bail(bail);
  return this;
};

/**
 * Add test `file`.
 *
 * @param {String} file
 * @api public
 */

Mocha.prototype.addFile = function(file){
  this.files.push(file);
  return this;
};

/**
 * Set reporter to `reporter`, defaults to "dot".
 *
 * @param {String|Function} reporter name or constructor
 * @api public
 */

Mocha.prototype.reporter = function(reporter){
  if ('function' == typeof reporter) {
    this._reporter = reporter;
  } else {
    reporter = reporter || 'dot';
    var _reporter;
    try { _reporter = require('./reporters/' + reporter); } catch (err) {};
    if (!_reporter) try { _repoter = require(reporter); } catch (err) {};
    if (!_reporter && reporter === 'teamcity')
      console.warn('The Teamcity reporter was moved to a package named ' +
        'mocha-teamcity-reporter ' +
        '(https://npmjs.org/package/mocha-teamcity-reporter).');
    if (!_reporter) throw new Error('invalid reporter "' + reporter + '"');
    this._reporter = _reporter;
  }
  return this;
};

/**
 * Set test UI `name`, defaults to "bdd".
 *
 * @param {String} bdd
 * @api public
 */

Mocha.prototype.ui = function(name){
  name = name || 'bdd';
  this._ui = exports.interfaces[name];
  if (!this._ui) throw new Error('invalid interface "' + name + '"');
  this._ui = this._ui(this.suite);
  return this;
};

/**
 * Load registered files.
 *
 * @api private
 */

Mocha.prototype.loadFiles = function(fn){
  var self = this;
  var suite = this.suite;
  var pending = this.files.length;
  this.files.forEach(function(file){
    file = path.resolve(file);
    suite.emit('pre-require', global, file, self);
    suite.emit('require', require(file), file, self);
    suite.emit('post-require', global, file, self);
    --pending || (fn && fn());
  });
};

/**
 * Enable growl support.
 *
 * @api private
 */

Mocha.prototype._growl = function(runner, reporter) {
  var notify = require('growl');

  runner.on('end', function(){
    var stats = reporter.stats;
    if (stats.failures) {
      var msg = stats.failures + ' of ' + runner.total + ' tests failed';
      notify(msg, { name: 'mocha', title: 'Failed', image: image('error') });
    } else {
      notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', {
          name: 'mocha'
        , title: 'Passed'
        , image: image('ok')
      });
    }
  });
};

/**
 * Add regexp to grep, if `re` is a string it is escaped.
 *
 * @param {RegExp|String} re
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.grep = function(re){
  this.options.grep = 'string' == typeof re
    ? new RegExp(utils.escapeRegexp(re))
    : re;
  return this;
};

/**
 * Invert `.grep()` matches.
 *
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.invert = function(){
  this.options.invert = true;
  return this;
};

/**
 * Ignore global leaks.
 *
 * @param {Boolean} ignore
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.ignoreLeaks = function(ignore){
  this.options.ignoreLeaks = !!ignore;
  return this;
};

/**
 * Enable global leak checking.
 *
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.checkLeaks = function(){
  this.options.ignoreLeaks = false;
  return this;
};

/**
 * Enable growl support.
 *
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.growl = function(){
  this.options.growl = true;
  return this;
};

/**
 * Ignore `globals` array or string.
 *
 * @param {Array|String} globals
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.globals = function(globals){
  this.options.globals = (this.options.globals || []).concat(globals);
  return this;
};

/**
 * Set the timeout in milliseconds.
 *
 * @param {Number} timeout
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.timeout = function(timeout){
  this.suite.timeout(timeout);
  return this;
};

/**
 * Set slowness threshold in milliseconds.
 *
 * @param {Number} slow
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.slow = function(slow){
  this.suite.slow(slow);
  return this;
};

/**
 * Makes all tests async (accepting a callback)
 *
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.asyncOnly = function(){
  this.options.asyncOnly = true;
  return this;
};

/**
 * Run tests and invoke `fn()` when complete.
 *
 * @param {Function} fn
 * @return {Runner}
 * @api public
 */

Mocha.prototype.run = function(fn){
  if (this.files.length) this.loadFiles();
  var suite = this.suite;
  var options = this.options;
  var runner = new exports.Runner(suite);
  var reporter = new this._reporter(runner);
  runner.ignoreLeaks = false !== options.ignoreLeaks;
  runner.asyncOnly = options.asyncOnly;
  if (options.grep) runner.grep(options.grep, options.invert);
  if (options.globals) runner.globals(options.globals);
  if (options.growl) this._growl(runner, reporter);
  return runner.run(fn);
};
<commit_msg>Fix typo (close #988)<commit_after>/*!
 * mocha
 * Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var path = require('path')
  , utils = require('./utils');

/**
 * Expose `Mocha`.
 */

exports = module.exports = Mocha;

/**
 * Expose internals.
 */

exports.utils = utils;
exports.interfaces = require('./interfaces');
exports.reporters = require('./reporters');
exports.Runnable = require('./runnable');
exports.Context = require('./context');
exports.Runner = require('./runner');
exports.Suite = require('./suite');
exports.Hook = require('./hook');
exports.Test = require('./test');

/**
 * Return image `name` path.
 *
 * @param {String} name
 * @return {String}
 * @api private
 */

function image(name) {
  return __dirname + '/../images/' + name + '.png';
}

/**
 * Setup mocha with `options`.
 *
 * Options:
 *
 *   - `ui` name "bdd", "tdd", "exports" etc
 *   - `reporter` reporter instance, defaults to `mocha.reporters.Dot`
 *   - `globals` array of accepted globals
 *   - `timeout` timeout in milliseconds
 *   - `bail` bail on the first test failure
 *   - `slow` milliseconds to wait before considering a test slow
 *   - `ignoreLeaks` ignore global leaks
 *   - `grep` string or regexp to filter tests with
 *
 * @param {Object} options
 * @api public
 */

function Mocha(options) {
  options = options || {};
  this.files = [];
  this.options = options;
  this.grep(options.grep);
  this.suite = new exports.Suite('', new exports.Context);
  this.ui(options.ui);
  this.bail(options.bail);
  this.reporter(options.reporter);
  if (null != options.timeout) this.timeout(options.timeout);
  if (options.slow) this.slow(options.slow);
}

/**
 * Enable or disable bailing on the first failure.
 *
 * @param {Boolean} [bail]
 * @api public
 */

Mocha.prototype.bail = function(bail){
  if (0 == arguments.length) bail = true;
  this.suite.bail(bail);
  return this;
};

/**
 * Add test `file`.
 *
 * @param {String} file
 * @api public
 */

Mocha.prototype.addFile = function(file){
  this.files.push(file);
  return this;
};

/**
 * Set reporter to `reporter`, defaults to "dot".
 *
 * @param {String|Function} reporter name or constructor
 * @api public
 */

Mocha.prototype.reporter = function(reporter){
  if ('function' == typeof reporter) {
    this._reporter = reporter;
  } else {
    reporter = reporter || 'dot';
    var _reporter;
    try { _reporter = require('./reporters/' + reporter); } catch (err) {};
    if (!_reporter) try { _reporter = require(reporter); } catch (err) {};
    if (!_reporter && reporter === 'teamcity')
      console.warn('The Teamcity reporter was moved to a package named ' +
        'mocha-teamcity-reporter ' +
        '(https://npmjs.org/package/mocha-teamcity-reporter).');
    if (!_reporter) throw new Error('invalid reporter "' + reporter + '"');
    this._reporter = _reporter;
  }
  return this;
};

/**
 * Set test UI `name`, defaults to "bdd".
 *
 * @param {String} bdd
 * @api public
 */

Mocha.prototype.ui = function(name){
  name = name || 'bdd';
  this._ui = exports.interfaces[name];
  if (!this._ui) throw new Error('invalid interface "' + name + '"');
  this._ui = this._ui(this.suite);
  return this;
};

/**
 * Load registered files.
 *
 * @api private
 */

Mocha.prototype.loadFiles = function(fn){
  var self = this;
  var suite = this.suite;
  var pending = this.files.length;
  this.files.forEach(function(file){
    file = path.resolve(file);
    suite.emit('pre-require', global, file, self);
    suite.emit('require', require(file), file, self);
    suite.emit('post-require', global, file, self);
    --pending || (fn && fn());
  });
};

/**
 * Enable growl support.
 *
 * @api private
 */

Mocha.prototype._growl = function(runner, reporter) {
  var notify = require('growl');

  runner.on('end', function(){
    var stats = reporter.stats;
    if (stats.failures) {
      var msg = stats.failures + ' of ' + runner.total + ' tests failed';
      notify(msg, { name: 'mocha', title: 'Failed', image: image('error') });
    } else {
      notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', {
          name: 'mocha'
        , title: 'Passed'
        , image: image('ok')
      });
    }
  });
};

/**
 * Add regexp to grep, if `re` is a string it is escaped.
 *
 * @param {RegExp|String} re
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.grep = function(re){
  this.options.grep = 'string' == typeof re
    ? new RegExp(utils.escapeRegexp(re))
    : re;
  return this;
};

/**
 * Invert `.grep()` matches.
 *
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.invert = function(){
  this.options.invert = true;
  return this;
};

/**
 * Ignore global leaks.
 *
 * @param {Boolean} ignore
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.ignoreLeaks = function(ignore){
  this.options.ignoreLeaks = !!ignore;
  return this;
};

/**
 * Enable global leak checking.
 *
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.checkLeaks = function(){
  this.options.ignoreLeaks = false;
  return this;
};

/**
 * Enable growl support.
 *
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.growl = function(){
  this.options.growl = true;
  return this;
};

/**
 * Ignore `globals` array or string.
 *
 * @param {Array|String} globals
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.globals = function(globals){
  this.options.globals = (this.options.globals || []).concat(globals);
  return this;
};

/**
 * Set the timeout in milliseconds.
 *
 * @param {Number} timeout
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.timeout = function(timeout){
  this.suite.timeout(timeout);
  return this;
};

/**
 * Set slowness threshold in milliseconds.
 *
 * @param {Number} slow
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.slow = function(slow){
  this.suite.slow(slow);
  return this;
};

/**
 * Makes all tests async (accepting a callback)
 *
 * @return {Mocha}
 * @api public
 */

Mocha.prototype.asyncOnly = function(){
  this.options.asyncOnly = true;
  return this;
};

/**
 * Run tests and invoke `fn()` when complete.
 *
 * @param {Function} fn
 * @return {Runner}
 * @api public
 */

Mocha.prototype.run = function(fn){
  if (this.files.length) this.loadFiles();
  var suite = this.suite;
  var options = this.options;
  var runner = new exports.Runner(suite);
  var reporter = new this._reporter(runner);
  runner.ignoreLeaks = false !== options.ignoreLeaks;
  runner.asyncOnly = options.asyncOnly;
  if (options.grep) runner.grep(options.grep, options.invert);
  if (options.globals) runner.globals(options.globals);
  if (options.growl) this._growl(runner, reporter);
  return runner.run(fn);
};
<|endoftext|>


<commit_before>"use strict";

var express = require('express');
var routes = require('./routes');
var utils = require('./utils');

module.exports = Model;

/**
 * Constructor.
 *
 * @param model - The mongoose model that is being restified.
 */
function Model(restifier, model) {
  this.restifier = restifier;
  this.model = model;

  // Special fields
  var id;
  var restricted = this.restricted = [];
  model.schema.eachPath(function(pathName, path) {
    if (path.options.restricted) {
      restricted.push(pathName);
    }
    if (path.options.id) {
      id = pathName;
    }
  });
  this.id = id || '_id';

  this.modifiers = [];
  this.filters = {};
  this.transformers = [];
  this.submodels = {};

  this.transform(function(req, doc) {
    restricted.filter(function(path) {
      delete doc[path];
    });
  });
}

/**
 * Modifies a query parameter.
 *
 * @param param - The param to modify
 * @paran fn(req, value) - The modifier function. Should return the new value.
 */
Model.prototype.modifyParam = function(param, fn) {
  this.modifiers.push({
    param: param,
    fn: fn
  });
  return this;
};

/**
 * Modifies the limit parameter.
 *
 * @param num - The maximum number of documents that can be returned.
 */
Model.prototype.limit = function(num) {
  return this.modifyParam('limit', function(req, value) {
    return value && !isNaN(value) ? Math.min(num, value) : num;
  });
};

/**
 * Adds a filter to this model.
 *
 * @param name - The name of the filter.
 * @param filter(req, query, params...) - The filter function that will be run on queries.
 */
Model.prototype.filter = function(name, filter) {
  this.filters[name] = filter;
  return this;
};

/**
 * Adds a transformer to this model.
 *
 * @param transformer(req, doc) - The transformer
 */
Model.prototype.transform = function(transformer) {
  this.transformers.push(transformer);
};

Model.prototype.applyTransforms = function(req, doc) {
  var jsoned = doc.toObject();
  this.transformers.filter(function(tf) {
    tf(req, jsoned);
  });
  return jsoned;
};

/**
 * Makes a field a submodel, meaning that field
 * has its own routes for query/crud.
 *
 * @param field - The field to submodel
 * @param model - The mongoose model
 */
Model.prototype.submodel = function(field, model) {
  // Check if valid
  if (this.parent) {
    throw new Error('Submodels of submodels not allowed for model "' + this.model.modelName + '".');
  }

  var path = this.model.schema.paths[field];
  if (!path) {
    throw new Error('Field "' + field + '" undefined for submodel of model "' + this.model.modelName + '".');
  }
  var type = path.options.type;
  if (!Array.isArray(type)) {
    throw new Error('Field "' + field + '" must be an array for submodel of model "' + this.model.modelName + '".');
  }
  if (type[0].ref !== model.modelName) {
    throw new Error('Field "' + field + '" has wrong ref for submodel of model "' + this.model.modelName + '".');
  }
  if (!type[0].correspondsTo) {
    throw new Error('Field "' + field + '" must have correspondsTo property set for submodel of model "' + this.model.modelName + '".');
  }

  var submodel = this.submodels[field] = new Model(this.restifier, model);
  submodel.parent = this;
  submodel.parentField = field;
  return submodel;
};

/**
 * Serves this model on a RESTful API.
 *
 * @param router - The Express router to serve the model on.
 */
Model.prototype.serve = function(router) {
  var thiz = this;
  var Mod = this.model;

  var baseModel = this.parent ? this.parent.model : this.model;
  var uriBase = '/' + baseModel.collection.name;
  var uriDoc = uriBase + '/:id';
  if (this.parent) {
    uriBase = uriDoc + '/' + this.parentField;
    uriDoc = uriBase + '/:sid';
  }

  router.route(uriBase)
    .get(routes.query(this, Mod))
    .post(routes.create(this, Mod));

  var docFetcher = function(req, res, next) {
    if (!req.params.id) {
      return next();
    }
    var query = {};
    var body = req.body;
    var idField = thiz.parent ? thiz.parent.id : thiz.id;
    query[idField] = req.params.id;

    var model = thiz.parent ? thiz.parent.model : thiz.model;
    model.findOne(query).exec(function(err, doc) {
      if (err) {
        return next(err);
      }
      if (!doc) {
        return res.error(404, model.modelName + ' "' + req.params.id + '" not found.');
      }

      if (!thiz.parent) {
        req.doc = doc;
        return next();
      }

      query = {};
      query[thiz.id] = req.params.sid;
      thiz.model.findOne(query).exec(function(err, sai) {
        if (err) {
          return next(err);
        }

        if (!doc) {
          return res.error(404, Mod.modelName + ' "' + req.params.id + '" not found.');
        }

        req.doc = sai; // get sai from sid
        return next();
      });
    });
  };

  router.route(uriDoc)
    .get(docFetcher, routes.get(this, Mod))
    .put(docFetcher, routes.update(this, Mod))
    .patch(docFetcher, routes.update(this, Mod))
    .delete(docFetcher, routes.delete(this, Mod));

  return this;
};

/**
 * Creates a middleware of this model.
 */
Model.prototype.middleware = function() {
  var router = express.Router();
  this.serve(router);
  return router;
};
<commit_msg>Set request parent doc if subdoc<commit_after>"use strict";

var express = require('express');
var routes = require('./routes');
var utils = require('./utils');

module.exports = Model;

/**
 * Constructor.
 *
 * @param model - The mongoose model that is being restified.
 */
function Model(restifier, model) {
  this.restifier = restifier;
  this.model = model;

  // Special fields
  var id;
  var restricted = this.restricted = [];
  model.schema.eachPath(function(pathName, path) {
    if (path.options.restricted) {
      restricted.push(pathName);
    }
    if (path.options.id) {
      id = pathName;
    }
  });
  this.id = id || '_id';

  this.modifiers = [];
  this.filters = {};
  this.transformers = [];
  this.submodels = {};

  this.transform(function(req, doc) {
    restricted.filter(function(path) {
      delete doc[path];
    });
  });
}

/**
 * Modifies a query parameter.
 *
 * @param param - The param to modify
 * @paran fn(req, value) - The modifier function. Should return the new value.
 */
Model.prototype.modifyParam = function(param, fn) {
  this.modifiers.push({
    param: param,
    fn: fn
  });
  return this;
};

/**
 * Modifies the limit parameter.
 *
 * @param num - The maximum number of documents that can be returned.
 */
Model.prototype.limit = function(num) {
  return this.modifyParam('limit', function(req, value) {
    return value && !isNaN(value) ? Math.min(num, value) : num;
  });
};

/**
 * Adds a filter to this model.
 *
 * @param name - The name of the filter.
 * @param filter(req, query, params...) - The filter function that will be run on queries.
 */
Model.prototype.filter = function(name, filter) {
  this.filters[name] = filter;
  return this;
};

/**
 * Adds a transformer to this model.
 *
 * @param transformer(req, doc) - The transformer
 */
Model.prototype.transform = function(transformer) {
  this.transformers.push(transformer);
};

Model.prototype.applyTransforms = function(req, doc) {
  var jsoned = doc.toObject();
  this.transformers.filter(function(tf) {
    tf(req, jsoned);
  });
  return jsoned;
};

/**
 * Makes a field a submodel, meaning that field
 * has its own routes for query/crud.
 *
 * @param field - The field to submodel
 * @param model - The mongoose model
 */
Model.prototype.submodel = function(field, model) {
  // Check if valid
  if (this.parent) {
    throw new Error('Submodels of submodels not allowed for model "' + this.model.modelName + '".');
  }

  var path = this.model.schema.paths[field];
  if (!path) {
    throw new Error('Field "' + field + '" undefined for submodel of model "' + this.model.modelName + '".');
  }
  var type = path.options.type;
  if (!Array.isArray(type)) {
    throw new Error('Field "' + field + '" must be an array for submodel of model "' + this.model.modelName + '".');
  }
  if (type[0].ref !== model.modelName) {
    throw new Error('Field "' + field + '" has wrong ref for submodel of model "' + this.model.modelName + '".');
  }
  if (!type[0].correspondsTo) {
    throw new Error('Field "' + field + '" must have correspondsTo property set for submodel of model "' + this.model.modelName + '".');
  }

  var submodel = this.submodels[field] = new Model(this.restifier, model);
  submodel.parent = this;
  submodel.parentField = field;
  return submodel;
};

/**
 * Serves this model on a RESTful API.
 *
 * @param router - The Express router to serve the model on.
 */
Model.prototype.serve = function(router) {
  var thiz = this;
  var Mod = this.model;

  var baseModel = this.parent ? this.parent.model : this.model;
  var uriBase = '/' + baseModel.collection.name;
  var uriDoc = uriBase + '/:id';
  if (this.parent) {
    uriBase = uriDoc + '/' + this.parentField;
    uriDoc = uriBase + '/:sid';
  }

  router.route(uriBase)
    .get(routes.query(this, Mod))
    .post(routes.create(this, Mod));

  var docFetcher = function(req, res, next) {
    if (!req.params.id) {
      return next();
    }
    var query = {};
    var body = req.body;
    var idField = thiz.parent ? thiz.parent.id : thiz.id;
    query[idField] = req.params.id;

    var model = thiz.parent ? thiz.parent.model : thiz.model;
    model.findOne(query).exec(function(err, doc) {
      if (err) {
        return next(err);
      }
      if (!doc) {
        return res.error(404, model.modelName + ' "' + req.params.id + '" not found.');
      }

      if (!thiz.parent) {
        req.doc = doc;
        return next();
      }
      req.parentDoc = doc;
      query = {};
      query[thiz.id] = req.params.sid;
      thiz.model.findOne(query).exec(function(err, sai) {
        if (err) {
          return next(err);
        }

        if (!doc) {
          return res.error(404, Mod.modelName + ' "' + req.params.id + '" not found.');
        }

        req.doc = sai; // get sai from sid
        return next();
      });
    });
  };

  router.route(uriDoc)
    .get(docFetcher, routes.get(this, Mod))
    .put(docFetcher, routes.update(this, Mod))
    .patch(docFetcher, routes.update(this, Mod))
    .delete(docFetcher, routes.delete(this, Mod));

  return this;
};

/**
 * Creates a middleware of this model.
 */
Model.prototype.middleware = function() {
  var router = express.Router();
  this.serve(router);
  return router;
};
<|endoftext|>


<commit_before>const apiInterface = require('./interface')
const five = require('johnny-five');
const board = new five.Board();

let connectedDevices = [];
//implement security

module.exports = board.on('ready', function() {
  let clickCount = 0;
//blink lights
//module.exports = () => {
//led = new five.Led(13); // pin 13
//led.blink(500); // 500ms interval
  var touch = new five.Button({
    controller: "TINKERKIT",
    pin: "I0",
   });

 // var accel = new five.Accelerometer({
 //    pins: ["I1", "I2"],
 //    freq: 100
 //  });

  // "axischange"
  //
  // Fires only when X, Y or Z has changed
  //
  /*accel.on("axischange", function() {

    console.log("axischange", this.raw);
  });*/

  //Handle push button events
/*function startTimer() {
    var timer = 4, minutes, seconds;
    setInterval(function () {
        minutes = parseInt(timer / 60, 10)
        seconds = parseInt(timer % 60, 10);

        minutes = minutes < 10 ? "0" + minutes : minutes;
        seconds = seconds < 10 ? "0" + seconds : seconds;
        //display.textContent = minutes + ":" + seconds;
        if(nrOfClicks>1){
          //Skicka JAAAAAA
          timer = duration;
        }
        if(--timer<0 || nrOfClicks<=1)
          timer = duration;
          //Skicka Neeeeej
      },1000);
  }*/

  /*create listener on channel with
  callback on established connection and on request*/
  apiInterface.addChannelListener('pinchy_channel', () => {
    //Connection callback
    // let testMessage = responseFactory('requestInfo', 'sender', null, null);
    console.log('connection established');
    // apiInterface.publishMessage(testMessage);
  },
  (m) => {
    //response callback
    let channel = m.channel;
    let message = JSON.parse(m.message);

    console.log('recieved message on ' + channel + ': ' + message.tag);
    formatResponse(channel, message);

  });

  let formatResponse = (channel, message) => {
    let videoId = null;
    let startTime = null;
    //check tag and format response accordingly
    if(channel=='pinchy_channel'){
      switch(message.tag){
        case 'connect':
          if(!connectedDevices.includes(message.sender)){
            connectedDevices.push(message.sender);
          }
          console.log('nr of connected devices: ' + connectedDevices.length);
          break;
        case 'notify':
          //activity started, initialize other device
          videoId = parseInt(message.info.videoId);
          startTime = parseInt(message.info.time);
          let init = responseFactory('init', 'pinchy', videoId, startTime, null);
          console.log('sending init');
          apiInterface.publishMessage(init);
          //addSensorListener();
          break;

        case 'ready':
          //activate sensor listeners
          addSensorListener();
          break;

        case 'info':
          //swicth playing device, send start and stop
          let transmitDuration = calcDiff(message.timeStamp)/1000;
          //console.log('transmitDuration in milliseconds: ' + transmitDuration);
          //console.log('time to start/stop: ' + response.info.time);
          videoId = message.info.videoId;
          console.log(videoId);
          startTime = parseInt(message.info.time)/1000;

          let start = responseFactory('start', 'pinchy', videoId, startTime + transmitDuration + 1, null);
          let stop = responseFactory('stop', 'pinchy', videoId, startTime*1000 + transmitDuration*1000 + 1000, null);
          apiInterface.publishMessage(start);
          apiInterface.publishMessage(stop);
          break;

        default:
          console.log('ignoring...');
      }

      //...
      //do publish
      //add new listener
    }
    else{
      //...no channel
    }
  }

  let calcDiff = (timestamp) => {
    return Date.now() - parseInt(timestamp);
  }

  let addSensorListener = () => {

    //when couch sensor is triggered, send question, then listen for finger input
    console.log('listen for sensor input');
     ["down"].forEach(function(type) {
      touch.on(type, function() {
        clickSensorInput();
      });
    });
    //when finger input, send choice and info

  }
  let clickSensorInput = function() {
    clickCount++;
    if (clickCount === 1) {
        singleClickTimer = setTimeout(function() {
            clickCount = 0;
            console.log("One click, send request");
            let request = responseFactory('requestInfo', 'pinchy', null, null, null);
            let choice = responseFactory('choice', 'pinchy', null, null, 'yes');
            apiInterface.publishMessage(request);
            apiInterface.publishMessage(choice);
        }, 400);
    } else if (clickCount >= 2) {
        clearTimeout(singleClickTimer);
        clickCount = 0;
        console.log("Double click, send choice");
        let choice = responseFactory('choice', 'pinchy', null, null, 'no');
        apiInterface.publishMessage(choice);
    }
  }
  let responseFactory = (tag, sender, videoId, time, choice) => {
      return JSON.stringify({
        "tag": tag,
        "sender": sender,
        "info": {
          "videoId": videoId,
          "time": time,
          "choice": choice
        },
        "timeStamp": Date.now()
      });
  }

});
// }
<commit_msg>edit synchronization algorithm<commit_after>const apiInterface = require('./interface')
const five = require('johnny-five');
const board = new five.Board();

let connectedDevices = [];
//implement security on device recognition,
//only those who are connected can communicate

module.exports = board.on('ready', function() {

  let clickCount = 0;
  let touch = new five.Button({
    controller: "TINKERKIT",
    pin: "I0",
  });
  let accel = new five.Accelerometer({
    pins: ["I1", "I2"],
    freq: 100
  });

  /*create listener on channel with
  callback on established connection and on request*/
  apiInterface.addChannelListener('pinchy_channel', () => {
    //Connection callback
    // let testMessage = responseFactory('requestInfo', 'sender', null, null);
    console.log('connection established');
    // apiInterface.publishMessage(testMessage);
  },
  (m) => {
    //response callback
    let channel = m.channel;
    let message = JSON.parse(m.message);

    console.log('recieved message on ' + channel + ': ' + message.tag);
    formatResponse(channel, message);

  });

  let formatResponse = (channel, message) => {
    let videoId = null;
    let startTime = null;
    //check tag and format response accordingly
    if(channel=='pinchy_channel'){
      switch(message.tag){
        case 'connect':
          if(!connectedDevices.includes(message.sender)){
            connectedDevices.push(message.sender);
          }
          console.log('nr of connected devices: ' + connectedDevices.length);
          break;
        case 'notify':
          //activity started, initialize other device
          videoId = message.info.videoId;
          startTime = parseInt(message.info.time);
          let init = responseFactory('init', 'pinchy', videoId, startTime, null, null);
          console.log('sending init');
          apiInterface.publishMessage(init);
          //addSensorListener();
          break;

        case 'ready':
          //activate sensor listeners
          addSensorListener();
          break;

        case 'info':
          //swicth playing device, send start and stop
          let transmitDuration = parseInt(calcDiff(message.timeStamp)/1000);

          //console.log('transmitDuration in milliseconds: ' + transmitDuration);
          //console.log('time to start/stop: ' + response.info.time);
          videoId = message.info.videoId;
          console.log(videoId);
          startTime = parseInt(message.info.time/1000);

          let start = responseFactory('start', 'pinchy', videoId, startTime + transmitDuration, transmitDuration, null);
          let stop = responseFactory('stop', 'pinchy', videoId, startTime, transmitDuration, null);
          apiInterface.publishMessage(start);
          apiInterface.publishMessage(stop);
          break;

        default:
          console.log('ignoring...');
      }
    }
    else{
      //...no channel
      console.log('received on unidentified channel');
    }
  }

  let calcDiff = (timestamp) => {
    return Date.now() - parseInt(timestamp);
  }

  let addSensorListener = () => {
    //when couch sensor is triggered, send question, then listen for finger input
    console.log('listen for sensor input');

    accel.on("axischange", () => {
      console.log("axischange", this.raw);
      let temp = this.raw;
      setTimeOut( () => {
        if(this.raw-temp >= 5 || this.raw-temp <= 5){
          console.log('couch sensor triggered, send question and listen for finger input');
          let question = responseFactory('question', 'pinchy', null, null, null, null);
          apiInterface.publishMessage(question);
          addfingerSensorListener();
        }
      }), 200);
    });
  }

  let addSensorListener = () => {
    //listen for finger input
    ["down"].forEach(function(type) {
      touch.on(type, function() {
        clickSensorInput();
      });
    });
  }

  let clickSensorInput = function() {
    clickCount++;
    if (clickCount == 1) {
        singleClickTimer = setTimeout(function() {
            if (clickCount >= 2) {
              console.log("Double click, send choice");
              let choice = responseFactory('choice', 'pinchy', null, null, null, 'no');
              apiInterface.publishMessage(choice);
            } else{
              console.log("One click, send request");
              let request = responseFactory('requestInfo', 'pinchy', null, null, null, null);
              let choice = responseFactory('choice', 'pinchy', null, null, null, 'yes');
              apiInterface.publishMessage(request);
              apiInterface.publishMessage(choice);
            }
            clickCount = 0;
        }, 400);
    }
  }
  let responseFactory = (tag, sender, videoId, startTime, delay, choice) => {
      return JSON.stringify({
        "tag": tag,
        "sender": sender,
        "info": {
          "videoId": videoId,
          "time": startTime,
          "delay": delay,
          "choice": choice
        },
        "timeStamp": Date.now()
      });
  }

});
// }
<|endoftext|>


<commit_before>var r = require('rethinkdb');
var Document = require('./document.js');

function Model(name, schema, settings, thinky) {
    settings = settings || {};

    this.name = name;
    this.schema = schema;
    this.checkEnforce = thinky.checkEnforce; // TODO fix ugliness

    this.settings = {
        primaryKey: settings.primaryKey || 'id',
        enforce: this.checkEnforce(settings.enforce, thinky.options.enforce)
    };

    this.thinky = thinky;
    this.pool = thinky.pool;
    this.thinkyOptions = thinky.options
}

Model.compile = function(name, schema, settings, thinky) {
    modelProto = new Model(name, schema, settings, thinky);
    function model(doc, docSettings) {
        doc = doc || {};

        docSettings = docSettings || {};
        var enforce = modelProto.checkEnforce(docSettings.enforce, modelProto.settings.enforce);

        result = {}
        modelProto.createBasedOnSchema(result, doc, doc, enforce);

        result.__proto__ = new Document(modelProto, docSettings);
        return result;
    };
    model.__proto__ = modelProto;
    return model;
};

/*
String, Number, Date, Boolean, null, Array, Object
{ name: String }
{ name: { type: String } } // {name: "Kitty"} or { name: { type: "Kitty" } }?
{ name: { type: String, default: value/function }
{ name: { type: [String, Number, ...] }
{ age: { type: Number, min: ..., max: ...} }
{ comments: { type: Array, min: ..., max: ...} }
{ arrayOfStrings: [ String ] }
{ arrayOfStrings: [ String, maxLength, minLength ] }
*/
//TODO Let people default arrays/object
//TODO Implement maxLenght, minLenght
Model.prototype.createBasedOnSchema = function(result, doc, originalDoc, enforce, prefix, schema) {
    var enforce = enforce; // Add another check here?
    var prefix = prefix || '';

    var schema = schema || this.schema;

    var iterator;
    if (Object.prototype.toString.call(schema) === '[object Array]') {
        iterator = doc;
    }
    else {
        iterator = schema;
    }

    for(var key in iterator) {
        key = (doc === iterator)? parseInt(key): key; // Trick or treat
        var schema_key = (doc === iterator)? schema[0] : schema[key];

        // Do not read or your eres are going to bleed
        if (this.checkType(result, doc, originalDoc, schema, key, schema_key, String, 'string', prefix, enforce) === true) { continue; }
        else if (this.checkType(result, doc, originalDoc, schema, key, schema_key, Number, 'number', prefix, enforce) === true) { continue; }
        else if (this.checkType(result, doc, originalDoc, schema, key, schema_key, Boolean, 'boolean',prefix, enforce) === true) { continue; }
        else if (this.checkType(result, doc, originalDoc, schema, key, schema_key, Array, 'array', prefix, enforce) === true) { continue; }
        else if (this.checkType(result, doc, originalDoc, schema, key, schema_key, Object, 'object', prefix, enforce) === true) { continue; }
    }

    if (enforce.extra === true) {
        for(key in doc) {
            if (schema[key] === undefined) {
                throw new Error('An extra field '+key+' not defined in the schema was found.')
            }
        }
    }

}

// Helper for createdBasedOnSchema
Model.prototype.checkType = function(result, doc, originalDoc, schema, key, schema_key, type, typeOf, prefix, enforce) {
    if (schema_key === type) {
        if (typeof doc[key] === typeOf) {
            result[key] = doc[key];
        }
        else if ((enforce.missing === true) && (doc[key] == null)) {
            throw new Error("Value for "+prefix+"["+key+"] must be defined")
        }
        else if ((doc[key] != null) && (enforce.type === true)) {
            throw new Error("Value for "+prefix+"["+key+"] must be a "+type.name)
        }
        return true;
    }
    else if ((Object.prototype.toString.call(schema_key) === '[object Object]') && (schema_key._type === type)) {
        if (typeof schema_key.enforce === 'boolean') { enforce = schema_key.enforce; }

        if (schema_key.enforce != null) {
            var enforce = modelProto.checkEnforce(schema_key.enforce, enforce);
        }

        // Deal with the special case of full objects and arrays
        if (type === Object) {
            //console.log(doc[key]);
            if (Object.prototype.toString.call(doc[key]) === '[object Object]') {
                result[key] = result[key] || {};
                prefix += '['+key+']';
                Model.prototype.createBasedOnSchema(result[key], doc[key], originalDoc, enforce, prefix, schema_key.schema);
            }
            else if ((doc[key] == null) && (schema_key['default'] != null)) {
                if (typeof schema_key['default'] === 'function') {
                    result[key] = schema_key['default'](originalDoc);
                    if ((enforce.type === true) && (typeof result[key] !== typeOf)) {
                        throw new Error("The default function did not return a "+type.name+" for "+prefix+"["+key+"]");
                    }
                }
                else if (typeof schema_key['default'] === typeOf) {
                    result[key] = schema_key['default']; // We are sure that we get the good type here since we have tested it before
                }
                else if ((schema['default'] == null) && (enforce.missing === true)) {
                    throw new Error("Default value for "+prefix+"["+key+"] must be defined")
                }
                else if (enforce.type === true) {
                    throw new Error("The default value is not a "+type.name);
                }
            }
            else if ((enforce.missing === true) && (doc[key] == null)) {
                throw new Error("Value for "+prefix+"["+key+"] must be defined")
            }
            else if ((doc[key] != null) && (enforce.type === true)) {
                throw new Error("Value for "+prefix+"["+key+"] must be an Object")
            }
            return true;

        }
        else if (type === Array) {
            if (Object.prototype.toString.call(doc[key]) === '[object Array]') {
                result[key] = [];
                prefix += '['+key+']';
                Model.prototype.createBasedOnSchema(result[key], doc[key], originalDoc, enforce, prefix, [schema_key.schema]);
            }
            else if ((doc[key] == null) && (schema_key['default'] != null)) {
                if (typeof schema_key['default'] === 'function') {
                    result[key] = schema_key['default'](originalDoc);
                    if ((enforce.type === true) && (typeof result[key] !== typeOf)) {
                        throw new Error("The default function did not return a "+type.name+" for "+prefix+"["+key+"]");
                    }
                }
                else if (Object.prototype.toString.call(schema_key['default']) === '[object Array]') {
                //else if (typeof schema_key['default'] === typeOf) {
                    result[key] = schema_key['default']; // We are sure that we get the good type here since we have tested it before
                }
                else if ((schema['default'] == null) && (enforce.missing === true)) {
                    throw new Error("Default value for "+prefix+"["+key+"] must be defined")
                }
                else if (enforce.type === true) {
                    throw new Error("The default value is not a "+type.name);
                }
            }
            else if ((enforce.missing === true) && (doc[key] == null)) {
                throw new Error("Value for "+prefix+"["+key+"] must be defined")
            }
            else if ((doc[key] != null) && (enforce.type === true)) {
                throw new Error("Value for "+prefix+"["+key+"] must be an Array")
            }
            return true;

        }
        else {
            if (typeof doc[key] === typeOf) {
                result[key] = doc[key];
            }
            else if ((doc[key] == null) && (schema_key['default'] != null)) {
                if (typeof schema_key['default'] === 'function') {
                    result[key] = schema_key['default'](originalDoc);
                    if ((enforce.type === true) && (typeof result[key] !== typeOf)) {
                        throw new Error("The default function did not return a "+type.name+" for "+prefix+"["+key+"]");
                    }
                }
                else if (typeof schema_key['default'] === typeOf) {
                    result[key] = schema_key['default']; // We are sure that we get the good type here since we have tested it before
                }
            }
            else if ((enforce.missing === true) && (doc[key] == null)) {
                throw new Error("Value for "+prefix+"["+key+"] must be defined")
            }
            else if ((doc[key] != null) && (enforce.type === true)) {
                throw new Error("Value for "+prefix+"["+key+"] must be a "+type.name);
            }
        }
        return true;
    }
    else if ((type === Array) && (Object.prototype.toString.call(schema_key) === '[object Array]')) {
        if (Object.prototype.toString.call(doc[key]) === '[object Array]') {
            result[key] = [];
            prefix += '['+key+']';
            Model.prototype.createBasedOnSchema(result[key], doc[key], originalDoc, enforce, prefix, schema_key);
        }
        else if ((enforce.missing === true) && (doc[key] == null)) {
            throw new Error("Value for "+prefix+"["+key+"] must be defined")
        }
        else if ((doc[key] != null) && (enforce.type === true)) {
            throw new Error("Value for "+prefix+"["+key+"] must be an Array")
        }
        return true;
    }
    else if ((type === Object) && (Object.prototype.toString.call(schema_key) === '[object Object]')) {
        if (Object.prototype.toString.call(doc[key]) === '[object Object]') {
            result[key] = result[key] || {};
            prefix += '['+key+']';
            Model.prototype.createBasedOnSchema(result[key], doc[key], originalDoc, enforce, prefix, schema_key);
        }
        else if ((enforce.missing === true) && (doc[key] == null)) {
            throw new Error("Value for "+prefix+"["+key+"] must be defined")
        }
        else if ((doc[key] != null) && (enforce.type === true)) {
            throw new Error("Value for "+prefix+"["+key+"] must be an Object")
        }
        return true;
    }

    return false;
}

// Define a method for the documents
Model.prototype.define = function(key, fn) {
    var model = this.__proto__;
    model[key] = fn;
}

Model.prototype.setSchema = function(schema) {
    this.schema = schema;
}

Model.prototype.getSettings = function() {
    return this.settings;
}

Model.prototype.getPrimaryKey = function() {
    return this.settings.primaryKey;
}

Model.prototype.save = function(callback) {
    //TODO Implement replace
    var self = this; // The document
    var model = this.getModel();
    model.pool.acquire( function(error, connection) {
        if (error) {
            return callback(error, null);
        }
        var query;
        var docSettings = self.getSettings()

        if (docSettings.saved === true) {
            var primaryKey = model.getPrimaryKey();
            //TODO remove the r.expr around self when the driver will be fixed regarding circular reference
            r.db(model.thinkyOptions.db).table(model.name).get(self[primaryKey]).update(r.expr(self), {returnVals: true}).run(connection, function(error, result) {
                if (error) {
                    if ((callback) && (typeof callback === 'function')) callback(error, null);;
                }
                else if ((result) && (result.errors > 0)) {
                    if ((callback) && (typeof callback === 'function')) callback(result, null);
                }
                else {
                    self.replace(result.new_val);

                    if ((callback) && (typeof callback === 'function')) callback(error, self);
                    model.pool.release(connection);
                    self.emit('save', self, result.old_val);
                }
            });

        }
        else {
            //TODO remove the r.expr around self when the driver will be fixed regarding circular reference
            r.db(model.thinkyOptions.db).table(model.name).insert(r.expr(self), {returnVals: true}).run(connection, function(error, result) {
                if (error) {
                    if ((callback) && (typeof callback === 'function')) callback(error, null);
                }
                else if ((result) && (result.errors > 0)) {
                    if ((callback) && (typeof callback === 'function')) callback(result, null);;
                }
                else {
                    self.replace(result.new_val);
                    self.getSettings().saved = true;
                    if ((callback) && (typeof callback === 'function')) callback(null, self);
                    // We can release the connection because we get back an object
                    model.pool.release(connection);
                    self.emit('save', self, result.old_val);
                }
            });

        }
    });
    return this;
};

Model.prototype.get = function(id, callback) {
    var self = this;
    var model = this.__proto__;
    model.pool.acquire( function(error, connection) {
        if (error) {
            return callback(error, null);
        }
        if (Object.prototype.toString.call(id) === '[object Array]') {
            var table = r.db(model.thinkyOptions.db).table(model.name);
            table.getAll.apply(table, id).run( connection, function(error, cursor) {
                if (error) {
                    callback(error, null);
                    model.pool.release(connection);
                }
                else {
                    cursor.toArray( function(error, results) {
                        if (error) {
                            callback(error, null);
                            model.pool.release(connection);
                        }
                        else {
                            var docs = [];
                            for (var index in results) {
                                result = results[index];
                                var doc = new self(result, true);
                                docs.push(doc);
                            }
                            callback(null, docs);
                            model.pool.release(connection);
                        }
                    });
                }
            });

        }
        else {
            //TODO Handle type error
            r.db(model.thinkyOptions.db).table(model.name).get(id).run( connection, function(error, result) {
                var doc = null;
                if ((error == null) && (result != null)) {
                    doc = new self(result, true);
                }
                callback(error, doc);
                model.pool.release(connection);
            });
        }
    });
}

Model.prototype.filter = function(filter, callback) {
    var self = this;
    var model = this.__proto__;
    model.pool.acquire( function(error, connection) {
        if (error) {
            return callback(error, null);
        }
        r.db(model.thinkyOptions.db).table(model.name).filter(filter).run( connection, function(error, cursor) {
            if (error) {
                callback(error, null);
                model.pool.release(connection);
            }
            else {
                cursor.toArray( function(error, results) {
                    if (error) {
                        callback(error, null);
                        model.pool.release(connection);
                    }
                    else {
                        var docs = [];
                        for (var index in results) {
                            result = results[index];
                            var doc = new self(result, true);
                            docs.push(doc);
                        }
                        callback(null, docs);
                        model.pool.release(connection);
                    }
                });
            }
        });
    });
}

Model.prototype.mapReduce = function(mapFunction, callback) {
    //TODO
}
Model.prototype.count = function(callback) {
    var self = this;
    var model = this.__proto__;
    model.pool.acquire( function(error, connection) {
        if (error) {
            return callback(error, null);
        }
        r.db(model.thinkyOptions.db).table(model.name).count().run( connection, function(error, result) {
            if (error) {
                callback(error, null);
                model.pool.release(connection);
            }
            else {
                callback(null, result);
                model.pool.release(connection);
            }
        });
    });
}

var model = module.exports = exports = Model;
<commit_msg>Update comment for the definition of a schema<commit_after>var r = require('rethinkdb');
var Document = require('./document.js');

function Model(name, schema, settings, thinky) {
    settings = settings || {};

    this.name = name;
    this.schema = schema;
    this.checkEnforce = thinky.checkEnforce; // TODO fix ugliness

    this.settings = {
        primaryKey: settings.primaryKey || 'id',
        enforce: this.checkEnforce(settings.enforce, thinky.options.enforce)
    };

    this.thinky = thinky;
    this.pool = thinky.pool;
    this.thinkyOptions = thinky.options
}

Model.compile = function(name, schema, settings, thinky) {
    modelProto = new Model(name, schema, settings, thinky);
    function model(doc, docSettings) {
        doc = doc || {};

        docSettings = docSettings || {};
        var enforce = modelProto.checkEnforce(docSettings.enforce, modelProto.settings.enforce);

        result = {}
        modelProto.createBasedOnSchema(result, doc, doc, enforce);

        result.__proto__ = new Document(modelProto, docSettings);
        return result;
    };
    model.__proto__ = modelProto;
    return model;
};

/*
- name: name of the model
- schema: An object which fields can map to the following value
    - String
    - Number
    - Boolean
    - Array with one type (like [String], [Number], [{name: String, age: Number}]
    - Object that contains a valid schema
    - {\_type: String, enforce: { missing: _boolean_, type: _boolean_, extra: _boolean_}, default: _value/function_ }
    - {\_type: Number, enforce: { missing: _boolean_, type: _boolean_, extra: _boolean_}, default: _value/function_ }
    - {\_type: Boolean, enforce: { missing: <boolean_, type: _boolean_, extra: _boolean_}, default: _value/function_ }
    - {\_type: Array, schema: _schema_, enforce: { missing: _boolean_, type: _boolean_, extra: _boolean_}, default: _value/function_ }
    - {\_type: Object, schama: _schema_, enforce: { missing: _boolean_, type: _boolean_, extra: _boolean_}, default: _value/function_ }
- settings (object): settings for the model
    - enforce: represents if the schemas should be enforced or not. Its value can be:
        - an object with the 3 fields:
            - missing -- throw on missing fields -- default to false
            - extra -- throw if extra fields are provided -- default to false
            - type -- throw if the type is not the one expected -- default to true
        - a boolean that set all 3 parameters to the same value

TODO: Don't force users to provide all fields in enforce
TODO: Let people custom _type // mostly about test
TODO: Implement maxLenght, minLenght

*/
Model.prototype.createBasedOnSchema = function(result, doc, originalDoc, enforce, prefix, schema) {
    var enforce = enforce; // Add another check here?
    var prefix = prefix || '';

    var schema = schema || this.schema;

    var iterator;
    if (Object.prototype.toString.call(schema) === '[object Array]') {
        iterator = doc;
    }
    else {
        iterator = schema;
    }

    for(var key in iterator) {
        key = (doc === iterator)? parseInt(key): key; // Trick or treat
        var schema_key = (doc === iterator)? schema[0] : schema[key];

        // Do not read or your eres are going to bleed
        if (this.checkType(result, doc, originalDoc, schema, key, schema_key, String, 'string', prefix, enforce) === true) { continue; }
        else if (this.checkType(result, doc, originalDoc, schema, key, schema_key, Number, 'number', prefix, enforce) === true) { continue; }
        else if (this.checkType(result, doc, originalDoc, schema, key, schema_key, Boolean, 'boolean',prefix, enforce) === true) { continue; }
        else if (this.checkType(result, doc, originalDoc, schema, key, schema_key, Array, 'array', prefix, enforce) === true) { continue; }
        else if (this.checkType(result, doc, originalDoc, schema, key, schema_key, Object, 'object', prefix, enforce) === true) { continue; }
    }

    if (enforce.extra === true) {
        for(key in doc) {
            if (schema[key] === undefined) {
                throw new Error('An extra field '+key+' not defined in the schema was found.')
            }
        }
    }

}

// Helper for createdBasedOnSchema
Model.prototype.checkType = function(result, doc, originalDoc, schema, key, schema_key, type, typeOf, prefix, enforce) {
    if (schema_key === type) {
        if (typeof doc[key] === typeOf) {
            result[key] = doc[key];
        }
        else if ((enforce.missing === true) && (doc[key] == null)) {
            throw new Error("Value for "+prefix+"["+key+"] must be defined")
        }
        else if ((doc[key] != null) && (enforce.type === true)) {
            throw new Error("Value for "+prefix+"["+key+"] must be a "+type.name)
        }
        return true;
    }
    else if ((Object.prototype.toString.call(schema_key) === '[object Object]') && (schema_key._type === type)) {
        if (typeof schema_key.enforce === 'boolean') { enforce = schema_key.enforce; }

        if (schema_key.enforce != null) {
            var enforce = modelProto.checkEnforce(schema_key.enforce, enforce);
        }

        // Deal with the special case of full objects and arrays
        if (type === Object) {
            //console.log(doc[key]);
            if (Object.prototype.toString.call(doc[key]) === '[object Object]') {
                result[key] = result[key] || {};
                prefix += '['+key+']';
                Model.prototype.createBasedOnSchema(result[key], doc[key], originalDoc, enforce, prefix, schema_key.schema);
            }
            else if ((doc[key] == null) && (schema_key['default'] != null)) {
                if (typeof schema_key['default'] === 'function') {
                    result[key] = schema_key['default'](originalDoc);
                    if ((enforce.type === true) && (typeof result[key] !== typeOf)) {
                        throw new Error("The default function did not return a "+type.name+" for "+prefix+"["+key+"]");
                    }
                }
                else if (typeof schema_key['default'] === typeOf) {
                    result[key] = schema_key['default']; // We are sure that we get the good type here since we have tested it before
                }
                else if ((schema['default'] == null) && (enforce.missing === true)) {
                    throw new Error("Default value for "+prefix+"["+key+"] must be defined")
                }
                else if (enforce.type === true) {
                    throw new Error("The default value is not a "+type.name);
                }
            }
            else if ((enforce.missing === true) && (doc[key] == null)) {
                throw new Error("Value for "+prefix+"["+key+"] must be defined")
            }
            else if ((doc[key] != null) && (enforce.type === true)) {
                throw new Error("Value for "+prefix+"["+key+"] must be an Object")
            }
            return true;

        }
        else if (type === Array) {
            if (Object.prototype.toString.call(doc[key]) === '[object Array]') {
                result[key] = [];
                prefix += '['+key+']';
                Model.prototype.createBasedOnSchema(result[key], doc[key], originalDoc, enforce, prefix, [schema_key.schema]);
            }
            else if ((doc[key] == null) && (schema_key['default'] != null)) {
                if (typeof schema_key['default'] === 'function') {
                    result[key] = schema_key['default'](originalDoc);
                    if ((enforce.type === true) && (typeof result[key] !== typeOf)) {
                        throw new Error("The default function did not return a "+type.name+" for "+prefix+"["+key+"]");
                    }
                }
                else if (Object.prototype.toString.call(schema_key['default']) === '[object Array]') {
                //else if (typeof schema_key['default'] === typeOf) {
                    result[key] = schema_key['default']; // We are sure that we get the good type here since we have tested it before
                }
                else if ((schema['default'] == null) && (enforce.missing === true)) {
                    throw new Error("Default value for "+prefix+"["+key+"] must be defined")
                }
                else if (enforce.type === true) {
                    throw new Error("The default value is not a "+type.name);
                }
            }
            else if ((enforce.missing === true) && (doc[key] == null)) {
                throw new Error("Value for "+prefix+"["+key+"] must be defined")
            }
            else if ((doc[key] != null) && (enforce.type === true)) {
                throw new Error("Value for "+prefix+"["+key+"] must be an Array")
            }
            return true;

        }
        else {
            if (typeof doc[key] === typeOf) {
                result[key] = doc[key];
            }
            else if ((doc[key] == null) && (schema_key['default'] != null)) {
                if (typeof schema_key['default'] === 'function') {
                    result[key] = schema_key['default'](originalDoc);
                    if ((enforce.type === true) && (typeof result[key] !== typeOf)) {
                        throw new Error("The default function did not return a "+type.name+" for "+prefix+"["+key+"]");
                    }
                }
                else if (typeof schema_key['default'] === typeOf) {
                    result[key] = schema_key['default']; // We are sure that we get the good type here since we have tested it before
                }
            }
            else if ((enforce.missing === true) && (doc[key] == null)) {
                throw new Error("Value for "+prefix+"["+key+"] must be defined")
            }
            else if ((doc[key] != null) && (enforce.type === true)) {
                throw new Error("Value for "+prefix+"["+key+"] must be a "+type.name);
            }
        }
        return true;
    }
    else if ((type === Array) && (Object.prototype.toString.call(schema_key) === '[object Array]')) {
        if (Object.prototype.toString.call(doc[key]) === '[object Array]') {
            result[key] = [];
            prefix += '['+key+']';
            Model.prototype.createBasedOnSchema(result[key], doc[key], originalDoc, enforce, prefix, schema_key);
        }
        else if ((enforce.missing === true) && (doc[key] == null)) {
            throw new Error("Value for "+prefix+"["+key+"] must be defined")
        }
        else if ((doc[key] != null) && (enforce.type === true)) {
            throw new Error("Value for "+prefix+"["+key+"] must be an Array")
        }
        return true;
    }
    else if ((type === Object) && (Object.prototype.toString.call(schema_key) === '[object Object]')) {
        if (Object.prototype.toString.call(doc[key]) === '[object Object]') {
            result[key] = result[key] || {};
            prefix += '['+key+']';
            Model.prototype.createBasedOnSchema(result[key], doc[key], originalDoc, enforce, prefix, schema_key);
        }
        else if ((enforce.missing === true) && (doc[key] == null)) {
            throw new Error("Value for "+prefix+"["+key+"] must be defined")
        }
        else if ((doc[key] != null) && (enforce.type === true)) {
            throw new Error("Value for "+prefix+"["+key+"] must be an Object")
        }
        return true;
    }

    return false;
}

// Define a method for the documents
Model.prototype.define = function(key, fn) {
    var model = this.__proto__;
    model[key] = fn;
}

Model.prototype.setSchema = function(schema) {
    this.schema = schema;
}

Model.prototype.getSettings = function() {
    return this.settings;
}

Model.prototype.getPrimaryKey = function() {
    return this.settings.primaryKey;
}

Model.prototype.save = function(callback) {
    //TODO Implement replace
    var self = this; // The document
    var model = this.getModel();
    model.pool.acquire( function(error, connection) {
        if (error) {
            return callback(error, null);
        }
        var query;
        var docSettings = self.getSettings()

        if (docSettings.saved === true) {
            var primaryKey = model.getPrimaryKey();
            //TODO remove the r.expr around self when the driver will be fixed regarding circular reference
            r.db(model.thinkyOptions.db).table(model.name).get(self[primaryKey]).update(r.expr(self), {returnVals: true}).run(connection, function(error, result) {
                if (error) {
                    if ((callback) && (typeof callback === 'function')) callback(error, null);;
                }
                else if ((result) && (result.errors > 0)) {
                    if ((callback) && (typeof callback === 'function')) callback(result, null);
                }
                else {
                    self.replace(result.new_val);

                    if ((callback) && (typeof callback === 'function')) callback(error, self);
                    model.pool.release(connection);
                    self.emit('save', self, result.old_val);
                }
            });

        }
        else {
            //TODO remove the r.expr around self when the driver will be fixed regarding circular reference
            r.db(model.thinkyOptions.db).table(model.name).insert(r.expr(self), {returnVals: true}).run(connection, function(error, result) {
                if (error) {
                    if ((callback) && (typeof callback === 'function')) callback(error, null);
                }
                else if ((result) && (result.errors > 0)) {
                    if ((callback) && (typeof callback === 'function')) callback(result, null);;
                }
                else {
                    self.replace(result.new_val);
                    self.getSettings().saved = true;
                    if ((callback) && (typeof callback === 'function')) callback(null, self);
                    // We can release the connection because we get back an object
                    model.pool.release(connection);
                    self.emit('save', self, result.old_val);
                }
            });

        }
    });
    return this;
};

Model.prototype.get = function(id, callback) {
    var self = this;
    var model = this.__proto__;
    model.pool.acquire( function(error, connection) {
        if (error) {
            return callback(error, null);
        }
        if (Object.prototype.toString.call(id) === '[object Array]') {
            var table = r.db(model.thinkyOptions.db).table(model.name);
            table.getAll.apply(table, id).run( connection, function(error, cursor) {
                if (error) {
                    callback(error, null);
                    model.pool.release(connection);
                }
                else {
                    cursor.toArray( function(error, results) {
                        if (error) {
                            callback(error, null);
                            model.pool.release(connection);
                        }
                        else {
                            var docs = [];
                            for (var index in results) {
                                result = results[index];
                                var doc = new self(result, true);
                                docs.push(doc);
                            }
                            callback(null, docs);
                            model.pool.release(connection);
                        }
                    });
                }
            });

        }
        else {
            //TODO Handle type error
            r.db(model.thinkyOptions.db).table(model.name).get(id).run( connection, function(error, result) {
                var doc = null;
                if ((error == null) && (result != null)) {
                    doc = new self(result, true);
                }
                callback(error, doc);
                model.pool.release(connection);
            });
        }
    });
}

Model.prototype.filter = function(filter, callback) {
    var self = this;
    var model = this.__proto__;
    model.pool.acquire( function(error, connection) {
        if (error) {
            return callback(error, null);
        }
        r.db(model.thinkyOptions.db).table(model.name).filter(filter).run( connection, function(error, cursor) {
            if (error) {
                callback(error, null);
                model.pool.release(connection);
            }
            else {
                cursor.toArray( function(error, results) {
                    if (error) {
                        callback(error, null);
                        model.pool.release(connection);
                    }
                    else {
                        var docs = [];
                        for (var index in results) {
                            result = results[index];
                            var doc = new self(result, true);
                            docs.push(doc);
                        }
                        callback(null, docs);
                        model.pool.release(connection);
                    }
                });
            }
        });
    });
}

Model.prototype.mapReduce = function(mapFunction, callback) {
    //TODO
}
Model.prototype.count = function(callback) {
    var self = this;
    var model = this.__proto__;
    model.pool.acquire( function(error, connection) {
        if (error) {
            return callback(error, null);
        }
        r.db(model.thinkyOptions.db).table(model.name).count().run( connection, function(error, result) {
            if (error) {
                callback(error, null);
                model.pool.release(connection);
            }
            else {
                callback(null, result);
                model.pool.release(connection);
            }
        });
    });
}

var model = module.exports = exports = Model;
<|endoftext|>