<commit_before>/* jslint node:true, plusplus: true */
/* jshint node: true */
"use strict";

var fs = require("fs"),
	fspath = require("path"),
	aws = require("aws-sdk"),
	glob = require("glob"),
	jsonFile = require("jsonfile"),
	console = require("better-console"),
	prettyjson = require("prettyjson");

function event(message, data, errorOrCallback, callbackOrCallbackData, callbackData) {
	var error, callback;
	if (typeof errorOrCallback === "function") {
		error = null;
		callback = errorOrCallback;
		callbackData = callbackOrCallbackData;
	}
	else {
		error = errorOrCallback;
		callback = callbackOrCallbackData;
	}
	if (error) {
		if (!data) data = {};
		data.error = error;
	}
	if (message || data) message = prettyjson.render({ message: message, data: data });
	if (message) console[error ? "error" : "log"](message);
	if (callback) callback(error, callbackData);
}

function getEC2Client(region) {
	return new aws.EC2( { region: region } );
}

function preProcess(regionName, templatePathPatterns, callback) {
	getEC2Client(regionName).describeRegions(function(error, regionsDescription) {
		if (error) event("Unable to get regions", { region: regionName }, error, callback);
		else {
			var regionNames = regionsDescription.Regions.map(function(o) { return o.RegionName; });
			if (!Array.isArray(templatePathPatterns) || templatePathPatterns.length === 0) templatePathPatterns = [ "./*.cloudformation" ];
			templatePathPatterns.forEach(function(templatePathPattern) {
				glob(templatePathPattern, function (error, templatePaths) {
					if (error) event("Unable to find templates", templatePathPatterns, error, callback);
					else {
						var templatesToDo = templatePaths.length,
							templateCallback = function(message, data, error) {
							event(message, data, error);
							if (--templatesToDo === 0) event("Templates completed", { paths: templatePaths }, callback, templatePaths);
						};
						templatePaths.forEach(function(templatePath) {
							var templateName = fspath.basename(templatePath),
								templateInfo = { template : templateName };
							jsonFile.readFile(templatePath, function (error, template) {
								if (error) templateCallback("Unable to read template", templateInfo, error);
								else {
									var originalTemplateString = JSON.stringify(template),
										tasksToDo = 2, // resources + mappings
										taskCallback = function(message, data, error) {
											event(message, data, error);
											if (error) templateCallback("Template not saved", templateInfo);
											else if (--tasksToDo === 0 ) {
												var templateString = JSON.stringify(template),
													templateChanged = (templateString !== originalTemplateString);
												if (templateChanged) jsonFile.writeFile(templatePath, template, function(error) {
													templateCallback(error ? "Unable to save template" : "Template completed", templateInfo, error);
												});
												else templateCallback("Template unchanged", templateInfo);
											}
										};
									event("Pre-processing template", { name : templateName, tasks: tasksToDo });
									var directoryPath = templatePath + ".d",
										resourcesPath = fspath.join(directoryPath, "resources"),
										filePathsPattern = resourcesPath + "/**";
									glob(filePathsPattern, function(error, filePaths) {
										if (error) taskCallback("Unable to get file paths", { pattern : filePathsPattern }, error);
										else {
											var filesToProcess = filePaths.length,
												includeCallback = function(message, data, error) {
													event(message, data, error);
													if (--filesToProcess === 0) taskCallback("Includes completed", templateInfo);
												};
											filePaths.forEach(function(filePath) {
												var includeInfo = { template: templateName, path: filePath },
													pathParts = fspath.relative(resourcesPath, filePath).split(fspath.sep);
												if (pathParts.length <= 1) includeCallback();
												else if (pathParts.some(function(pathPart) { return pathPart === "."; })) includeCallback("Ignoring include as it is a hidden file", includeInfo);
												else {
													var resourceKey = pathParts[0],
														resource = template.Resources[resourceKey],
														match = pathParts[1].match(/^configs|userdata(.ps1|.cmd|.sh|)$/);
													includeInfo.resource = resourceKey;
													if (!match) includeCallback("File must be config or userdata", includeInfo);
													else {
														var includeType, // config or userdata
															configKey,
															configType, // file or command
															userdataWrapper, // powershell, cmd or empty
															ignore;
														switch (match[0]) {
															case "configs":
																includeType = "config";
																if (pathParts.length <=3) {
																	includeCallback();
																	ignore = true;
																}
																else {
																	includeInfo.config = configKey = pathParts[2];
																	var fileOrCommandMatch = pathParts[3].match(/^files|commands$/);
																	if (!fileOrCommandMatch) {
																		includeCallback("Config file must be file or command", includeInfo);
																		ignore = true;
																	}
																	else {
																		switch (fileOrCommandMatch[0]) {
																			case "files":
																				configType = "file";
																				break;
																			case "commands":
																				configType = "command";
																				break;
																		}
																		includeInfo.configType = configType;
																	}
																}
																break;
															default:
																includeType = "userdata";
																switch (match[1]) {
																	case ".ps1":
																		userdataWrapper = "powershell";
																		break;
																	case ".cmd":
																		userdataWrapper = "script";
																		break;
																}
																includeInfo.userdataType = match[1];
														}
														includeInfo.type = includeType;
														if (!ignore) fs.stat(filePath, function(error, stat) {
															if (error) includeCallback("Unable to get file stats", includeInfo, error);
															else if (!stat.isFile()) includeCallback();
															//need to support ignoring of windows system/hidden files here
															else fs.readFile(filePath, "utf8", function (error, contentString) {
																if (error) includeCallback(error.errno === 34 ? "File not found" : "Unable to read file", includeInfo, error);
																else {
																	var includeObject,
																		includeProperty,
																		objectPath = "Resources/" + resourceKey;
																	switch (includeType) {
																		case "config":
																			var keyParts = pathParts.slice(4);
																			switch (configType) {
																				case "file":
																					for (var i = 0; i < keyParts.length; i++) {
																						var original = keyParts[i],
																							working = original;
																						//if first part is e.g. C$, change to C:
																						if (i === 0) {
																							var match = working.match(/^([A-z])\$$/);
																							if (match) working = match[1] + ":";
																						}
																						//if part starts with $$ or $., remove the first $
																						var dollarMatch = working.match(/^\$(\.\$.*)$/);
																						if (dollarMatch) working = dollarMatch[1];
																						if (working !== original) keyParts[i] = working;
																					}
																					includeProperty = "content";
																					break;
																				case "command":
																					includeProperty = "command";
																					break;
																			}
																			var key = keyParts.join("/"),
																				typeKey = configType + "s";
																			includeInfo.key = key;
																			objectPath += "/Metadata/AWS::CloudFormation::Init/" + configKey + "/" + typeKey + "/\"" + key + "\"";
																			includeObject = resource.Metadata["AWS::CloudFormation::Init"][configKey][typeKey][key];
																			break;
																		case "userdata":
																			if (userdataWrapper && contentString) contentString = "<" + userdataWrapper + ">\n" + contentString + "\n</" + userdataWrapper + ">";
																			objectPath += "/Properties";
																			includeObject = resource.Properties;
																			includeProperty = "UserData";
																			break;
																	}
																	includeInfo.objectPath = (objectPath += "/" + includeProperty);
																	if (!includeObject) includeCallback("Template object not found", includeInfo);
																	else {
																		var originalContentString = JSON.stringify(includeObject[includeProperty]),
																			contentArray = [];
																		for (var i = 0; ; true) {
																			var searchString = contentString.substring(i),
																				attRefMatch = searchString.match(/(?:\{\{((?:b64)?ref) ([\w.:]*)\}\})|(?:\{\{(att) (\w*) ([\w.]*)\}\})/),
																				stringToPush = null,
																				j;
																			if (!attRefMatch) stringToPush = searchString;
																			else {
																				j = attRefMatch.index;
																				if (j > 0) stringToPush = searchString.substring(0, j);
																			}
																			if (stringToPush) {
																				var lastIndex = contentArray.length - 1;
																				if (lastIndex > -1) {
																					var last = contentArray[lastIndex];
																					if (typeof last === "string") {
																						contentArray[lastIndex] = last + stringToPush;
																						stringToPush = null;
																					}
																				}
																				if (stringToPush) contentArray.push(stringToPush);
																			}
																			if (!attRefMatch) break;
																			var variableType = (attRefMatch[1] || attRefMatch[3]);
																			if (variableType) {
																				var variable = null;
																				switch (variableType) {
																					case "ref":
																					case "b64ref":
																						variable = { "Ref": attRefMatch[2] };
																						if (variableType === "b64ref") variable = { "Fn::Base64" : variable };
																						break;
																					case "att":
																						variable = { "Fn::GetAtt": [ attRefMatch[4], attRefMatch[5] ] };
																						break;
																				}
																				contentArray.push(variable);
																				var variableInfo = {
																					template: templateName,
																					type: variableType,
																					string: attRefMatch[0],
																					object: JSON.stringify(variable)
																				};
																				event("Variable found", variableInfo);
																			}
																			i += j + attRefMatch[0].length;
																		}
																		includeInfo.variableCount = contentArray.filter(function(o) {
																			return typeof o === "object";
																		}).length;
																		var contentObject = (contentArray.length === 1) ? contentArray[0] : { "Fn::Join": [ "", contentArray ] };
																		if (contentObject && (includeType === "userdata")) contentObject = { "Fn::Base64": contentObject };
																		contentString = JSON.stringify(contentObject);
																		var contentChanged = (contentString !== originalContentString);
																		if (contentChanged) includeObject[includeProperty] = contentObject;
																		includeCallback(contentChanged ? "Include changed" : "Include unchanged", includeInfo);
																	}
																}
															});
														});
													}
												}
											});
										}
									});
									var mappingsPath = fspath.join(directoryPath, "mappings"),
										mappingPathsPattern = mappingsPath + "/*";
									glob(mappingPathsPattern, function(error, mappingPaths) {
										if (error) taskCallback("Unable to get mappings paths", { pattern : mappingPathsPattern }, error);
										else {
											var mappingsToProcess = mappingPaths.length,
												mappingCallback = function(message, data, error) {
												event(message, data, error);
												if (--mappingsToProcess === 0) taskCallback("Mappings completed", templateInfo);
											};
											mappingPaths.forEach(function(mappingPath) {
												var mappingExtension = fspath.extname(mappingPath),
													mappingName = fspath.basename(mappingPath, mappingExtension),
													mappingInfo = {
														template: templateName,
														path: mappingPath,
														name: mappingName
													};
												jsonFile.readFile(mappingPath, function(error, mapping) {
													if (error) {
														if (error.no === 34) taskCallback("Config not found", mappingInfo);
														else taskCallback("Unable to read mapping", mappingInfo, error);
													}
													else {
														var type = mapping.type,
															templateMapping = template.Mappings[mappingName];
														mappingInfo.type = type;
														if (!templateMapping) mappingCallback("Mapping ignored", mappingInfo);
														else {
															switch (mapping.type) {
																case "ami":
																	var ami = mapping.ami,
																		owner = ami.owner,
																		name = ami.name,
																		amiMappingRegionsToDo = regionNames.length,
																		amiMappingRegionCallback = function(message, data, error) {
																			event(message, data, error);
																			if (--amiMappingRegionsToDo === 0) mappingCallback("Regions' AMIs completed", mappingInfo);
																		};
																	mappingInfo.ami = { owner: owner, name: name };
																	regionNames.forEach(function(regionName) {
																		mappingInfo.region = regionName;
																		var amiMappingRegion = templateMapping[regionName],
																			originalAmiId = amiMappingRegion ? amiMappingRegion.ID : null;
																		event("Searching for AMI", mappingInfo);
																		getEC2Client(regionName).describeImages({
																			Owners: [ owner ],
																			Filters: [ { Name: "name", Values: [ name ] } ]
																		}, function(error, amisDescription) {
																			if (error) amiMappingRegionCallback("AMI not found", mappingInfo, error);
																			else {
																				var amis = amisDescription.Images.sort(function(a, b) {
																					a = a.Name;
																					b = b.Name;
																					return (a > b) ? 1 : ((a < b) ? -1 : 0);
																				});
																				if (amis.length === 0) amiMappingRegionCallback("AMI not found", mappingInfo);
																				else {
																					var ami = amis[0],
																						amiId = ami.ImageId,
																						amiChanged = (amiId !== originalAmiId);
																					mappingInfo.ami = {
																						name: ami.Name,
																						id: amiId,
																						owner: ami.ImageOwnerAlias,
																						platform: ami.Platform,
																						architecture: ami.Architecture
																					};
																					if (amiChanged) {
																						if (originalAmiId) mappingInfo.originalAmiId = originalAmiId;
																						templateMapping[regionName] = { ID: amiId };
																					}
																					amiMappingRegionCallback(amiChanged ? "AMI changed" : "AMI unchanged", mappingInfo);
																					delete mappingInfo.amiId;
																					delete mappingInfo.previousAmiId;
																				}
																			}
																		});
																		delete mappingInfo.region;
																	});
																	break;
																default:
																	mappingCallback("Currently, only AMI mappings supported", mappingInfo);
																	break;
															}
														}
													}
												});
											});
										}
									});
								}
							});
						});
					}
				});
			});
		}
	});
}

