<commit_before>/*
 * grunt-include-source
 * https://github.com/jwvdiermen/grunt-include-source
 *
 * Copyright (c) 2013 Jan Willem van Diermen
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {
	var path = require('path');
	var url = require('url');
	var os = require('os');
	var util = require('util');
	var extendr = require('extendr');

	var parseSource = function (name, pattern) {
		return function(source) {
			var matches,
				results = [];

			grunt.log.debug('Parsing ' + name + '...');
			while ((matches = pattern.exec(source)) !== null) {
				if (matches.length <= 1) continue;
				grunt.log.debug('Got match, options are { ' + matches[1] + ' }.');
				var optionsObj = JSON.parse("{" + matches[1] + "}");
				results.push({
					start: matches.index,
					end: matches.index + matches[0].length - 1,
					options: optionsObj
				});
			}

			return results;
		};
	};

	// Generates function to find end marker, e.g. <!-- /include -->
	var findEndMarker = function(name, pattern){
		/*
			source: Code between the current include and the next one. (Or the end of the file)
			offset: Stat position of the source fragement inside the file.
		*/
		return function(source, offset){
			var endMarker = pattern.exec(source);
			if (endMarker === null){
				return null;
			}
			return {
				start: endMarker.index + offset,
				length: endMarker[0].length
			};
		};
	};

	var parsers = {
		'html': parseSource('HTML', /<!---?\s*include:\s+(.*)\s*-?--\s*>/gi),
		'haml': parseSource('HAML', /-#\s+include:\s+(.*)/gi),
		'jade': parseSource('JADE', /\/\/-?\s+include:\s+(.*)/gi),
		'scss': parseSource('SASS', /\/\/\s+include:\s+(.*)/gi),
		'ts': parseSource('TS', /\/\/\/\s<!---?\s*include:\s+(.*)\s*-?--\s*>/gi)
	};

	var endMarkerParsers = {
		'html': findEndMarker('HTML', /<!---?\s*\/include\s+-?--\>/i),
		'haml': findEndMarker('HAML', /-#\s+\/include/i),
		'jade': findEndMarker('JADE', /\/\/-?\s+\/include/i),
		'scss': findEndMarker('SASS', /\/\/\s+\/include/i),
		'ts': findEndMarker('TS', /\/\/\/\s<!---?\s*\/include\s+-?--\>/i)
	};

	var templates = {
		'html':
		{
			'js': '<script src="{filePath}"></script>',
			'css': '<link href="{filePath}" rel="stylesheet" type="text/css">'
		},
		'haml':
		{
			'js': '%script{src: "{filePath}"}',
			'css': '%link{href: "{filePath}", rel: "stylesheet", type: "text/css"}/'
		},
		'jade':
		{
			'js': 'script(src="{filePath}")',
			'css': 'link(href="{filePath}", rel="stylesheet", type="text/css")'
		},
		'scss':
		{
			'scss': '@import "{filePath}";',
			'css': '@import "{filePath}";'
		},
		'ts':
		{
			'ts': '/// <reference path="{filePath}" />'
		}
	};

	var defaultTypeMappings = {
		'cshtml': 'html',
		'less': 'scss'
	};

	var resolveFiles = function (options, includeOptions) {
		var basePath = includeOptions.basePath || options.basePath;
		grunt.log.debug('Resolving files on base path "' + basePath + '"...');
		grunt.log.debug('Include options: ' + util.inspect(includeOptions));

		basePath = grunt.config.process(basePath);

		var files, sourcePath = '';
		if (includeOptions.bower) {
			grunt.log.debug('Resolving files from Bower component "' + includeOptions.bower + '"...');

			sourcePath = path.join('bower_components', includeOptions.bower);
			basePath = path.join(basePath, sourcePath);
			grunt.log.debug('Full path is "' + basePath + '".');

			var sources = grunt.file.readJSON(path.join(basePath, 'bower.json')).sources;
			files = sources[includeOptions.type];
		} else {
			grunt.log.debug('Resolving files from include property...');

			// Retrieve files property. If it's a string, process it as a template.
			files = includeOptions.files;
			files = grunt.config.process(files);
		}

		if (!files) {
			grunt.log.debug('No files found.');
			return [];
		}

		grunt.log.debug('Expanding files: ' + util.inspect(files));

		// Split patterns. Apperently, grunt.file.expandMapping doesn't automatically split
		// comma seperated lists, even if it says so in the documentation.
		// Split here manually so the exclusion patterns work.
		if (typeof files == 'string') {
			files = files.split(',');
		}

		// Expand patterns.
		var expandedFiles, i;
		var expand = function(x, f) {
			return grunt.file.expandMapping(f, sourcePath, { cwd: x, rename: options.rename, flatten: options.flatten });
		};
		if (basePath instanceof Array) {
			expandedFiles = [];
			for (i = 0 ; i < basePath.length; ++i) {
				grunt.log.debug('Expanding files at: ' + basePath[i]);
				expandedFiles = expandedFiles.concat(expand(basePath[i], files));
			}
		} else {
			expandedFiles = expand(basePath, files);
		}

		var results = [];
		for (i = 0; i < expandedFiles.length; ++i) {
			var file = expandedFiles[i].dest.replace(/\\/g, '/');
			grunt.log.debug('Found file "' + file + '".');
			results.push(file);
		}

		return results;
	};

	var pathCompareNatural = function (path1, path2) {
		if (path1 > path2) {
			return 1;
		} else if (path1 < path2) {
			return -1;
		} else {
			return 0;
		}
	};

	var pathCompareTopDown = function (path1, path2) {
		var seg1 = path1.split(/[\\/]/);
		var seg2 = path2.split(/[\\/]/);
		var i, j;

		if (seg1.length === seg2.length) {
			return pathCompareNatural(path1, path2);
		} else {
			j = Math.min(seg1.length, seg2.length) - 1;
			for (i = 0; i < j; ++i) {
				if (seg1[i] !== seg2[i]) {
					return pathCompareNatural(path1, path2);
				}
			}
			return seg1.length - seg2.length;
		}
	};

	var orderFiles = function (files, includeOptions) {
		if ('top-down' === includeOptions.ordering) {
			files.sort(pathCompareTopDown);
		}
	};

	// Register the task.
	grunt.registerMultiTask('includeSource', 'Include lists of files into your source files automatically.', function() {
		grunt.log.debug('Starting task "includeSource"...');
		var options = this.options({
			basePath: '',
			baseUrl: '',
			templates: {},
			typeMappings: {}
		});
		options.target = this.target;

		var typeMappings = extendr.clone(defaultTypeMappings);
		extendr.extend(typeMappings, options.typeMappings);

		grunt.log.debug('Base path is "' + options.basePath + '".');

		// Iterate over all specified file groups.
		this.files.forEach(function(file) {
			grunt.log.debug('Handling output file "' + file.dest + "'...");

			// Concatenate the source files.
			var contents = '';
			var contentSources = file.src.filter(function(filePath) {
					grunt.log.debug('Using input file "' + filePath + '".');
					// Remove nonexistent files.
					if (!grunt.file.exists(filePath)) {
						grunt.log.warn('Source file "' + filePath + '" not found.');
						return false;
					} else {
						return true;
					}
				}).map(	function(filePath) {
					// Read and return the file's source.
					return grunt.file.read(filePath);
				});

			// Don't bother detecting newline if we have no more than 1 file.
			if (contentSources.length > 1) {
				// Detect the newline to use for the content files.
				// Use the first file to detect the newlines, no use to test all of them.
				var contentNewline = /\r\n/g.test(contentSources[0]) ? '\r\n' : '\n';

				// Join the content files as one to be processed.
				contents = contentSources.join(contentNewline);
			}
			else {
				// Still use join here, since 'contentSources' could have a length of 0?
				contents = contentSources.join();
			}

			// Parse the contents, using a parser based on the target file.
			var fileType = path.extname(file.dest).substr(1);
			grunt.log.debug('File type is "' + fileType + '"');

			// Try and map the file type, using it instead.
			var mappedFileType = typeMappings[fileType];
			if (mappedFileType) {
				grunt.log.debug('File type "' + fileType + '" maps to "' + mappedFileType + '"');
				fileType = mappedFileType;
			}

			var parserFn = parsers[fileType];
			var findEndMarker = endMarkerParsers[fileType];

			if (!parserFn) {
				grunt.log.error('No parser found found file type "' + fileType + '".');
				return false;
			}

			// Get the available templates.
			var localTemplates = extendr.deepClone(templates);
			extendr.safeDeepExtendPlainObjects(localTemplates, options.templates);
			var typeTemplates = localTemplates[fileType];

			// Get the includes and rewrite the contents.
			var includes = parserFn(contents);
			var currentOffset = 0;

			grunt.log.debug('Found ' + includes.length + ' include statement(s).');

			includes.forEach(function(include, includeIndex) {
				var files = [];
				var baseUrl;
				if (include.options.target && options.target !== include.options.target) {
					grunt.log.debug('Include target is "' + include.options.target + '" and task target is "' + options.target + '", skipping include.');
				} else {
					files = resolveFiles(options, include.options);
				}
				orderFiles(files, include.options);

				if(include.options.baseUrl) {
					baseUrl = grunt.config.process(include.options.baseUrl);
				} else {
					baseUrl = options.baseUrl;
				}

				var sep = os.EOL,
					lookFor = [ ' ', '\t', '\r', '\n' ],
					i;

				// Find separators to maintain indentation when including fragment
				for (i = include.start + currentOffset - 1; i > 0 && lookFor.indexOf(contents[i]) >= 0; --i){
					// Don't allow multiple lines as separator, so break when we found one
					if (contents[i] === '\n'){
						i--;
						// Skip an extra character in case the endline is \r\n instead of just \n
						if (contents[i] === '\r'){
							i--;
						}
						break;
					}
				}
				if (i > 0) {
					++i; // Increment by one, since the index we found is the first non-indentation character.
					sep = contents.substr(i, include.start + currentOffset - i);
				}
				else {
					// So we probably are at the beginning of the file.
					// Scan forward so we can atleast detect what line endings are being used.
					// Keep it simple, if \r\n exists, use that, else just \n.
					sep = /\r\n/g.test(contents) ? '\r\n' : '\n';
				}

				var	includeFragments = [];
				files.forEach(function(file) {
					grunt.log.debug('Including file "' + file + '".');

					// Map the include type.
					var includeType = include.options.type;
					var mappedIncludeType = typeMappings[includeType];
					if (mappedIncludeType) {
						grunt.log.debug('Include type "' + includeType + '" maps to "' + mappedIncludeType + '"');
						includeType = mappedIncludeType;
					}

					includeFragments.push(typeTemplates[includeType]
						.replace(/\{filePath\}/g, url.resolve(baseUrl, file))
						.replace(/\{filePathDecoded\}/g, decodeURI(url.resolve(baseUrl, file)))
					);
				});

				var includeFragment = includeFragments.join(sep);

				// If there is an end marker before the next include or the end of the file
				// we want to replace the entire content between it and the include comment.
				var endMarker = null;
				if (findEndMarker){
					var nextInclude = includes[includeIndex + 1];
					var nextPartIndex = nextInclude ? (nextInclude.start + currentOffset): contents.length;
					var between = contents.substring(include.start + currentOffset, nextPartIndex);

					endMarker = findEndMarker(between, include.start);
				}

				var replacementStart;
				var replacementEnd;
				if (endMarker === null) {
					replacementStart = include.start + currentOffset;
					replacementEnd = include.end + 1 + currentOffset;
				} else {
					replacementStart = include.end + 1 + currentOffset + sep.length;
					replacementEnd = endMarker.start + currentOffset;
					includeFragment += sep;
				}

				contents =
					contents.substr(0, replacementStart) +
					includeFragment +
					contents.substr(replacementEnd);

				var addedCharacters = includeFragment.length;
				var removedCharacters = replacementEnd - replacementStart;
				currentOffset += addedCharacters - removedCharacters;
			});

			grunt.log.debug('Writing output...');
			grunt.file.write(file.dest, contents);
			grunt.log.writeln('File "' + file.dest + '" created.');
		});
	});
};
<commit_msg>Allow include directive in html to be multiline<commit_after>/*
 * grunt-include-source
 * https://github.com/jwvdiermen/grunt-include-source
 *
 * Copyright (c) 2013 Jan Willem van Diermen
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {
	var path = require('path');
	var url = require('url');
	var os = require('os');
	var util = require('util');
	var extendr = require('extendr');

	var parseSource = function (name, pattern) {
		return function(source) {
			var matches,
				results = [];

			grunt.log.debug('Parsing ' + name + '...');
			while ((matches = pattern.exec(source)) !== null) {
				if (matches.length <= 1) continue;
				grunt.log.debug('Got match, options are { ' + matches[1] + ' }.');
				var optionsObj = JSON.parse("{" + matches[1] + "}");
				results.push({
					start: matches.index,
					end: matches.index + matches[0].length - 1,
					options: optionsObj
				});
			}

			return results;
		};
	};

	// Generates function to find end marker, e.g. <!-- /include -->
	var findEndMarker = function(name, pattern){
		/*
			source: Code between the current include and the next one. (Or the end of the file)
			offset: Stat position of the source fragement inside the file.
		*/
		return function(source, offset){
			var endMarker = pattern.exec(source);
			if (endMarker === null){
				return null;
			}
			return {
				start: endMarker.index + offset,
				length: endMarker[0].length
			};
		};
	};

	var parsers = {
		'html': parseSource('HTML', /<!---?\s*include:([\s\S]*?)-?--\s*>/gi),
		'haml': parseSource('HAML', /-#\s+include:\s+(.*)/gi),
		'jade': parseSource('JADE', /\/\/-?\s+include:\s+(.*)/gi),
		'scss': parseSource('SASS', /\/\/\s+include:\s+(.*)/gi),
		'ts': parseSource('TS', /\/\/\/\s<!---?\s*include:\s+(.*)\s*-?--\s*>/gi)
	};

	var endMarkerParsers = {
		'html': findEndMarker('HTML', /<!---?\s*\/include\s+-?--\>/i),
		'haml': findEndMarker('HAML', /-#\s+\/include/i),
		'jade': findEndMarker('JADE', /\/\/-?\s+\/include/i),
		'scss': findEndMarker('SASS', /\/\/\s+\/include/i),
		'ts': findEndMarker('TS', /\/\/\/\s<!---?\s*\/include\s+-?--\>/i)
	};

	var templates = {
		'html':
		{
			'js': '<script src="{filePath}"></script>',
			'css': '<link href="{filePath}" rel="stylesheet" type="text/css">'
		},
		'haml':
		{
			'js': '%script{src: "{filePath}"}',
			'css': '%link{href: "{filePath}", rel: "stylesheet", type: "text/css"}/'
		},
		'jade':
		{
			'js': 'script(src="{filePath}")',
			'css': 'link(href="{filePath}", rel="stylesheet", type="text/css")'
		},
		'scss':
		{
			'scss': '@import "{filePath}";',
			'css': '@import "{filePath}";'
		},
		'ts':
		{
			'ts': '/// <reference path="{filePath}" />'
		}
	};

	var defaultTypeMappings = {
		'cshtml': 'html',
		'less': 'scss'
	};

	var resolveFiles = function (options, includeOptions) {
		var basePath = includeOptions.basePath || options.basePath;
		grunt.log.debug('Resolving files on base path "' + basePath + '"...');
		grunt.log.debug('Include options: ' + util.inspect(includeOptions));

		basePath = grunt.config.process(basePath);

		var files, sourcePath = '';
		if (includeOptions.bower) {
			grunt.log.debug('Resolving files from Bower component "' + includeOptions.bower + '"...');

			sourcePath = path.join('bower_components', includeOptions.bower);
			basePath = path.join(basePath, sourcePath);
			grunt.log.debug('Full path is "' + basePath + '".');

			var sources = grunt.file.readJSON(path.join(basePath, 'bower.json')).sources;
			files = sources[includeOptions.type];
		} else {
			grunt.log.debug('Resolving files from include property...');

			// Retrieve files property. If it's a string, process it as a template.
			files = includeOptions.files;
			files = grunt.config.process(files);
		}

		if (!files) {
			grunt.log.debug('No files found.');
			return [];
		}

		grunt.log.debug('Expanding files: ' + util.inspect(files));

		// Split patterns. Apperently, grunt.file.expandMapping doesn't automatically split
		// comma seperated lists, even if it says so in the documentation.
		// Split here manually so the exclusion patterns work.
		if (typeof files == 'string') {
			files = files.split(',');
		}

		// Expand patterns.
		var expandedFiles, i;
		var expand = function(x, f) {
			return grunt.file.expandMapping(f, sourcePath, { cwd: x, rename: options.rename, flatten: options.flatten });
		};
		if (basePath instanceof Array) {
			expandedFiles = [];
			for (i = 0 ; i < basePath.length; ++i) {
				grunt.log.debug('Expanding files at: ' + basePath[i]);
				expandedFiles = expandedFiles.concat(expand(basePath[i], files));
			}
		} else {
			expandedFiles = expand(basePath, files);
		}

		var results = [];
		for (i = 0; i < expandedFiles.length; ++i) {
			var file = expandedFiles[i].dest.replace(/\\/g, '/');
			grunt.log.debug('Found file "' + file + '".');
			results.push(file);
		}

		return results;
	};

	var pathCompareNatural = function (path1, path2) {
		if (path1 > path2) {
			return 1;
		} else if (path1 < path2) {
			return -1;
		} else {
			return 0;
		}
	};

	var pathCompareTopDown = function (path1, path2) {
		var seg1 = path1.split(/[\\/]/);
		var seg2 = path2.split(/[\\/]/);
		var i, j;

		if (seg1.length === seg2.length) {
			return pathCompareNatural(path1, path2);
		} else {
			j = Math.min(seg1.length, seg2.length) - 1;
			for (i = 0; i < j; ++i) {
				if (seg1[i] !== seg2[i]) {
					return pathCompareNatural(path1, path2);
				}
			}
			return seg1.length - seg2.length;
		}
	};

	var orderFiles = function (files, includeOptions) {
		if ('top-down' === includeOptions.ordering) {
			files.sort(pathCompareTopDown);
		}
	};

	// Register the task.
	grunt.registerMultiTask('includeSource', 'Include lists of files into your source files automatically.', function() {
		grunt.log.debug('Starting task "includeSource"...');
		var options = this.options({
			basePath: '',
			baseUrl: '',
			templates: {},
			typeMappings: {}
		});
		options.target = this.target;

		var typeMappings = extendr.clone(defaultTypeMappings);
		extendr.extend(typeMappings, options.typeMappings);

		grunt.log.debug('Base path is "' + options.basePath + '".');

		// Iterate over all specified file groups.
		this.files.forEach(function(file) {
			grunt.log.debug('Handling output file "' + file.dest + "'...");

			// Concatenate the source files.
			var contents = '';
			var contentSources = file.src.filter(function(filePath) {
					grunt.log.debug('Using input file "' + filePath + '".');
					// Remove nonexistent files.
					if (!grunt.file.exists(filePath)) {
						grunt.log.warn('Source file "' + filePath + '" not found.');
						return false;
					} else {
						return true;
					}
				}).map(	function(filePath) {
					// Read and return the file's source.
					return grunt.file.read(filePath);
				});

			// Don't bother detecting newline if we have no more than 1 file.
			if (contentSources.length > 1) {
				// Detect the newline to use for the content files.
				// Use the first file to detect the newlines, no use to test all of them.
				var contentNewline = /\r\n/g.test(contentSources[0]) ? '\r\n' : '\n';

				// Join the content files as one to be processed.
				contents = contentSources.join(contentNewline);
			}
			else {
				// Still use join here, since 'contentSources' could have a length of 0?
				contents = contentSources.join();
			}

			// Parse the contents, using a parser based on the target file.
			var fileType = path.extname(file.dest).substr(1);
			grunt.log.debug('File type is "' + fileType + '"');

			// Try and map the file type, using it instead.
			var mappedFileType = typeMappings[fileType];
			if (mappedFileType) {
				grunt.log.debug('File type "' + fileType + '" maps to "' + mappedFileType + '"');
				fileType = mappedFileType;
			}

			var parserFn = parsers[fileType];
			var findEndMarker = endMarkerParsers[fileType];

			if (!parserFn) {
				grunt.log.error('No parser found found file type "' + fileType + '".');
				return false;
			}

			// Get the available templates.
			var localTemplates = extendr.deepClone(templates);
			extendr.safeDeepExtendPlainObjects(localTemplates, options.templates);
			var typeTemplates = localTemplates[fileType];

			// Get the includes and rewrite the contents.
			var includes = parserFn(contents);
			var currentOffset = 0;

			grunt.log.debug('Found ' + includes.length + ' include statement(s).');

			includes.forEach(function(include, includeIndex) {
				var files = [];
				var baseUrl;
				if (include.options.target && options.target !== include.options.target) {
					grunt.log.debug('Include target is "' + include.options.target + '" and task target is "' + options.target + '", skipping include.');
				} else {
					files = resolveFiles(options, include.options);
				}
				orderFiles(files, include.options);

				if(include.options.baseUrl) {
					baseUrl = grunt.config.process(include.options.baseUrl);
				} else {
					baseUrl = options.baseUrl;
				}

				var sep = os.EOL,
					lookFor = [ ' ', '\t', '\r', '\n' ],
					i;

				// Find separators to maintain indentation when including fragment
				for (i = include.start + currentOffset - 1; i > 0 && lookFor.indexOf(contents[i]) >= 0; --i){
					// Don't allow multiple lines as separator, so break when we found one
					if (contents[i] === '\n'){
						i--;
						// Skip an extra character in case the endline is \r\n instead of just \n
						if (contents[i] === '\r'){
							i--;
						}
						break;
					}
				}
				if (i > 0) {
					++i; // Increment by one, since the index we found is the first non-indentation character.
					sep = contents.substr(i, include.start + currentOffset - i);
				}
				else {
					// So we probably are at the beginning of the file.
					// Scan forward so we can atleast detect what line endings are being used.
					// Keep it simple, if \r\n exists, use that, else just \n.
					sep = /\r\n/g.test(contents) ? '\r\n' : '\n';
				}

				var	includeFragments = [];
				files.forEach(function(file) {
					grunt.log.debug('Including file "' + file + '".');

					// Map the include type.
					var includeType = include.options.type;
					var mappedIncludeType = typeMappings[includeType];
					if (mappedIncludeType) {
						grunt.log.debug('Include type "' + includeType + '" maps to "' + mappedIncludeType + '"');
						includeType = mappedIncludeType;
					}

					includeFragments.push(typeTemplates[includeType]
						.replace(/\{filePath\}/g, url.resolve(baseUrl, file))
						.replace(/\{filePathDecoded\}/g, decodeURI(url.resolve(baseUrl, file)))
					);
				});

				var includeFragment = includeFragments.join(sep);

				// If there is an end marker before the next include or the end of the file
				// we want to replace the entire content between it and the include comment.
				var endMarker = null;
				if (findEndMarker){
					var nextInclude = includes[includeIndex + 1];
					var nextPartIndex = nextInclude ? (nextInclude.start + currentOffset): contents.length;
					var between = contents.substring(include.start + currentOffset, nextPartIndex);

					endMarker = findEndMarker(between, include.start);
				}

				var replacementStart;
				var replacementEnd;
				if (endMarker === null) {
					replacementStart = include.start + currentOffset;
					replacementEnd = include.end + 1 + currentOffset;
				} else {
					replacementStart = include.end + 1 + currentOffset + sep.length;
					replacementEnd = endMarker.start + currentOffset;
					includeFragment += sep;
				}

				contents =
					contents.substr(0, replacementStart) +
					includeFragment +
					contents.substr(replacementEnd);

				var addedCharacters = includeFragment.length;
				var removedCharacters = replacementEnd - replacementStart;
				currentOffset += addedCharacters - removedCharacters;
			});

			grunt.log.debug('Writing output...');
			grunt.file.write(file.dest, contents);
			grunt.log.writeln('File "' + file.dest + '" created.');
		});
	});
};
<|endoftext|>


<commit_before>/**
 * grunt-listfiles
 * https://github.com/MoLow/grunt-listfiles-json
 *
 * Copyright (c) 2013 Larry Gordon
 * Licensed under the MIT License
 */

module.exports = function (grunt) {
    'use strict';

    var path = require('path');

    // Create a list of files and perform an action on each file in the list then write the results to a file
    grunt.registerMultiTask('jsonlistfiles', 'Create a list of files and perform an action on each file in the list then write the results to a json file', function () {
        // Tell Grunt this task is asynchronous.
        var done = this.async();
        var variables = {};
        var data = this.data;

        for (var _var in data.variables) {
            var __var = grunt.file.expand(data.variables[_var]);
            __var.forEach(function (file) {
                var ext = path.extname(file);
                var filename = path.basename(file, ext);
                variables[_var] = variables[_var] || [];
                variables[_var].push({
                    path: file,
                    filename: filename,
                    ext: ext
                });
            })
        };

        grunt.template.addDelimiters('myNewDelimiters', '{%', '%}');

        function formatString(str, index) {
            if ((str.indexOf("{%") === 0) && (str.indexOf("%}", str.length - "%}".length) !== -1)) {
                var arr = [];
                for (var _var in variables) {
                    if (index !== undefined && index !== null) {
                        var nstr = str.replace('#' + _var, _var + '[' + index + ']');
                        if (nstr !== str) return grunt.template.process(nstr, { delimiters: 'myNewDelimiters', data: variables });
                    }

                    for (var i in variables[_var]) {
                        var nstr = str.replace(_var, _var + '[' + i + ']');
                        if (nstr !== str) {
                            arr.push(grunt.template.process(nstr, { delimiters: 'myNewDelimiters', data: variables }))
                        }
                    }
                }

                return arr;
            }

            var files = grunt.file.expand(str);
            if (files.length > 0) {
                return files;
            }
            return str;
        }

        function merge(obj, key, data, odata) {
            if ((grunt.util.kindOf(odata) !== grunt.util.kindOf(data)) && (grunt.util.kindOf(data) === "object" || grunt.util.kindOf(data) === "array")) {
                if (grunt.util.kindOf(obj) == "array") {
                    for (var i in data) {
                        obj.push(data[i]);
                    }
                } else if (grunt.util.kindOf(obj) == "object") {
                    for (var i in data) {
                        obj[i] = data[i];
                    }
                }
            } else {
                obj[key] = data;
            }
        }

        function recursiveSearch(data, stringIndex) {
            if (grunt.util.kindOf(data) === "object" || grunt.util.kindOf(data) === "array") {
                var obj = grunt.util.kindOf(data) === "array" ? [] : {};
                for (var key in data) {
                    var nkeys = formatString(key, stringIndex);
                    if (grunt.util.kindOf(nkeys) === "array") {
                        for (var nkey in nkeys) {
                            merge(obj, nkeys[nkey], recursiveSearch(data[key], nkey), data[key]);
                        }
                    } else {
                        merge(obj, key, recursiveSearch(data[key], stringIndex), data[key]);
                    }
                }
                return obj;
            }
            else if (grunt.util.kindOf(data) === "string") {
                return formatString(data, stringIndex);
            }
            else {
                return data;
            }
        }

        for (var dest in data.files) {
            var file = data.files[dest];
            var output = JSON.stringify(recursiveSearch(file), null, 4);
            grunt.file.write(dest, output);
            grunt.log.ok('Created file ' + dest);
        };

        // Iterate over all specified file groups.
        /* this.files.forEach(function(f) {
           // Concat specified files.
           var src = f.src.filter(function(filepath) {
             // Warn on and remove invalid source files (if nonull was set).
             if (!grunt.file.exists(filepath)) {
               grunt.log.warn('Source file "' + filepath + '" not found.');
               return false;
             } else {
               return true;
             }
           });

           // Add prefix and postfix to each line
           var totalFiles = src.length,
               last = totalFiles - 1,
               output = '';
           // Perform replacements
           if (options.replacements.length > 0) {
             src = src.map(function (filePath) {
               return options.replacements.reduce(function (filePath, replacement) {
                 return filePath.replace(replacement[0], replacement[1]);
               }, filePath);
             });
           }
           output = JSON.stringify(src)
           grunt.file.write(f.dest, output);

        // grunt.log.ok(totalFiles + ' file' + (totalFiles === 1 ? '' : 's') + ' processed.');
        // grunt.log.ok('Created file ' + f.dest);
    });
           */

        // Tell grunt the async task is complete
        done();
    });

};<commit_msg>remove old code<commit_after>/**
 * grunt-listfiles
 * https://github.com/MoLow/grunt-listfiles-json
 *
 * Copyright (c) 2013 Larry Gordon
 * Licensed under the MIT License
 */

module.exports = function (grunt) {
    'use strict';

    var path = require('path');

    // Create a list of files and perform an action on each file in the list then write the results to a file
    grunt.registerMultiTask('jsonlistfiles', 'Create a list of files and perform an action on each file in the list then write the results to a json file', function () {
        // Tell Grunt this task is asynchronous.
        var done = this.async();
        var variables = {};
        var data = this.data;

        for (var _var in data.variables) {
            var __var = grunt.file.expand(data.variables[_var]);
            __var.forEach(function (file) {
                var ext = path.extname(file);
                var filename = path.basename(file, ext);
                variables[_var] = variables[_var] || [];
                variables[_var].push({
                    path: file,
                    filename: filename,
                    ext: ext
                });
            })
        };

        grunt.template.addDelimiters('myNewDelimiters', '{%', '%}');

        function formatString(str, index) {
            if ((str.indexOf("{%") === 0) && (str.indexOf("%}", str.length - "%}".length) !== -1)) {
                var arr = [];
                for (var _var in variables) {
                    if (index !== undefined && index !== null) {
                        var nstr = str.replace('#' + _var, _var + '[' + index + ']');
                        if (nstr !== str) return grunt.template.process(nstr, { delimiters: 'myNewDelimiters', data: variables });
                    }

                    for (var i in variables[_var]) {
                        var nstr = str.replace(_var, _var + '[' + i + ']');
                        if (nstr !== str) {
                            arr.push(grunt.template.process(nstr, { delimiters: 'myNewDelimiters', data: variables }))
                        }
                    }
                }

                return arr;
            }

            var files = grunt.file.expand(str);
            if (files.length > 0) {
                return files;
            }
            return str;
        }

        function merge(obj, key, data, odata) {
            if ((grunt.util.kindOf(odata) !== grunt.util.kindOf(data)) && (grunt.util.kindOf(data) === "object" || grunt.util.kindOf(data) === "array")) {
                if (grunt.util.kindOf(obj) == "array") {
                    for (var i in data) {
                        obj.push(data[i]);
                    }
                } else if (grunt.util.kindOf(obj) == "object") {
                    for (var i in data) {
                        obj[i] = data[i];
                    }
                }
            } else {
                obj[key] = data;
            }
        }

        function recursiveSearch(data, stringIndex) {
            if (grunt.util.kindOf(data) === "object" || grunt.util.kindOf(data) === "array") {
                var obj = grunt.util.kindOf(data) === "array" ? [] : {};
                for (var key in data) {
                    var nkeys = formatString(key, stringIndex);
                    if (grunt.util.kindOf(nkeys) === "array") {
                        for (var nkey in nkeys) {
                            merge(obj, nkeys[nkey], recursiveSearch(data[key], nkey), data[key]);
                        }
                    } else {
                        merge(obj, key, recursiveSearch(data[key], stringIndex), data[key]);
                    }
                }
                return obj;
            }
            else if (grunt.util.kindOf(data) === "string") {
                return formatString(data, stringIndex);
            }
            else {
                return data;
            }
        }

        for (var dest in data.files) {
            var file = data.files[dest];
            var output = JSON.stringify(recursiveSearch(file), null, 4);
            grunt.file.write(dest, output);
            grunt.log.ok('Created file ' + dest);
        };

        // Tell grunt the async task is complete
        done();
    });

};<|endoftext|>


<commit_before>'use strict';

const run = require('../../run').run,
    fs = require('fs-extra');

async function distPackage (source, destination, packageTransformer) {
    fs.ensureDirSync(`dist/${destination}`);
    const pkg = JSON.parse(fs.readFileSync(`${source}/package.json`));

    pkg.files.forEach(file => {
        fs.copySync(`${source}/${file}`, `dist/${destination}/${file}`);
    });

    packageTransformer(pkg);
    fs.writeFileSync(`dist/${destination}/package.json`, JSON.stringify(pkg, null, 2));

    await run('npm', ['ci'], { cwd: `dist/${destination}` });
    await run('npm', ['dedupe'], { cwd: `dist/${destination}` });
}

async function packageMountebank () {
    await distPackage('.', 'mountebank', pkg => {
        delete pkg.devDependencies;
        Object.keys(pkg.scripts).forEach(script => {
            // We don't package most tasks and don't want users running them anyhow
            if (['start', 'restart', 'stop'].indexOf(script) < 0) {
                delete pkg.scripts[script];
            }
        });
    });
}

async function packageMbTest () {
    await distPackage('mbTest', 'test', pkg => {
        pkg.dependencies.mountebank = 'file:../mountebank';
        const lockfile = JSON.parse(fs.readFileSync('dist/test/package-lock.json'));
        lockfile.dependencies.mountebank.version = 'file:../mountebank';
        fs.writeFileSync('dist/test/package-lock.json', JSON.stringify(lockfile, null, 2));
    });
}

fs.removeSync('dist');
packageMountebank()
    .then(() => packageMbTest())
    .then(() => console.log('packages available in dist directory'))
    .catch(error => {
        console.error(error);
        process.exit(1); // eslint-disable-line no-process-exit
    });
<commit_msg>Switching npm ci -> npm install as running mb failed<commit_after>'use strict';

const run = require('../../run').run,
    fs = require('fs-extra');

async function distPackage (source, destination, packageTransformer) {
    fs.ensureDirSync(`dist/${destination}`);
    const pkg = JSON.parse(fs.readFileSync(`${source}/package.json`));

    pkg.files.forEach(file => {
        fs.copySync(`${source}/${file}`, `dist/${destination}/${file}`);
    });

    packageTransformer(pkg);
    fs.writeFileSync(`dist/${destination}/package.json`, JSON.stringify(pkg, null, 2));

    await run('npm', ['install', '--production'], { cwd: `dist/${destination}` });
    await run('npm', ['dedupe'], { cwd: `dist/${destination}` });
}

async function packageMountebank () {
    await distPackage('.', 'mountebank', pkg => {
        delete pkg.devDependencies;
        Object.keys(pkg.scripts).forEach(script => {
            // We don't package most tasks and don't want users running them anyhow
            if (['start', 'restart', 'stop'].indexOf(script) < 0) {
                delete pkg.scripts[script];
            }
        });
    });
}

async function packageMbTest () {
    await distPackage('mbTest', 'test', pkg => {
        pkg.dependencies.mountebank = 'file:../mountebank';
        const lockfile = JSON.parse(fs.readFileSync('dist/test/package-lock.json'));
        lockfile.dependencies.mountebank.version = 'file:../mountebank';
        fs.writeFileSync('dist/test/package-lock.json', JSON.stringify(lockfile, null, 2));
    });
}

fs.removeSync('dist');
packageMountebank()
    .then(() => packageMbTest())
    .then(() => console.log('packages available in dist directory'))
    .catch(error => {
        console.error(error);
        process.exit(1); // eslint-disable-line no-process-exit
    });
<|endoftext|>


<commit_before>import React from 'react'
import TestUtils from 'react-addons-test-utils'
import { mount, shallow } from 'enzyme'
import sinon from 'sinon'
import { expect } from './testHelper'
import PlacesAutocomplete, { geocodeByAddress } from '../index.js'

const testInputProps = {
  value: 'San Francisco, CA',
  onChange: () => {},
}

/*** Enzyme Rocks ***/
describe('<PlacesAutocomplete />', () => {
  let wrapper
  beforeEach(() => {
    wrapper = shallow(<PlacesAutocomplete inputProps={testInputProps} />)
  })

  it('renders an input element', () => {
    expect(wrapper.find('input')).to.have.length(1)
  })
})

// TODO: What's the value of this test?
describe('PlacesAutocomplete callbacks', () => {
  it('executes onError callback passed in as prop when status is not OK', () => {
    const spy = sinon.spy()
    const wrapper = mount(
      <PlacesAutocomplete onError={spy} inputProps={testInputProps} />
    )
    wrapper.instance().autocompleteCallback([], 'ZERO_RESULTS')
    expect(spy.calledOnce).to.equal(true)
    expect(spy.calledWith('ZERO_RESULTS')).to.equal(true)
  })

  it('executes default onError function when there is no custom prop and status is not OK', () => {
    sinon.stub(console, 'error')
    const wrapper = mount(<PlacesAutocomplete inputProps={testInputProps} />)
    wrapper.instance().autocompleteCallback([], 'ZERO_RESULTS')
    expect(console.error.calledOnce).to.equal(true)
  })
})

describe('PlacesAutocomplete props', () => {
  it('allows user to set the value of the input through prop', () => {
    const inputProps = {
      ...testInputProps,
      value: 'New York, NY',
    }

    const wrapper = mount(<PlacesAutocomplete inputProps={inputProps} />)
    expect(wrapper.find('input').props().value).to.equal('New York, NY')
  })
})

describe('autocomplete dropdown', () => {
  let wrapper
  const renderSuggestion = ({ suggestion }) => (

{suggestion}

  )
  const renderFooter = () => (

Footer element

  )
  beforeEach(() => {
    wrapper = shallow(
      <PlacesAutocomplete
        inputProps={testInputProps}
        renderSuggestion={renderSuggestion}
        renderFooter={renderFooter}
      />
    )
  })

  it('initially does not have an autocomplete dropdown', () => {
    expect(
      wrapper.find('#PlacesAutocomplete__autocomplete-container')
    ).to.have.length(0)
  })

  it('renders autocomplete dropdown once it receives data from google maps', () => {
    const data = [
      {
        suggestion: 'San Francisco, CA',
        placeId: 1,
        active: false,
        index: 0,
      },
      {
        suggestion: 'San Jose, CA',
        placeId: 2,
        active: false,
        index: 1,
      },
      {
        suggestion: 'San Diego, CA',
        placeId: 3,
        active: false,
        index: 2,
      },
    ]
    wrapper.setState({ autocompleteItems: data })
    expect(
      wrapper.find('#PlacesAutocomplete__autocomplete-container')
    ).to.have.length(1)
    expect(wrapper.find('.autocomplete-item')).to.have.length(3)
    expect(wrapper.find('.my-dropdown-footer')).to.have.length(1)
  })

  it('clears the autocomplete items when PlacesServiceStatus is not OK and clearSuggestionsOnError prop is true', () => {
    const initialItems = [
      {
        suggestion: 'San Francisco, CA',
        placeId: 1,
        active: false,
        index: 0,
      },
    ]
    const wrapper = shallow(
      <PlacesAutocomplete
        inputProps={testInputProps}
        clearSuggestionsOnError={true}
      />
    )
    wrapper.setState({ autocompleteItems: initialItems })
    wrapper.instance().autocompleteCallback([], 'ZERO_RESULTS')
    expect(wrapper.find('.autocomplete-item')).to.have.length(0)
  })

  it('does not clear the autocomplete items when PlacesServiceStatus is not OK and clearSuggestionsOnError prop is false', () => {
    const initialItems = [
      {
        suggestion: 'San Francisco, CA',
        placeId: 1,
        active: false,
        index: 0,
      },
    ]
    wrapper.setState({ autocompleteItems: initialItems })
    wrapper.instance().autocompleteCallback([], 'ZERO_RESULTS')
    expect(wrapper.find('.autocomplete-item')).to.have.length(1)
  })
})

describe('custom classNames, placeholder', () => {
  const inputProps = {
    ...testInputProps,
    placeholder: 'Your Address',
  }

  const classNames = {
    root: 'my-container',
    input: 'my-input',
    autocompleteContainer: 'my-autocomplete-container',
  }

  let wrapper

  beforeEach(() => {
    wrapper = shallow(
      <PlacesAutocomplete inputProps={inputProps} classNames={classNames} />
    )
  })

  it('lets you set a custom className for the container', () => {
    expect(wrapper.find('.my-container')).to.have.length(1)
  })

  it('lets you set a custom className for the input', () => {
    expect(wrapper.find('input')).to.have.className('my-input')
  })

  it('lets you set a custom className for autocomplete container', () => {
    wrapper.setState({
      autocompleteItems: [
        {
          suggestion: 'San Francisco, CA',
          placeId: 1,
          active: false,
          index: 0,
        },
      ],
    })
    expect(
      wrapper.find('#PlacesAutocomplete__autocomplete-container')
    ).to.have.className('my-autocomplete-container')
  })

  it('lets you set a custom placeholder for the input', () => {
    expect(wrapper.find('input').props().placeholder).to.equal('Your Address')
  })
})

// TODO: test formattedSuggestion
describe('customizable autocompleteItem', () => {
  it('lets you provide a custom autocomplete item', () => {
    const renderSuggestion = ({ suggestion }) => (

    )
    const wrapper = shallow(
      <PlacesAutocomplete
        inputProps={testInputProps}
        renderSuggestion={renderSuggestion}
      />
    )
    wrapper.setState({
      autocompleteItems: [
        {
          suggestion: 'San Francisco, CA',
          placeId: 1,
          active: false,
          index: 0,
        },
      ],
    })
    expect(wrapper.find('.my-autocomplete-item')).to.have.length(1)
    expect(wrapper.find('.my-autocomplete-item')).to.contain(

    )
  })
})

describe('custom inline styles', () => {
  const styles = {
    root: { position: 'absolute' },
    input: { width: '100%' },
    autocompleteContainer: { backgroundColor: 'green' },
    autocompleteItem: { color: 'black' },
    autocompleteItemActive: { color: 'blue' },
  }

  let wrapper
  beforeEach(() => {
    wrapper = shallow(
      <PlacesAutocomplete inputProps={testInputProps} styles={styles} />
    )
  })

  it('lets you set custom styles for the root element', () => {
    expect(
      wrapper.find('#PlacesAutocomplete__root').props().style.position
    ).to.equal('absolute')
  })

  it('lets you set custom styles for the input element', () => {
    expect(wrapper.find('input').props().style.width).to.equal('100%')
  })

  it('lets you set custom styles for the autocomplete container element', () => {
    wrapper.setState({
      autocompleteItems: [
        {
          suggestion: 'San Francisco, CA',
          placeId: 1,
          active: false,
          index: 0,
        },
      ],
    })
    expect(
      wrapper.find('#PlacesAutocomplete__autocomplete-container').props().style
        .backgroundColor
    ).to.equal('green')
  })

  it('lets you set custom styles for autocomplete items', () => {
    wrapper.setState({
      autocompleteItems: [
        {
          suggestion: 'San Francisco, CA',
          placeId: 1,
          active: false,
          index: 0,
        },
      ],
    })
    const item = wrapper
      .find('#PlacesAutocomplete__autocomplete-container')
      .childAt(0)
    expect(item.props().style.color).to.equal('black')
  })

  it('lets you set custom styles for active autocomplete items', () => {
    wrapper.setState({
      autocompleteItems: [
        { suggestion: 'San Francisco, CA', placeId: 1, active: true, index: 0 },
      ],
    })
    const item = wrapper
      .find('#PlacesAutocomplete__autocomplete-container')
      .childAt(0)
    expect(item.props().style.color).to.equal('blue')
  })
})

describe('AutocompletionRequest options', () => {
  const inputProps = {
    ...testInputProps,
    value: 'Boston, MA',
  }
  it('calls getPlacePredictions with the correct options', done => {
    global.google.maps.places.AutocompleteService.prototype.getPlacePredictions = (
      request,
      callback
    ) => {}
    const spy = sinon.spy(
      global.google.maps.places.AutocompleteService.prototype,
      'getPlacePredictions'
    )
    const options = { radius: 2000, types: ['address'] }
    const wrapper = mount(
      <PlacesAutocomplete inputProps={inputProps} options={options} />
    )
    wrapper
      .find('input')
      .simulate('change', { target: { value: 'Los Angeles, CA' } })
    setTimeout(() => {
      done()
      expect(spy.calledWith({ ...options, input: 'Los Angeles, CA' })).to.be
        .true
    }, 0)
  })
})

describe('autoFocus prop', () => {
  const inputProps = {
    ...testInputProps,
    autoFocus: true,
  }

  it('automatically gives focus when set to true', () => {
    const wrapper = mount(<PlacesAutocomplete inputProps={inputProps} />)
    expect(wrapper.find('input').node).to.equal(document.activeElement)
  })

  it('does not give the input element a focus by default', () => {
    const wrapper = mount(<PlacesAutocomplete inputProps={testInputProps} />)
    expect(wrapper.find('input').node).to.not.equal(document.activeElement)
  })
})

describe('Enables using original input\'s value as it was another entry in autocomplete', () => {
  const data = [
    {
      suggestion: 'San Francisco, CA',
      placeId: 1,
      active: false,
      index: 0,
    },
    {
      suggestion: 'San Jose, CA',
      placeId: 2,
      active: false,
      index: 1,
    },
    {
      suggestion: 'San Diego, CA',
      placeId: 3,
      active: false,
      index: 2,
    },
  ]
  const spy = sinon.spy()
  const inputProps = {
    value: 'san',
    onChange: spy,
  }
  let wrapper

  beforeEach(() => {
    wrapper = shallow(
        <PlacesAutocomplete inputProps={inputProps} />
    )
    spy.reset()
  })

  it('save value of input when pressing arrow down key and none of autocomplete entries is being focused', () => {
    wrapper.setState({ autocompleteItems: data })
    wrapper
      .instance()
      .handleInputKeyDown({ key: 'ArrowDown', preventDefault: () => {} })
    expect(wrapper.state().userInputValue).to.equal('san')
  })

  it('save value of input when pressing arrow up key and none of autocomplete entries is being focused', () => {
    wrapper.setState({ autocompleteItems: data })
    wrapper
      .instance()
      .handleInputKeyDown({ key: 'ArrowUp', preventDefault: () => {} })
    expect(wrapper.state().userInputValue).to.equal('san')
  })

  it("don't focus on any entry when focus is on last item and arrow down key is pressed", () => {
    const lastItemActive = data.map((item, idx) => {
      return idx === data.length - 1 ? { ...item, active: true } : item
    })
    wrapper.setState({ autocompleteItems: lastItemActive })
    wrapper
      .instance()
      .handleInputKeyDown({ key: 'ArrowDown', preventDefault: () => {} })
    wrapper.state().autocompleteItems.forEach(item => {
      expect(item.active).to.be.false
    })
  })

  it("don't focus on any entry when focus is on first item and arrow up key is pressed", () => {
    const firstItemActive = data.map((item, idx) => {
      return idx === 0 ? { ...item, active: true } : item
    })
    wrapper.setState({ autocompleteItems: firstItemActive })
    wrapper
      .instance()
      .handleInputKeyDown({ key: 'ArrowUp', preventDefault: () => {} })
    wrapper.state().autocompleteItems.forEach(item => {
      expect(item.active).to.be.false
    })
  })

  it('onChange function is called with appropriate value', () => {
    // Amount of entries is 3 for this test case, so when we press arrow down fourth time
    // we expect onChange function to be called with original input value
    // being stored in `userInputValue` state entry
    // rest of calls should be called with appropraite entries from autocomplete items
    wrapper.setState({ autocompleteItems: data })
    wrapper
      .instance()
      .handleInputKeyDown({ key: 'ArrowDown', preventDefault: () => {} })
    wrapper
      .instance()
      .handleInputKeyDown({ key: 'ArrowDown', preventDefault: () => {} })
    wrapper
      .instance()
      .handleInputKeyDown({ key: 'ArrowDown', preventDefault: () => {} })
    wrapper
      .instance()
      .handleInputKeyDown({ key: 'ArrowDown', preventDefault: () => {} })
    expect(spy.getCall(0).args[0]).to.equal(data[0].suggestion)
    expect(spy.getCall(1).args[0]).to.equal(data[1].suggestion)
    expect(spy.getCall(2).args[0]).to.equal(data[2].suggestion)
    expect(spy.getCall(3).args[0]).to.equal(wrapper.state().userInputValue)
  })
})

// TODOs:
// * Test geocodeByAddress function
<commit_msg>chore(prettier): Run code formatter<commit_after>import React from 'react'
import TestUtils from 'react-addons-test-utils'
import { mount, shallow } from 'enzyme'
import sinon from 'sinon'
import { expect } from './testHelper'
import PlacesAutocomplete, { geocodeByAddress } from '../index.js'

const testInputProps = {
  value: 'San Francisco, CA',
  onChange: () => {},
}

/*** Enzyme Rocks ***/
describe('<PlacesAutocomplete />', () => {
  let wrapper
  beforeEach(() => {
    wrapper = shallow(<PlacesAutocomplete inputProps={testInputProps} />)
  })

  it('renders an input element', () => {
    expect(wrapper.find('input')).to.have.length(1)
  })
})

// TODO: What's the value of this test?
describe('PlacesAutocomplete callbacks', () => {
  it('executes onError callback passed in as prop when status is not OK', () => {
    const spy = sinon.spy()
    const wrapper = mount(
      <PlacesAutocomplete onError={spy} inputProps={testInputProps} />
    )
    wrapper.instance().autocompleteCallback([], 'ZERO_RESULTS')
    expect(spy.calledOnce).to.equal(true)
    expect(spy.calledWith('ZERO_RESULTS')).to.equal(true)
  })

  it('executes default onError function when there is no custom prop and status is not OK', () => {
    sinon.stub(console, 'error')
    const wrapper = mount(<PlacesAutocomplete inputProps={testInputProps} />)
    wrapper.instance().autocompleteCallback([], 'ZERO_RESULTS')
    expect(console.error.calledOnce).to.equal(true)
  })
})

describe('PlacesAutocomplete props', () => {
  it('allows user to set the value of the input through prop', () => {
    const inputProps = {
      ...testInputProps,
      value: 'New York, NY',
    }

    const wrapper = mount(<PlacesAutocomplete inputProps={inputProps} />)
    expect(wrapper.find('input').props().value).to.equal('New York, NY')
  })
})

describe('autocomplete dropdown', () => {
  let wrapper
  const renderSuggestion = ({ suggestion }) => (

{suggestion}

  )
  const renderFooter = () => (

Footer element

  )
  beforeEach(() => {
    wrapper = shallow(
      <PlacesAutocomplete
        inputProps={testInputProps}
        renderSuggestion={renderSuggestion}
        renderFooter={renderFooter}
      />
    )
  })

  it('initially does not have an autocomplete dropdown', () => {
    expect(
      wrapper.find('#PlacesAutocomplete__autocomplete-container')
    ).to.have.length(0)
  })

  it('renders autocomplete dropdown once it receives data from google maps', () => {
    const data = [
      {
        suggestion: 'San Francisco, CA',
        placeId: 1,
        active: false,
        index: 0,
      },
      {
        suggestion: 'San Jose, CA',
        placeId: 2,
        active: false,
        index: 1,
      },
      {
        suggestion: 'San Diego, CA',
        placeId: 3,
        active: false,
        index: 2,
      },
    ]
    wrapper.setState({ autocompleteItems: data })
    expect(
      wrapper.find('#PlacesAutocomplete__autocomplete-container')
    ).to.have.length(1)
    expect(wrapper.find('.autocomplete-item')).to.have.length(3)
    expect(wrapper.find('.my-dropdown-footer')).to.have.length(1)
  })

  it('clears the autocomplete items when PlacesServiceStatus is not OK and clearSuggestionsOnError prop is true', () => {
    const initialItems = [
      {
        suggestion: 'San Francisco, CA',
        placeId: 1,
        active: false,
        index: 0,
      },
    ]
    const wrapper = shallow(
      <PlacesAutocomplete
        inputProps={testInputProps}
        clearSuggestionsOnError={true}
      />
    )
    wrapper.setState({ autocompleteItems: initialItems })
    wrapper.instance().autocompleteCallback([], 'ZERO_RESULTS')
    expect(wrapper.find('.autocomplete-item')).to.have.length(0)
  })

  it('does not clear the autocomplete items when PlacesServiceStatus is not OK and clearSuggestionsOnError prop is false', () => {
    const initialItems = [
      {
        suggestion: 'San Francisco, CA',
        placeId: 1,
        active: false,
        index: 0,
      },
    ]
    wrapper.setState({ autocompleteItems: initialItems })
    wrapper.instance().autocompleteCallback([], 'ZERO_RESULTS')
    expect(wrapper.find('.autocomplete-item')).to.have.length(1)
  })
})

describe('custom classNames, placeholder', () => {
  const inputProps = {
    ...testInputProps,
    placeholder: 'Your Address',
  }

  const classNames = {
    root: 'my-container',
    input: 'my-input',
    autocompleteContainer: 'my-autocomplete-container',
  }

  let wrapper

  beforeEach(() => {
    wrapper = shallow(
      <PlacesAutocomplete inputProps={inputProps} classNames={classNames} />
    )
  })

  it('lets you set a custom className for the container', () => {
    expect(wrapper.find('.my-container')).to.have.length(1)
  })

  it('lets you set a custom className for the input', () => {
    expect(wrapper.find('input')).to.have.className('my-input')
  })

  it('lets you set a custom className for autocomplete container', () => {
    wrapper.setState({
      autocompleteItems: [
        {
          suggestion: 'San Francisco, CA',
          placeId: 1,
          active: false,
          index: 0,
        },
      ],
    })
    expect(
      wrapper.find('#PlacesAutocomplete__autocomplete-container')
    ).to.have.className('my-autocomplete-container')
  })

  it('lets you set a custom placeholder for the input', () => {
    expect(wrapper.find('input').props().placeholder).to.equal('Your Address')
  })
})

// TODO: test formattedSuggestion
describe('customizable autocompleteItem', () => {
  it('lets you provide a custom autocomplete item', () => {
    const renderSuggestion = ({ suggestion }) => (

    )
    const wrapper = shallow(
      <PlacesAutocomplete
        inputProps={testInputProps}
        renderSuggestion={renderSuggestion}
      />
    )
    wrapper.setState({
      autocompleteItems: [
        {
          suggestion: 'San Francisco, CA',
          placeId: 1,
          active: false,
          index: 0,
        },
      ],
    })
    expect(wrapper.find('.my-autocomplete-item')).to.have.length(1)
    expect(wrapper.find('.my-autocomplete-item')).to.contain(

    )
  })
})

describe('custom inline styles', () => {
  const styles = {
    root: { position: 'absolute' },
    input: { width: '100%' },
    autocompleteContainer: { backgroundColor: 'green' },
    autocompleteItem: { color: 'black' },
    autocompleteItemActive: { color: 'blue' },
  }

  let wrapper
  beforeEach(() => {
    wrapper = shallow(
      <PlacesAutocomplete inputProps={testInputProps} styles={styles} />
    )
  })

  it('lets you set custom styles for the root element', () => {
    expect(
      wrapper.find('#PlacesAutocomplete__root').props().style.position
    ).to.equal('absolute')
  })

  it('lets you set custom styles for the input element', () => {
    expect(wrapper.find('input').props().style.width).to.equal('100%')
  })

  it('lets you set custom styles for the autocomplete container element', () => {
    wrapper.setState({
      autocompleteItems: [
        {
          suggestion: 'San Francisco, CA',
          placeId: 1,
          active: false,
          index: 0,
        },
      ],
    })
    expect(
      wrapper.find('#PlacesAutocomplete__autocomplete-container').props().style
        .backgroundColor
    ).to.equal('green')
  })

  it('lets you set custom styles for autocomplete items', () => {
    wrapper.setState({
      autocompleteItems: [
        {
          suggestion: 'San Francisco, CA',
          placeId: 1,
          active: false,
          index: 0,
        },
      ],
    })
    const item = wrapper
      .find('#PlacesAutocomplete__autocomplete-container')
      .childAt(0)
    expect(item.props().style.color).to.equal('black')
  })

  it('lets you set custom styles for active autocomplete items', () => {
    wrapper.setState({
      autocompleteItems: [
        { suggestion: 'San Francisco, CA', placeId: 1, active: true, index: 0 },
      ],
    })
    const item = wrapper
      .find('#PlacesAutocomplete__autocomplete-container')
      .childAt(0)
    expect(item.props().style.color).to.equal('blue')
  })
})

describe('AutocompletionRequest options', () => {
  const inputProps = {
    ...testInputProps,
    value: 'Boston, MA',
  }
  it('calls getPlacePredictions with the correct options', done => {
    global.google.maps.places.AutocompleteService.prototype.getPlacePredictions = (
      request,
      callback
    ) => {}
    const spy = sinon.spy(
      global.google.maps.places.AutocompleteService.prototype,
      'getPlacePredictions'
    )
    const options = { radius: 2000, types: ['address'] }
    const wrapper = mount(
      <PlacesAutocomplete inputProps={inputProps} options={options} />
    )
    wrapper
      .find('input')
      .simulate('change', { target: { value: 'Los Angeles, CA' } })
    setTimeout(() => {
      done()
      expect(spy.calledWith({ ...options, input: 'Los Angeles, CA' })).to.be
        .true
    }, 0)
  })
})

describe('autoFocus prop', () => {
  const inputProps = {
    ...testInputProps,
    autoFocus: true,
  }

  it('automatically gives focus when set to true', () => {
    const wrapper = mount(<PlacesAutocomplete inputProps={inputProps} />)
    expect(wrapper.find('input').node).to.equal(document.activeElement)
  })

  it('does not give the input element a focus by default', () => {
    const wrapper = mount(<PlacesAutocomplete inputProps={testInputProps} />)
    expect(wrapper.find('input').node).to.not.equal(document.activeElement)
  })
})

describe("Enables using original input's value as it was another entry in autocomplete", () => {
  const data = [
    {
      suggestion: 'San Francisco, CA',
      placeId: 1,
      active: false,
      index: 0,
    },
    {
      suggestion: 'San Jose, CA',
      placeId: 2,
      active: false,
      index: 1,
    },
    {
      suggestion: 'San Diego, CA',
      placeId: 3,
      active: false,
      index: 2,
    },
  ]
  const spy = sinon.spy()
  const inputProps = {
    value: 'san',
    onChange: spy,
  }
  let wrapper

  beforeEach(() => {
    wrapper = shallow(<PlacesAutocomplete inputProps={inputProps} />)
    spy.reset()
  })

  it('save value of input when pressing arrow down key and none of autocomplete entries is being focused', () => {
    wrapper.setState({ autocompleteItems: data })
    wrapper
      .instance()
      .handleInputKeyDown({ key: 'ArrowDown', preventDefault: () => {} })
    expect(wrapper.state().userInputValue).to.equal('san')
  })

  it('save value of input when pressing arrow up key and none of autocomplete entries is being focused', () => {
    wrapper.setState({ autocompleteItems: data })
    wrapper
      .instance()
      .handleInputKeyDown({ key: 'ArrowUp', preventDefault: () => {} })
    expect(wrapper.state().userInputValue).to.equal('san')
  })

  it("don't focus on any entry when focus is on last item and arrow down key is pressed", () => {
    const lastItemActive = data.map((item, idx) => {
      return idx === data.length - 1 ? { ...item, active: true } : item
    })
    wrapper.setState({ autocompleteItems: lastItemActive })
    wrapper
      .instance()
      .handleInputKeyDown({ key: 'ArrowDown', preventDefault: () => {} })
    wrapper.state().autocompleteItems.forEach(item => {
      expect(item.active).to.be.false
    })
  })

  it("don't focus on any entry when focus is on first item and arrow up key is pressed", () => {
    const firstItemActive = data.map((item, idx) => {
      return idx === 0 ? { ...item, active: true } : item
    })
    wrapper.setState({ autocompleteItems: firstItemActive })
    wrapper
      .instance()
      .handleInputKeyDown({ key: 'ArrowUp', preventDefault: () => {} })
    wrapper.state().autocompleteItems.forEach(item => {
      expect(item.active).to.be.false
    })
  })

  it('onChange function is called with appropriate value', () => {
    // Amount of entries is 3 for this test case, so when we press arrow down fourth time
    // we expect onChange function to be called with original input value
    // being stored in `userInputValue` state entry
    // rest of calls should be called with appropraite entries from autocomplete items
    wrapper.setState({ autocompleteItems: data })
    wrapper
      .instance()
      .handleInputKeyDown({ key: 'ArrowDown', preventDefault: () => {} })
    wrapper
      .instance()
      .handleInputKeyDown({ key: 'ArrowDown', preventDefault: () => {} })
    wrapper
      .instance()
      .handleInputKeyDown({ key: 'ArrowDown', preventDefault: () => {} })
    wrapper
      .instance()
      .handleInputKeyDown({ key: 'ArrowDown', preventDefault: () => {} })
    expect(spy.getCall(0).args[0]).to.equal(data[0].suggestion)
    expect(spy.getCall(1).args[0]).to.equal(data[1].suggestion)
    expect(spy.getCall(2).args[0]).to.equal(data[2].suggestion)
    expect(spy.getCall(3).args[0]).to.equal(wrapper.state().userInputValue)
  })
})

// TODOs:
// * Test geocodeByAddress function
<|endoftext|>


<commit_before>'use strict';

var ResultsPage = function() {
  this.get = function() {
    browser.get('http://localhost:3000/pro');
  };

  this.fillQuery = function(subject, predicate, object) {
    if (!subject) {
      subject = 'Drug';
    }
    if (!predicate) {
      predicate = 'has indications';
    }
    if (!object) {
      object = 'diabetes';
    }
    element(by.model('form.subject')).clear().sendKeys(subject);
    element(by.model('form.subject')).sendKeys(protractor.Key.ENTER);
    element(by.model('form.predicate')).clear().sendKeys(predicate);
    element(by.model('form.predicate')).sendKeys(protractor.Key.ENTER);
    element(by.model('form.object')).clear().sendKeys(object);
    return element(by.model('form.object')).sendKeys(protractor.Key.ENTER);
  };

  this.fillBadQuery = function() {
    element(by.model('form.subject')).clear().sendKeys('Drug');
    element(by.model('form.subject')).sendKeys(protractor.Key.ENTER);
    element(by.model('form.predicate')).clear().sendKeys('bad entry');
    return element(by.model('form.predicate')).sendKeys(protractor.Key.ENTER);
  };

  this.fillAndEdit = function() {
    element(by.model('form.subject')).clear().sendKeys('Drug');
    element(by.model('form.subject')).sendKeys(protractor.Key.ENTER);
    element(by.model('form.predicate')).clear().sendKeys('has indications');
    element(by.model('form.predicate')).sendKeys(protractor.Key.ENTER);
    element(by.model('form.object')).clear().sendKeys('diabetes');
    element(by.model('form.predicate')).clear().sendKeys('has indications');
    return element(by.model('form.predicate')).sendKeys(protractor.Key.ENTER);
  };

  this.execSearch = function(subject, predicate, object) {
    return this.fillQuery(subject, predicate, object);
  };

  this.getResults = function() {
    return element.all(
      by.repeater(
        'link in results track by $index'
      )
    );
  };

  this.getFathead = function(type) {
    if (!type) {
      type = '.fathead';
    }
    return element(by.css(type));
  };
};

var getColor = function(element, cls) {
  return element
};

var hasClass = function(element, cls) {
  return element.getAttribute('class').then(function(classes) {
    return classes.split(' ').indexOf(cls) !== -1;
  });
};

describe('Protractor Results Page', function() {
  var resultsPage = new ResultsPage();

  beforeEach(function() {
    resultsPage.get();
  });

  it('shouldn\'t keep unknown entry', function() {
    resultsPage.fillBadQuery().then(function() {
    expect(element(by.model('form.predicate')).getAttribute('value')).not.toEqual('bad entry');
    expect(element(by.model('form.predicate')).getAttribute('value')).toEqual('');
    });
  });

  // TODO check edit previous input clear others
  it('should clear next inputs on edit', function() {
    resultsPage.fillAndEdit().then(function() {
      expect(element(by.model('form.object')).getAttribute('value')).not.toEqual('diabetes');
      expect(element(by.model('form.object')).getAttribute('value')).toEqual('');
    });
  });

  it('should do a search', function() {
    resultsPage.execSearch().then(function() {
      // OK expect(browser.getCurrentUrl()).toContain('?q=cancer');
      resultsPage.getResults().count().then(function(count) {
        expect(count).not.toEqual(0);
      });
    });
  });

  it('should have pro class', function() {
    resultsPage.execSearch().then(function() {
      resultsPage.getResults().each(function(element, index) {
        expect(hasClass(element, 'pro')).toBe(true);
      });
    });
  });

});
<commit_msg>add protractor tests<commit_after>'use strict';

var ResultsPage = function() {
  this.get = function() {
    browser.get('http://localhost:3000/pro');
  };

  this.fillSubject = function(subject) {
    if (!subject) {
      subject = 'Drug';
    }
    element(by.model('form.subject')).clear().sendKeys(subject);
    return element(by.model('form.subject')).sendKeys(protractor.Key.ENTER);
  };

  this.fillPredicate = function(predicate) {
    if (!predicate) {
      predicate = 'has indications';
    }
    element(by.model('form.predicate')).clear().sendKeys(predicate);
    return element(by.model('form.predicate')).sendKeys(protractor.Key.ENTER);
  };

  this.fillObject = function(object) {
    if (!object) {
      object = 'diabetes';
    }
    element(by.model('form.object')).clear().sendKeys(object);
    return element(by.model('form.object')).sendKeys(protractor.Key.ENTER);
  };

  this.fillQuery = function(subject, predicate, object) {
    this.fillSubject(subject);
    this.fillPredicate(predicate);
    return this.fillObject(object);
  };

  this.fillBadQuery = function() {
    this.fillSubject();
    return this.fillPredicate('bad entry');
  };

  this.fillAndEdit = function() {
    this.fillSubject();
    this.fillPredicate();
    element(by.model('form.object')).clear().sendKeys('diabetes');
    return this.fillPredicate();
  };

  this.execSearch = function(subject, predicate, object) {
    return this.fillQuery(subject, predicate, object);
  };

  this.getResults = function() {
    return element.all(
      by.repeater(
        'link in results track by $index'
      )
    );
  };

  this.getFathead = function(type) {
    if (!type) {
      type = '.fathead';
    }
    return element(by.css(type));
  };
};

var getColor = function(element, cls) {
  return element
};

var hasClass = function(element, cls) {
  return element.getAttribute('class').then(function(classes) {
    return classes.split(' ').indexOf(cls) !== -1;
  });
};

describe('Protractor Results Page', function() {
  var resultsPage = new ResultsPage();

  beforeEach(function() {
    resultsPage.get();
  });

  // TODO check no uncomplete submit
  it('shouldn\'t submit incomplete form', function() {
    resultsPage.fillSubject('bad entry').then(function() {
      expect(browser.getCurrentUrl()).not.toContain('?form=');
      resultsPage.getResults().count().then(function(count) {
        expect(count).toEqual(0);
      });
    });
    resultsPage.fillSubject().then(function() {
      resultsPage.fillPredicate().then(function() {
        expect(browser.getCurrentUrl()).not.toContain('?form=');
        resultsPage.getResults().count().then(function(count) {
          expect(count).toEqual(0);
        });
        resultsPage.fillObject('bad entry').then(function() {
          expect(browser.getCurrentUrl()).not.toContain('?form=');
          resultsPage.getResults().count().then(function(count) {
            expect(count).toEqual(0);
          });
        });
      });
    });
  });

  /* it('shouldn\'t keep unknown entry', function() {
    resultsPage.fillBadQuery().then(function() {
      expect(element(by.model('form.predicate')).getAttribute('value')).toEqual('bad entry');
      expect(element(by.model('form.object')).isPresent()).toBe(false);
      element(by.model('form.subject')).click().then(function() {
        expect(element(by.model('form.predicate')).getAttribute('value')).toEqual('');
      });
    });
  });

  it('should clear next inputs on edit', function() {
    resultsPage.fillAndEdit().then(function() {
      expect(element(by.model('form.object')).getAttribute('value')).not.toEqual('diabetes');
      expect(element(by.model('form.object')).getAttribute('value')).toEqual('');
    });
  });

  it('should do a search', function() {
    resultsPage.execSearch().then(function() {
      resultsPage.getResults().count().then(function(count) {
        expect(count).not.toEqual(0);
      });
    });
  });

  it('should have pro class', function() {
    resultsPage.execSearch().then(function() {
      resultsPage.getResults().each(function(element, index) {
        expect(hasClass(element, 'pro')).toBe(true);
      });
    });
  });*/

});
<|endoftext|>


<commit_before>var path = require('path');
var webpack = require('webpack');
var Mix = require('laravel-mix').config;
var plugins = require('laravel-mix').plugins;

/*
 |--------------------------------------------------------------------------
 | Mix Initialization
 |--------------------------------------------------------------------------
 |
 | As our first step, we'll require the project's Laravel Mix file
 | and record the user's requested compilation and build steps.
 | Once those steps have been recorded, we may get to work.
 |
 */

require('./webpack.mix');

Mix.finalize();

/*
 |--------------------------------------------------------------------------
 | Webpack Entry
 |--------------------------------------------------------------------------
 |
 | We'll first specify the entry point for Webpack. By default, we'll
 | assume a single bundled file, but you may call Mix.extract()
 | to make a separate bundle specifically for vendor libraries.
 |
 */

module.exports.entry = Mix.entry();

if (Mix.js.vendor) {
    module.exports.entry.vendor = Mix.js.vendor;
}

/*
 |--------------------------------------------------------------------------
 | Webpack Output
 |--------------------------------------------------------------------------
 |
 | Webpack naturally requires us to specify our desired output path and
 | file name. We'll simply echo what you passed to with Mix.js().
 | Note that, for Mix.version(), we'll properly hash the file.
 |
 */

module.exports.output = Mix.output();

/*
 |--------------------------------------------------------------------------
 | Rules
 |--------------------------------------------------------------------------
 |
 | Webpack rules allow us to register any number of loaders and options.
 | Out of the box, we'll provide a handful to get you up and running
 | as quickly as possible, though feel free to add to this list.
 |
 */

module.exports.module = {
    rules: [
        {
            test: /\.vue$/,
            loader: 'vue-loader',
            options: {
                loaders: {
                    js: 'babel-loader' + Mix.babelConfig()
                  },

                  postcss: [
                    require('autoprefixer')
                ]
            }
        },

        {
            test: /\.js$/,
            exclude: /(node_modules|bower_components)/,
            loader: 'babel-loader' + Mix.babelConfig()
        },

        {
            test: /\.(png|jpg|gif)$/,
            loader: 'file-loader',
            options: {
                name: '[name].[ext]?[hash]'
            }
        },

        {
            test: /\.(woff2?|ttf|eot|svg)$/,
            loader: 'file-loader',
            options: {
                name: path.relative(__dirname, './fonts/[name].[ext]?[hash]')
            }
        }
    ]
};

if (Mix.sass) {
    module.exports.module.rules.push({
        test: /\.s[ac]ss$/,
        loader: plugins.ExtractTextPlugin.extract({
            fallbackLoader: 'style-loader',
            loader: [
                'css-loader', 'postcss-loader',
                'resolve-url-loader', 'sass-loader?sourceMap'
            ]
        })
    });
}

if (Mix.less) {
    module.exports.module.rules.push({
        test: /\.less$/,
        loader: plugins.ExtractTextPlugin.extract({
            fallbackLoader: 'style-loader',
            loader: ['css-loader', 'postcss-loader', 'less-loader']
        })
    });
}

/*
 |--------------------------------------------------------------------------
 | Resolve
 |--------------------------------------------------------------------------
 |
 | Here, we may set any options/aliases that affect Webpack's resolving
 | of modules. To begin, we will provide the necessary Vue alias to
 | load the Vue common library. You may delete this, if needed.
 |
 */

module.exports.resolve = {
    extensions: ['*', '.js', '.jsx', '.vue'],

    alias: {
        'vue$': 'vue/dist/vue.common.js'
    }
};

/*
 |--------------------------------------------------------------------------
 | Stats
 |--------------------------------------------------------------------------
 |
 | By default, Webpack spits a lot of information out to the terminal,
 | each you time you compile. Let's keep things a bit more minimal
 | and hide a few of those bits and pieces. Adjust as you wish.
 |
 */

module.exports.stats = {
    hash: false,
    version: false,
    timings: false,
    children: false
};

module.exports.performance = { hints: Mix.inProduction };

/*
 |--------------------------------------------------------------------------
 | Devtool
 |--------------------------------------------------------------------------
 |
 | Sourcemaps allow us to access our original source code within the
 | browser, even if we're serving a bundled script or stylesheet.
 | You may activate sourcemaps, by adding Mix.sourceMaps().
 |
 */

module.exports.devtool = Mix.sourcemaps;

/*
 |--------------------------------------------------------------------------
 | Webpack Dev Server Configuration
 |--------------------------------------------------------------------------
 |
 | If you want to use that flashy hot module replacement feature, then
 | we've got you covered. Here, we'll set some basic initial config
 | for the Node server. You very likely won't want to edit this.
 |
 */
module.exports.devServer = {
    historyApiFallback: true,
    noInfo: true
};

/*
 |--------------------------------------------------------------------------
 | Plugins
 |--------------------------------------------------------------------------
 |
 | Lastly, we'll register a number of plugins to extend and configure
 | Webpack. To get you started, we've included a handful of useful
 | extensions, for versioning, OS notifications, and much more.
 |
 */

module.exports.plugins = [];

if (Mix.notifications) {
    module.exports.plugins.push(
        new plugins.WebpackNotifierPlugin({
            title: 'Laravel Mix',
            alwaysNotify: true,
            contentImage: 'node_modules/laravel-mix/icons/laravel.png'
        })
    );
}

module.exports.plugins.push(
    function() {
        this.plugin('done', stats => Mix.manifest.write(stats));
    }
);

module.exports.plugins.push(
    new webpack.LoaderOptionsPlugin({
        minimize: Mix.inProduction,
        options: {
            postcss: [
                require('autoprefixer')
            ],
            context: __dirname,
            output: { path: './' }
        }
    })
);

if (Mix.versioning.enabled) {
    Mix.versioning.record();

    module.exports.plugins.push(
        new plugins.WebpackOnBuildPlugin(stats => {
            Mix.versioning.prune(Mix.publicPath);
        })
    );
}

if (Mix.combine || Mix.minify) {
    module.exports.plugins.push(
        new plugins.WebpackOnBuildPlugin(stats => {
            Mix.concatenateAll().minifyAll();
        })
    );
}

if (Mix.copy) {
    Mix.copy.forEach(copy => {
        module.exports.plugins.push(
            new plugins.CopyWebpackPlugin([copy])
        );
    });
}

if (Mix.js.vendor) {
    module.exports.plugins.push(
        new webpack.optimize.CommonsChunkPlugin({
            names: ['vendor', 'manifest']
        })
    );
}

if (Mix.cssPreprocessor) {
    module.exports.plugins.push(
        new plugins.ExtractTextPlugin({
            filename: Mix.cssOutput()
        })
    );
}

if (Mix.inProduction) {
    module.exports.plugins = module.exports.plugins.concat([
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: '"production"'
            }
        }),

        new webpack.optimize.UglifyJsPlugin({
            sourceMap: true,
            compress: {
                warnings: false
            }
        })
    ]);
}
<commit_msg>No hints by default<commit_after>var path = require('path');
var webpack = require('webpack');
var Mix = require('laravel-mix').config;
var plugins = require('laravel-mix').plugins;

