<commit_before>var localconfig = require('./config');
var helpers = require('./helpers');
var fs = require('fs');
var marked = require('marked');
var postsPath = './src/posts/';
var path = require('path');
var pagesPath = './src/pages/';
var nodePath = require('path');
var markextra = require('markdown-extra');
var _ = require('underscore');
var nunjucks = require('nunjucks');
var co = require('co');
var Promise = require('promise');
var ncp = require('ncp').ncp;
var permalinks = require('permalinks');
var nodefs = require('node-fs');
var stylus = require('stylus');
var mkmeta = require('marked-metadata');
var util = require('./cli/util');
var traceur = require('traceur');
var clc = helpers.cli_color();

var Helper =  {
	getPagesFiles : function () {
		return new Promise(function (resolve, reject) {
			/* Reading pages dir */
			fs.readdir(pagesPath, function (err, files) {
				if (err) {
					throw err;
				}

				resolve(files);
			});
		});
	},

	sortPosts : function (posts) {
		/*posts.sort(function(a,b) {
			return new Date(b.date) - new Date(a.date);
		});*/
		return posts;
	},

	parsePages : function (files) {
		return new Promise(function (resolve, reject) {
			var pages = [];
			var curTemplate = GLOBAL.config.template;
			var nunjucksEnv = GLOBAL.nunjucksEnv;
			var config = GLOBAL.config;
			var tokens = [config.header_tokens ? config.header_tokens[0] : '<!--',
						  config.header_tokens ? config.header_tokens[1] : '-->'];

			files.forEach(function (file, i) {
				var page = fs.readFileSync( pagesPath + "/" + file).toString();
				var pageTemplate = fs.readFileSync('./src/templates/' + curTemplate + '/page.html');
				var pageTemplateNJ = nunjucks.compile(pageTemplate.toString(), nunjucksEnv);
				var markfile = page.toString();
				var filename = path.extname(file) === '.md' ? path.basename(file, '.md') : path.basename(file, '.markdown');
				var md = new mkmeta(pagesPath + '/' + file);
				md.defineTokens(tokens[0], tokens[1]);

				/* Markdown extra */
				var metadata = md.metadata();
				var pagePermalink = permalinks(config.pages_permalink, { title : filename });

				var _page = {
					content : md.markdown(),
					metadata : metadata
				}

				var pageContent = nunjucks.compile(page, nunjucksEnv);
				var pageHTMLFile = pageTemplateNJ.render({ page : _page, config : GLOBAL.config });
				/* Removing header metadata */
				pageHTMLFile = pageHTMLFile.replace(/<!--[\s\S]*?-->/g, '');

				metadata['content'] = pageHTMLFile;
				metadata['file'] = postsPath + file;
				metadata['filename'] = filename;
				metadata['link'] = '/' + filename + '.html';
				metadata.date = new Date(metadata.date);

				nodefs.mkdir('./public/' + pagePermalink, 0777, true, function (err) {
					if (err) {
						reject(err);
					} else {
						/* write page html file */
						fs.writeFile('./public/' + pagePermalink + '/' + 'index.html', pageHTMLFile, function (err) {
							if (err) throw err;
							console.log(clc.info('Successfully generated page ' + pagePermalink));
						});
					}
				});

				if (i === files.length - 1) {
					resolve(pages);
				}

			});
		});
	},

	normalizeMetaData : function (data) {
		data.title = data.title.replace(/\"/g,'');
		return data;
	},

	normalizeContent : function (data) {
		return data;
	},

	compileES6 : function (context, data) {
		var result = '',
			traceur_runtime = fs.readFileSync(localconfig.rootdir + '/bin/client/traceur-runtime.js').toString(),
			config = fs.readFileSync('./harmonic.json').toString(),
			harmonic_client = fs.readFileSync(localconfig.rootdir + '/bin/client/harmonic-client.js').toString();

		harmonic_client =
			harmonic_client.replace(/\{\{posts\}\}/, JSON.stringify(Helper.sortPosts(data)))
							.replace(/\{\{config\}\}/, config);

		switch (context) {
			case 'posts' :
				result = traceur.compile(harmonic_client, {
					filename : 'harmonic-client.js'
				});

				if (result.error) {
				  throw result.error;
				}

				fs.writeFileSync('./public/harmonic.js', '//traceur runtime\n' + traceur_runtime + '\n//harmonic code\n' +result.js);
			break;
		}
	}
}

var Parser = function() {

	this.start = function() {
		return new Promise(function (resolve, reject) {
			resolve('starting the parser');
		});
	};

	this.clean = function() {
		return new Promise(function (resolve, reject) {
			var exec = require('child_process').exec,
				child = null;
			child = exec('rm -rf ./public',function(err,out) {
			 	console.log(clc.warn('Cleaning up...'));
			 	resolve();
			});
		});
	};

	this.createPublicFolder = function(argument) {
		return new Promise(function(resolve, reject) {
			fs.exists('./public', function(exists) {
				if(!exists) {
					fs.mkdirSync("public", 0766);
					console.log(clc.info('Successfully generated public folder'));
					resolve();
				}
			});
		});
	};

    this.compileCss = function() {
        console.log(GLOBAL.config.preprocessor);
        this.compileCss[GLOBAL.config.preprocessor]();
    }

	this.compileCss.stylus = function() {
		return new Promise(function (resolve, reject) {
			var subDirs = ['./src/templates/default/resources/_stylus/'];
			var curTemplate = './src/templates/' + GLOBAL.config.template;
			var stylDir = curTemplate + '/resources/_stylus';
			var cssDir = curTemplate + '/resources/css';
			var code = fs.readFileSync(stylDir + '/index.styl', 'utf8');

			stylus(code)
				.set('paths', [stylDir, stylDir + '/engine', stylDir + '/partials'])
				.render(function(err, css) {
					if (err) {
						reject(err);
					} else {
						fs.writeFileSync(cssDir + '/main.css', css);
						console.log(clc.info('Successfully generated CSS'));
						resolve();
					}
				});
		});
	};

    this.compileCss.less = function() {
        console.log("less compiled");
    };

	this.generateTagsPages = function(postsMetadata) {
		var postsByTag = {};
		var curTemplate = GLOBAL.config.template;
		var nunjucksEnv = GLOBAL.nunjucksEnv;
		var tagTemplate = fs.readFileSync('./src/templates/' + curTemplate + '/index.html');
		var tagTemplateNJ = nunjucks.compile(tagTemplate.toString(), nunjucksEnv);
		var indexContent = '';
		var tagPath = null;

		return new Promise(function(resolve, reject) {
			for (var lang in postsMetadata) {
				for (var i = 0; i < postsMetadata[lang].length; i += 1) {
					var tags = postsMetadata[lang][i].categories;
					for (var y = 0; y < tags.length; y += 1) {
						var tag = tags[y]
								.toLowerCase()
								.trim()
								.split(' ')
								.join('-');

						if (Array.isArray(postsByTag[tag])) {
							postsByTag[tag].push(postsMetadata[lang][i]);
						} else {
							postsByTag[tag] = [postsMetadata[lang][i]];
						}
					}
				}

				for (var i in postsByTag) {
					tagContent = tagTemplateNJ.render({ posts : _.where(postsByTag[i], { lang: lang}), config : GLOBAL.config, category: i });

					/* If is the default language, generate in the root path */
					if (config.i18n.default === lang) {
						tagPath = './public/categories/' + i;
					} else {
						tagPath = './public/categories/' + lang + '/' + i;
					}

					nodefs.mkdirSync(tagPath, 0777, true);
					fs.writeFileSync(tagPath + '/index.html', tagContent);
					console.log(clc.info('Successfully generated tag[' + i + '] archive html file'));
				}
				resolve(postsMetadata);
			}
		});
	};

	this.generateIndex = function(postsMetadata) {
		return new Promise(function(resolve, reject) {
			var _posts = null;
			var curTemplate = GLOBAL.config.template;
			var nunjucksEnv = GLOBAL.nunjucksEnv;
			var indexTemplate = fs.readFileSync('./src/templates/' + curTemplate + '/index.html');
			var indexTemplateNJ = nunjucks.compile(indexTemplate.toString(), nunjucksEnv);
			var indexContent = '';
			var indexPath = null;

			for (var lang in postsMetadata) {
				postsMetadata[lang].sort(function(a,b) {
					return new Date(b.date) - new Date(a.date);
				});
				_posts = postsMetadata[lang].slice(0,GLOBAL.config.index_posts || 10);
				indexContent = indexTemplateNJ.render({ posts : _posts, config : GLOBAL.config });

				if (config.i18n.default === lang) {
					indexPath = './public/';
				} else {
					indexPath = './public/' + lang;
				}
				nodefs.mkdirSync(indexPath, 0777, true);
				fs.writeFileSync(indexPath + '/index.html', indexContent);
				console.log(clc.info(lang + '/index file successfully created'));
			}
			resolve(postsMetadata);
		});
	};

	this.copyResources = function() {

		var imagesP = new Promise(function(resolve, reject) {
			ncp('./src/img', './public/img', function (err) {
				if (err) {
					reject(err);
					return;
				}
				resolve();
			});
		});

		var resourcesP = new Promise(function(resolve, reject) {
			var curTemplate = './src/templates/' + GLOBAL.config.template;
			ncp(curTemplate + '/resources', './public', function (err) {
				if (err) {
					reject(err);
					return;
				}
				resolve();
			});
		});

		return new Promise(function (resolve, reject) {
			Promise.all([resourcesP, imagesP])
			.then(function() {
				resolve('Resources copied');
			});
		});
	};

	this.generatePages = function (pagesMetadata) {
		return new Promise(function(resolve, reject) {
			Helper.getPagesFiles()
				.then(Helper.parsePages)
				.then(function(data) {
					resolve(data);
				}, function(e) {
					reject(e);
				})
		});
	};

	this.generatePosts = function (files) {
		return new Promise(function(resolve, reject) {
			var config = GLOBAL.config,
				posts = {},
				curTemplate = config.template,
				postsTemplate = fs.readFileSync('./src/templates/' + curTemplate + '/post.html'),
				nunjucksEnv = GLOBAL.nunjucksEnv,
				postsTemplateNJ = nunjucks.compile(postsTemplate.toString(), nunjucksEnv),
                tokens = [config.header_tokens ? config.header_tokens[0] : '<!--',
                          config.header_tokens ? config.header_tokens[1] : '-->'];

			for (var lang in files) {
				files[lang].forEach(function(file, i) {
					var md = new mkmeta(postsPath + lang + '/' + file);
					md.defineTokens(tokens[0], tokens[1]);
					var metadata = Helper.normalizeMetaData(md.metadata());
					var post = Helper.normalizeContent(md.markdown());
					var postCropped = md.markdown( { crop : '<!--more-->'});
					var filename = path.extname(file) === '.md' ? path.basename(file, '.md') : path.basename(file, '.markdown');
					var checkDate = new Date(filename.substr(0,10));
					filename = isNaN(checkDate.getDate()) ? filename : filename.substr(11, filename.length);
					var postPath = null;
					var categories = metadata.categories.split(',');

					/* If is the default language, generate in the root path */
					if (config.i18n.default === lang) {
						postPath = permalinks(config.posts_permalink.split(':language/')[1], { title : filename });
					} else {
						postPath = permalinks(config.posts_permalink, { title : filename, language : lang });
					}
					metadata.link = postPath;
					metadata.categories = categories;
					metadata['content'] = postCropped;
					metadata['file'] = postsPath + file;
					metadata['filename'] = filename;
					metadata['link'] = postPath;
					metadata['lang'] = lang;
					metadata['default_lang'] = config.i18n.default === lang ? false : true;
					metadata.date = new Date(metadata.date);

					var _post = {
						content : post,
						metadata : metadata
					}
					var postHTMLFile = postsTemplateNJ
						.render({ post : _post, config : GLOBAL.config })
						.replace(/<!--[\s\S]*?-->/g, '');

					if(metadata.published && metadata.published === 'false') {
						return;
					}

					nodefs.mkdir('./public/' + postPath, 0777, true, function (err) {
						if (err) {
							reject(err);
						} else {
							/* write post html file */
							fs.writeFile('./public/' + postPath + '/index.html', postHTMLFile, function (err) {
								if (err) {
									reject(err);
								}
								console.log(clc.info('Successfully generated post ' + postPath));
							});
						}
					});
					if (posts[lang]) {
						posts[lang].push(metadata);
					} else {
						posts[lang] = [metadata];
					}

					if (i === files[lang].length - 1) {
						Helper.compileES6('posts', posts);
						resolve(posts);
					}
				});
			}
		});
	};

	this.getFiles = function() {
		return new Promise(function (resolve, reject) {

			var config = GLOBAL.config,
				langs = config.i18n.languages,
				langsLen = langs.length,
				i = 0,
				files = {};

			for (i; i < langsLen; i += 1) {
				files[langs[i]] = fs.readdirSync(postsPath  + langs[i]);
			}

			resolve(files);
		});
	};

	this.getConfig = function() {
		return new Promise(function (resolve, reject) {
			var config = JSON.parse(fs.readFileSync("./harmonic.json").toString());
			var custom = null;
			var newConfig = null;

			try {
				custom = JSON.parse(fs.readFileSync("./src/templates/" + config.template + "/harmonic.json").toString());
			} catch (e) {
			}
			if (custom) {
				newConfig = _.extend(config, custom);
			} else {
				newConfig = config;
			}
			GLOBAL.config = newConfig;
			GLOBAL.nunjucksEnv = new nunjucks.Environment(new nunjucks.FileSystemLoader('./src/templates/' + config.template));
			resolve(newConfig);
		});
	};
}

module.exports = Parser;
<commit_msg>changed files<commit_after>var localconfig = require('./config');
var helpers = require('./helpers');
var fs = require('fs');
var marked = require('marked');
var postsPath = './src/posts/';
var path = require('path');
var pagesPath = './src/pages/';
var nodePath = require('path');
var markextra = require('markdown-extra');
var _ = require('underscore');
var nunjucks = require('nunjucks');
var co = require('co');
var Promise = require('promise');
var ncp = require('ncp').ncp;
var permalinks = require('permalinks');
var nodefs = require('node-fs');
var stylus = require('stylus');
var mkmeta = require('marked-metadata');
var util = require('./cli/util');
var traceur = require('traceur');
var clc = helpers.cli_color();

var Helper =  {
	getPagesFiles : function () {
		return new Promise(function (resolve, reject) {
			/* Reading pages dir */
			fs.readdir(pagesPath, function (err, files) {
				if (err) {
					throw err;
				}

				resolve(files);
			});
		});
	},

	sortPosts : function (posts) {
		/*posts.sort(function(a,b) {
			return new Date(b.date) - new Date(a.date);
		});*/
		return posts;
	},

	parsePages : function (files) {
		return new Promise(function (resolve, reject) {
			var pages = [];
			var curTemplate = GLOBAL.config.template;
			var nunjucksEnv = GLOBAL.nunjucksEnv;
			var config = GLOBAL.config;
			var tokens = [config.header_tokens ? config.header_tokens[0] : '<!--',
						  config.header_tokens ? config.header_tokens[1] : '-->'];

			files.forEach(function (file, i) {
				var page = fs.readFileSync( pagesPath + "/" + file).toString();
				var pageTemplate = fs.readFileSync('./src/templates/' + curTemplate + '/page.html');
				var pageTemplateNJ = nunjucks.compile(pageTemplate.toString(), nunjucksEnv);
				var markfile = page.toString();
				var filename = path.extname(file) === '.md' ? path.basename(file, '.md') : path.basename(file, '.markdown');
				var md = new mkmeta(pagesPath + '/' + file);
				md.defineTokens(tokens[0], tokens[1]);

				/* Markdown extra */
				var metadata = md.metadata();
				var pagePermalink = permalinks(config.pages_permalink, { title : filename });

				var _page = {
					content : md.markdown(),
					metadata : metadata
				}

				var pageContent = nunjucks.compile(page, nunjucksEnv);
				var pageHTMLFile = pageTemplateNJ.render({ page : _page, config : GLOBAL.config });
				/* Removing header metadata */
				pageHTMLFile = pageHTMLFile.replace(/<!--[\s\S]*?-->/g, '');

				metadata['content'] = pageHTMLFile;
				metadata['file'] = postsPath + file;
				metadata['filename'] = filename;
				metadata['link'] = '/' + filename + '.html';
				metadata.date = new Date(metadata.date);

				nodefs.mkdir('./public/' + pagePermalink, 0777, true, function (err) {
					if (err) {
						reject(err);
					} else {
						/* write page html file */
						fs.writeFile('./public/' + pagePermalink + '/' + 'index.html', pageHTMLFile, function (err) {
							if (err) throw err;
							console.log(clc.info('Successfully generated page ' + pagePermalink));
						});
					}
				});

				if (i === files.length - 1) {
					resolve(pages);
				}

			});
		});
	},

	normalizeMetaData : function (data) {
		data.title = data.title.replace(/\"/g,'');
		return data;
	},

	normalizeContent : function (data) {
		return data;
	},

	compileES6 : function (context, data) {
		var result = '',
			traceur_runtime = fs.readFileSync(localconfig.rootdir + '/bin/client/traceur-runtime.js').toString(),
			config = fs.readFileSync('./harmonic.json').toString(),
			harmonic_client = fs.readFileSync(localconfig.rootdir + '/bin/client/harmonic-client.js').toString();

		harmonic_client =
			harmonic_client.replace(/\{\{posts\}\}/, JSON.stringify(Helper.sortPosts(data)))
							.replace(/\{\{config\}\}/, config);

		switch (context) {
			case 'posts' :
				result = traceur.compile(harmonic_client, {
					filename : 'harmonic-client.js'
				});

				if (result.error) {
				  throw result.error;
				}

				fs.writeFileSync('./public/harmonic.js', '//traceur runtime\n' + traceur_runtime + '\n//harmonic code\n' +result.js);
			break;
		}
	}
}

var Parser = function() {

	this.start = function() {
		return new Promise(function (resolve, reject) {
			resolve('starting the parser');
		});
	};

	this.clean = function() {
		return new Promise(function (resolve, reject) {
			var exec = require('child_process').exec,
				child = null;
			child = exec('rm -rf ./public',function(err,out) {
			 	console.log(clc.warn('Cleaning up...'));
			 	resolve();
			});
		});
	};

	this.createPublicFolder = function(argument) {
		return new Promise(function(resolve, reject) {
			fs.exists('./public', function(exists) {
				if(!exists) {
					fs.mkdirSync("public", 0766);
					console.log(clc.info('Successfully generated public folder'));
					resolve();
				}
			});
		});
	};

    this.compileCss = function() {
        var compiler = {
            less: function() {
                console.log("less compiled");
            }
        };
        console.log(GLOBAL.config.preprocessor);
        compiler[GLOBAL.config.preprocessor]();
    }

	this.compileStylus = function() {
		return new Promise(function (resolve, reject) {
			var subDirs = ['./src/templates/default/resources/_stylus/'];
			var curTemplate = './src/templates/' + GLOBAL.config.template;
			var stylDir = curTemplate + '/resources/_stylus';
			var cssDir = curTemplate + '/resources/css';
			var code = fs.readFileSync(stylDir + '/index.styl', 'utf8');

			stylus(code)
				.set('paths', [stylDir, stylDir + '/engine', stylDir + '/partials'])
				.render(function(err, css) {
					if (err) {
						reject(err);
					} else {
						fs.writeFileSync(cssDir + '/main.css', css);
						console.log(clc.info('Successfully generated CSS'));
						resolve();
					}
				});
		});
	};

	this.generateTagsPages = function(postsMetadata) {
		var postsByTag = {};
		var curTemplate = GLOBAL.config.template;
		var nunjucksEnv = GLOBAL.nunjucksEnv;
		var tagTemplate = fs.readFileSync('./src/templates/' + curTemplate + '/index.html');
		var tagTemplateNJ = nunjucks.compile(tagTemplate.toString(), nunjucksEnv);
		var indexContent = '';
		var tagPath = null;

		return new Promise(function(resolve, reject) {
			for (var lang in postsMetadata) {
				for (var i = 0; i < postsMetadata[lang].length; i += 1) {
					var tags = postsMetadata[lang][i].categories;
					for (var y = 0; y < tags.length; y += 1) {
						var tag = tags[y]
								.toLowerCase()
								.trim()
								.split(' ')
								.join('-');

						if (Array.isArray(postsByTag[tag])) {
							postsByTag[tag].push(postsMetadata[lang][i]);
						} else {
							postsByTag[tag] = [postsMetadata[lang][i]];
						}
					}
				}

				for (var i in postsByTag) {
					tagContent = tagTemplateNJ.render({ posts : _.where(postsByTag[i], { lang: lang}), config : GLOBAL.config, category: i });

					/* If is the default language, generate in the root path */
					if (config.i18n.default === lang) {
						tagPath = './public/categories/' + i;
					} else {
						tagPath = './public/categories/' + lang + '/' + i;
					}

					nodefs.mkdirSync(tagPath, 0777, true);
					fs.writeFileSync(tagPath + '/index.html', tagContent);
					console.log(clc.info('Successfully generated tag[' + i + '] archive html file'));
				}
				resolve(postsMetadata);
			}
		});
	};

	this.generateIndex = function(postsMetadata) {
		return new Promise(function(resolve, reject) {
			var _posts = null;
			var curTemplate = GLOBAL.config.template;
			var nunjucksEnv = GLOBAL.nunjucksEnv;
			var indexTemplate = fs.readFileSync('./src/templates/' + curTemplate + '/index.html');
			var indexTemplateNJ = nunjucks.compile(indexTemplate.toString(), nunjucksEnv);
			var indexContent = '';
			var indexPath = null;

			for (var lang in postsMetadata) {
				postsMetadata[lang].sort(function(a,b) {
					return new Date(b.date) - new Date(a.date);
				});
				_posts = postsMetadata[lang].slice(0,GLOBAL.config.index_posts || 10);
				indexContent = indexTemplateNJ.render({ posts : _posts, config : GLOBAL.config });

				if (config.i18n.default === lang) {
					indexPath = './public/';
				} else {
					indexPath = './public/' + lang;
				}
				nodefs.mkdirSync(indexPath, 0777, true);
				fs.writeFileSync(indexPath + '/index.html', indexContent);
				console.log(clc.info(lang + '/index file successfully created'));
			}
			resolve(postsMetadata);
		});
	};

	this.copyResources = function() {

		var imagesP = new Promise(function(resolve, reject) {
			ncp('./src/img', './public/img', function (err) {
				if (err) {
					reject(err);
					return;
				}
				resolve();
			});
		});

		var resourcesP = new Promise(function(resolve, reject) {
			var curTemplate = './src/templates/' + GLOBAL.config.template;
			ncp(curTemplate + '/resources', './public', function (err) {
				if (err) {
					reject(err);
					return;
				}
				resolve();
			});
		});

		return new Promise(function (resolve, reject) {
			Promise.all([resourcesP, imagesP])
			.then(function() {
				resolve('Resources copied');
			});
		});
	};

	this.generatePages = function (pagesMetadata) {
		return new Promise(function(resolve, reject) {
			Helper.getPagesFiles()
				.then(Helper.parsePages)
				.then(function(data) {
					resolve(data);
				}, function(e) {
					reject(e);
				})
		});
	};

	this.generatePosts = function (files) {
		return new Promise(function(resolve, reject) {
			var config = GLOBAL.config,
				posts = {},
				curTemplate = config.template,
				postsTemplate = fs.readFileSync('./src/templates/' + curTemplate + '/post.html'),
				nunjucksEnv = GLOBAL.nunjucksEnv,
				postsTemplateNJ = nunjucks.compile(postsTemplate.toString(), nunjucksEnv),
                tokens = [config.header_tokens ? config.header_tokens[0] : '<!--',
                          config.header_tokens ? config.header_tokens[1] : '-->'];

			for (var lang in files) {
				files[lang].forEach(function(file, i) {
					var md = new mkmeta(postsPath + lang + '/' + file);
					md.defineTokens(tokens[0], tokens[1]);
					var metadata = Helper.normalizeMetaData(md.metadata());
					var post = Helper.normalizeContent(md.markdown());
					var postCropped = md.markdown( { crop : '<!--more-->'});
					var filename = path.extname(file) === '.md' ? path.basename(file, '.md') : path.basename(file, '.markdown');
					var checkDate = new Date(filename.substr(0,10));
					filename = isNaN(checkDate.getDate()) ? filename : filename.substr(11, filename.length);
					var postPath = null;
					var categories = metadata.categories.split(',');

					/* If is the default language, generate in the root path */
					if (config.i18n.default === lang) {
						postPath = permalinks(config.posts_permalink.split(':language/')[1], { title : filename });
					} else {
						postPath = permalinks(config.posts_permalink, { title : filename, language : lang });
					}
					metadata.link = postPath;
					metadata.categories = categories;
					metadata['content'] = postCropped;
					metadata['file'] = postsPath + file;
					metadata['filename'] = filename;
					metadata['link'] = postPath;
					metadata['lang'] = lang;
					metadata['default_lang'] = config.i18n.default === lang ? false : true;
					metadata.date = new Date(metadata.date);

					var _post = {
						content : post,
						metadata : metadata
					}
					var postHTMLFile = postsTemplateNJ
						.render({ post : _post, config : GLOBAL.config })
						.replace(/<!--[\s\S]*?-->/g, '');

					if(metadata.published && metadata.published === 'false') {
						return;
					}

					nodefs.mkdir('./public/' + postPath, 0777, true, function (err) {
						if (err) {
							reject(err);
						} else {
							/* write post html file */
							fs.writeFile('./public/' + postPath + '/index.html', postHTMLFile, function (err) {
								if (err) {
									reject(err);
								}
								console.log(clc.info('Successfully generated post ' + postPath));
							});
						}
					});
					if (posts[lang]) {
						posts[lang].push(metadata);
					} else {
						posts[lang] = [metadata];
					}

					if (i === files[lang].length - 1) {
						Helper.compileES6('posts', posts);
						resolve(posts);
					}
				});
			}
		});
	};

	this.getFiles = function() {
		return new Promise(function (resolve, reject) {

			var config = GLOBAL.config,
				langs = config.i18n.languages,
				langsLen = langs.length,
				i = 0,
				files = {};

			for (i; i < langsLen; i += 1) {
				files[langs[i]] = fs.readdirSync(postsPath  + langs[i]);
			}

			resolve(files);
		});
	};

	this.getConfig = function() {
		return new Promise(function (resolve, reject) {
			var config = JSON.parse(fs.readFileSync("./harmonic.json").toString());
			var custom = null;
			var newConfig = null;

			try {
				custom = JSON.parse(fs.readFileSync("./src/templates/" + config.template + "/harmonic.json").toString());
			} catch (e) {
			}
			if (custom) {
				newConfig = _.extend(config, custom);
			} else {
				newConfig = config;
			}
			GLOBAL.config = newConfig;
			GLOBAL.nunjucksEnv = new nunjucks.Environment(new nunjucks.FileSystemLoader('./src/templates/' + config.template));
			resolve(newConfig);
		});
	};
}

module.exports = Parser;
<|endoftext|>


<commit_before>// Define module using Universal Module Definition pattern
// https://github.com/umdjs/umd/blob/master/returnExports.js

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['vega', 'vegalite', 'clusterfck'], factory);
  } else if (typeof exports === 'object') {
    // Node. Does not work with strict CommonJS, but
    // only CommonJS-like environments that support module.exports,
    // like Node.
    module.exports = factory(
      require('vega'),
      require('vegalite'),
      require('clusterfck')
    );
  } else {
    // Browser globals (root is window)
    root.vgn = factory(root.vg, root.vl, root.clusterfck);
  }
}(this, function (vg, vl, clusterfck) {
  var vgn = {}; //VisGeN

  vgn.DEFAULT_OPT = {
    /**
     * Eliminate all transpose
     * - keeping horizontal dot plot only.
     * - for OxQ charts, always put O on Y
     */
    omitTranpose: true,
    /** remove all dot plot with >1 encoding */
    omitDotPlotWithExtraEncoding: true

  };

  var ENCODING_TYPES = ["x", "y", "row", "col", "size", "shape", "color", "alpha"]; //geo

  var CHART_TYPES = {
    TABLE: 'TABLE',
    BAR: 'BAR',
    PLOT: 'PLOT',
    LINE: 'LINE',
    AREA: 'AREA',
    MAP: 'MAP',
    HISTOGRAM: 'HISTOGRAM'
  };

  var MARK_TYPES = ["point", "bar", "line", "area",  "text"]; //filled_map

  var ANY_DATA_TYPES= (1<<4)-1;

  // BEGINING OF RULES

  //TODO markTypesAggregateSupport

  var marksRule = {
    point: pointRule,
    bar: barRule,
    line: lineRule,
    area: lineRule
  };

  function pointRule(enc, opt){
    if(enc.x && enc.y){

      if(enc.x.type == "O" && enc.y.type == "O"){ OxO
        // shape doesn't work with both x, y as ordinal
        if(enc.shape){
          return false;
        }
        if(enc.color && enc.color.type == "O"){
          return false;
        }
      }

    }else{ // plot with one axis = dot plot

      // Dot plot should always be horizontal
      if(opt.omitTranpose && enc.y) return false;

      // dot plot shouldn't have other encoding
      if(opt.omitDotPlotWithExtraEncoding && vl.keys(enc).length > 1) return false;

      // dot plot with shape is non-sense
      if (enc.shape) return false;
    }
    return true;
  }

  function barRule(enc){
    // Bar Chart requires at least one aggregate
    var hasAgg = false;
    for(var e in enc){
      if(enc[e].aggr){
        hasAgg = true;
        break;
      }
    }
    return hasAgg;
  }

  function lineRule(enc){
    // Line chart should be only horizontal
    return enc.x == "T" && enc.y == "Q";
  }

  var ENCODING_RULES = {
    x: {
      dataTypes: vl.dataTypes.O + vl.dataTypes.Q + vl.dataTypes.T,
      multiple: true //FIXME should allow multiple only for Q
    },
    y: {
      dataTypes: vl.dataTypes.O + vl.dataTypes.Q + vl.dataTypes.T,
      multiple: true //FIXME should allow multiple only for Q
    },
    row: {
      dataTypes: vl.dataTypes.O,
      multiple: true
    },
    col: {
      dataTypes: vl.dataTypes.O,
      multiple: true
    },
    shape: {
      dataTypes: vl.dataTypes.O
    },
    size: {
      dataTypes: vl.dataTypes.Q
    },
    color: {
      dataTypes: vl.dataTypes.O + vl.dataTypes.Q
    },
    alpha: {
      dataTypes: vl.dataTypes.Q
    },
    text: {
      dataTypes: ANY_DATA_TYPES
    }
    //geo: {
    //  dataTypes: [vl.dataTypes.G]
    //},
    //arc: { // pie
    //
    //}
  };

  var AGGREGATION_FN = { //all possible aggregate function listed by each data type
    Q: ["avg", "sum", "min", "max", "count"]
  };

  var TRANSFORM_FN = { //all possible transform function listed by each data type
    Q: ["log", "sqrt", "abs"], // "logit?"
    T: ["year","month","day"] //,"hr", "min", "bmon", "bday", "bdow", "bhr"]
  };

  // END OF RULES

  var json = function(s,sp){ return JSON.stringify(s, null, sp);};

  // Begin of Distance

  var DIST_BY_ENCTYPE = [
    ["x", "y", 0.2],
    ["color", "shape", 0.2],
    ["row", "col", 0.2],
    ["color", "alpha", 0.2]
  ].reduce(function(r, x){
    var a=x[0], b=x[1], d=x[2];
    r[a] = r[a] || {};
    r[b] = r[b] || {};
    r[a][b] = r[b][a] = d;
    return r;
  }, {}),
    DIST_MISSING = 100, CLUSTER_THRESHOLD=1;

  function colenc(encoding){
    var colenc = {},
      enc = encoding.enc;

    vl.keys(enc).forEach(function(encType){
      var e = vl.duplicate(enc[encType]);
      e.type = encType;
      colenc[e.name || ""] = e;
      delete e.name;
    });

    return {
      marktype: encoding.marktype,
      col: colenc
    };
  }

  vgn._getDistance = function(colenc1, colenc2){
    var cols = union(vl.keys(colenc1.col), vl.keys(colenc2.col)),
      dist = 0;

    cols.forEach(function(col){
      var e1 = colenc1.col[col], e2 = colenc2.col[col];

      if(e1 && e2){
        if(e1.type != e2.type){
          dist += (DIST_BY_ENCTYPE[e1.type]||{})[e2.type] || 1;
        }
        //FIXME add aggregation
      }else{
        dist += DIST_MISSING;
      }
    })
    return dist;
  }

  vgn.getDistanceTable = function(encodings){
    var len = encodings.length,
      colencs = encodings.map(function(e){ return colenc(e);}),
      diff = new Array(len), i;

    for(i=0; i<len; i++) diff[i] = new Array(len);

    for(i=0 ; i<len; i++){
      for(j=i+1; j<len; j++){
        diff[j][i] = diff[i][j] = vgn._getDistance(colencs[i], colencs[j]);
      }
    }
    return diff;
  }

  vgn.cluster = function(encodings, maxDistance){
    var dist = vgn.getDistanceTable(encodings),
      n = encodings.length;

    var clusterTrees = clusterfck.hcluster(range(n), function(i, j){
      return dist[i][j];
    }, "average", CLUSTER_THRESHOLD);

    var clusters = clusterTrees.map(function(tree){
      return traverse(tree, []);
    })

    console.log("clusters", clusters.map(function(c){ return c.join("+"); }));
    return clusters;
  }

  function traverse(node, arr){
    if(node.value !== undefined){
      arr.push(node.value);
    }else{
      if(node.left) traverse(node.left, arr)
      if(node.right) traverse(node.right, arr);
    }
    return arr;
  }

  // End of Clustering

  // Beginning of Chart Generation

  vgn.generateCharts = function (fields, marktypes, cfg, flat, opt){
    marktypes = marktypes || MARK_TYPES; // assign * if there is no constraints

    opt = opt ? vl.keys(opt).reduce(function(c ,k){ //merge with default
      c[k] = opt[k];
      return c;
    }, Object.create(vgn.DEFAULT_OPT)) : Object.create(vgn.DEFAULT_OPT)

    // generate permutation of encoding mappings
    var encodings = vgn._generateEncodings(fields);

    var chartGroups = encodings.map(function(enc){
      return vgn._getSupportedMarkTypes(enc, opt)
        .map(function(markType){
          return { marktype: markType, enc: enc, cfg: cfg };
        });
    });

    return flat ?
      [].concat.apply([], chartGroups) //flatten
      : chartGroups.filter(function(grp){ return grp.length > 0;}); //return non-empty groups
  };

  //TODO(kanitw): write test case
  vgn._getSupportedMarkTypes = function(enc, opt){
    var markTypes = MARK_TYPES.filter(function(markType){
      var mark = vl.marks[markType],
        reqs = mark.requiredEncoding,
        support = mark.supportedEncoding;

      for(var i in reqs){ // all required encodings in enc
        if(!(reqs[i] in enc)) return false;
      }

      for(var encType in enc){ // all encodings in enc are supported
        if(!support[encType]) return false;
      }

      return !marksRule[markType] || marksRule[markType](enc, opt);
    });

    //console.log('enc:', json(enc), " ~ marks:", markTypes);

    return markTypes;
  };

  //TODO(kanitw): write test case
  vgn._generateEncodings = function (fields){ // generate encodings (_enc property in vega)
    var encodings=[], tmpEnc = {};

    function assignField(i){
      // If all fields are assigned, save
      if(i===fields.length){
        // at the minimal all chart should have x, y, geo, text or arc
        if("x" in tmpEnc ||
          "y" in tmpEnc ||
          "geo" in tmpEnc ||
          "text" in tmpEnc ||
          "arc" in tmpEnc
        ){
          encodings.push(vg.duplicate(tmpEnc));
        }
        return;
      }

      // Otherwise, assign i-th field
      var field = fields[i];

      // TODO(kanitw): Aggregation
      // TODO(kanitw): Bin

      for(var j in ENCODING_TYPES){
        var et = ENCODING_TYPES[j];

        //TODO: support "multiple" assignment
        if(!(et in tmpEnc) &&
          (ENCODING_RULES[et].dataTypes & vl.dataTypes[field.type]) > 0){
          tmpEnc[et] = field;
          assignField(i+1);
          delete tmpEnc[et];
        }
      }
    }

    assignField(0);

    return encodings;
  };

  // UTILITY

  function union(a,b){
    var o = {};
    a.forEach(function(x){ o[x] = true;});
    b.forEach(function(x){ o[x] = true;});
    return vl.keys(o);
  }

  var abs = Math.abs;

  function range(start, stop, step) {
    if (arguments.length < 3) {
      step = 1;
      if (arguments.length < 2) {
        stop = start;
        start = 0;
      }
    }
    if ((stop - start) / step === Infinity) throw new Error("infinite range");
    var range = [], k = d3_range_integerScale(abs(step)), i = -1, j;
    start *= k, stop *= k, step *= k;
    if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k);
    return range;
  };

  function d3_range_integerScale(x) {
    var k = 1;
    while (x * k % 1) k *= 10;
    return k;
  }

  return vgn;
}));<commit_msg><refactor: move rules to near generation code><commit_after>// Define module using Universal Module Definition pattern
// https://github.com/umdjs/umd/blob/master/returnExports.js

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['vega', 'vegalite', 'clusterfck'], factory);
  } else if (typeof exports === 'object') {
    // Node. Does not work with strict CommonJS, but
    // only CommonJS-like environments that support module.exports,
    // like Node.
    module.exports = factory(
      require('vega'),
      require('vegalite'),
      require('clusterfck')
    );
  } else {
    // Browser globals (root is window)
    root.vgn = factory(root.vg, root.vl, root.clusterfck);
  }
}(this, function (vg, vl, clusterfck) {
  var vgn = {}; //VisGeN

  vgn.DEFAULT_OPT = {
    /**
     * Eliminate all transpose
     * - keeping horizontal dot plot only.
     * - for OxQ charts, always put O on Y
     */
    omitTranpose: true,
    /** remove all dot plot with >1 encoding */
    omitDotPlotWithExtraEncoding: true

  };

  var ENCODING_TYPES = ["x", "y", "row", "col", "size", "shape", "color", "alpha"]; //geo

  var CHART_TYPES = {
    TABLE: 'TABLE',
    BAR: 'BAR',
    PLOT: 'PLOT',
    LINE: 'LINE',
    AREA: 'AREA',
    MAP: 'MAP',
    HISTOGRAM: 'HISTOGRAM'
  };

  var MARK_TYPES = ["point", "bar", "line", "area",  "text"]; //filled_map

  var ANY_DATA_TYPES= (1<<4)-1;

  var AGGREGATION_FN = { //all possible aggregate function listed by each data type
    Q: ["avg", "sum", "min", "max", "count"]
  };

  var TRANSFORM_FN = { //all possible transform function listed by each data type
    Q: ["log", "sqrt", "abs"], // "logit?"
    T: ["year","month","day"] //,"hr", "min", "bmon", "bday", "bdow", "bhr"]
  };

  var json = function(s,sp){ return JSON.stringify(s, null, sp);};

  // Begin of Distance

  var DIST_BY_ENCTYPE = [
    ["x", "y", 0.2],
    ["color", "shape", 0.2],
    ["row", "col", 0.2],
    ["color", "alpha", 0.2]
  ].reduce(function(r, x){
    var a=x[0], b=x[1], d=x[2];
    r[a] = r[a] || {};
    r[b] = r[b] || {};
    r[a][b] = r[b][a] = d;
    return r;
  }, {}),
    DIST_MISSING = 100, CLUSTER_THRESHOLD=1;

  function colenc(encoding){
    var colenc = {},
      enc = encoding.enc;

    vl.keys(enc).forEach(function(encType){
      var e = vl.duplicate(enc[encType]);
      e.type = encType;
      colenc[e.name || ""] = e;
      delete e.name;
    });

    return {
      marktype: encoding.marktype,
      col: colenc
    };
  }

  vgn._getDistance = function(colenc1, colenc2){
    var cols = union(vl.keys(colenc1.col), vl.keys(colenc2.col)),
      dist = 0;

    cols.forEach(function(col){
      var e1 = colenc1.col[col], e2 = colenc2.col[col];

      if(e1 && e2){
        if(e1.type != e2.type){
          dist += (DIST_BY_ENCTYPE[e1.type]||{})[e2.type] || 1;
        }
        //FIXME add aggregation
      }else{
        dist += DIST_MISSING;
      }
    })
    return dist;
  }

  vgn.getDistanceTable = function(encodings){
    var len = encodings.length,
      colencs = encodings.map(function(e){ return colenc(e);}),
      diff = new Array(len), i;

    for(i=0; i<len; i++) diff[i] = new Array(len);

    for(i=0 ; i<len; i++){
      for(j=i+1; j<len; j++){
        diff[j][i] = diff[i][j] = vgn._getDistance(colencs[i], colencs[j]);
      }
    }
    return diff;
  }

  vgn.cluster = function(encodings, maxDistance){
    var dist = vgn.getDistanceTable(encodings),
      n = encodings.length;

    var clusterTrees = clusterfck.hcluster(range(n), function(i, j){
      return dist[i][j];
    }, "average", CLUSTER_THRESHOLD);

    var clusters = clusterTrees.map(function(tree){
      return traverse(tree, []);
    })

    console.log("clusters", clusters.map(function(c){ return c.join("+"); }));
    return clusters;
  }

  function traverse(node, arr){
    if(node.value !== undefined){
      arr.push(node.value);
    }else{
      if(node.left) traverse(node.left, arr)
      if(node.right) traverse(node.right, arr);
    }
    return arr;
  }

  // End of Clustering

  // BEGINING OF RULES

  //TODO markTypesAggregateSupport

  var marksRule = {
    point: pointRule,
    bar: barRule,
    line: lineRule,
    area: lineRule
  };

  function pointRule(enc, opt){
    if(enc.x && enc.y){

      if(enc.x.type == "O" && enc.y.type == "O"){ OxO
        // shape doesn't work with both x, y as ordinal
        if(enc.shape){
          return false;
        }
        if(enc.color && enc.color.type == "O"){
          return false;
        }
      }

    }else{ // plot with one axis = dot plot

      // Dot plot should always be horizontal
      if(opt.omitTranpose && enc.y) return false;

      // dot plot shouldn't have other encoding
      if(opt.omitDotPlotWithExtraEncoding && vl.keys(enc).length > 1) return false;

      // dot plot with shape is non-sense
      if (enc.shape) return false;
    }
    return true;
  }

  function barRule(enc){
    // Bar Chart requires at least one aggregate
    var hasAgg = false;
    for(var e in enc){
      if(enc[e].aggr){
        hasAgg = true;
        break;
      }
    }
    return hasAgg;
  }

  function lineRule(enc){
    // Line chart should be only horizontal
    return enc.x == "T" && enc.y == "Q";
  }

  var ENCODING_RULES = {
    x: {
      dataTypes: vl.dataTypes.O + vl.dataTypes.Q + vl.dataTypes.T,
      multiple: true //FIXME should allow multiple only for Q
    },
    y: {
      dataTypes: vl.dataTypes.O + vl.dataTypes.Q + vl.dataTypes.T,
      multiple: true //FIXME should allow multiple only for Q
    },
    row: {
      dataTypes: vl.dataTypes.O,
      multiple: true
    },
    col: {
      dataTypes: vl.dataTypes.O,
      multiple: true
    },
    shape: {
      dataTypes: vl.dataTypes.O
    },
    size: {
      dataTypes: vl.dataTypes.Q
    },
    color: {
      dataTypes: vl.dataTypes.O + vl.dataTypes.Q
    },
    alpha: {
      dataTypes: vl.dataTypes.Q
    },
    text: {
      dataTypes: ANY_DATA_TYPES
    }
    //geo: {
    //  dataTypes: [vl.dataTypes.G]
    //},
    //arc: { // pie
    //
    //}
  };

  // END OF RULES

  // Beginning of Chart Generation

  vgn.generateCharts = function (fields, marktypes, cfg, flat, opt){
    marktypes = marktypes || MARK_TYPES; // assign * if there is no constraints

    opt = opt ? vl.keys(opt).reduce(function(c ,k){ //merge with default
      c[k] = opt[k];
      return c;
    }, Object.create(vgn.DEFAULT_OPT)) : Object.create(vgn.DEFAULT_OPT)

    // generate permutation of encoding mappings
    var encodings = vgn._generateEncodings(fields);

    var chartGroups = encodings.map(function(enc){
      return vgn._getSupportedMarkTypes(enc, opt)
        .map(function(markType){
          return { marktype: markType, enc: enc, cfg: cfg };
        });
    });

    return flat ?
      [].concat.apply([], chartGroups) //flatten
      : chartGroups.filter(function(grp){ return grp.length > 0;}); //return non-empty groups
  };

  //TODO(kanitw): write test case
  vgn._getSupportedMarkTypes = function(enc, opt){
    var markTypes = MARK_TYPES.filter(function(markType){
      var mark = vl.marks[markType],
        reqs = mark.requiredEncoding,
        support = mark.supportedEncoding;

      for(var i in reqs){ // all required encodings in enc
        if(!(reqs[i] in enc)) return false;
      }

      for(var encType in enc){ // all encodings in enc are supported
        if(!support[encType]) return false;
      }

      return !marksRule[markType] || marksRule[markType](enc, opt);
    });

    //console.log('enc:', json(enc), " ~ marks:", markTypes);

    return markTypes;
  };

  //TODO(kanitw): write test case
  vgn._generateEncodings = function (fields){ // generate encodings (_enc property in vega)
    var encodings=[], tmpEnc = {};

    function assignField(i){
      // If all fields are assigned, save
      if(i===fields.length){
        // at the minimal all chart should have x, y, geo, text or arc
        if("x" in tmpEnc ||
          "y" in tmpEnc ||
          "geo" in tmpEnc ||
          "text" in tmpEnc ||
          "arc" in tmpEnc
        ){
          encodings.push(vg.duplicate(tmpEnc));
        }
        return;
      }

      // Otherwise, assign i-th field
      var field = fields[i];

      // TODO(kanitw): Aggregation
      // TODO(kanitw): Bin

      for(var j in ENCODING_TYPES){
        var et = ENCODING_TYPES[j];

        //TODO: support "multiple" assignment
        if(!(et in tmpEnc) &&
          (ENCODING_RULES[et].dataTypes & vl.dataTypes[field.type]) > 0){
          tmpEnc[et] = field;
          assignField(i+1);
          delete tmpEnc[et];
        }
      }
    }

    assignField(0);

    return encodings;
  };

  // UTILITY

  function union(a,b){
    var o = {};
    a.forEach(function(x){ o[x] = true;});
    b.forEach(function(x){ o[x] = true;});
    return vl.keys(o);
  }

  var abs = Math.abs;

  function range(start, stop, step) {
    if (arguments.length < 3) {
      step = 1;
      if (arguments.length < 2) {
        stop = start;
        start = 0;
      }
    }
    if ((stop - start) / step === Infinity) throw new Error("infinite range");
    var range = [], k = d3_range_integerScale(abs(step)), i = -1, j;
    start *= k, stop *= k, step *= k;
    if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k);
    return range;
  };

  function d3_range_integerScale(x) {
    var k = 1;
    while (x * k % 1) k *= 10;
    return k;
  }

  return vgn;
}));<|endoftext|>