module.exports = preProcess;
<commit_msg>Renamed includeCallback to fileCallback<commit_after>/* jslint node:true, plusplus: true */
/* jshint node: true */
"use strict";

var fs = require("fs"),
	fspath = require("path"),
	aws = require("aws-sdk"),
	glob = require("glob"),
	jsonFile = require("jsonfile"),
	console = require("better-console"),
	prettyjson = require("prettyjson");

function event(message, data, errorOrCallback, callbackOrCallbackData, callbackData) {
	var error, callback;
	if (typeof errorOrCallback === "function") {
		error = null;
		callback = errorOrCallback;
		callbackData = callbackOrCallbackData;
	}
	else {
		error = errorOrCallback;
		callback = callbackOrCallbackData;
	}
	if (error) {
		if (!data) data = {};
		data.error = error;
	}
	if (message || data) message = prettyjson.render({ message: message, data: data });
	if (message) console[error ? "error" : "log"](message);
	if (callback) callback(error, callbackData);
}

function getEC2Client(region) {
	return new aws.EC2( { region: region } );
}

function preProcess(regionName, templatePathPatterns, callback) {
	getEC2Client(regionName).describeRegions(function(error, regionsDescription) {
		if (error) event("Unable to get regions", { region: regionName }, error, callback);
		else {
			var regionNames = regionsDescription.Regions.map(function(o) { return o.RegionName; });
			if (!Array.isArray(templatePathPatterns) || templatePathPatterns.length === 0) templatePathPatterns = [ "./*.cloudformation" ];
			templatePathPatterns.forEach(function(templatePathPattern) {
				glob(templatePathPattern, function (error, templatePaths) {
					if (error) event("Unable to find templates", templatePathPatterns, error, callback);
					else {
						var templatesToDo = templatePaths.length,
							templateCallback = function(message, data, error) {
							event(message, data, error);
							if (--templatesToDo === 0) event("Templates completed", { paths: templatePaths }, callback, templatePaths);
						};
						templatePaths.forEach(function(templatePath) {
							var templateName = fspath.basename(templatePath),
								templateInfo = { template : templateName };
							jsonFile.readFile(templatePath, function (error, template) {
								if (error) templateCallback("Unable to read template", templateInfo, error);
								else {
									var originalTemplateString = JSON.stringify(template),
										tasksToDo = 2, // resources + mappings
										taskCallback = function(message, data, error) {
											event(message, data, error);
											if (error) templateCallback("Template not saved", templateInfo);
											else if (--tasksToDo === 0 ) {
												var templateString = JSON.stringify(template),
													templateChanged = (templateString !== originalTemplateString);
												if (templateChanged) jsonFile.writeFile(templatePath, template, function(error) {
													templateCallback(error ? "Unable to save template" : "Template completed", templateInfo, error);
												});
												else templateCallback("Template unchanged", templateInfo);
											}
										};
									event("Pre-processing template", { name : templateName, tasks: tasksToDo });
									var directoryPath = templatePath + ".d",
										resourcesPath = fspath.join(directoryPath, "resources"),
										filePathsPattern = resourcesPath + "/**";
									glob(filePathsPattern, function(error, filePaths) {
										if (error) taskCallback("Unable to get file paths", { pattern : filePathsPattern }, error);
										else {
											var filesToProcess = filePaths.length,
												fileCallback = function(message, data, error) {
													event(message, data, error);
													if (--filesToProcess === 0) taskCallback("Includes completed", templateInfo);
												};
											filePaths.forEach(function(filePath) {
												var includeInfo = { template: templateName, path: filePath },
													pathParts = fspath.relative(resourcesPath, filePath).split(fspath.sep);
												if (pathParts.length <= 1) fileCallback();
												else if (pathParts.some(function(pathPart) { return pathPart === "."; })) fileCallback("Ignoring include as it is a hidden file", includeInfo);
												else {
													var resourceKey = pathParts[0],
														resource = template.Resources[resourceKey],
														match = pathParts[1].match(/^configs|userdata(.ps1|.cmd|.sh|)$/);
													includeInfo.resource = resourceKey;
													if (!match) fileCallback("File must be config or userdata", includeInfo);
													else {
														var includeType, // config or userdata
															configKey,
															configType, // file or command
															userdataWrapper, // powershell, cmd or empty
															ignore;
														switch (match[0]) {
															case "configs":
																includeType = "config";
																if (pathParts.length <=3) {
																	fileCallback();
																	ignore = true;
																}
																else {
																	includeInfo.config = configKey = pathParts[2];
																	var fileOrCommandMatch = pathParts[3].match(/^files|commands$/);
																	if (!fileOrCommandMatch) {
																		fileCallback("Config file must be file or command", includeInfo);
																		ignore = true;
																	}
																	else {
																		switch (fileOrCommandMatch[0]) {
																			case "files":
																				configType = "file";
																				break;
																			case "commands":
																				configType = "command";
																				break;
																		}
																		includeInfo.configType = configType;
																	}
																}
																break;
															default:
																includeType = "userdata";
																switch (match[1]) {
																	case ".ps1":
																		userdataWrapper = "powershell";
																		break;
																	case ".cmd":
																		userdataWrapper = "script";
																		break;
																}
																includeInfo.userdataType = match[1];
														}
														includeInfo.type = includeType;
														if (!ignore) fs.stat(filePath, function(error, stat) {
															if (error) fileCallback("Unable to get file stats", includeInfo, error);
															else if (!stat.isFile()) fileCallback();
															//need to support ignoring of windows system/hidden files here
															else fs.readFile(filePath, "utf8", function (error, contentString) {
																if (error) fileCallback(error.errno === 34 ? "File not found" : "Unable to read file", includeInfo, error);
																else {
																	var includeObject,
																		includeProperty,
																		objectPath = "Resources/" + resourceKey;
																	switch (includeType) {
																		case "config":
																			var keyParts = pathParts.slice(4);
																			switch (configType) {
																				case "file":
																					for (var i = 0; i < keyParts.length; i++) {
																						var original = keyParts[i],
																							working = original;
																						//if first part is e.g. C$, change to C:
																						if (i === 0) {
																							var match = working.match(/^([A-z])\$$/);
																							if (match) working = match[1] + ":";
																						}
																						//if part starts with $$ or $., remove the first $
																						var dollarMatch = working.match(/^\$(\.\$.*)$/);
																						if (dollarMatch) working = dollarMatch[1];
																						if (working !== original) keyParts[i] = working;
																					}
																					includeProperty = "content";
																					break;
																				case "command":
																					includeProperty = "command";
																					break;
																			}
																			var key = keyParts.join("/"),
																				typeKey = configType + "s";
																			includeInfo.key = key;
																			objectPath += "/Metadata/AWS::CloudFormation::Init/" + configKey + "/" + typeKey + "/\"" + key + "\"";
																			includeObject = resource.Metadata["AWS::CloudFormation::Init"][configKey][typeKey][key];
																			break;
																		case "userdata":
																			if (userdataWrapper && contentString) contentString = "<" + userdataWrapper + ">\n" + contentString + "\n</" + userdataWrapper + ">";
																			objectPath += "/Properties";
																			includeObject = resource.Properties;
																			includeProperty = "UserData";
																			break;
																	}
																	includeInfo.objectPath = (objectPath += "/" + includeProperty);
																	if (!includeObject) fileCallback("Template object not found", includeInfo);
																	else {
																		var originalContentString = JSON.stringify(includeObject[includeProperty]),
																			contentArray = [];
																		for (var i = 0; ; true) {
																			var searchString = contentString.substring(i),
																				attRefMatch = searchString.match(/(?:\{\{((?:b64)?ref) ([\w.:]*)\}\})|(?:\{\{(att) (\w*) ([\w.]*)\}\})/),
																				stringToPush = null,
																				j;
																			if (!attRefMatch) stringToPush = searchString;
																			else {
																				j = attRefMatch.index;
																				if (j > 0) stringToPush = searchString.substring(0, j);
																			}
																			if (stringToPush) {
																				var lastIndex = contentArray.length - 1;
																				if (lastIndex > -1) {
																					var last = contentArray[lastIndex];
																					if (typeof last === "string") {
																						contentArray[lastIndex] = last + stringToPush;
																						stringToPush = null;
																					}
																				}
																				if (stringToPush) contentArray.push(stringToPush);
																			}
																			if (!attRefMatch) break;
																			var variableType = (attRefMatch[1] || attRefMatch[3]);
																			if (variableType) {
																				var variable = null;
																				switch (variableType) {
																					case "ref":
																					case "b64ref":
																						variable = { "Ref": attRefMatch[2] };
																						if (variableType === "b64ref") variable = { "Fn::Base64" : variable };
																						break;
																					case "att":
																						variable = { "Fn::GetAtt": [ attRefMatch[4], attRefMatch[5] ] };
																						break;
																				}
																				contentArray.push(variable);
																				var variableInfo = {
																					template: templateName,
																					type: variableType,
																					string: attRefMatch[0],
																					object: JSON.stringify(variable)
																				};
																				event("Variable found", variableInfo);
																			}
																			i += j + attRefMatch[0].length;
																		}
																		includeInfo.variableCount = contentArray.filter(function(o) {
																			return typeof o === "object";
																		}).length;
																		var contentObject = (contentArray.length === 1) ? contentArray[0] : { "Fn::Join": [ "", contentArray ] };
																		if (contentObject && (includeType === "userdata")) contentObject = { "Fn::Base64": contentObject };
																		contentString = JSON.stringify(contentObject);
																		var contentChanged = (contentString !== originalContentString);
																		if (contentChanged) includeObject[includeProperty] = contentObject;
																		fileCallback(contentChanged ? "Include changed" : "Include unchanged", includeInfo);
																	}
																}
															});
														});
													}
												}
											});
										}
									});
									var mappingsPath = fspath.join(directoryPath, "mappings"),
										mappingPathsPattern = mappingsPath + "/*";
									glob(mappingPathsPattern, function(error, mappingPaths) {
										if (error) taskCallback("Unable to get mappings paths", { pattern : mappingPathsPattern }, error);
										else {
											var mappingsToProcess = mappingPaths.length,
												mappingCallback = function(message, data, error) {
												event(message, data, error);
												if (--mappingsToProcess === 0) taskCallback("Mappings completed", templateInfo);
											};
											mappingPaths.forEach(function(mappingPath) {
												var mappingExtension = fspath.extname(mappingPath),
													mappingName = fspath.basename(mappingPath, mappingExtension),
													mappingInfo = {
														template: templateName,
														path: mappingPath,
														name: mappingName
													};
												jsonFile.readFile(mappingPath, function(error, mapping) {
													if (error) {
														if (error.no === 34) taskCallback("Config not found", mappingInfo);
														else taskCallback("Unable to read mapping", mappingInfo, error);
													}
													else {
														var type = mapping.type,
															templateMapping = template.Mappings[mappingName];
														mappingInfo.type = type;
														if (!templateMapping) mappingCallback("Mapping ignored", mappingInfo);
														else {
															switch (mapping.type) {
																case "ami":
																	var ami = mapping.ami,
																		owner = ami.owner,
																		name = ami.name,
																		amiMappingRegionsToDo = regionNames.length,
																		amiMappingRegionCallback = function(message, data, error) {
																			event(message, data, error);
																			if (--amiMappingRegionsToDo === 0) mappingCallback("Regions' AMIs completed", mappingInfo);
																		};
																	mappingInfo.ami = { owner: owner, name: name };
																	regionNames.forEach(function(regionName) {
																		mappingInfo.region = regionName;
																		var amiMappingRegion = templateMapping[regionName],
																			originalAmiId = amiMappingRegion ? amiMappingRegion.ID : null;
																		event("Searching for AMI", mappingInfo);
																		getEC2Client(regionName).describeImages({
																			Owners: [ owner ],
																			Filters: [ { Name: "name", Values: [ name ] } ]
																		}, function(error, amisDescription) {
																			if (error) amiMappingRegionCallback("AMI not found", mappingInfo, error);
																			else {
																				var amis = amisDescription.Images.sort(function(a, b) {
																					a = a.Name;
																					b = b.Name;
																					return (a > b) ? 1 : ((a < b) ? -1 : 0);
																				});
																				if (amis.length === 0) amiMappingRegionCallback("AMI not found", mappingInfo);
																				else {
																					var ami = amis[0],
																						amiId = ami.ImageId,
																						amiChanged = (amiId !== originalAmiId);
																					mappingInfo.ami = {
																						name: ami.Name,
																						id: amiId,
																						owner: ami.ImageOwnerAlias,
																						platform: ami.Platform,
																						architecture: ami.Architecture
																					};
																					if (amiChanged) {
																						if (originalAmiId) mappingInfo.originalAmiId = originalAmiId;
																						templateMapping[regionName] = { ID: amiId };
																					}
																					amiMappingRegionCallback(amiChanged ? "AMI changed" : "AMI unchanged", mappingInfo);
																					delete mappingInfo.amiId;
																					delete mappingInfo.previousAmiId;
																				}
																			}
																		});
																		delete mappingInfo.region;
																	});
																	break;
																default:
																	mappingCallback("Currently, only AMI mappings supported", mappingInfo);
																	break;
															}
														}
													}
												});
											});
										}
									});
								}
							});
						});
					}
				});
			});
		}
	});
}