/*
 |--------------------------------------------------------------------------
 | Mix Initialization
 |--------------------------------------------------------------------------
 |
 | As our first step, we'll require the project's Laravel Mix file
 | and record the user's requested compilation and build steps.
 | Once those steps have been recorded, we may get to work.
 |
 */

require('./webpack.mix');

Mix.finalize();

/*
 |--------------------------------------------------------------------------
 | Webpack Entry
 |--------------------------------------------------------------------------
 |
 | We'll first specify the entry point for Webpack. By default, we'll
 | assume a single bundled file, but you may call Mix.extract()
 | to make a separate bundle specifically for vendor libraries.
 |
 */

module.exports.entry = Mix.entry();

if (Mix.js.vendor) {
    module.exports.entry.vendor = Mix.js.vendor;
}

/*
 |--------------------------------------------------------------------------
 | Webpack Output
 |--------------------------------------------------------------------------
 |
 | Webpack naturally requires us to specify our desired output path and
 | file name. We'll simply echo what you passed to with Mix.js().
 | Note that, for Mix.version(), we'll properly hash the file.
 |
 */

module.exports.output = Mix.output();

/*
 |--------------------------------------------------------------------------
 | Rules
 |--------------------------------------------------------------------------
 |
 | Webpack rules allow us to register any number of loaders and options.
 | Out of the box, we'll provide a handful to get you up and running
 | as quickly as possible, though feel free to add to this list.
 |
 */