<commit_before>'use strict';

/*
 * Protractor Plugin to run axe-core accessibility audits after Angular bootstrapped.
 */

const AxeBuilder = require('axe-webdriverjs');
const {buildMessage} = require('./build-message');

/* Resolved violations */
const violations = {};

/** When the page is ready and Angular is bootstrapped. */
function onPageStable() {

  console.log(this.config);

  AxeBuilder(browser.driver)
    .options(this.config || {})
    .analyze(results => handleResults(this, results));
}

/** Handles the results of axe-core. */
function handleResults(context, results) {

  if (!violations.hasOwnProperty(results.url)) {
    violations[results.url] = true;

    results.violations.forEach(violation => {

      let specName = `${violation.help} (${results.url})`;
      let message = '\n' + buildMessage(violation);

      context.addFailure(message, {specName});

    });

  }

}

exports.name = 'protractor-axe';
exports.onPageStable = onPageStable;
<commit_msg>remove options log<commit_after>'use strict';

/*
 * Protractor Plugin to run axe-core accessibility audits after Angular bootstrapped.
 */

const AxeBuilder = require('axe-webdriverjs');
const {buildMessage} = require('./build-message');

/* Resolved violations */
const violations = {};

/** When the page is ready and Angular is bootstrapped. */
function onPageStable() {
  AxeBuilder(browser.driver)
    .options(this.config || {})
    .analyze(results => handleResults(this, results));
}