module.exports = preProcess;
<|endoftext|>


<commit_before>var animating = false,
    position = 0,
    detail = {};

var X_OFFSET = 50,
    Y_OFFSET = 50,
    SLIDE_WIDTH,
    SLIDE_HEIGHT;

$(function(){
    bindSearch();

    if (!$(".slides").length)
        return;

    SLIDE_WIDTH = $(".slide").outerWidth();
    SLIDE_HEIGHT = $(".slide").outerHeight();

    positionSlides();
    bindKeys();
    bindTouch();
});

function positionSlides() {
    var initialPos = -(SLIDE_WIDTH / 2);

    if (window.location.hash) {
        position = parseInt(window.location.hash.slice(1));
        initialPos -= (SLIDE_WIDTH + X_OFFSET) * position;
    }

    $(".slide:not(.continue)").each(function(i){
        $(this).css("margin-left", initialPos);
        $(this).addClass("group-" + i);

        var yPos = (SLIDE_HEIGHT / 2) + Y_OFFSET;
        $(this).nextUntil(":not(.continue)").each(function(){
            $(this).css({
                "margin-left": initialPos,
                "margin-top": yPos
            });

            $(this).addClass("group-" + i);

            yPos += (SLIDE_HEIGHT + Y_OFFSET);
        });

        initialPos += SLIDE_WIDTH + X_OFFSET;
    });
}

function bindKeys() {
    $(document).bind("keydown", "left", function(){
        if (animating || position == 0)
            return;

        $(".slide").each(moveX(1));
    });

    $(document).bind("keydown", "right", function(){
        if (animating || position + 1 == $(".slide:not(.continue)").length)
            return;

        $(".slide").each(moveX(-1));
    });

    $(document).bind("keydown", "up", function(){
        var d = detail[position],
            group = $(".group-" + position);

        if (animating || d == 0)
            return;

        group.each(moveY(1));
    });

    $(document).bind("keydown", "down", function(){
        var d = detail[position],
            group = $(".group-" + position);

        if (animating || d + 1 == group.length)
            return;

        group.each(moveY(-1));
    });
}

function bindTouch() {
    var startX, endX;
    $(".slides").bind("touchstart", function(e){
        startX = e.originalEvent.touches[0].pageX;
    });

    $(".slides").bind("touchmove", function(e){
        endX = e.originalEvent.touches[0].pageX;
        e.preventDefault();
    });

    $(".slides").bind("touchend", function(e){
        if ((startX - endX) > 50)
            $(".slide").each(moveX(-1));
        else if ((endX - startX) > 50)
            $(".slide").each(moveX(1));
    });
}