module.exports.module = {
    rules: [
        {
            test: /\.vue$/,
            loader: 'vue-loader',
            options: {
                loaders: {
                    js: 'babel-loader' + Mix.babelConfig()
                  },

                  postcss: [
                    require('autoprefixer')
                ]
            }
        },

        {
            test: /\.js$/,
            exclude: /(node_modules|bower_components)/,
            loader: 'babel-loader' + Mix.babelConfig()
        },

        {
            test: /\.(png|jpg|gif)$/,
            loader: 'file-loader',
            options: {
                name: '[name].[ext]?[hash]'
            }
        },

        {
            test: /\.(woff2?|ttf|eot|svg)$/,
            loader: 'file-loader',
            options: {
                name: path.relative(__dirname, './fonts/[name].[ext]?[hash]')
            }
        }
    ]
};

if (Mix.sass) {
    module.exports.module.rules.push({
        test: /\.s[ac]ss$/,
        loader: plugins.ExtractTextPlugin.extract({
            fallbackLoader: 'style-loader',
            loader: [
                'css-loader', 'postcss-loader',
                'resolve-url-loader', 'sass-loader?sourceMap'
            ]
        })
    });
}

if (Mix.less) {
    module.exports.module.rules.push({
        test: /\.less$/,
        loader: plugins.ExtractTextPlugin.extract({
            fallbackLoader: 'style-loader',
            loader: ['css-loader', 'postcss-loader', 'less-loader']
        })
    });
}