/** Handles the results of axe-core. */
function handleResults(context, results) {

  if (!violations.hasOwnProperty(results.url)) {
    violations[results.url] = true;

    results.violations.forEach(violation => {

      let specName = `${violation.help} (${results.url})`;
      let message = '\n' + buildMessage(violation);

      context.addFailure(message, {specName});

    });

  }

}

exports.name = 'protractor-axe';
exports.onPageStable = onPageStable;
<|endoftext|>


<commit_before>define(["pex/core/Line2D"], function(Line2D) {
  function Triangle2D(a, b, c) {
    this.a = a;
    this.b = b;
    this.c = c;
  }

  //Doesn't handle point on the edge
  Triangle2D.prototype.contains = function(p) {
    var ab = new Line2D(this.a, this.b);
    var bc = new Line2D(this.b, this.c);
    var ca = new Line2D(this.c, this.a);
    var isLeftAB = ab.isPointOnTheLeftSide(p);
    var isLeftBC = bc.isPointOnTheLeftSide(p);
    var isLeftCA = ca.isPointOnTheLeftSide(p);
    return (isLeftAB && isLeftBC && isLeftCA) || (!isLeftAB && !isLeftBC && !isLeftCA);
  }

  return Triangle2D;
})

<commit_msg>Updated Triangle hit testing<commit_after>define(["pex/core/Line2D"], function(Line2D) {
  function sign(a, b, c)
  {
    //return ((this.b.x - this.a.x)*(p.y - this.a.y) - (this.b.y - this.a.y)*(p.x - this.a.x)) >= 0;
    return (a.x - c.x) * (b.y - c.y) - (b.x - c.x) * (a.y - c.y);
  }

  function Triangle2D(a, b, c) {
    this.a = a;
    this.b = b;
    this.c = c;
  }

  //http://stackoverflow.com/a/2049593
  //doesn't properly handle points on the edge of the triangle
  Triangle2D.prototype.contains = function(p) {
    var signAB = sign(this.a, this.b, p) < 0;
    var signBC = sign(this.b, this.c, p) < 0;
    var signCA = sign(this.c, this.a, p) < 0;

    return (signAB == signBC) && (signBC == signCA);
  }

  return Triangle2D;
})