function bindSearch() {
    $("#search").keyup(function(){
        $(".search-results").empty();

        if (!$("#search").val().length)
            return;

        var q = $("#search").val().toLowerCase();
        $.each(SEARCH_TAGS, function(i, t){
            var k = t[0],
                n = t[1],
                url = t[2];
            if (k.toLowerCase().indexOf(q) != -1)
                $(".search-results").append("
" + n + "
");
        });
    });
}

function moveX(diff) {
    animating = true;
    position -= diff;

    if (detail[position] == undefined)
        detail[position] = 0;

    return function(){
        $(this).animate({
            "margin-left": getLeft($(this)) + ((SLIDE_WIDTH + X_OFFSET) * diff)
        }, {
            "duration": 250,
            "complete": (function(){
                animating = false;
                window.location.hash = position;
            })
        });
    };
}

function moveY(diff) {
    animating = true;
    detail[position] -= diff;
    return function(){
        $(this).animate({
            "margin-top": getTop($(this)) + ((SLIDE_HEIGHT + Y_OFFSET) * diff)
        }, {
            "duration": 250,
            "complete": (function(){
                animating = false;
            })
        });
    };
}

function getLeft(ele) {
    return parseInt(ele.css("margin-left"), 10);
}

function getTop(ele) {
    return parseInt(ele.css("margin-top"), 10);
}<commit_msg>don't add duplicate results, and add exact amtches to the top<commit_after>var animating = false,
    position = 0,
    detail = {};

var X_OFFSET = 50,
    Y_OFFSET = 50,
    SLIDE_WIDTH,
    SLIDE_HEIGHT;

$(function(){
    bindSearch();

    if (!$(".slides").length)
        return;

    SLIDE_WIDTH = $(".slide").outerWidth();
    SLIDE_HEIGHT = $(".slide").outerHeight();

    positionSlides();
    bindKeys();
    bindTouch();
});

function positionSlides() {
    var initialPos = -(SLIDE_WIDTH / 2);

    if (window.location.hash) {
        position = parseInt(window.location.hash.slice(1));
        initialPos -= (SLIDE_WIDTH + X_OFFSET) * position;
    }

    $(".slide:not(.continue)").each(function(i){
        $(this).css("margin-left", initialPos);
        $(this).addClass("group-" + i);

        var yPos = (SLIDE_HEIGHT / 2) + Y_OFFSET;
        $(this).nextUntil(":not(.continue)").each(function(){
            $(this).css({
                "margin-left": initialPos,
                "margin-top": yPos
            });

            $(this).addClass("group-" + i);

            yPos += (SLIDE_HEIGHT + Y_OFFSET);
        });

        initialPos += SLIDE_WIDTH + X_OFFSET;
    });
}

function bindKeys() {
    $(document).bind("keydown", "left", function(){
        if (animating || position == 0)
            return;

        $(".slide").each(moveX(1));
    });

    $(document).bind("keydown", "right", function(){
        if (animating || position + 1 == $(".slide:not(.continue)").length)
            return;

        $(".slide").each(moveX(-1));
    });

    $(document).bind("keydown", "up", function(){
        var d = detail[position],
            group = $(".group-" + position);

        if (animating || d == 0)
            return;

        group.each(moveY(1));
    });

    $(document).bind("keydown", "down", function(){
        var d = detail[position],
            group = $(".group-" + position);

        if (animating || d + 1 == group.length)
            return;

        group.each(moveY(-1));
    });
}

function bindTouch() {
    var startX, endX;
    $(".slides").bind("touchstart", function(e){
        startX = e.originalEvent.touches[0].pageX;
    });

    $(".slides").bind("touchmove", function(e){
        endX = e.originalEvent.touches[0].pageX;
        e.preventDefault();
    });

    $(".slides").bind("touchend", function(e){
        if ((startX - endX) > 50)
            $(".slide").each(moveX(-1));
        else if ((endX - startX) > 50)
            $(".slide").each(moveX(1));
    });
}

function bindSearch() {
    $("#search").keyup(function(){
        $(".search-results").empty();

        if (!$("#search").val().length)
            return;

        var q = $("#search").val().toLowerCase(),
            matches = [];
        $.each(SEARCH_TAGS, function(i, t){
            var k = t[0].toLowerCase(),
                n = t[1],
                url = t[2],
                item = "
" + n + "
";

            if (k.indexOf(q) == -1 || matches.indexOf(url) != -1)
                return;

            matches.push(url);

            if (k == q)
                $(".search-results").prepend(item);
            else
                $(".search-results").append(item);
        });
    });
}

function moveX(diff) {
    animating = true;
    position -= diff;

    if (detail[position] == undefined)
        detail[position] = 0;

    return function(){
        $(this).animate({
            "margin-left": getLeft($(this)) + ((SLIDE_WIDTH + X_OFFSET) * diff)
        }, {
            "duration": 250,
            "complete": (function(){
                animating = false;
                window.location.hash = position;
            })
        });
    };
}

function moveY(diff) {
    animating = true;
    detail[position] -= diff;
    return function(){
        $(this).animate({
            "margin-top": getTop($(this)) + ((SLIDE_HEIGHT + Y_OFFSET) * diff)
        }, {
            "duration": 250,
            "complete": (function(){
                animating = false;
            })
        });
    };
}

function getLeft(ele) {
    return parseInt(ele.css("margin-left"), 10);
}

function getTop(ele) {
    return parseInt(ele.css("margin-top"), 10);
}<|endoftext|>


<commit_before>var _ = require('underscore');
var css = require('css');
var path = require('path');
var sass = require('node-sass');

module.exports.runSass = function (options, describe, it) {
  var sassPath = path.join(__dirname, '..', 'sass');
  if (options.includePaths) {
    options.includePaths.push(sassPath);
  } else {
    options.includePaths = [sassPath];
  }
  var css = sass.renderSync(options).css.toString();
  var modules = parse(css);

  _.each(modules, function (module) {
    describeModule(module, describe, it);
  });
};

var describeModule = function (module, describe, it) {
  var assert = require('assert');
  describe(module.module, function () {
    _.each(module.modules, function (submodule) {
      describeModule(submodule, describe, it);
    });
    _.each(module.tests, function (test) {
      it(test.test, function () {
        _.each(test.assertions, function (assertion) {
          if (!assertion.passed) {
            var msg = (
              assertion.description + ' ("' +
                assertion.output + '" ' +
                assertion.assertionType + ' "' +
                assertion.expected +
                '")'
            );
            assert.fail(
              assertion.output,
              assertion.expected,
              msg,
              assertion.assertionType
            );
          }
        });
      });
    });
  });
};

var parse = module.exports.parse = function (rawCss) {
  var ast = css.parse(rawCss);
  var ctx = { modules: [] };
  var handler = parseModule;

  _.each(ast.stylesheet.rules, function (rule) {
    handler = handler(rule, ctx);
  });

  finishCurrentModule(ctx);

  return ctx.modules;
};

var parseError = function (msg, seeking, pos) {
  throw new Error(
    'Line ' + pos.start.line + ', column ' + pos.start.column + ': ' +
      msg + '; looking for ' + seeking);
}

var parseModule = function (rule, ctx) {
  if (rule.type === 'charset') { return parseModule; }
  if (rule.type === 'comment') {
    var text = rule.comment.trim();
    if (!text) { return parseModule; }
    if (text.substring(0, 10) === '# Module: ') {
      finishCurrentModule(ctx);
      ctx.currentModule = { module: text.substring(10), tests: [] };
      return parseTest;
    }
    if (text.substring(0, 10) === '# SUMMARY ') {
      return ignoreUntilEndSummary;
    }
    parseError('Unexpected comment "' + text + '"', 'module header', rule.position);
  }
  parseError('Unexpected rule type "' + rule.type + '"', 'module header', rule.position);
};

var ignoreUntilEndSummary = function (rule, ctx) {
  if (rule.type === 'comment') {
    var text = rule.comment.trim();
    if (text.substring(0, 10) === '----------') {
      return parseModule;
    }
    return ignoreUntilEndSummary;
  }
  parseError('Unexpected rule type "' + rule.type + '"', 'end summary', rule.position);
};

var parseTest = function (rule, ctx) {
  if (rule.type === 'comment') {
    var text = rule.comment.trim();
    if (!text) { return parseTest; }
    if (text.match(/^-+$/)) {
      return parseTest;
    }
    if (text.substring(0, 6) === 'Test: ') {
      finishCurrentTest(ctx);
      ctx.currentTest = { test: text.substring(6), assertions: [] };
      return parseAssertion;
    }
    return parseModule(rule, ctx);
  }
  parseError('Unexpected rule type "' + rule.type + '"', 'test', rule.position);
};

var parseAssertion = function (rule, ctx) {
  if (rule.type === 'comment') {
    var text = rule.comment.trim();
    if (!text) { return parseAssertion; }
    if (text.substring(0, 2) === '✔ ') {
      finishCurrentAssertion(ctx);
      ctx.currentAssertion = {
        description: text.substring(2),
        passed: true
      };
      return parseAssertion;
    } else if (text.substring(0, 10) === '✖ FAILED [') {
      finishCurrentAssertion(ctx);
      var endAssertionType = text.indexOf(']');
      ctx.currentAssertion = {
        description: text.substring(endAssertionType + 2).trim(),
        passed: false,
        assertionType: text.substring(10, endAssertionType),
      };
      return parseFailureDetail;
    } else if (text.substring(0, 8) === 'ASSERT: ') {
      finishCurrentAssertion(ctx);
      ctx.currentAssertion = {
        description: text.substring(8),
        assertionType: 'equal'
      };
      return parseAssertionOutputStart;
    }
    return parseTest(rule, ctx);
  }
  parseError('Unexpected rule type "' + rule.type + '"', 'assertion', rule.position);
}

var parseFailureDetail = function (rule, ctx) {
  if (rule.type === 'comment') {
    var text = rule.comment.trim();
    if (text.substring(0, 2) === '- ') {
      var startType = text.indexOf('[');
      var endType = text.indexOf(']');
      var type = text.substring(startType, endType + 1);
      var content = text.substring(endType + 3);
      var outputOrExpected;
      if (text.substring(2, startType) === 'Output ') {
        outputOrExpected = 'output';
      } else if (text.substring(2, startType) === 'Expected ') {
        outputOrExpected = 'expected';
      }
      if (outputOrExpected) {
        ctx.currentAssertion[outputOrExpected] = type + ' ' + content;
        return parseFailureDetail;
      }
    }
    return parseAssertion(rule, ctx);
  }
  parseError('Unexpected rule type "' + rule.type + '"', 'output/expected', rule.position);
};

var parseAssertionOutputStart = function (rule, ctx) {
  if (rule.type === 'comment') {
    var text = rule.comment.trim();
    if (!text) { return parseAssertionOutputStart; }
    if (text === 'OUTPUT') {
      ctx.currentOutputRules = [];
      return parseAssertionOutput;
    }
    parseError('Unexpected comment "' + text + '"', 'OUTPUT', rule.position);
  }
  parseError('Unexpected rule type "' + rule.type + '"', 'OUTPUT', rule.position);
};

var parseAssertionOutput = function (rule, ctx) {
  if (rule.type === 'comment') {
    if (rule.comment.trim() === 'END_OUTPUT') {
      ctx.currentAssertion.output = css.stringify(
        { stylesheet: { rules: ctx.currentOutputRules }});
      delete ctx.currentOutputRules;
      return parseAssertionExpectedStart;
    }
  }
  ctx.currentOutputRules.push(rule);
  return parseAssertionOutput;
};

var parseAssertionExpectedStart = function (rule, ctx) {
  if (rule.type === 'comment') {
    var text = rule.comment.trim();
    if (!text) { return parseAssertionExpectedStart; }
    if (text === 'EXPECTED') {
      ctx.currentExpectedRules = [];
      return parseAssertionExpected;
    }
    parseError('Unexpected comment "' + text + '"', 'EXPECTED', rule.position);
  }
  parseError('Unexpected rule type "' + rule.type + '"', 'EXPECTED', rule.position);
};

var parseAssertionExpected = function (rule, ctx) {
  if (rule.type === 'comment') {
    if (rule.comment.trim() === 'END_EXPECTED') {
      ctx.currentAssertion.expected = css.stringify(
        { stylesheet: { rules: ctx.currentExpectedRules }});
      delete ctx.currentExpectedRules;
      ctx.currentAssertion.passed = (
        ctx.currentAssertion.output === ctx.currentAssertion.expected);
      return parseEndAssertion;
    }
  }
  ctx.currentExpectedRules.push(rule);
  return parseAssertionExpected;
};

var parseEndAssertion = function (rule, ctx) {
  if (rule.type === 'comment') {
    var text = rule.comment.trim();
    if (!text) { return parseEndAssertion; }
    if (text === 'END_ASSERT') {
      finishCurrentAssertion(ctx);
      return parseAssertion;
    }
    parseError('Unexpected comment "' + text + '"', 'END_ASSERT', rule.position);
  }
  parseError('Unexpected rule type "' + rule.type + '"', 'END_ASSERT', rule.position);
};

var finishCurrentModule = function (ctx) {
  finishCurrentTest(ctx);
  if (ctx.currentModule) {
    var path = ctx.currentModule.module.split(' :: ');
    ctx.currentModule.module = _.last(path);
    insertModule(path, ctx.currentModule, ctx);
    delete ctx.currentModule;
  }
}

var finishCurrentTest = function (ctx) {
  finishCurrentAssertion(ctx);
  if (ctx.currentTest) {
    ctx.currentModule.tests.push(ctx.currentTest);
    delete ctx.currentTest;
  }
}

var finishCurrentAssertion = function (ctx) {
  if (ctx.currentAssertion) {
    ctx.currentTest.assertions.push(ctx.currentAssertion);
    delete ctx.currentAssertion;
  }
}

var insertModule = function (path, module, ctx) {
  if (!ctx.modules) { ctx.modules = [] };

  if (path.length > 1) {
    var newCtx = _.findWhere(ctx.modules, {module: path[0]});
    if (!newCtx) {
      newCtx = {module: path[0]};
      ctx.modules.push(newCtx);
    }
    insertModule(path.slice(1), module, newCtx);
  } else {
    ctx.modules.push(module);
  }
}
<commit_msg>Have parseError return an error instead of throw it.<commit_after>var _ = require('underscore');
var css = require('css');
var path = require('path');
var sass = require('node-sass');

module.exports.runSass = function (options, describe, it) {
  var sassPath = path.join(__dirname, '..', 'sass');
  if (options.includePaths) {
    options.includePaths.push(sassPath);
  } else {
    options.includePaths = [sassPath];
  }
  var css = sass.renderSync(options).css.toString();
  var modules = parse(css);

  _.each(modules, function (module) {
    describeModule(module, describe, it);
  });
};

var describeModule = function (module, describe, it) {
  var assert = require('assert');
  describe(module.module, function () {
    _.each(module.modules, function (submodule) {
      describeModule(submodule, describe, it);
    });
    _.each(module.tests, function (test) {
      it(test.test, function () {
        _.each(test.assertions, function (assertion) {
          if (!assertion.passed) {
            var msg = (
              assertion.description + ' ("' +
                assertion.output + '" ' +
                assertion.assertionType + ' "' +
                assertion.expected +
                '")'
            );
            assert.fail(
              assertion.output,
              assertion.expected,
              msg,
              assertion.assertionType
            );
          }
        });
      });
    });
  });
};

var parse = module.exports.parse = function (rawCss) {
  var ast = css.parse(rawCss);
  var ctx = { modules: [] };
  var handler = parseModule;

  _.each(ast.stylesheet.rules, function (rule) {
    handler = handler(rule, ctx);
  });

  finishCurrentModule(ctx);

  return ctx.modules;
};

var parseError = function (msg, seeking, pos) {
  return new Error(
    'Line ' + pos.start.line + ', ' +
    'column ' + pos.start.column + ': ' +
    msg + '; ' +
    'looking for ' + seeking
  );
}

var parseModule = function (rule, ctx) {
  if (rule.type === 'charset') { return parseModule; }
  if (rule.type === 'comment') {
    var text = rule.comment.trim();
    if (!text) { return parseModule; }
    if (text.substring(0, 10) === '# Module: ') {
      finishCurrentModule(ctx);
      ctx.currentModule = { module: text.substring(10), tests: [] };
      return parseTest;
    }
    if (text.substring(0, 10) === '# SUMMARY ') {
      return ignoreUntilEndSummary;
    }
    throw parseError('Unexpected comment "' + text + '"', 'module header', rule.position);
  }
  throw parseError('Unexpected rule type "' + rule.type + '"', 'module header', rule.position);
};

var ignoreUntilEndSummary = function (rule, ctx) {
  if (rule.type === 'comment') {
    var text = rule.comment.trim();
    if (text.substring(0, 10) === '----------') {
      return parseModule;
    }
    return ignoreUntilEndSummary;
  }
  throw parseError('Unexpected rule type "' + rule.type + '"', 'end summary', rule.position);
};

var parseTest = function (rule, ctx) {
  if (rule.type === 'comment') {
    var text = rule.comment.trim();
    if (!text) { return parseTest; }
    if (text.match(/^-+$/)) {
      return parseTest;
    }
    if (text.substring(0, 6) === 'Test: ') {
      finishCurrentTest(ctx);
      ctx.currentTest = { test: text.substring(6), assertions: [] };
      return parseAssertion;
    }
    return parseModule(rule, ctx);
  }
  throw parseError('Unexpected rule type "' + rule.type + '"', 'test', rule.position);
};

var parseAssertion = function (rule, ctx) {
  if (rule.type === 'comment') {
    var text = rule.comment.trim();
    if (!text) { return parseAssertion; }
    if (text.substring(0, 2) === '✔ ') {
      finishCurrentAssertion(ctx);
      ctx.currentAssertion = {
        description: text.substring(2),
        passed: true
      };
      return parseAssertion;
    } else if (text.substring(0, 10) === '✖ FAILED [') {
      finishCurrentAssertion(ctx);
      var endAssertionType = text.indexOf(']');
      ctx.currentAssertion = {
        description: text.substring(endAssertionType + 2).trim(),
        passed: false,
        assertionType: text.substring(10, endAssertionType),
      };
      return parseFailureDetail;
    } else if (text.substring(0, 8) === 'ASSERT: ') {
      finishCurrentAssertion(ctx);
      ctx.currentAssertion = {
        description: text.substring(8),
        assertionType: 'equal'
      };
      return parseAssertionOutputStart;
    }
    return parseTest(rule, ctx);
  }
  throw parseError('Unexpected rule type "' + rule.type + '"', 'assertion', rule.position);
}

var parseFailureDetail = function (rule, ctx) {
  if (rule.type === 'comment') {
    var text = rule.comment.trim();
    if (text.substring(0, 2) === '- ') {
      var startType = text.indexOf('[');
      var endType = text.indexOf(']');
      var type = text.substring(startType, endType + 1);
      var content = text.substring(endType + 3);
      var outputOrExpected;
      if (text.substring(2, startType) === 'Output ') {
        outputOrExpected = 'output';
      } else if (text.substring(2, startType) === 'Expected ') {
        outputOrExpected = 'expected';
      }
      if (outputOrExpected) {
        ctx.currentAssertion[outputOrExpected] = type + ' ' + content;
        return parseFailureDetail;
      }
    }
    return parseAssertion(rule, ctx);
  }
  throw parseError('Unexpected rule type "' + rule.type + '"', 'output/expected', rule.position);
};

var parseAssertionOutputStart = function (rule, ctx) {
  if (rule.type === 'comment') {
    var text = rule.comment.trim();
    if (!text) { return parseAssertionOutputStart; }
    if (text === 'OUTPUT') {
      ctx.currentOutputRules = [];
      return parseAssertionOutput;
    }
    throw parseError('Unexpected comment "' + text + '"', 'OUTPUT', rule.position);
  }
  throw parseError('Unexpected rule type "' + rule.type + '"', 'OUTPUT', rule.position);
};

var parseAssertionOutput = function (rule, ctx) {
  if (rule.type === 'comment') {
    if (rule.comment.trim() === 'END_OUTPUT') {
      ctx.currentAssertion.output = css.stringify(
        { stylesheet: { rules: ctx.currentOutputRules }});
      delete ctx.currentOutputRules;
      return parseAssertionExpectedStart;
    }
  }
  ctx.currentOutputRules.push(rule);
  return parseAssertionOutput;
};

var parseAssertionExpectedStart = function (rule, ctx) {
  if (rule.type === 'comment') {
    var text = rule.comment.trim();
    if (!text) { return parseAssertionExpectedStart; }
    if (text === 'EXPECTED') {
      ctx.currentExpectedRules = [];
      return parseAssertionExpected;
    }
    throw parseError('Unexpected comment "' + text + '"', 'EXPECTED', rule.position);
  }
  throw parseError('Unexpected rule type "' + rule.type + '"', 'EXPECTED', rule.position);
};

var parseAssertionExpected = function (rule, ctx) {
  if (rule.type === 'comment') {
    if (rule.comment.trim() === 'END_EXPECTED') {
      ctx.currentAssertion.expected = css.stringify(
        { stylesheet: { rules: ctx.currentExpectedRules }});
      delete ctx.currentExpectedRules;
      ctx.currentAssertion.passed = (
        ctx.currentAssertion.output === ctx.currentAssertion.expected);
      return parseEndAssertion;
    }
  }
  ctx.currentExpectedRules.push(rule);
  return parseAssertionExpected;
};

var parseEndAssertion = function (rule, ctx) {
  if (rule.type === 'comment') {
    var text = rule.comment.trim();
    if (!text) { return parseEndAssertion; }
    if (text === 'END_ASSERT') {
      finishCurrentAssertion(ctx);
      return parseAssertion;
    }
    throw parseError('Unexpected comment "' + text + '"', 'END_ASSERT', rule.position);
  }
  throw parseError('Unexpected rule type "' + rule.type + '"', 'END_ASSERT', rule.position);
};

var finishCurrentModule = function (ctx) {
  finishCurrentTest(ctx);
  if (ctx.currentModule) {
    var path = ctx.currentModule.module.split(' :: ');
    ctx.currentModule.module = _.last(path);
    insertModule(path, ctx.currentModule, ctx);
    delete ctx.currentModule;
  }
}

var finishCurrentTest = function (ctx) {
  finishCurrentAssertion(ctx);
  if (ctx.currentTest) {
    ctx.currentModule.tests.push(ctx.currentTest);
    delete ctx.currentTest;
  }
}

var finishCurrentAssertion = function (ctx) {
  if (ctx.currentAssertion) {
    ctx.currentTest.assertions.push(ctx.currentAssertion);
    delete ctx.currentAssertion;
  }
}

var insertModule = function (path, module, ctx) {
  if (!ctx.modules) { ctx.modules = [] };

  if (path.length > 1) {
    var newCtx = _.findWhere(ctx.modules, {module: path[0]});
    if (!newCtx) {
      newCtx = {module: path[0]};
      ctx.modules.push(newCtx);
    }
    insertModule(path.slice(1), module, newCtx);
  } else {
    ctx.modules.push(module);
  }
}
<|endoftext|>


<commit_before>const getItems = require('./getItems');
const calcCombinations = require('./calcCombinations');
const ora = require('ora');
const colors = require('colors');

const qualityMap = item => parseInt(item.properties.find(property => property.name === 'Quality').values[0][0], 10);
const combinationReducer = (accumulator, combination) => accumulator = accumulator.concat(combination);
const numericSorter = (a,b) => a - b;

const joinString = ', '
const spinnerConfig = {
    interval: 80,
    frames: [
        "⠋",
        "⠙",
        "⠹",
        "⠸",
        "⠼",
        "⠴",
        "⠦",
        "⠧",
        "⠇",
        "⠏"
    ]
};

let spinner = ora({
    text: 'Requesting your stash items.',
    spinner: spinnerConfig
}).start();

getItems().then(items => {
    spinner.stopAndPersist({
        symbol: '✔ '
    });
    spinner = ora({
        text: 'Parsing found items.',
        spinner: spinnerConfig
    }).start();

    const flaskQualities = items.filter(item => /Flask/.test(item.typeLine)).map(qualityMap);
    const gemQualities = items.filter(item => /socket/.test(item.descrText)).map(qualityMap);
    const mapQualities = items.filter(item => /Map/.test(item.typeLine)).map(qualityMap);

    const flaskCombinations = calcCombinations(flaskQualities);
    const gemCombinations = calcCombinations(gemQualities);
    const mapCombinations = calcCombinations(mapQualities);

    spinner.stopAndPersist({
        symbol: '✔ '
    });
    console.log('');

    if (flaskCombinations.length > 0) {
        console.log(`✔  Vendor flasks with those qualities for ${flaskCombinations.length} ` + 'Glassblower\'s Bauble'.green + ':');
        console.log('   ' + flaskCombinations.reduce(combinationReducer, []).sort(numericSorter).join(joinString));
    }

    if (gemCombinations.length > 0) {
        console.log('');
        console.log(`✔  Vendor gems with those qualities for ${gemCombinations.length} ` + 'Gemcutter\'s Prism'.rainbow + ':');
        console.log('   ' + gemCombinations.reduce(combinationReducer, []).sort(numericSorter).join(joinString));
    }

    if (mapCombinations.length > 0) {
        console.log('');
        console.log(`✔  Vendor maps with those qualities for  ${mapCombinations.length} ` + 'Cartographer\'s Chisel'.white + ':');
        console.log('   ' + mapCombinations.reduce(combinationReducer, []).sort(numericSorter).join(joinString));
    }

    if (flaskCombinations.length + gemCombinations.length + mapCombinations.length === 0) {
        console.log('❌  No combinations found!');
    }
})
.catch(error => {
    spinner.stopAndPersist({
        symbol: '❌ ',
        text: error
    });
});
<commit_msg>add failsafe for non-quality items in stash<commit_after>const getItems = require('./getItems');
const calcCombinations = require('./calcCombinations');
const ora = require('ora');
const colors = require('colors');

const qualityMap = item => {
    const qualityProperty = item.properties.find(property => property.name === 'Quality');
    if (!qualityProperty) {
        console.log('This item has no quality!');
        console.log(item.typeLine);
        console.log('');
        return 0;
    }
    return parseInt(qualityProperty.values[0][0], 10);
}
const combinationReducer = (accumulator, combination) => accumulator = accumulator.concat(combination);
const numericSorter = (a,b) => a - b;

const joinString = ', '
const spinnerConfig = {
    interval: 80,
    frames: [
        "⠋",
        "⠙",
        "⠹",
        "⠸",
        "⠼",
        "⠴",
        "⠦",
        "⠧",
        "⠇",
        "⠏"
    ]
};

let spinner = ora({
    text: 'Requesting your stash items.',
    spinner: spinnerConfig
}).start();

getItems().then(items => {
    spinner.stopAndPersist({
        symbol: '✔ '
    });
    spinner = ora({
        text: 'Parsing found items.',
        spinner: spinnerConfig
    }).start();

    const flaskQualities = items.filter(item => /Flask/.test(item.typeLine)).map(qualityMap);
    const gemQualities = items.filter(item => /socket/.test(item.descrText)).map(qualityMap);
    const mapQualities = items.filter(item => /Map/.test(item.typeLine)).map(qualityMap);

    const flaskCombinations = calcCombinations(flaskQualities);
    const gemCombinations = calcCombinations(gemQualities);
    const mapCombinations = calcCombinations(mapQualities);

    spinner.stopAndPersist({
        symbol: '✔ '
    });
    console.log('');

    if (flaskCombinations.length > 0) {
        console.log(`✔  Vendor flasks with those qualities for ${flaskCombinations.length} ` + 'Glassblower\'s Bauble'.green + ':');
        console.log('   ' + flaskCombinations.reduce(combinationReducer, []).sort(numericSorter).join(joinString));
    }

    if (gemCombinations.length > 0) {
        console.log('');
        console.log(`✔  Vendor gems with those qualities for ${gemCombinations.length} ` + 'Gemcutter\'s Prism'.rainbow + ':');
        console.log('   ' + gemCombinations.reduce(combinationReducer, []).sort(numericSorter).join(joinString));
    }

    if (mapCombinations.length > 0) {
        console.log('');
        console.log(`✔  Vendor maps with those qualities for  ${mapCombinations.length} ` + 'Cartographer\'s Chisel'.white + ':');
        console.log('   ' + mapCombinations.reduce(combinationReducer, []).sort(numericSorter).join(joinString));
    }

    if (flaskCombinations.length + gemCombinations.length + mapCombinations.length === 0) {
        console.log('❌  No combinations found!');
    }
})
.catch(error => {
    spinner.stopAndPersist({
        symbol: '❌ ',
        text: error
    });
});
<|endoftext|>


<commit_before>var exec = require('child_process').exec;
var http = require('http-request');
var url = require('url');

exports.getFolderName = function(arg, threadName) {
    if (typeof arg == 'undefined' || arg == "") {
        return threadName;
    } else {
        return arg;
    }
};

exports.generateDownloadFolder = function(folder)
{
    exec('mkdir -p ' + folder, function (err, stdout, stderr) {
        if (err) {
            console.log('Impossible to create the folder. Do you have write access in the current folder ?');
            process.exit(0);
        } else {
            return true;
        }
    });
};

exports.getFullPath = function (folderName, href) {
    var splited = href.split("/");
    var filename = splited[splited.length - 1];

    return "./" + folderName + "/" + filename;
};

exports.downloadFile = function(httpOptions, fullPath)
{
    http.get(httpOptions, fullPath, function (error, result) {
        if (error) {
            console.error(error);
        } else {
            console.log('File downloaded at: ' + result.file);
        }
    });
};

exports.excludePattern = function(links, pattern)
{
    //TODO must finish functions
    for (var i = 0; i < links.length; i++) {
        path = links[i].split('.');

        if (path[path.length - 1] == pattern) {
            links.splice(i, 1);
        }
    }
    return links;
};<commit_msg>undefined entry if excluded<commit_after>var exec = require('child_process').exec;
var http = require('http-request');
var url = require('url');

exports.getFolderName = function(arg, threadName) {
    if (typeof arg == 'undefined' || arg == "") {
        return threadName;
    } else {
        return arg;
    }
};

exports.generateDownloadFolder = function(folder)
{
    exec('mkdir -p ' + folder, function (err, stdout, stderr) {
        if (err) {
            console.log('Impossible to create the folder. Do you have write access in the current folder ?');
            process.exit(0);
        } else {
            return true;
        }
    });
};

exports.getFullPath = function (folderName, href) {
    var splited = href.split("/");
    var filename = splited[splited.length - 1];

    return "./" + folderName + "/" + filename;
};

exports.downloadFile = function(httpOptions, fullPath)
{
    http.get(httpOptions, fullPath, function (error, result) {
        if (error) {
            console.error(error);
        } else {
            console.log('File downloaded at: ' + result.file);
        }
    });
};

exports.excludePattern = function(links, pattern)
{
    //TODO must finish functions
    for (var i = 0; i < links.length; i++) {
        path = links[i].split('.');
        if (path[path.length - 1] == pattern) {
            delete links[i];
        }
    }
    return links;
};<|endoftext|>


<commit_before>var obSvc = require('observer-service');
var prefs = require('preferences-service');
var url = require('url');
var data = require("self").data;
var {Cc, Ci, Cr} = require('chrome');
var eTLDSvc = Cc["@mozilla.org/network/effective-tld-service;1"].
                getService(Ci.nsIEffectiveTLDService);

var baseUrls = prefs.get("collusion.urls",
                         "http://collusion.toolness.org/")
                    .split(",");

var baseHosts = {};

baseUrls.forEach(function(baseUrl) {
  try {
    baseHosts[url.URL(baseUrl).host] = true;
  } catch (e) {}
});

require("page-mod").PageMod({
  include: baseUrls,
  contentScriptWhen: 'start',
  contentScriptFile: data.url("index-content-script.js"),
  onAttach: function(worker) {
    workers.push(worker);
    worker.on("detach", function() {
      workers.splice(workers.indexOf(worker), 1);
    });
    worker.port.on("init", function() {
      worker.port.emit("log", JSON.stringify(log));
    });
  }
});

require("widget").Widget({
  id: "collusion",
  label: "Display Collusion Diagram",
  contentURL: data.url("favicon.ico"),
  onClick: function() {
    require("tabs").open({url: baseUrls[0]});
  }
});

var startTime = new Date();
var log = {};
var workers = [];

function queueInfo(info) {
  if (!(info.domain in log))
    log[info.domain] = {};

  var referrers = log[info.domain];

  if (!(info.referrer in referrers))
    referrers[info.referrer] = [(new Date()) - startTime];

  var types = referrers[info.referrer];

  if (types.indexOf(info.type) == -1) {
    types.push(info.type);
    workers.forEach(function(worker) {
      worker.port.emit("log", JSON.stringify(log));
    });
  }
}

function getDomain(host) {
  try {
    return eTLDSvc.getBaseDomainFromHost(host);
  } catch (e if e.result == Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
    return host;
  } catch (e if e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS) {
    return host;
  }
}

obSvc.add("http-on-examine-response", function(subject, topic, data) {
  var channel = subject.QueryInterface(Ci.nsIHttpChannel),
      type = null,
      cookie = null;

  if (channel.referrer) {
    var referrerDomain = getDomain(channel.referrer.host);
    var domain = getDomain(channel.URI.host);

    // This is a fix for https://github.com/toolness/collusion/issues/7.
    if (channel.referrer.host in baseHosts)
      return;

    if (domain != referrerDomain) {
      try {
        type = subject.getResponseHeader("Content-Type");
      } catch (e if e.result == Cr.NS_ERROR_NOT_AVAILABLE) {}

      try {
        var cookie = subject.getRequestHeader("Cookie");
      } catch (e if e.result == Cr.NS_ERROR_NOT_AVAILABLE) {}

      if (!cookie) {
        try {
          cookie = subject.getResponseHeader("Set-Cookie");
        } catch (e if e.result == Cr.NS_ERROR_NOT_AVAILABLE) {}
      }

      if (cookie)
        queueInfo({
          type: type,
          domain: domain,
          referrer: referrerDomain
        });
    }
  }
});
<commit_msg>the add-on now attaches the page mod to any existing collusion tabs when installed/enabled.<commit_after>var obSvc = require('observer-service');
var prefs = require('preferences-service');
var url = require('url');
var data = require("self").data;
var tabs = require("tabs");
var {Cc, Ci, Cr} = require('chrome');
var eTLDSvc = Cc["@mozilla.org/network/effective-tld-service;1"].
                getService(Ci.nsIEffectiveTLDService);

var baseUrls = prefs.get("collusion.urls",
                         "http://collusion.toolness.org/")
                    .split(",");

var baseHosts = {};

baseUrls.forEach(function(baseUrl) {
  try {
    baseHosts[url.URL(baseUrl).host] = true;
  } catch (e) {}
});

function attachToCollusionPage(worker) {
  workers.push(worker);
  worker.on("detach", function() {
    workers.splice(workers.indexOf(worker), 1);
  });
  worker.port.on("init", function() {
    worker.port.emit("log", JSON.stringify(log));
  });
}

require("page-mod").PageMod({
  include: baseUrls,
  contentScriptWhen: 'start',
  contentScriptFile: data.url("index-content-script.js"),
  onAttach: attachToCollusionPage
});

require("widget").Widget({
  id: "collusion",
  label: "Display Collusion Diagram",
  contentURL: data.url("favicon.ico"),
  onClick: function() {
    tabs.open({url: baseUrls[0]});
  }
});

var startTime = new Date();
var log = {};
var workers = [];

function queueInfo(info) {
  if (!(info.domain in log))
    log[info.domain] = {};

  var referrers = log[info.domain];

  if (!(info.referrer in referrers))
    referrers[info.referrer] = [(new Date()) - startTime];

  var types = referrers[info.referrer];

  if (types.indexOf(info.type) == -1) {
    types.push(info.type);
    workers.forEach(function(worker) {
      worker.port.emit("log", JSON.stringify(log));
    });
  }
}

function getDomain(host) {
  try {
    return eTLDSvc.getBaseDomainFromHost(host);
  } catch (e if e.result == Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
    return host;
  } catch (e if e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS) {
    return host;
  }
}

obSvc.add("http-on-examine-response", function(subject, topic, data) {
  var channel = subject.QueryInterface(Ci.nsIHttpChannel),
      type = null,
      cookie = null;

  if (channel.referrer) {
    var referrerDomain = getDomain(channel.referrer.host);
    var domain = getDomain(channel.URI.host);

    // This is a fix for https://github.com/toolness/collusion/issues/7.
    if (channel.referrer.host in baseHosts)
      return;

    if (domain != referrerDomain) {
      try {
        type = subject.getResponseHeader("Content-Type");
      } catch (e if e.result == Cr.NS_ERROR_NOT_AVAILABLE) {}

      try {
        var cookie = subject.getRequestHeader("Cookie");
      } catch (e if e.result == Cr.NS_ERROR_NOT_AVAILABLE) {}

      if (!cookie) {
        try {
          cookie = subject.getResponseHeader("Set-Cookie");
        } catch (e if e.result == Cr.NS_ERROR_NOT_AVAILABLE) {}
      }

      if (cookie)
        queueInfo({
          type: type,
          domain: domain,
          referrer: referrerDomain
        });
    }
  }
});

function attachToExistingCollusionPages() {
  for each (var tab in tabs)
    baseUrls.forEach(function(baseUrl) {
      if (tab.url == baseUrl) {
        var worker = tab.attach({
          contentScriptFile: data.url("index-content-script.js")
        });
        attachToCollusionPage(worker);
      }
    });
}

attachToExistingCollusionPages();
<|endoftext|>


<commit_before>'use strict'

import { Emitter, CompositeDisposable } from 'atom'
import MinimapElement from './minimap-element'
import Minimap from './minimap'
import config from './config.json'
import * as PluginManagement from './plugin-management'
import { treeSitterWarning } from './performance-monitor'

export * as config from './config.json'
export * from './plugin-management'
export { default as Minimap } from './minimap'

/**
 * The `Minimap` package provides an eagle-eye view of text buffers.
 *
 * It also provides API for plugin packages that want to interact with the
 * minimap and be available to the user through the minimap settings.
 */

/**
     * The activation state of the package.
     *
     * @type {boolean}
     * @access private
     */
let active = false
/**
     * The toggle state of the package.
     *
     * @type {boolean}
     * @access private
     */
let toggled = false
/**
     * The `Map` where Minimap instances are stored with the text editor they
     * target as key.
     *
     * @type {Map}
     * @access private
     */
let editorsMinimaps = null
/**
     * The composite disposable that stores the package's subscriptions.
     *
     * @type {CompositeDisposable}
     * @access private
     */
let subscriptions = null
/**
     * The disposable that stores the package's commands subscription.
     *
     * @type {Disposable}
     * @access private
     */
let subscriptionsOfCommands = null

/**
     * The package's events emitter.
     *
     * @type {Emitter}
     * @access private
     */
export const emitter = new Emitter()

/**
   * Activates the minimap package.
   */
export function activate () {
  if (active) { return }

  subscriptionsOfCommands = atom.commands.add('atom-workspace', {
    'minimap:toggle': () => {
      toggle()
    },
    'minimap:generate-coffee-plugin': async () => {
      await generatePlugin('coffee')
    },
    'minimap:generate-javascript-plugin': async () => {
      await generatePlugin('javascript')
    },
    'minimap:generate-babel-plugin': async () => {
      await generatePlugin('babel')
    }
  })

  editorsMinimaps = new Map()
  subscriptions = new CompositeDisposable()
  active = true

  if (atom.config.get('minimap.autoToggle')) { toggle() }
}

/**
   * Returns a {MinimapElement} for the passed-in model if it's a {Minimap}.
   *
   * @param {*} model the model for which returning a view
   * @return {MinimapElement}
   */
export function minimapViewProvider (model) {
  if (model instanceof Minimap) {
    const element = new MinimapElement()
    element.setModel(model)
    return element
  }
}

/**
   * Deactivates the minimap package.
   */
export function deactivate () {
  if (!active) { return }

  PluginManagement.deactivateAllPlugins()

  if (editorsMinimaps) {
    editorsMinimaps.forEach((value, key) => {
      value.destroy()
      editorsMinimaps.delete(key)
    })
  }

  subscriptions.dispose()
  subscriptions = null
  subscriptionsOfCommands.dispose()
  subscriptionsOfCommands = null
  editorsMinimaps = undefined
  toggled = false
  active = false
}

export function getConfigSchema () {
  return config || atom.packages.getLoadedPackage('minimap').metadata.configSchema
}

/**
   * Toggles the minimap display.
   */
export function toggle () {
  if (!active) { return }

  if (toggled) {
    toggled = false

    if (editorsMinimaps) {
      editorsMinimaps.forEach((value, key) => {
        value.destroy()
        editorsMinimaps.delete(key)
      })
    }
    subscriptions.dispose()
  } else {
    toggled = true
    initSubscriptions()
  }
}

/**
   * Opens the plugin generation view.
   *
   * @param  {string} template the name of the template to use
   */
async function generatePlugin (template) {
  const { default: MinimapPluginGeneratorElement } = await import('./minimap-plugin-generator-element')
  const view = new MinimapPluginGeneratorElement()
  view.template = template
  view.attach()
}

/**
   * Registers a callback to listen to the `did-activate` event of the package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
export function onDidActivate (callback) {
  return emitter.on('did-activate', callback)
}

/**
   * Registers a callback to listen to the `did-deactivate` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
export function onDidDeactivate (callback) {
  return emitter.on('did-deactivate', callback)
}

/**
   * Registers a callback to listen to the `did-create-minimap` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
export function onDidCreateMinimap (callback) {
  return emitter.on('did-create-minimap', callback)
}

/**
   * Registers a callback to listen to the `did-add-plugin` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
export function onDidAddPlugin (callback) {
  return emitter.on('did-add-plugin', callback)
}

/**
   * Registers a callback to listen to the `did-remove-plugin` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
export function onDidRemovePlugin (callback) {
  return emitter.on('did-remove-plugin', callback)
}

/**
   * Registers a callback to listen to the `did-activate-plugin` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
export function onDidActivatePlugin (callback) {
  return emitter.on('did-activate-plugin', callback)
}

/**
   * Registers a callback to listen to the `did-deactivate-plugin` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
export function onDidDeactivatePlugin (callback) {
  return emitter.on('did-deactivate-plugin', callback)
}

/**
   * Registers a callback to listen to the `did-change-plugin-order` event of
   * the package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
export function onDidChangePluginOrder (callback) {
  return emitter.on('did-change-plugin-order', callback)
}

/**
   * Returns the `Minimap` class
   *
   * @return {Function} the `Minimap` class constructor
   */
export function minimapClass () {
  return Minimap
}

/**
   * Returns the `Minimap` object associated to the passed-in
   * `TextEditorElement`.
   *
   * @param  {TextEditorElement} editorElement a text editor element
   * @return {Minimap} the associated minimap
   */
export function minimapForEditorElement (editorElement) {
  if (!editorElement) { return }
  return minimapForEditor(editorElement.getModel())
}

/**
   * Returns the `Minimap` object associated to the passed-in
   * `TextEditor`.
   *
   * @param  {TextEditor} textEditor a text editor
   * @return {Minimap} the associated minimap
   */
export function minimapForEditor (textEditor) {
  if (!textEditor) { return }
  if (!editorsMinimaps) { return }

  let minimap = editorsMinimaps.get(textEditor)

  if (!minimap) {
    minimap = new Minimap({ textEditor })
    editorsMinimaps.set(textEditor, minimap)

    const editorSubscription = textEditor.onDidDestroy(() => {
      const minimaps = editorsMinimaps
      if (minimaps) { minimaps.delete(textEditor) }
      editorSubscription.dispose()
    })
  }

  return minimap
}

/**
   * Returns a new stand-alone {Minimap} for the passed-in `TextEditor`.
   *
   * @param  {TextEditor} textEditor a text editor instance to create
   *                                 a minimap for
   * @return {Minimap} a new stand-alone Minimap for the passed-in editor
   */
export function standAloneMinimapForEditor (textEditor) {
  if (!textEditor) { return }

  return new Minimap({
    textEditor,
    standAlone: true
  })
}

/**
   * Returns the `Minimap` associated to the active `TextEditor`.
   *
   * @return {Minimap} the active Minimap
   */
export function getActiveMinimap () {
  return minimapForEditor(atom.workspace.getActiveTextEditor())
}

/**
   * Calls a function for each present and future minimaps.
   *
   * @param  {function(minimap:Minimap):void} iterator a function to call with
   *                                                   the existing and future
   *                                                   minimaps
   * @return {Disposable} a disposable to unregister the observer
   */
export function observeMinimaps (iterator) {
  if (!iterator) { return }

  if (editorsMinimaps) {
    editorsMinimaps.forEach((minimap) => { iterator(minimap) })
  }
  return onDidCreateMinimap((minimap) => { iterator(minimap) })
}

/**
   * Registers to the `observeTextEditors` method.
   *
   * @access private
   */
function initSubscriptions () {
  subscriptions.add(
    atom.workspace.observeTextEditors((textEditor) => {
      const minimap = minimapForEditor(textEditor)
      const minimapElement = atom.views.getView(minimap)

      emitter.emit('did-create-minimap', minimap)
      minimapElement.attach()
    }),
    treeSitterWarning()
  )
}

// The public exports included in the service:
const MinimapServiceV1 = {
  minimapViewProvider,
  getConfigSchema,
  onDidActivate,
  onDidDeactivate,
  onDidCreateMinimap,
  onDidAddPlugin,
  onDidRemovePlugin,
  onDidActivatePlugin,
  onDidDeactivatePlugin,
  onDidChangePluginOrder,
  minimapClass,
  minimapForEditorElement,
  minimapForEditor,
  standAloneMinimapForEditor,
  getActiveMinimap,
  observeMinimaps,
  registerPlugin: PluginManagement.registerPlugin,
  unregisterPlugin: PluginManagement.unregisterPlugin,
  togglePluginActivation: PluginManagement.togglePluginActivation,
  deactivateAllPlugins: PluginManagement.deactivateAllPlugins,
  activatePlugin: PluginManagement.activatePlugin,
  deactivatePlugin: PluginManagement.deactivatePlugin,
  getPluginsOrder: PluginManagement.getPluginsOrder
}

/**
   * Returns the Minimap main module instance.
   *
   * @return {Main} The Minimap main module instance.
   */
export function provideMinimapServiceV1 () { return MinimapServiceV1 }
<commit_msg>chore: use export default for exporting json<commit_after>'use strict'

import { Emitter, CompositeDisposable } from 'atom'
import MinimapElement from './minimap-element'
import Minimap from './minimap'
import config from './config.json'
import * as PluginManagement from './plugin-management'
import { treeSitterWarning } from './performance-monitor'

export { default as config } from './config.json'
export * from './plugin-management'
export { default as Minimap } from './minimap'

/**
 * The `Minimap` package provides an eagle-eye view of text buffers.
 *
 * It also provides API for plugin packages that want to interact with the
 * minimap and be available to the user through the minimap settings.
 */

/**
     * The activation state of the package.
     *
     * @type {boolean}
     * @access private
     */
let active = false
/**
     * The toggle state of the package.
     *
     * @type {boolean}
     * @access private
     */
let toggled = false
/**
     * The `Map` where Minimap instances are stored with the text editor they
     * target as key.
     *
     * @type {Map}
     * @access private
     */
let editorsMinimaps = null
/**
     * The composite disposable that stores the package's subscriptions.
     *
     * @type {CompositeDisposable}
     * @access private
     */
let subscriptions = null
/**
     * The disposable that stores the package's commands subscription.
     *
     * @type {Disposable}
     * @access private
     */
let subscriptionsOfCommands = null

/**
     * The package's events emitter.
     *
     * @type {Emitter}
     * @access private
     */
export const emitter = new Emitter()

/**
   * Activates the minimap package.
   */
export function activate () {
  if (active) { return }

  subscriptionsOfCommands = atom.commands.add('atom-workspace', {
    'minimap:toggle': () => {
      toggle()
    },
    'minimap:generate-coffee-plugin': async () => {
      await generatePlugin('coffee')
    },
    'minimap:generate-javascript-plugin': async () => {
      await generatePlugin('javascript')
    },
    'minimap:generate-babel-plugin': async () => {
      await generatePlugin('babel')
    }
  })

  editorsMinimaps = new Map()
  subscriptions = new CompositeDisposable()
  active = true

  if (atom.config.get('minimap.autoToggle')) { toggle() }
}

/**
   * Returns a {MinimapElement} for the passed-in model if it's a {Minimap}.
   *
   * @param {*} model the model for which returning a view
   * @return {MinimapElement}
   */
export function minimapViewProvider (model) {
  if (model instanceof Minimap) {
    const element = new MinimapElement()
    element.setModel(model)
    return element
  }
}

/**
   * Deactivates the minimap package.
   */
export function deactivate () {
  if (!active) { return }

  PluginManagement.deactivateAllPlugins()

  if (editorsMinimaps) {
    editorsMinimaps.forEach((value, key) => {
      value.destroy()
      editorsMinimaps.delete(key)
    })
  }

  subscriptions.dispose()
  subscriptions = null
  subscriptionsOfCommands.dispose()
  subscriptionsOfCommands = null
  editorsMinimaps = undefined
  toggled = false
  active = false
}

export function getConfigSchema () {
  return config || atom.packages.getLoadedPackage('minimap').metadata.configSchema
}

/**
   * Toggles the minimap display.
   */
export function toggle () {
  if (!active) { return }

  if (toggled) {
    toggled = false

    if (editorsMinimaps) {
      editorsMinimaps.forEach((value, key) => {
        value.destroy()
        editorsMinimaps.delete(key)
      })
    }
    subscriptions.dispose()
  } else {
    toggled = true
    initSubscriptions()
  }
}

/**
   * Opens the plugin generation view.
   *
   * @param  {string} template the name of the template to use
   */
async function generatePlugin (template) {
  const { default: MinimapPluginGeneratorElement } = await import('./minimap-plugin-generator-element')
  const view = new MinimapPluginGeneratorElement()
  view.template = template
  view.attach()
}

/**
   * Registers a callback to listen to the `did-activate` event of the package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
export function onDidActivate (callback) {
  return emitter.on('did-activate', callback)
}

/**
   * Registers a callback to listen to the `did-deactivate` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
export function onDidDeactivate (callback) {
  return emitter.on('did-deactivate', callback)
}

/**
   * Registers a callback to listen to the `did-create-minimap` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
export function onDidCreateMinimap (callback) {
  return emitter.on('did-create-minimap', callback)
}

/**
   * Registers a callback to listen to the `did-add-plugin` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
export function onDidAddPlugin (callback) {
  return emitter.on('did-add-plugin', callback)
}

/**
   * Registers a callback to listen to the `did-remove-plugin` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
export function onDidRemovePlugin (callback) {
  return emitter.on('did-remove-plugin', callback)
}

/**
   * Registers a callback to listen to the `did-activate-plugin` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
export function onDidActivatePlugin (callback) {
  return emitter.on('did-activate-plugin', callback)
}

/**
   * Registers a callback to listen to the `did-deactivate-plugin` event of the
   * package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
export function onDidDeactivatePlugin (callback) {
  return emitter.on('did-deactivate-plugin', callback)
}

/**
   * Registers a callback to listen to the `did-change-plugin-order` event of
   * the package.
   *
   * @param  {function(event:Object):void} callback the callback function
   * @return {Disposable} a disposable to stop listening to the event
   */
export function onDidChangePluginOrder (callback) {
  return emitter.on('did-change-plugin-order', callback)
}

/**
   * Returns the `Minimap` class
   *
   * @return {Function} the `Minimap` class constructor
   */
export function minimapClass () {
  return Minimap
}

/**
   * Returns the `Minimap` object associated to the passed-in
   * `TextEditorElement`.
   *
   * @param  {TextEditorElement} editorElement a text editor element
   * @return {Minimap} the associated minimap
   */
export function minimapForEditorElement (editorElement) {
  if (!editorElement) { return }
  return minimapForEditor(editorElement.getModel())
}

/**
   * Returns the `Minimap` object associated to the passed-in
   * `TextEditor`.
   *
   * @param  {TextEditor} textEditor a text editor
   * @return {Minimap} the associated minimap
   */
export function minimapForEditor (textEditor) {
  if (!textEditor) { return }
  if (!editorsMinimaps) { return }

  let minimap = editorsMinimaps.get(textEditor)

  if (!minimap) {
    minimap = new Minimap({ textEditor })
    editorsMinimaps.set(textEditor, minimap)

    const editorSubscription = textEditor.onDidDestroy(() => {
      const minimaps = editorsMinimaps
      if (minimaps) { minimaps.delete(textEditor) }
      editorSubscription.dispose()
    })
  }

  return minimap
}

/**
   * Returns a new stand-alone {Minimap} for the passed-in `TextEditor`.
   *
   * @param  {TextEditor} textEditor a text editor instance to create
   *                                 a minimap for
   * @return {Minimap} a new stand-alone Minimap for the passed-in editor
   */
export function standAloneMinimapForEditor (textEditor) {
  if (!textEditor) { return }

  return new Minimap({
    textEditor,
    standAlone: true
  })
}

/**
   * Returns the `Minimap` associated to the active `TextEditor`.
   *
   * @return {Minimap} the active Minimap
   */
export function getActiveMinimap () {
  return minimapForEditor(atom.workspace.getActiveTextEditor())
}

/**
   * Calls a function for each present and future minimaps.
   *
   * @param  {function(minimap:Minimap):void} iterator a function to call with
   *                                                   the existing and future
   *                                                   minimaps
   * @return {Disposable} a disposable to unregister the observer
   */
export function observeMinimaps (iterator) {
  if (!iterator) { return }

  if (editorsMinimaps) {
    editorsMinimaps.forEach((minimap) => { iterator(minimap) })
  }
  return onDidCreateMinimap((minimap) => { iterator(minimap) })
}

/**
   * Registers to the `observeTextEditors` method.
   *
   * @access private
   */
function initSubscriptions () {
  subscriptions.add(
    atom.workspace.observeTextEditors((textEditor) => {
      const minimap = minimapForEditor(textEditor)
      const minimapElement = atom.views.getView(minimap)

      emitter.emit('did-create-minimap', minimap)
      minimapElement.attach()
    }),
    treeSitterWarning()
  )
}

// The public exports included in the service:
const MinimapServiceV1 = {
  minimapViewProvider,
  getConfigSchema,
  onDidActivate,
  onDidDeactivate,
  onDidCreateMinimap,
  onDidAddPlugin,
  onDidRemovePlugin,
  onDidActivatePlugin,
  onDidDeactivatePlugin,
  onDidChangePluginOrder,
  minimapClass,
  minimapForEditorElement,
  minimapForEditor,
  standAloneMinimapForEditor,
  getActiveMinimap,
  observeMinimaps,
  registerPlugin: PluginManagement.registerPlugin,
  unregisterPlugin: PluginManagement.unregisterPlugin,
  togglePluginActivation: PluginManagement.togglePluginActivation,
  deactivateAllPlugins: PluginManagement.deactivateAllPlugins,
  activatePlugin: PluginManagement.activatePlugin,
  deactivatePlugin: PluginManagement.deactivatePlugin,
  getPluginsOrder: PluginManagement.getPluginsOrder
}

/**
   * Returns the Minimap main module instance.
   *
   * @return {Main} The Minimap main module instance.
   */
export function provideMinimapServiceV1 () { return MinimapServiceV1 }
<|endoftext|>