/*
 |--------------------------------------------------------------------------
 | Resolve
 |--------------------------------------------------------------------------
 |
 | Here, we may set any options/aliases that affect Webpack's resolving
 | of modules. To begin, we will provide the necessary Vue alias to
 | load the Vue common library. You may delete this, if needed.
 |
 */

module.exports.resolve = {
    extensions: ['*', '.js', '.jsx', '.vue'],

    alias: {
        'vue$': 'vue/dist/vue.common.js'
    }
};

/*
 |--------------------------------------------------------------------------
 | Stats
 |--------------------------------------------------------------------------
 |
 | By default, Webpack spits a lot of information out to the terminal,
 | each you time you compile. Let's keep things a bit more minimal
 | and hide a few of those bits and pieces. Adjust as you wish.
 |
 */

module.exports.stats = {
    hash: false,
    version: false,
    timings: false,
    children: false
};

module.exports.performance = { hints: false};

/*
 |--------------------------------------------------------------------------
 | Devtool
 |--------------------------------------------------------------------------
 |
 | Sourcemaps allow us to access our original source code within the
 | browser, even if we're serving a bundled script or stylesheet.
 | You may activate sourcemaps, by adding Mix.sourceMaps().
 |
 */

module.exports.devtool = Mix.sourcemaps;