<|endoftext|>


<commit_before>'use strict';

Object.defineProperty(exports, "__esModule", {
	value: true
});
exports.default = createRunner;

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

var withStacktrace = function withStacktrace(errors) {
	return errors.map(function (e) {
		return e.stack ? e.message + ': ' + e.stack : JSON.stringify(e, null, 4);
	});
};

function createRunner(_ref) {
	var _ref$name = _ref.name;
	var name = _ref$name === undefined ? 'MockFunctionName' : _ref$name;
	var _ref$memoryLimitInMB = _ref.memoryLimitInMB;
	var memoryLimitInMB = _ref$memoryLimitInMB === undefined ? '128' : _ref$memoryLimitInMB;
	var _ref$timeoutInSec = _ref.timeoutInSec;
	var timeoutInSec = _ref$timeoutInSec === undefined ? 3 : _ref$timeoutInSec;
	var _ref$invokeid = _ref.invokeid;
	var invokeid = _ref$invokeid === undefined ? '' + Date.now() : _ref$invokeid;
	var _ref$functionVersion = _ref.functionVersion;
	var functionVersion = _ref$functionVersion === undefined ? '$LATEST' : _ref$functionVersion;
	var _ref$invokedFunctionA = _ref.invokedFunctionArn;
	var invokedFunctionArn = _ref$invokedFunctionA === undefined ? null : _ref$invokedFunctionA;
	var _ref$logGroupName = _ref.logGroupName;
	var logGroupName = _ref$logGroupName === undefined ? null : _ref$logGroupName;
	var _ref$logStreamName = _ref.logStreamName;
	var logStreamName = _ref$logStreamName === undefined ? 'mockLogStreamName' : _ref$logStreamName;

	if (!invokedFunctionArn) {
		invokedFunctionArn = 'arn:mock:lambda:abc:function:' + name;
	}

	if (logGroupName) {
		logGroupName = '/aws/lambda/' + name;
	}

	var start = Date.now();
	function getRemainingTimeInMillis() {
		return start + timeoutInSec * 1000 - Date.now();
	}

	return {
		succeed: function succeed() {
			var _console;

			for (var _len = arguments.length, results = Array(_len), _key = 0; _key < _len; _key++) {
				results[_key] = arguments[_key];
			}

			(_console = console).log.apply(_console, _toConsumableArray(['SUCCESS'].concat(results)));
		},
		fail: function fail() {
			var _console2;

			for (var _len2 = arguments.length, results = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
				results[_key2] = arguments[_key2];
			}

			(_console2 = console).log.apply(_console2, _toConsumableArray(['FAILURE'].concat(withStacktrace(results))));
		},
		done: function done() {
			var _console3;

			for (var _len3 = arguments.length, results = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
				results[_key3] = arguments[_key3];
			}

			(_console3 = console).log.apply(_console3, _toConsumableArray(['DONE'].concat(results)));
		},
		name: name,
		memoryLimitInMB: memoryLimitInMB,
		timeoutInSec: timeoutInSec,
		invokeid: invokeid,
		functionVersion: functionVersion,
		invokedFunctionArn: invokedFunctionArn,
		getRemainingTimeInMillis: getRemainingTimeInMillis
	};
}<commit_msg>update bin<commit_after>'use strict';