/*
 |--------------------------------------------------------------------------
 | Webpack Dev Server Configuration
 |--------------------------------------------------------------------------
 |
 | If you want to use that flashy hot module replacement feature, then
 | we've got you covered. Here, we'll set some basic initial config
 | for the Node server. You very likely won't want to edit this.
 |
 */
module.exports.devServer = {
    historyApiFallback: true,
    noInfo: true
};

/*
 |--------------------------------------------------------------------------
 | Plugins
 |--------------------------------------------------------------------------
 |
 | Lastly, we'll register a number of plugins to extend and configure
 | Webpack. To get you started, we've included a handful of useful
 | extensions, for versioning, OS notifications, and much more.
 |
 */

module.exports.plugins = [];

if (Mix.notifications) {
    module.exports.plugins.push(
        new plugins.WebpackNotifierPlugin({
            title: 'Laravel Mix',
            alwaysNotify: true,
            contentImage: 'node_modules/laravel-mix/icons/laravel.png'
        })
    );
}

module.exports.plugins.push(
    function() {
        this.plugin('done', stats => Mix.manifest.write(stats));
    }
);

module.exports.plugins.push(
    new webpack.LoaderOptionsPlugin({
        minimize: Mix.inProduction,
        options: {
            postcss: [
                require('autoprefixer')
            ],
            context: __dirname,
            output: { path: './' }
        }
    })
);

if (Mix.versioning.enabled) {
    Mix.versioning.record();

    module.exports.plugins.push(
        new plugins.WebpackOnBuildPlugin(stats => {
            Mix.versioning.prune(Mix.publicPath);
        })
    );
}

if (Mix.combine || Mix.minify) {
    module.exports.plugins.push(
        new plugins.WebpackOnBuildPlugin(stats => {
            Mix.concatenateAll().minifyAll();
        })
    );
}

if (Mix.copy) {
    Mix.copy.forEach(copy => {
        module.exports.plugins.push(
            new plugins.CopyWebpackPlugin([copy])
        );
    });
}

if (Mix.js.vendor) {
    module.exports.plugins.push(
        new webpack.optimize.CommonsChunkPlugin({
            names: ['vendor', 'manifest']
        })
    );
}

if (Mix.cssPreprocessor) {
    module.exports.plugins.push(
        new plugins.ExtractTextPlugin({
            filename: Mix.cssOutput()
        })
    );
}

if (Mix.inProduction) {
    module.exports.plugins = module.exports.plugins.concat([
        new webpack.DefinePlugin({
            'process.env': {
                NODE_ENV: '"production"'
            }
        }),

        new webpack.optimize.UglifyJsPlugin({
            sourceMap: true,
            compress: {
                warnings: false
            }
        })
    ]);
}
<|endoftext|>