var _objectAssign = require('object-assign');

var _objectAssign2 = _interopRequireDefault(_objectAssign);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

var withStacktrace = function withStacktrace(errors) {
	return errors.map(function (e) {
		return e.stack ? e.message + ': ' + e.stack : JSON.stringify(e, null, 4);
	});
};

function run(handler, event) {
	var opts = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];

	var defaults = {
		name: 'MockFunctionName',
		memoryLimitInMB: '128',
		timeoutInSec: 3,
		invokeid: '' + Date.now(),
		functionVersion: '$LATEST',
		invokedFunctionArn: null,
		logGroupName: null,
		logStreamName: 'mockLogStreamName'
	};

	opts = (0, _objectAssign2.default)({}, defaults, opts);

	if (!invokedFunctionArn) {
		invokedFunctionArn = 'arn:mock:lambda:abc:function:' + name;
	}

	if (!logGroupName) {
		logGroupName = '/aws/lambda/' + name;
	}

	var start = Date.now();
	function getRemainingTimeInMillis() {
		return start + timeoutInSec * 1000 - Date.now();
	}

	var context = {
		succeed: function succeed() {
			var _console;

			for (var _len = arguments.length, results = Array(_len), _key = 0; _key < _len; _key++) {
				results[_key] = arguments[_key];
			}

			(_console = console).log.apply(_console, _toConsumableArray(['SUCCESS'].concat(results)));
		},
		fail: function fail() {
			var _console2;

			for (var _len2 = arguments.length, results = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
				results[_key2] = arguments[_key2];
			}

			(_console2 = console).log.apply(_console2, _toConsumableArray(['FAILURE'].concat(withStacktrace(results))));
		},
		done: function done() {
			var _console3;

			for (var _len3 = arguments.length, results = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
				results[_key3] = arguments[_key3];
			}

			(_console3 = console).log.apply(_console3, _toConsumableArray(['DONE'].concat(results)));
		},
		name: name,
		memoryLimitInMB: memoryLimitInMB,
		timeoutInSec: timeoutInSec,
		invokeid: invokeid,
		functionVersion: functionVersion,
		invokedFunctionArn: invokedFunctionArn,
		getRemainingTimeInMillis: getRemainingTimeInMillis
	};

	handler(event, context);
}

module.exports = {
	run: run
};<|endoftext|>


<commit_before>"use strict";

const parse = require("github-calendar-parser")
    , $ = require("elly")
    , addSubtractDate = require("add-subtract-date")
    , formatoid = require("formatoid")

const DATE_FORMAT1 = "MMM D, YYYY"
    , DATE_FORMAT2 = "MMMM D"

const MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

function printDayCount(dayCount) {
    return  `${dayCount} ${(dayCount === 1) ? "day" : "days"}`
}

function addTooltips(container) {
    const tooltip = document.createElement("div")
    tooltip.classList.add("day-tooltip")
    container.appendChild(tooltip)

    // Add mouse event listener to show & hide tooltip
    const days = container.querySelectorAll("rect.ContributionCalendar-day")
    days.forEach(day => {
        day.addEventListener("mouseenter", (e) => {
            let contribCount = e.target.getAttribute("data-count")
            if (contribCount === "0") {
                contribCount = "No contributions"
            } else if (contribCount === "1") {
                contribCount = "1 contribution"
            } else {
                contribCount = `${contribCount} contributions`
            }
            const date = new Date(e.target.getAttribute("data-date"))
            const dateText = `${MONTH_NAMES[date.getUTCMonth()]} ${date.getUTCDate()}, ${date.getUTCFullYear()}`
            tooltip.innerHTML = `<strong>${contribCount}</strong> on ${dateText}`
            tooltip.classList.add("is-visible")
            const size = e.target.getBoundingClientRect()
                , leftPos = size.left + window.pageXOffset - tooltip.offsetWidth / 2 + size.width / 2
                , topPos = size.bottom + window.pageYOffset - tooltip.offsetHeight - 2 * size.height
            tooltip.style.top = `${topPos}px`
            tooltip.style.left = `${leftPos}px`
        })
        day.addEventListener("mouseleave", () => {
            tooltip.classList.remove("is-visible")
        })
    })
}

/**
 * GitHubCalendar
 * Brings the contributions calendar from GitHub (provided username) into your page.
 *
 * @name GitHubCalendar
 * @function
 * @param {String|HTMLElement} container The calendar container (query selector or the element itself).
 * @param {String} username The GitHub username.
 * @param {Object} options An object containing the following fields:
 *
 *    - `summary_text` (String): The text that appears under the calendar (defaults to: `"Summary of
 *      pull requests, issues opened, and commits made by <username>"`).
 *    - `proxy` (Function): A function that receives as argument the username (string) and should return a promise resolving the HTML content of the contributions page.
 *      The default is using @Bloggify's APIs.
 *    - `global_stats` (Boolean): If `false`, the global stats (total, longest and current streaks) will not be calculated and displayed. By default this is enabled.
 *    - `responsive` (Boolean): If `true`, the graph is changed to scale with the container. Custom CSS should be applied to the element to scale it appropriately. By default this is disabled.
 *    - `tooltips` (Boolean): If `true`, tooltips will be shown when hovered over calendar days. By default this is disabled.
 *    - `cache` (Number) The cache time in seconds.
 *
 * @return {Promise} A promise returned by the `fetch()` call.
 */
module.exports = function GitHubCalendar (container, username, options) {

    container = $(container)

    options = options || {}
    options.summary_text = options.summary_text || `Summary of pull requests, issues opened, and commits made by <a href="https://github.com/${username}" target="blank">@${username}</a>`
    options.cache = (options.cache || (24 * 60 * 60)) * 1000

    if (options.global_stats === false) {
        container.style.minHeight = "175px"
    }

    const cacheKeys = {
        content: `gh_calendar_content.${username}`,
        expire_at: `gh_calendar_expire.${username}`
    }

    // We need a proxy for CORS
    options.proxy = options.proxy || (username => {
        return fetch(`https://api.bloggify.net/gh-calendar/?username=${username}`).then(r => r.text())
    })

    options.getCalendar = options.getCalendar || (username => {
        if (options.cache && Date.now() < +localStorage.getItem(cacheKeys.expire_at)) {
            const content = localStorage.getItem(cacheKeys.content)
            if (content) {
                return Promise.resolve(content)
            }
        }

        return options.proxy(username).then(body => {
            if (options.cache) {
                localStorage.setItem(cacheKeys.content, body)
                localStorage.setItem(cacheKeys.expire_at, Date.now() + options.cache)
            }
            return body
        })
    })

    let fetchCalendar = () => options.getCalendar(username).then(body => {
        let div = document.createElement("div")
        div.innerHTML = body
        let cal = div.querySelector(".js-yearly-contributions")
        $(".position-relative h2", cal).remove()
        //cal.querySelector(".float-left.text-gray").innerHTML = options.summary_text

        // If 'include-fragment' with spinner img loads instead of the svg, fetchCalendar again
        if (cal.querySelector("include-fragment")) {
            setTimeout(fetchCalendar, 500)
        } else {
            // If options includes responsive, SVG element has to be manipulated to be made responsive
            if (options.responsive === true) {
                let svg = cal.querySelector("svg.js-calendar-graph-svg")
                // Get the width/height properties and use them to create the viewBox
                let width = svg.getAttribute("width")
                let height = svg.getAttribute("height")
                // Remove height property entirely
                svg.removeAttribute("height")
                // Width property should be set to 100% to fill entire container
                svg.setAttribute("width", "100%")
                // Add a viewBox property based on the former width/height
                svg.setAttribute("viewBox", "0 0 " + width + " " + height)
            }

            if (options.global_stats !== false) {
                let parsed = parse($("svg", cal).outerHTML)
                  , currentStreakInfo = parsed.current_streak
                                      ? `${formatoid(parsed.current_streak_range[0], DATE_FORMAT2)} – ${formatoid(parsed.current_streak_range[1], DATE_FORMAT2)}`
                                      : parsed.last_contributed
                                      ? `Last contributed in ${formatoid(parsed.last_contributed, DATE_FORMAT2)}.`
                                      : "Rock - Hard Place"
                  , longestStreakInfo = parsed.longest_streak
                                      ? `${formatoid(parsed.longest_streak_range[0], DATE_FORMAT2)} – ${formatoid(parsed.longest_streak_range[1], DATE_FORMAT2)}`
                                      : parsed.last_contributed
                                      ? `Last contributed in ${formatoid(parsed.last_contributed, DATE_FORMAT2)}.`
                                      : "Rock - Hard Place"
                  , firstCol = $("
", {
                        "class": "contrib-column contrib-column-first table-column"
                      , html: `Contributions in the last year
                               ${parsed.last_year} total
                               ${formatoid(addSubtractDate.add(addSubtractDate.subtract(new Date(), 1, "year"), 1, "day"), DATE_FORMAT1)} – ${formatoid(new Date(), DATE_FORMAT1)}`
                    })
                  , secondCol = $("
", {
                        "class": "contrib-column table-column"
                      , html: `Longest streak
                               ${printDayCount(parsed.longest_streak)}
                               ${longestStreakInfo}`
                    })
                  , thirdCol = $("
", {
                        "class": "contrib-column table-column"
                      , html: `Current streak
                               ${printDayCount(parsed.current_streak)}
                               ${currentStreakInfo}`
                    })

                cal.appendChild(firstCol)
                cal.appendChild(secondCol)
                cal.appendChild(thirdCol)
            }

            container.innerHTML = cal.innerHTML

            // If options includes tooltips, add tooltips listeners to SVG
            if (options.tooltips === true) {
                addTooltips(container)
            }
        }
    }).catch(e => console.error(e))

    return fetchCalendar()
}
<commit_msg>fix: tooltips display wrong position.<commit_after>"use strict";