<commit_before>/**
 * Created by Scott on 5/29/2015.
 */
define(['../../lib/jSQL', '../../lib/SQLish'], function(jSQL, SQLish) {
    var app = {
        baseNode: document.getElementById('jato') || document.querySelector('body')
    };

    app.start = function() {
        // Eventually this will be a sample application and tests will be in the tests folder...
        // but for now, testing here allows for rapid development :)

        // make sure auto-publish knows the current URI
        // solves initial load of url, ie. from a bookmark - site.com/index.html#info
        // otherwise, initial url hash is not evaluated
        window.dispatchEvent(new Event('hashchange'));

        var model = SQLish('truth'), // Create model named 'truth'
            collection,
            dataObj = new DataObject(),
            eventName;

        model.addListener('truth', function (e) { // listen to the model for changes
            this.log('truth was heard with args:', e);
        }, console);

        collection = model.add('about');  // add a collection to the model;

        model.addListener('truth.about', function (e) { // listen to the collection for changes
            this.log('truth.about was heard with args:', e);
        }, console);

        eventName = collection.add(dataObj);  // event name to listen for changes to this DataObject

        model.addListener(eventName, function (e) {  // listen to DataObject for changes
            // this === currentTarget
            this.log(eventName + ' was heard with args:', e);
            //this.log('The truth about me is:', e.detail.value);
        }, console); // console was passed as the currentTarget

        model.addListener(eventName + '.me', function (e) { // listen to the 'me' property of the DataObject
            this.log(eventName + '.me was heard with args:', e);
        }, console);

        dataObj.set('Scott Loves Jaime', 'me');

        model.addListener(eventName + '.dataChange', function (e) { // listen to the generic 'dataChange' property of the DataObject
            this.log(eventName + '.dataChange was heard with args:', e);
        }, console);

        dataObj.set('Scott Loves Jaime!');

        // todo: how do I kill off a handler when the element bound to it is destroyed?
        //       especially since the closure on the element keeps it alive (zombie element)

        // todo: drop should get the index from passed in dataObj
        // collection.drop(0);
        // model.dispatch(eventName + '.dataChange', {});

        console.log('---------------------------------------------------------------');

        // TODO: move to tests!!!
        jSQL.create.db('testDB').table('Customers')([
            ['CustomerID', {
                'primary': true,
                'auto': true,
                'domain': /\d+/i,
                'data': [
                    1, 2, 3, 4, 5
                ]
            }],
            ['CustomerName', {
                'domain': /[\w]+/gi,
                'data': [
                    'Alfreds Futterkiste',
                    'Ana Trujillo Emparedados',
                    'Antonio Moreno Taqueria',
                    'Around the Horn',
                    'Berglunds snabbkop'
                ]
            }],
            ['ContactName', {
                'domain': /[\w]+/gi,
                'data': [
                    'Maria Anders',
                    'Ana Trujillo',
                    'Antonio Moreno',
                    'Thomas Hardy',
                    'Christina Berglund'
                ]
            }],
            ['Address', {
                'domain': /.+]/gi,
                'data': [
                    'Obere Str. 57',
                    'Avda. de la Constitucion 2222',
                    'Mataderos 2312',
                    '120 Hanover Sq.',
                    'Berguvsvagen 8'
                ]
            }],
            ['City', {
                'domain': /\d+/i,
                'data': [
                    'Berlin',
                    'Mexico City',
                    'Mexico City',
                    'London',
                    'Hamburg'
                ]
            }],
            ['PostalCode', {
                'domain': /\d+/i,
                'data': [
                    'S-542 35',
                    '05021',
                    '05023',
                    'WA1 1DP',
                    'S-958 22'
                ]
            }],
            ['Country', {
                'domain': /\d+/i,
                'data': [
                    'Germany',
                    'Mexico',
                    'Mexico',
                    'United Kingdom',
                    'Germany'
                ]
            }]
        ]);

        console.log('Running jSQL Tests...');

        var query;

        query = jSQL.select('testDB', {
            'select': 'City',
            'from':   'Customers'
        });

        console.log('SELECT City FROM Customers: ', query);

        query = jSQL.select('testDB', {
            'select': 'City',
            'from':   'Customers',
            'distinct': true
        });

        console.log('SELECT DISTINCT City FROM Customers: ', query);

        query = jSQL.select('testDB', {
            'select': 'City',
            'from':   'Customers',
            'distinct': true,
            'where': [
                {
                    'column': 'Country',
                    'operator': '==',
                    'value': 'Germany'
                }
            ]
        });

        console.log('SELECT DISTINCT City FROM Customers WHERE Country="GERMANY": ', query);

        query = jSQL.select('testDB', {
            'select': 'PostalCode',
            'from':   'Customers',
            'where': [
                {
                    'column': 'Country',
                    'operator': '==',
                    'value': 'Germany'
                }
            ]
        });

        console.log('SELECT PostalCode FROM Customers WHERE Country="GERMANY": ', query);
        /*
         //...working on it...
        query = jSQL.select('testDB', {
            'select': 'PostalCode',
            'from':   'Customers',
            'where': [
                {
                    'column': 'Country',
                    'operator': '==',
                    'value': 'Germany',
                    // AND
                    'and': {
                        'column': 'City',
                        'operator': '==',
                        'value': 'Berlin'
                    }
                },
                // OR
                {
                    'column': 'City',
                    'operator': '==',
                    'value': 'London'
                }
            ]
        });

        console.log('SELECT PostalCode FROM Customers WHERE (Country="Germany" AND City="Berlin") OR City = "London": ', query);
         */
        query = jSQL.select('testDB', {
            'select': '*',
            'from':   'Customers'
        });

        console.log('SELECT * FROM Customers: ', query);

        query = jSQL.select('testDB', {
            'select': 'City',
            'from':   'Customers',
            'where': [
                {
                    'column': 'City',
                    'operator': '==',
                    'value': 'Berlin'
                }
            ]
        });

        console.log('SELECT City FROM Customers WHERE City="Berlin": ', query);

        query = jSQL.select('testDB', {
            'select': 'CustomerID',
            'from':   'Customers',
            'where': [
                {
                    'column': 'CustomerID',
                    'operator': '==',
                    'value': 3
                }
            ]
        });

        console.log('SELECT CustomerID FROM Customers WHERE CustomerID=3: ', query);

        query = jSQL.select('testDB', {
            'select': 'CustomerID, City',
            'from':   'Customers',
            'where': [
                {
                    'column': 'CustomerID',
                    'operator': '==',
                    'value': 4
                }
            ]
        });

        console.log('SELECT CustomerID, City FROM Customers WHERE CustomerID=4: ', query);

    };

    return app;
});<commit_msg>removed old console tests<commit_after>/**
 * Created by Scott on 5/29/2015.
 */
define([], function() {
    var app = {
        baseNode: document.getElementById('jato') || document.querySelector('body')
    };

    app.start = function() {
        // make sure auto-publish knows the current URI
        // solves initial load of url, ie. from a bookmark - site.com/index.html#info
        // otherwise, initial url hash is not evaluated
        window.dispatchEvent(new Event('hashchange'));
    };

    return app;
});<|endoftext|>