const parse = require("github-calendar-parser")
    , $ = require("elly")
    , addSubtractDate = require("add-subtract-date")
    , formatoid = require("formatoid")

const DATE_FORMAT1 = "MMM D, YYYY"
    , DATE_FORMAT2 = "MMMM D"

const MONTH_NAMES = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

function printDayCount(dayCount) {
    return  `${dayCount} ${(dayCount === 1) ? "day" : "days"}`
}

function addTooltips(container) {
    const tooltip = document.createElement("div")
    tooltip.classList.add("day-tooltip")
    container.appendChild(tooltip)

    // Add mouse event listener to show & hide tooltip
    const days = container.querySelectorAll(".js-calendar-graph-svg rect.ContributionCalendar-day")
    days.forEach(day => {
        day.addEventListener("mouseenter", (e) => {
            let contribCount = e.target.getAttribute("data-count")
            if (contribCount === "0") {
                contribCount = "No contributions"
            } else if (contribCount === "1") {
                contribCount = "1 contribution"
            } else {
                contribCount = `${contribCount} contributions`
            }
            const date = new Date(e.target.getAttribute("data-date"))
            const dateText = `${MONTH_NAMES[date.getUTCMonth()]} ${date.getUTCDate()}, ${date.getUTCFullYear()}`
            tooltip.innerHTML = `<strong>${contribCount}</strong> on ${dateText}`
            tooltip.classList.add("is-visible")
            const size = e.target.getBoundingClientRect()
                , leftPos = size.left + window.pageXOffset - tooltip.offsetWidth / 2 + size.width / 2
                , topPos = size.bottom + window.pageYOffset - tooltip.offsetHeight - 2 * size.height
            tooltip.style.top = `${topPos}px`
            tooltip.style.left = `${leftPos}px`
        })
        day.addEventListener("mouseleave", () => {
            tooltip.classList.remove("is-visible")
        })
    })
}

/**
 * GitHubCalendar
 * Brings the contributions calendar from GitHub (provided username) into your page.
 *
 * @name GitHubCalendar
 * @function
 * @param {String|HTMLElement} container The calendar container (query selector or the element itself).
 * @param {String} username The GitHub username.
 * @param {Object} options An object containing the following fields:
 *
 *    - `summary_text` (String): The text that appears under the calendar (defaults to: `"Summary of
 *      pull requests, issues opened, and commits made by <username>"`).
 *    - `proxy` (Function): A function that receives as argument the username (string) and should return a promise resolving the HTML content of the contributions page.
 *      The default is using @Bloggify's APIs.
 *    - `global_stats` (Boolean): If `false`, the global stats (total, longest and current streaks) will not be calculated and displayed. By default this is enabled.
 *    - `responsive` (Boolean): If `true`, the graph is changed to scale with the container. Custom CSS should be applied to the element to scale it appropriately. By default this is disabled.
 *    - `tooltips` (Boolean): If `true`, tooltips will be shown when hovered over calendar days. By default this is disabled.
 *    - `cache` (Number) The cache time in seconds.
 *
 * @return {Promise} A promise returned by the `fetch()` call.
 */
module.exports = function GitHubCalendar (container, username, options) {

    container = $(container)

    options = options || {}
    options.summary_text = options.summary_text || `Summary of pull requests, issues opened, and commits made by <a href="https://github.com/${username}" target="blank">@${username}</a>`
    options.cache = (options.cache || (24 * 60 * 60)) * 1000

    if (options.global_stats === false) {
        container.style.minHeight = "175px"
    }

    const cacheKeys = {
        content: `gh_calendar_content.${username}`,
        expire_at: `gh_calendar_expire.${username}`
    }

    // We need a proxy for CORS
    options.proxy = options.proxy || (username => {
        return fetch(`https://api.bloggify.net/gh-calendar/?username=${username}`).then(r => r.text())
    })

    options.getCalendar = options.getCalendar || (username => {
        if (options.cache && Date.now() < +localStorage.getItem(cacheKeys.expire_at)) {
            const content = localStorage.getItem(cacheKeys.content)
            if (content) {
                return Promise.resolve(content)
            }
        }

        return options.proxy(username).then(body => {
            if (options.cache) {
                localStorage.setItem(cacheKeys.content, body)
                localStorage.setItem(cacheKeys.expire_at, Date.now() + options.cache)
            }
            return body
        })
    })

    let fetchCalendar = () => options.getCalendar(username).then(body => {
        let div = document.createElement("div")
        div.innerHTML = body
        let cal = div.querySelector(".js-yearly-contributions")
        $(".position-relative h2", cal).remove()
        //cal.querySelector(".float-left.text-gray").innerHTML = options.summary_text

        // If 'include-fragment' with spinner img loads instead of the svg, fetchCalendar again
        if (cal.querySelector("include-fragment")) {
            setTimeout(fetchCalendar, 500)
        } else {
            // If options includes responsive, SVG element has to be manipulated to be made responsive
            if (options.responsive === true) {
                let svg = cal.querySelector("svg.js-calendar-graph-svg")
                // Get the width/height properties and use them to create the viewBox
                let width = svg.getAttribute("width")
                let height = svg.getAttribute("height")
                // Remove height property entirely
                svg.removeAttribute("height")
                // Width property should be set to 100% to fill entire container
                svg.setAttribute("width", "100%")
                // Add a viewBox property based on the former width/height
                svg.setAttribute("viewBox", "0 0 " + width + " " + height)
            }

            if (options.global_stats !== false) {
                let parsed = parse($("svg", cal).outerHTML)
                  , currentStreakInfo = parsed.current_streak
                                      ? `${formatoid(parsed.current_streak_range[0], DATE_FORMAT2)} – ${formatoid(parsed.current_streak_range[1], DATE_FORMAT2)}`
                                      : parsed.last_contributed
                                      ? `Last contributed in ${formatoid(parsed.last_contributed, DATE_FORMAT2)}.`
                                      : "Rock - Hard Place"
                  , longestStreakInfo = parsed.longest_streak
                                      ? `${formatoid(parsed.longest_streak_range[0], DATE_FORMAT2)} – ${formatoid(parsed.longest_streak_range[1], DATE_FORMAT2)}`
                                      : parsed.last_contributed
                                      ? `Last contributed in ${formatoid(parsed.last_contributed, DATE_FORMAT2)}.`
                                      : "Rock - Hard Place"
                  , firstCol = $("
", {
                        "class": "contrib-column contrib-column-first table-column"
                      , html: `Contributions in the last year
                               ${parsed.last_year} total
                               ${formatoid(addSubtractDate.add(addSubtractDate.subtract(new Date(), 1, "year"), 1, "day"), DATE_FORMAT1)} – ${formatoid(new Date(), DATE_FORMAT1)}`
                    })
                  , secondCol = $("
", {
                        "class": "contrib-column table-column"
                      , html: `Longest streak
                               ${printDayCount(parsed.longest_streak)}
                               ${longestStreakInfo}`
                    })
                  , thirdCol = $("
", {
                        "class": "contrib-column table-column"
                      , html: `Current streak
                               ${printDayCount(parsed.current_streak)}
                               ${currentStreakInfo}`
                    })

                cal.appendChild(firstCol)
                cal.appendChild(secondCol)
                cal.appendChild(thirdCol)
            }

            container.innerHTML = cal.innerHTML

            // If options includes tooltips, add tooltips listeners to SVG
            if (options.tooltips === true) {
                addTooltips(container)
            }
        }
    }).catch(e => console.error(e))

    return fetchCalendar()
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

/**
 * Dependencies
 */
var updateNotifier = require('./util/notifier');
var npm            = require('./util/npm');
var pkg            = require('../package.json');
var scripts        = require('sailor-scripts');
var parser         = require('nomnom')(pkg);
var chalk          = require('chalk');

/**
 * Initialize
 */
 updateNotifier();
 parser.script("sailor");

/**
 * version
 */
 parser
 .option('version', {
   abbr: 'v',
   flag: true,
   help: "Output the version of the CLI",
   callback: function() {
    return pkg.name + "@" + pkg.version;
  }
});

/**
 * help
 */
 parser
 .option('help', {
  abbr: 'h',
  flag: true,
  help: "Ouput this information"
});

/**
* install command
*/
 parser.command('install')
 .options({
   save: {
     flag: true,
     help: "Save the module in your package.json as production dependency"
   },
   'save-dev': {
     flag: true,
     help: "Save the module in your package.json as development dependency"
   }
 })
 .help("Install a dependency in the base of your project")
 .callback(function(input) {
  if(!npm.isSailorProyect(process.cwd()))
    parser.message.error("First create a new base project.", 1);

  if(npm.isAlreadyInstalled(process.cwd(), input[1]))
    parser.message.error("Module '" + input[1] + "' is already installed.", 1);

  var command = 'npm install ' + input[1];
  if (input['save']) command += ' --save';
  if (input['save-dev']) command += ' --save-dev';

  scripts.run(command);
  scripts.writeModuleFile(input[1], process.cwd());
  parser.message.info("Module '" + chalk.cyan(input[1]) + "' installed.");
});

/**
 * lift command
 */
 parser.command('lift')
 .options({
   silly: {
     flag: true,
     help: "Activate silly level in logs"
   },
   production: {
     flag: true,
     help: "Activate production environment"
   }
 })
 .help("Start with a Sailor Proyect")
 .callback(function(action) {
  var opts = {};

  if (action.silly){
    opts.logs = {
      level: 'silly'
    };
  }
  if (action.production)
    opts.environment = 'production';

  scripts.lift(process.cwd(), opts);
});

/**
 * new command
 */
 parser.command('new')
 .options({
   organization: {
     abbr: 'org',
     default: 'sailorjs',
     help: "Link your Github organization with the project"
   },
   repository: {
     abbr: 'rep',
     help: "Link your Github repository with the project"
   },
   description: {
     abbr: 'desc',
     help: "Added description for your project"
   }
 })
 .callback(function(input) {

  if (input[1]==='module'){
    if(input[2]===undefined){
      parser.message.error("Need a name for a new module.", 1);
    } else {

      options = {
        name        : input[2],
        organization: input.organization,
        repository  : input.repository || input[2],
        description : input.description || "A new module for Sailor"
      };

      scripts.newModule(options, function(){
        parser.message.info("Module '" + chalk.cyan(input[2]) + "' created.", "");
      });
    }
    return;
  }

  if(input[1]===undefined){
    parser.message.error("Need a name for a new proyect.", 1);
  } else {

    options = {
      name        : input[1],
      organization: input.organization,
      repository  : input.repository || input[1],
      description : input.description || "A new Sailor Proyect"
    };

    scripts.newBase(options, function(){
      parser.message.info("Proyect '" + chalk.cyan(input[1]) + "' created.", "");
    });
  }

})
 .help("Created a new Sailor Proyect");

/**
 * new module command
 */
 parser.command('new module')
 .help("Created a new module for Sailor");

/**
 * Parse!
 */
 parser.parse();
<commit_msg>improve commands options<commit_after>#!/usr/bin/env node

/**
 * Dependencies
 */
var updateNotifier = require('./util/notifier');
var npm            = require('./util/npm');
var pkg            = require('../package.json');
var scripts        = require('sailor-scripts');
var parser         = require('nomnom')(pkg);
var chalk          = require('chalk');

/**
 * Initialize
 */
 updateNotifier();
 parser.script("sailor");

/**
 * version
 */
 parser
 .option('version', {
   abbr: 'v',
   flag: true,
   help: "Output the version of the CLI",
   callback: function() {
    return pkg.name + "@" + pkg.version;
  }
});

/**
 * help
 */
 parser
 .option('help', {
  abbr: 'h',
  flag: true,
  help: "Ouput this information"
});

/**
* install command
*/
 parser.command('install')
 .options({
   save: {
     flag: true,
     help: "Save the module in your package.json as production dependency"
   },
   'save-dev': {
     flag: true,
     help: "Save the module in your package.json as development dependency"
   }
 })
 .help("Install a dependency in the base of your project")
 .callback(function(input) {
  if(!npm.isSailorProyect(process.cwd()))
    parser.message.error("First create a new base project.", 1);

  if(npm.isAlreadyInstalled(process.cwd(), input[1]))
    parser.message.error("Module '" + input[1] + "' is already installed.", 1);

  var command = 'npm install ' + input[1];
  if (input['save']) command += ' --save';
  if (input['save-dev']) command += ' --save-dev';

  scripts.run(command);
  scripts.writeModuleFile(input[1], process.cwd());
  parser.message.info("Module '" + chalk.cyan(input[1]) + "' installed.");
});

/**
 * lift command
 */
 parser.command('lift')
 .options({
   silly: {
     flag: true,
     help: "Activate silly level in logs"
   },
   production: {
     flag: true,
     help: "Activate production environment"
   }
 })
 .help("Start with a Sailor Proyect")
 .callback(function(action) {
  var opts = {};

  if (action.silly){
    opts.logs = {
      level: 'silly'
    };
  }
  if (action.production)
    opts.environment = 'production';

  scripts.lift(process.cwd(), opts);
});

/**
 * new command
 */
 parser.command('new')
 .options({
  name: {
    abbr: 'n',
    help: "Set the name of your new Sailor proyet"
  },
   organization: {
     abbr: 'o',
     help: "Link your Github organization with the project"
   },
   repository: {
     abbr: 'r',
     help: "Link your Github repository with the project"
   },
   description: {
     abbr: 'd',
     help: "Added description for your project"
   }
 })
 .callback(function(input) {

  if (input[1]==='module'){
    if(input[2]===undefined){
      parser.message.error("Need a name for a new module.", 1);
    } else {

      options = {
        folder      : input[2],
        name        : input.name || input[2],
        organization: input.organization,
        repository  : input.repository || input.name || input[2],
        description : input.description
      };

      scripts.newModule(options, function(){
        parser.message.info("Module '" + chalk.cyan(input[2]) + "' created.", "");
      });
    }
    return;
  }

  if(input[1]===undefined){
    parser.message.error("Need a name for a new proyect.", 1);
  } else {

    options = {
      folder      : input[1],
      name        : input.name || input[1],
      organization: input.organization,
      repository  : input.repository || input.name || input[1],
      description : input.description
    };

    scripts.newBase(options, function(){
      parser.message.info("Proyect '" + chalk.cyan(input[1]) + "' created.", "");
    });
  }

})
 .help("Created a new Sailor Proyect");

/**
 * new module command
 */
 parser.command('new module')
 .help("Created a new module for Sailor");

/**
 * Parse!
 */
 parser.parse();
<|endoftext|>


<commit_before>var crypto = require('crypto');
var fs = require('fs');
var path = require('path');
var zlib = require('zlib');

var mime = require('mime');

exports.static = function(directory) {
    directory = path.resolve(directory || 'public');

    var files = {};

    function dehashifyPath(filePath) {
        return filePath.replace(/-[0-9a-f]+\./, '.');
    };

    function hashifyPath(filePath, hash) {
        if (filePath.indexOf('.') != -1) {
            return filePath.replace('.', '-' + hash + '.');
        }
    };

    // Load all files synchronously
    (function loadFiles(directory) {
        var contents;

        try {
            contents = fs.readdirSync(directory);
        }
        catch (e) {
            switch (e.code) {
                case 'ENOENT':
                    console.error('Directory does not exist');
                    break;

                case 'ENOTDIR':
                    console.error('Not a directory');
                    break;

                case 'EACCES':
                    console.error('Insufficient permissions to access directory');
                    break;
            }

            throw e;
        }

        contents.forEach(function(file) {
            var filePath = path.join(directory, file);
            var stat = fs.statSync(filePath);

            if (stat.isDirectory()) {
                return loadFiles(filePath);
            }

            var data = fs.readFileSync(filePath);

            // Ensure the path we use is relative to the public dir
            var relativePath = path.relative(directory, path.resolve(filePath));

            // Prepend a / since that's the format used by express
            relativePath = path.join('/', relativePath);

            // TODO: Store Gzip version
            files[relativePath] = {
                content: data,
                contentLength: typeof data === 'string' ? Buffer.byteLength(data) : data.length,
                contentType: mime.lookup(relativePath),
                hash: crypto.createHash('md5').update(data).digest('hex')
            };
        });
    })(directory);

    return function(req, res, next) {
        // Register helpers if we haven't already
        // Doing this at this stage because we can't access the express app from the initialization
        // without passing it in as a parameter, which would be poor API design.
        if (!req.app.locals.electricity) {
            req.app.locals.electricity = {
                url: function(filePath) {
                    var file = files[path.join('/' + filePath)];

                    if (file) {
                        return hashifyPath(filePath, file.hash);
                    } else {
                        return filePath;
                    }
                }
            };
        }

        if (req.method !== 'GET' && req.method !== 'HEAD') {
            return next();
        }

        var filePath = dehashifyPath(req.path);
        var file = files[filePath];

        // TODO: Verify file matches the specified hash, otherwise 404
        // Maybe the files cache should be more like, { '/styles/main.css': [{ version1 }, { version2 }, { version3 }...] }
        if (file) {
            res.set({
                'Content-Length': file.contentLength,
                'Content-Type': file.contentType,
                'ETag': file.hash
            });

            res.send(file);
        } else {
            next();
        }
    };
};
<commit_msg>Named functions<commit_after>var crypto = require('crypto');
var fs = require('fs');
var path = require('path');
var zlib = require('zlib');

var mime = require('mime');

exports.static = function static(directory) {
    directory = path.resolve(directory || 'public');

    var files = {};

    function dehashifyPath(filePath) {
        return filePath.replace(/-[0-9a-f]+\./, '.');
    };

    function hashifyPath(filePath, hash) {
        if (filePath.indexOf('.') != -1) {
            return filePath.replace('.', '-' + hash + '.');
        }
    };

    // Load all files synchronously
    (function loadFiles(directory) {
        var contents;

        try {
            contents = fs.readdirSync(directory);
        }
        catch (e) {
            switch (e.code) {
                case 'ENOENT':
                    console.error('Directory does not exist');
                    break;

                case 'ENOTDIR':
                    console.error('Not a directory');
                    break;

                case 'EACCES':
                    console.error('Insufficient permissions to access directory');
                    break;
            }

            throw e;
        }

        contents.forEach(function loadDirectory(file) {
            var filePath = path.join(directory, file);
            var stat = fs.statSync(filePath);

            if (stat.isDirectory()) {
                return loadFiles(filePath);
            }

            var data = fs.readFileSync(filePath);

            // Ensure the path we use is relative to the public dir
            var relativePath = path.relative(directory, path.resolve(filePath));

            // Prepend a / since that's the format used by express
            relativePath = path.join('/', relativePath);

            // TODO: Store Gzip version
            files[relativePath] = {
                content: data,
                contentLength: typeof data === 'string' ? Buffer.byteLength(data) : data.length,
                contentType: mime.lookup(relativePath),
                hash: crypto.createHash('md5').update(data).digest('hex')
            };
        });
    })(directory);

    return function staticMiddleware(req, res, next) {
        // Register helpers if we haven't already
        // Doing this at this stage because we can't access the express app from the initialization
        // without passing it in as a parameter, which would be poor API design.
        if (!req.app.locals.electricity) {
            req.app.locals.electricity = {
                url: function urlHelper(filePath) {
                    var file = files[path.join('/' + filePath)];

                    if (file) {
                        return hashifyPath(filePath, file.hash);
                    } else {
                        return filePath;
                    }
                }
            };
        }

        if (req.method !== 'GET' && req.method !== 'HEAD') {
            return next();
        }

        var filePath = dehashifyPath(req.path);
        var file = files[filePath];

        // TODO: Verify file matches the specified hash, otherwise 404
        // Maybe the files cache should be more like, { '/styles/main.css': [{ version1 }, { version2 }, { version3 }...] }
        if (file) {
            res.set({
                'Content-Length': file.contentLength,
                'Content-Type': file.contentType,
                'ETag': file.hash
            });

            res.send(file);
        } else {
            next();
        }
    };
};
<|endoftext|>