<commit_before>const Transform = require('./transform');

module.exports = (function() {
	let exports = {};

	let buildMeshData = (json, meshIndex, buffers) => {
		let meshData = {};

		// TODO: Load TANGENT, JOINTS_n & WEIGHTS_n once supported by Fury.Mesh
		// c.f. https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#meshes-overview

		let primitive = json.meshes[meshIndex].primitives[0];
		// TODO: Consider support for multiple primitives
		// Note createSceneHierarchy has single primitive assumption baked

		let attributes = primitive.attributes;
		let positionIndex = attributes.POSITION;	// index into accessors
		let normalsIndex = attributes.NORMAL;		// index into accessors
		let uvIndex = attributes.TEXCOORD_0;		// index into accessors
		// TODO: Load all sets of texture coordinates
		let colorIndices = [];

		let propertyName = "COLOR_";
		let propertyNameIndex = 0;
		while (attributes.hasOwnProperty(propertyName + propertyNameIndex)) {
			colorIndices.push(attributes[propertyName + propertyNameIndex]);
			propertyNameIndex++;
		}

		let indicesIndex = primitive.indices;
		// ^^ I think this is the index and not the index count, should check with a more complex / varied model

		// Calculate bounding radius
		let max = json.accessors[positionIndex].max;
		let min = json.accessors[positionIndex].min;
		let maxPointSqrDistance = max[0]*max[0] + max[1]*max[1] + max[2]*max[2];
		let minPointSqrDistance = min[0]*min[0] + min[1]*min[1] + min[2]*min[2];
		meshData.boundingRadius = Math.sqrt(Math.max(maxPointSqrDistance, minPointSqrDistance));

		let vertexCount = json.accessors[positionIndex].count;
		let positionBufferView = json.bufferViews[json.accessors[positionIndex].bufferView];

		let indexCount = json.accessors[indicesIndex].count;
		let indicesBufferView = json.bufferViews[json.accessors[indicesIndex].bufferView];

		let normalsCount, uvCount;
		let normalsBufferView, uvBufferView;

		if (normalsIndex !== undefined) {
			normalsCount = json.accessors[normalsIndex].count;
			normalsBufferView = json.bufferViews[json.accessors[normalsIndex].bufferView];
		}

		if (uvIndex !== undefined) {
			uvCount = json.accessors[uvIndex].count;
			uvBufferView = json.bufferViews[json.accessors[uvIndex].bufferView];
		}

		let colorsCounts = [];
		let colorsBufferViews = [];

		for (let i = 0, l = colorIndices.length; i < l; i++) {
			let colorIndex = colorIndices[i];
			let accessor = json.accessors[colorIndex];
			colorsCounts[i] = accessor.count;
			colorsBufferViews[i] = json.bufferViews[accessor.bufferView];
		}

		// TODO: pick typedarray type from accessors[index].componentType (5126 => Float32, 5123 => Int16 - see renderer.DataType)
		// TODO: Get size from data from accessors[index].type rather than hardcoding
		meshData.vertices = new Float32Array(buffers[positionBufferView.buffer], positionBufferView.byteOffset, vertexCount * 3);

		if (normalsIndex !== undefined) {
			meshData.normals = new Float32Array(buffers[normalsBufferView.buffer], normalsBufferView.byteOffset, normalsCount * 3);
		}

		if (uvIndex !== undefined) {
			meshData.textureCoordinates = new Float32Array(buffers[uvBufferView.buffer], uvBufferView.byteOffset, uvCount * 2);
		}

		meshData.indices = new Int16Array(buffers[indicesBufferView.buffer], indicesBufferView.byteOffset, indexCount);

		if(colorIndices.length > 0) {
			meshData.customAttributes = [];
			// Assumed componentType = 5126 => Float32, type = "VEC4" => count * 4
			for (let i = 0, l = colorIndices.length; i < l; i++) {
				let name = "COLOR_" + i;
				meshData[name] = new Float32Array(buffers[colorsBufferViews[i].buffer], colorsBufferViews[i].byteOffset, colorsCounts[i] * 4);
				meshData.customAttributes.push({ name: name, source: name, size: 4 });
			}
		}

		return meshData;
	};

	let calculateArrayLength = (accessor) => {
		switch(accessor.type) {
			case "VEC4":
				return 4 * accessor.count;
			case "VEC3":
				return 3 * accessor.count;
			case "VEC2":
				return 2 * accessor.count;
			default: // "SCALAR"
				return accessor.count;
		}
	};

	let buildAnimationData = (out, json, animationIndex, buffers, nodeList) => {
		let animation = json.animations[animationIndex];
		let result = {};

		result.name = animation.name;
		result.channels = [];
		result.duration = 0;

		for (let i = 0, l = animation.channels.length; i < l; i++) {
			let channel = animation.channels[i];

			let sampler = animation.samplers[channel.sampler];
			let times = []; // accessors at sampler.input
			let values = []; // accessors at sampler.output

			let timesAccessor = json.accessors[sampler.input];
			let timesBufferView = json.bufferViews[timesAccessor.bufferView];
			let valuesAccesor = json.accessors[sampler.output];
			let valuesBufferView = json.bufferViews[valuesAccesor.bufferView];

			result.duration = Math.max(result.duration, timesAccessor.max[0]);

			// Could assert target.path against accessor.type (translation => VEC3, rotation => VEC4, scale => VEC3)

			// Assuming float for now, should read from accessor.componentType
			// Note: needs transforming to float as per https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#animations
			times = new Float32Array(buffers[timesBufferView.buffer], timesBufferView.byteOffset, calculateArrayLength(timesAccessor));
			values = new Float32Array(buffers[valuesBufferView.buffer], valuesBufferView.byteOffset, calculateArrayLength(valuesAccesor));

			result.channels[i] = {
				type: channel.target.path,
				transform: nodeList[channel.target.node].transform,
				times: times,
				values: values,
				interpolation: sampler.interpolation
			};
		}

		out[animation.name] = result;
	};

	let createSceneHierarchy = (out, json, index, parent) => {
		let nodes = json.nodes;
		let { name, mesh, children, translation, rotation, scale } = nodes[index];

		let result = {};

		result.name = name;
		result.modelMeshIndex = mesh;
		if (!isNaN(mesh)) {
			result.modelMaterialIndex = json.meshes[mesh].primitives[0].material;
		}

		result.transform = Transform.create({
			position: translation,
			rotation: rotation,
			scale: scale
		});

		if (parent) {
			result.transform.parent = parent.transform;
		}
		result.transform.children = [];
		result.children = [];

		if (children) {
			for (let i = 0, l = children.length; i < l; i++) {
				let childNode = createSceneHierarchy(
					out,
					json,
					children[i],
					result);
				result.children.push(childNode);
				result.transform.children.push(childNode.transform);
			}
		}

		out[index] = result;

		return result;
	};

	// Takes a URI of a glTF file to load
	// Returns an object containing an array meshdata ready for use with Fury.Mesh
	// As well as an array of images to use in material creation
	// Includes a cut down set of information on materialData and textureData arrays
	// however these are not ready to be used with Fury.Material and Fury.Texture
	// and must be manipulated further
	// Might be sensible to separate the asset load and format conversions
	exports.load = (uri, callback) => {
		// TODO: Check file extension, only gltf currently supported
		// https://github.com/KhronosGroup/glTF -> https://github.com/KhronosGroup/glTF/tree/master/specification/2.0
		// https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/figures/gltfOverview-2.0.0b.png
		// https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html
		fetch(uri).then((response) => {
			return response.json();
		}).then((json) => {

			let model = { meshData: [], images: [], materialData: [], textureData: [] };
			let arrayBuffers = [];
			let nodeList = [];

			let assetsLoading = 0;
			let onAssetLoadComplete = () => {
				assetsLoading--;
				if (assetsLoading == 0) {
					// All buffers loaded so build mesh data
					for(let i = 0, l = json.meshes.length; i < l; i++) {
						model.meshData[i] = buildMeshData(json, i, arrayBuffers)
					}

					if (json.animations && json.animations.length) {
						model.animations = {};
						for (let i = 0, l = json.animations.length; i < l; i++) {
							buildAnimationData(model.animations, json, i, arrayBuffers, nodeList);
						}
					}
					callback(model);
				}
			};

			for (let i = 0, l = json.buffers.length; i < l; i++) {
				assetsLoading++;
				fetch(json.buffers[i].uri).then((response) => {
					return response.arrayBuffer();
				}).then((buffer) => {
					arrayBuffers[i] = buffer;
					onAssetLoadComplete();
				}).catch((error) => {
					console.error(error);
					console.error("Unable to fetch data buffer from model");
				});
			}

			// Hierarchy information
			if (json.scenes && json.scenes.length) {
				let scene = json.scenes[json.scene]; // Only one scene currently supported
				let nodeIndex = scene.nodes[0]; // Expect single scene node
				model.hierarchy = createSceneHierarchy(nodeList, json, nodeIndex);
			}

			if (json.materials && json.materials.length) {
				// As PBR is not supported flatten the material structure
				for (let i = 0, l = json.materials.length; i < l; i++) {
					let textureIndex = -1;
					let material = json.materials[i];
					if (material.pbrMetallicRoughness
						&& material.pbrMetallicRoughness.baseColorTexture) {
						let index = material.pbrMetallicRoughness.baseColorTexture.index;
						textureIndex = !isNaN(index) ? index : -1;
					}

					// Only texture index is currently relevant
					model.materialData[i] = {
						textureIndex: textureIndex
					};
				}
			}

			if (json.textures && json.textures.length) {
				for (let i = 0, l = json.textures.length; i < l; i++) {
					let { sampler, source, name } = json.textures[i];
					// samplers not currently supported, so just put name and imageIndex
					model.textureData[i] = {
						name: name,
						imageIndex: source
					};
				}
			}

			if (json.images && json.images.length) {
				for (let i = 0, l = json.images.length; i < l; i++) {
					assetsLoading++;
					fetch(json.images[i].uri).then(response => response.blob()).then(blob => {
						let image = new Image();
						image.src = URL.createObjectURL(blob);
						// Note if we wanted to unload the model
						// we would need to call URL.revokeObjectURL(image.src)
						image.decode().then(() => {
							model.images[i] = image;
							onAssetLoadComplete();
						}).catch((error) => {
							console.error(error);
							console.error("Unable to decode provide image data");
						});
					}).catch((error) => {
						console.error(error);
						console.error("Unable to fetch image data from model");
					});
				}
			}
		}).catch((error) => {
			console.error(error);
			console.error("Unable to load model at " + uri);
		});
	};

	return exports;
})();
<commit_msg>Removed animation dependency on hierarchy<commit_after>const Transform = require('./transform');

module.exports = (function() {
	let exports = {};

	let buildMeshData = (json, meshIndex, buffers) => {
		let meshData = {};

		// TODO: Load TANGENT, JOINTS_n & WEIGHTS_n once supported by Fury.Mesh
		// c.f. https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#meshes-overview

		let primitive = json.meshes[meshIndex].primitives[0];
		// TODO: Consider support for multiple primitives
		// Note createSceneHierarchy has single primitive assumption baked

		let attributes = primitive.attributes;
		let positionIndex = attributes.POSITION;	// index into accessors
		let normalsIndex = attributes.NORMAL;		// index into accessors
		let uvIndex = attributes.TEXCOORD_0;		// index into accessors
		// TODO: Load all sets of texture coordinates
		let colorIndices = [];

		let propertyName = "COLOR_";
		let propertyNameIndex = 0;
		while (attributes.hasOwnProperty(propertyName + propertyNameIndex)) {
			colorIndices.push(attributes[propertyName + propertyNameIndex]);
			propertyNameIndex++;
		}

		let indicesIndex = primitive.indices;
		// ^^ I think this is the index and not the index count, should check with a more complex / varied model

		// Calculate bounding radius
		let max = json.accessors[positionIndex].max;
		let min = json.accessors[positionIndex].min;
		let maxPointSqrDistance = max[0]*max[0] + max[1]*max[1] + max[2]*max[2];
		let minPointSqrDistance = min[0]*min[0] + min[1]*min[1] + min[2]*min[2];
		meshData.boundingRadius = Math.sqrt(Math.max(maxPointSqrDistance, minPointSqrDistance));

		let vertexCount = json.accessors[positionIndex].count;
		let positionBufferView = json.bufferViews[json.accessors[positionIndex].bufferView];

		let indexCount = json.accessors[indicesIndex].count;
		let indicesBufferView = json.bufferViews[json.accessors[indicesIndex].bufferView];

		let normalsCount, uvCount;
		let normalsBufferView, uvBufferView;

		if (normalsIndex !== undefined) {
			normalsCount = json.accessors[normalsIndex].count;
			normalsBufferView = json.bufferViews[json.accessors[normalsIndex].bufferView];
		}

		if (uvIndex !== undefined) {
			uvCount = json.accessors[uvIndex].count;
			uvBufferView = json.bufferViews[json.accessors[uvIndex].bufferView];
		}

		let colorsCounts = [];
		let colorsBufferViews = [];

		for (let i = 0, l = colorIndices.length; i < l; i++) {
			let colorIndex = colorIndices[i];
			let accessor = json.accessors[colorIndex];
			colorsCounts[i] = accessor.count;
			colorsBufferViews[i] = json.bufferViews[accessor.bufferView];
		}

		// TODO: pick typedarray type from accessors[index].componentType (5126 => Float32, 5123 => Int16 - see renderer.DataType)
		// TODO: Get size from data from accessors[index].type rather than hardcoding
		meshData.vertices = new Float32Array(buffers[positionBufferView.buffer], positionBufferView.byteOffset, vertexCount * 3);

		if (normalsIndex !== undefined) {
			meshData.normals = new Float32Array(buffers[normalsBufferView.buffer], normalsBufferView.byteOffset, normalsCount * 3);
		}

		if (uvIndex !== undefined) {
			meshData.textureCoordinates = new Float32Array(buffers[uvBufferView.buffer], uvBufferView.byteOffset, uvCount * 2);
		}

		meshData.indices = new Int16Array(buffers[indicesBufferView.buffer], indicesBufferView.byteOffset, indexCount);

		if(colorIndices.length > 0) {
			meshData.customAttributes = [];
			// Assumed componentType = 5126 => Float32, type = "VEC4" => count * 4
			for (let i = 0, l = colorIndices.length; i < l; i++) {
				let name = "COLOR_" + i;
				meshData[name] = new Float32Array(buffers[colorsBufferViews[i].buffer], colorsBufferViews[i].byteOffset, colorsCounts[i] * 4);
				meshData.customAttributes.push({ name: name, source: name, size: 4 });
			}
		}

		return meshData;
	};

	let calculateArrayLength = (accessor) => {
		switch(accessor.type) {
			case "VEC4":
				return 4 * accessor.count;
			case "VEC3":
				return 3 * accessor.count;
			case "VEC2":
				return 2 * accessor.count;
			default: // "SCALAR"
				return accessor.count;
		}
	};

	let buildAnimationData = (out, json, animationIndex, buffers) => {
		let animation = json.animations[animationIndex];
		let result = {};

		result.name = animation.name;
		result.channels = [];
		result.duration = 0;

		for (let i = 0, l = animation.channels.length; i < l; i++) {
			let channel = animation.channels[i];

			let sampler = animation.samplers[channel.sampler];
			let times = []; // accessors at sampler.input
			let values = []; // accessors at sampler.output

			let timesAccessor = json.accessors[sampler.input];
			let timesBufferView = json.bufferViews[timesAccessor.bufferView];
			let valuesAccesor = json.accessors[sampler.output];
			let valuesBufferView = json.bufferViews[valuesAccesor.bufferView];

			result.duration = Math.max(result.duration, timesAccessor.max[0]);

			// Could assert target.path against accessor.type (translation => VEC3, rotation => VEC4, scale => VEC3)

			// Assuming float for now, should read from accessor.componentType
			// Note: needs transforming to float as per https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#animations
			times = new Float32Array(buffers[timesBufferView.buffer], timesBufferView.byteOffset, calculateArrayLength(timesAccessor));
			values = new Float32Array(buffers[valuesBufferView.buffer], valuesBufferView.byteOffset, calculateArrayLength(valuesAccesor));

			result.channels[i] = {
				type: channel.target.path,
				node: channel.target.node,
				times: times,
				values: values,
				interpolation: sampler.interpolation
			};
		}

		out[animation.name] = result;
	};

	let createSceneHierarchy = (json, index, parent) => {
		let nodes = json.nodes;
		let { name, mesh, children, translation, rotation, scale } = nodes[index];

		let result = {};

		result.index = index;
		result.name = name;
		result.modelMeshIndex = mesh;
		if (!isNaN(mesh)) {
			result.modelMaterialIndex = json.meshes[mesh].primitives[0].material;
		}

		result.transform = Transform.create({
			position: translation,
			rotation: rotation,
			scale: scale
		});

		if (parent) {
			result.transform.parent = parent.transform;
		}
		result.transform.children = [];
		result.children = [];

		if (children) {
			for (let i = 0, l = children.length; i < l; i++) {
				let childNode = createSceneHierarchy(
					json,
					children[i],
					result);
				result.children.push(childNode);
				result.transform.children.push(childNode.transform);
			}
		}

		return result;
	};

	// Takes a URI of a glTF file to load
	// Returns an object containing an array meshdata ready for use with Fury.Mesh
	// As well as an array of images to use in material creation
	// Includes a cut down set of information on materialData and textureData arrays
	// however these are not ready to be used with Fury.Material and Fury.Texture
	// and must be manipulated further
	// Might be sensible to separate the asset load and format conversions
	exports.load = (uri, callback) => {
		// TODO: Check file extension, only gltf currently supported
		// https://github.com/KhronosGroup/glTF -> https://github.com/KhronosGroup/glTF/tree/master/specification/2.0
		// https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/figures/gltfOverview-2.0.0b.png
		// https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html
		fetch(uri).then((response) => {
			return response.json();
		}).then((json) => {

			let model = { meshData: [], images: [], materialData: [], textureData: [] };
			let arrayBuffers = [];

			let assetsLoading = 0;
			let onAssetLoadComplete = () => {
				assetsLoading--;
				if (assetsLoading == 0) {
					// All buffers loaded so build mesh data
					for(let i = 0, l = json.meshes.length; i < l; i++) {
						model.meshData[i] = buildMeshData(json, i, arrayBuffers)
					}

					if (json.animations && json.animations.length) {
						model.animations = {};
						for (let i = 0, l = json.animations.length; i < l; i++) {
							buildAnimationData(model.animations, json, i, arrayBuffers);
						}
					}
					callback(model);
				}
			};

			for (let i = 0, l = json.buffers.length; i < l; i++) {
				assetsLoading++;
				fetch(json.buffers[i].uri).then((response) => {
					return response.arrayBuffer();
				}).then((buffer) => {
					arrayBuffers[i] = buffer;
					onAssetLoadComplete();
				}).catch((error) => {
					console.error(error);
					console.error("Unable to fetch data buffer from model");
				});
			}

			// Hierarchy information
			if (json.scenes && json.scenes.length) {
				let scene = json.scenes[json.scene]; // Only one scene currently supported
				let nodeIndex = scene.nodes[0]; // Expect single scene node
				model.hierarchy = createSceneHierarchy(json, nodeIndex);
			}

			if (json.materials && json.materials.length) {
				// As PBR is not supported flatten the material structure
				for (let i = 0, l = json.materials.length; i < l; i++) {
					let textureIndex = -1;
					let material = json.materials[i];
					if (material.pbrMetallicRoughness
						&& material.pbrMetallicRoughness.baseColorTexture) {
						let index = material.pbrMetallicRoughness.baseColorTexture.index;
						textureIndex = !isNaN(index) ? index : -1;
					}

					// Only texture index is currently relevant
					model.materialData[i] = {
						textureIndex: textureIndex
					};
				}
			}

			if (json.textures && json.textures.length) {
				for (let i = 0, l = json.textures.length; i < l; i++) {
					let { sampler, source, name } = json.textures[i];
					// samplers not currently supported, so just put name and imageIndex
					model.textureData[i] = {
						name: name,
						imageIndex: source
					};
				}
			}

			if (json.images && json.images.length) {
				for (let i = 0, l = json.images.length; i < l; i++) {
					assetsLoading++;
					fetch(json.images[i].uri).then(response => response.blob()).then(blob => {
						let image = new Image();
						image.src = URL.createObjectURL(blob);
						// Note if we wanted to unload the model
						// we would need to call URL.revokeObjectURL(image.src)
						image.decode().then(() => {
							model.images[i] = image;
							onAssetLoadComplete();
						}).catch((error) => {
							console.error(error);
							console.error("Unable to decode provide image data");
						});
					}).catch((error) => {
						console.error(error);
						console.error("Unable to fetch image data from model");
					});
				}
			}
		}).catch((error) => {
			console.error(error);
			console.error("Unable to load model at " + uri);
		});
	};

	return exports;
})();
<|endoftext|>


<commit_before>'use strict';
let data;

self.addEventListener('install', skipWaiting);

self.addEventListener('message', function (event) {
	if (event.data) {
		if (typeof event.data === 'string') {
			data = new URL(event.data, event.source.url).href;
		} else if (event.data.framework && event.data.modules) {
			data = event.data;
			for (let i = 0; i < data.framework.length; i++) {
				data.framework[i] = new URL(data.framework[i], event.source.url).href;
			}
			for (let i = 0; i < data.modules.length; i++) {
				data.modules[i] = new URL(data.modules[i] + '/', event.source.url).href;
			}
			caches.open(data.prefix + 'modules').then(function (cache) {
				return cache.keys().then(function (keys) {
					keys.forEach(function (request) {
						if (!findModule(request.url)) {
							cache.delete(request);
						}
					});
				});
			});
			caches.open(data.prefix + 'framework').then(function (cache) {
				return cache.keys().then(function (keys) {
					keys.forEach(function (request) {
						if (data.framework.indexOf(request.url) < 0) {
							cache.delete(request);
						}
					});
				});
			});
		}
	}
});

self.addEventListener('fetch', function (event) {
	if (data && event.request.method === 'GET') {
		if (typeof data === 'string') {
			if (event.request.url.length >= data.length && event.request.url.substr(0, data.length) === data) {
				event.respondWith(fetch(new Request(event.request, {
					cache: 'no-cache'
				})));
			}
		} else {
			let type;
			if (data.framework.indexOf(event.request.url) >= 0) {
				type = 'framework';
			} else if (findModule(event.request.url)) {
				type = 'modules';
			}
			if (type) {
				event.respondWith(caches.open(data.prefix + type).then(function (cache) {
					return cache.match(event.request).then(function (response) {
						if (response) {
							return response;
						} else {
							return fetch(event.request).then(function (response) {
								if (response.ok) {
									cache.put(event.request, response.clone());
								}
								return response;
							});
						}
					});
				}));
			}
		}
	}
});

function findModule(url) {
	let found;
	for (let i = 0; i < data.modules.length; i++) {
		if (url.length > data.modules[i].length && url.substr(0, data.modules[i].length) === data.modules[i]) {
			found = true;
			break;
		}
	}
	return found;
}<commit_msg>no message<commit_after>'use strict';
let data;

self.addEventListener('install', skipWaiting);

self.addEventListener('message', function (event) {
	if (event.data) {
		if (typeof event.data === 'string') {
			data = new URL(event.data, event.source.url).href;
		} else if (event.data.framework && event.data.modules && event.data.prefix) {
			data = event.data;
			for (let i = 0; i < data.framework.length; i++) {
				data.framework[i] = new URL(data.framework[i], event.source.url).href;
			}
			for (let i = 0; i < data.modules.length; i++) {
				data.modules[i] = new URL(data.modules[i] + '/', event.source.url).href;
			}
			caches.open(data.prefix + 'modules').then(function (cache) {
				return cache.keys().then(function (keys) {
					keys.forEach(function (request) {
						if (!findModule(request.url)) {
							cache.delete(request);
						}
					});
				});
			});
			caches.open(data.prefix + 'framework').then(function (cache) {
				return cache.keys().then(function (keys) {
					keys.forEach(function (request) {
						if (data.framework.indexOf(request.url) < 0) {
							cache.delete(request);
						}
					});
				});
			});
		}
	}
});

self.addEventListener('fetch', function (event) {
	if (data && event.request.method === 'GET') {
		if (typeof data === 'string') {
			if (event.request.url.length >= data.length && event.request.url.substr(0, data.length) === data) {
				event.respondWith(fetch(new Request(event.request, {
					cache: 'no-cache'
				})));
			}
		} else {
			let type;
			if (data.framework.indexOf(event.request.url) >= 0) {
				type = 'framework';
			} else if (findModule(event.request.url)) {
				type = 'modules';
			}
			if (type) {
				event.respondWith(caches.open(data.prefix + type).then(function (cache) {
					return cache.match(event.request).then(function (response) {
						if (response) {
							return response;
						} else {
							return fetch(event.request).then(function (response) {
								if (response.ok) {
									cache.put(event.request, response.clone());
								}
								return response;
							});
						}
					});
				}));
			}
		}
	}
});

function findModule(url) {
	let found;
	for (let i = 0; i < data.modules.length; i++) {
		if (url.length > data.modules[i].length && url.substr(0, data.modules[i].length) === data.modules[i]) {
			found = true;
			break;
		}
	}
	return found;
}<|endoftext|>


<commit_before>import h from 'snabbdom/h';
import { createTextVNode, transformName } from './utils';
import listeners from './event-listeners';

export default function virtualizeNodes(element, options = {}) {

    const context = options.context || document;

    if (!element) {
        return null;
    }

    const createdVNodes = [];
    const vnode = convertNode(element, createdVNodes, context);
    options.hooks && options.hooks.create && createdVNodes.forEach((node) => { options.hooks.create(node); });
    return vnode;
}

function convertNode(element, createdVNodes, context) {
    // If our node is a text node, then we only want to set the `text` part of
    // the VNode.
    if (element.nodeType === context.defaultView.Node.TEXT_NODE) {
        const newNode = createTextVNode(element.textContent, context);
        newNode.elm = element;
        createdVNodes.push(newNode);
        return newNode
    }

    // If not a text node, then build up a VNode based on the element's tag
    // name, class and style attributes, and remaining attributes.

    // Special values: style, class. We don't include these in the attrs hash
    // of the VNode.
    const data = {};
    const classes = getClasses(element);
    if (Object.keys(classes).length !== 0) {
        data.class = classes;
    }
    const style = getStyle(element);
    if (Object.keys(style).length !== 0) {
        data.style = style;
    }

    // Build up set of attributes on the element.
    const attributes = element.attributes;
    for (let i = 0; i < attributes.length; i++) {
        const attr = attributes.item(i);
        const name = attr.name;
        if (name !== 'style' && name !== 'class') {
            if (!data.attrs) {
                data.attrs = {};
            }
            data.attrs[name] = attr.value;
        }
    }

    // Check for event listeners.
    const on = {};
    listeners.forEach((key) => {
        if (element[key]) {
            on[key.substring(2)] = element[key];
        }
    });
    if (Object.keys(on).length > 0) {
        data.on = on;
    }

    // Build up set of children.
    let childNodes = null;
    const children = element.childNodes;
    if (children.length > 0) {
        childNodes = [];
        for (var i = 0; i < children.length; i++) {
            childNodes.push(convertNode(children.item(i), createdVNodes, context));
        }
    }
    const newNode = h(element.tagName.toLowerCase(), data, childNodes);
    newNode.elm = element;
    createdVNodes.push(newNode);
    return newNode
}

// Builds the class object for the VNode.
function getClasses(element) {
    const className = element.className;
    const classes = {};
    if (className !== null && className.length > 0) {
        className.split(' ').forEach((className) => {
            classes[className] = true;
        });
    }
    return classes;
}

// Builds the style object for the VNode.
function getStyle(element) {
    const style = element.style;
    const styles = {};
    for (let i = 0; i < style.length; i++) {
        const name = style.item(i);
        const transformedName = transformName(name);
        styles[transformedName] = style.getPropertyValue(name);
    }
    return styles;
}
<commit_msg>Fix multiline classes<commit_after>import h from 'snabbdom/h';
import { createTextVNode, transformName } from './utils';
import listeners from './event-listeners';

export default function virtualizeNodes(element, options = {}) {

    const context = options.context || document;

    if (!element) {
        return null;
    }

    const createdVNodes = [];
    const vnode = convertNode(element, createdVNodes, context);
    options.hooks && options.hooks.create && createdVNodes.forEach((node) => { options.hooks.create(node); });
    return vnode;
}

function convertNode(element, createdVNodes, context) {
    // If our node is a text node, then we only want to set the `text` part of
    // the VNode.
    if (element.nodeType === context.defaultView.Node.TEXT_NODE) {
        const newNode = createTextVNode(element.textContent, context);
        newNode.elm = element;
        createdVNodes.push(newNode);
        return newNode
    }

    // If not a text node, then build up a VNode based on the element's tag
    // name, class and style attributes, and remaining attributes.

    // Special values: style, class. We don't include these in the attrs hash
    // of the VNode.
    const data = {};
    const classes = getClasses(element);
    if (Object.keys(classes).length !== 0) {
        data.class = classes;
    }
    const style = getStyle(element);
    if (Object.keys(style).length !== 0) {
        data.style = style;
    }

    // Build up set of attributes on the element.
    const attributes = element.attributes;
    for (let i = 0; i < attributes.length; i++) {
        const attr = attributes.item(i);
        const name = attr.name;
        if (name !== 'style' && name !== 'class') {
            if (!data.attrs) {
                data.attrs = {};
            }
            data.attrs[name] = attr.value;
        }
    }

    // Check for event listeners.
    const on = {};
    listeners.forEach((key) => {
        if (element[key]) {
            on[key.substring(2)] = element[key];
        }
    });
    if (Object.keys(on).length > 0) {
        data.on = on;
    }

    // Build up set of children.
    let childNodes = null;
    const children = element.childNodes;
    if (children.length > 0) {
        childNodes = [];
        for (var i = 0; i < children.length; i++) {
            childNodes.push(convertNode(children.item(i), createdVNodes, context));
        }
    }
    const newNode = h(element.tagName.toLowerCase(), data, childNodes);
    newNode.elm = element;
    createdVNodes.push(newNode);
    return newNode
}

// Builds the class object for the VNode.
function getClasses(element) {
    const className = element.className;
    const classes = {};
    if (className !== null && className.length > 0) {
        className.split(' ').forEach((className) => {
            if (className.trim().length) {
                classes[className.trim()] = true;
            }
        });
    }
    return classes;
}

// Builds the style object for the VNode.
function getStyle(element) {
    const style = element.style;
    const styles = {};
    for (let i = 0; i < style.length; i++) {
        const name = style.item(i);
        const transformedName = transformName(name);
        styles[transformedName] = style.getPropertyValue(name);
    }
    return styles;
}
<|endoftext|>


<commit_before>const util = require('util');
const EventEmitter = require('events');
const natural = require('natural');

function Ai (name) {
    EventEmitter.call(this);
    var Ai = this;

    this.name = name;
    this.isOnline = false;
    this.tokenizer = new natural.WordTokenizer();

    this.start = function () {
        this.emit('started');
    };

    this.setOnline = function (online) {
        this.isOnline = online;
        this.say('default', 'Hello! I am now online', ':grinning:');
    };

    this.hasWord = function (words, index, string) {
        return natural.JaroWinklerDistance(words[index], string) > 0.7;
    };

    this.analyzeMessage = function (origin, message) {
        var words = this.tokenizer.tokenize(message);

        if (words.length == 1 && this.hasWord(words, 0, Ai.name)) {
            this.say(origin, 'Yes sire?', ':slightly_smiling_face:');
        } else if (words.length >= 2 && this.hasWord(words, 1, 'music')) {
            if (this.hasWord(words, 0, 'start')) {
                this.run(origin, { id: 'start-music' });
            } else if (this.hasWord(words, 0, 'stop')) {
                this.run(origin, { id: 'stop-music' });
            } else {
                this.say(origin, 'Sorry, I don\'t know what you mean.', ':thinking_face:');
            }
        } else if (words.length >= 3 && this.hasWord(words, 0, 'wake') && this.hasWord(words, 1, 'me') && this.hasWord(words, 2, 'up')) {
            var time = words.splice(0, 3);
            this.say(origin, 'I will wake up at', time.join(' '));
            // this.run(origin, { id:'wake-up', args: { time: time } });
        } else {
            this.say(origin, 'Sorry, I don\'t know what you mean.', ':thinking_face:');
        }
    };

    this.say = function (origin, text, icon_emoji) {
        if (!icon_emoji) {
            icon_emoji = ':grinning:';
        }
        this.emit('say', { message: { text: text, icon_emoji: icon_emoji }, origin: origin });
    };

    this.run = function (origin, cmd) {
        this.emit('run', { cmd: cmd, origin: origin });
    };
}

util.inherits(Ai, EventEmitter);

module.exports = Ai;
<commit_msg>add time<commit_after>const util = require('util');
const EventEmitter = require('events');
const natural = require('natural');

function Ai (name) {
    EventEmitter.call(this);
    var Ai = this;

    this.name = name;
    this.isOnline = false;
    this.tokenizer = new natural.WordTokenizer();

    this.start = function () {
        this.emit('started');
    };

    this.setOnline = function (online) {
        this.isOnline = online;
        this.say('default', 'Hello! I am now online', ':grinning:');
    };

    this.hasWord = function (words, index, string) {
        return natural.JaroWinklerDistance(words[index], string) > 0.7;
    };

    this.analyzeMessage = function (origin, message) {
        var words = this.tokenizer.tokenize(message);

        if (words.length == 1 && this.hasWord(words, 0, Ai.name)) {
            this.say(origin, 'Yes sire?', ':slightly_smiling_face:');
        } else if (words.length >= 2 && this.hasWord(words, 1, 'music')) {
            if (this.hasWord(words, 0, 'start')) {
                this.run(origin, { id: 'start-music' });
            } else if (this.hasWord(words, 0, 'stop')) {
                this.run(origin, { id: 'stop-music' });
            } else {
                this.say(origin, 'Sorry, I don\'t know what you mean.', ':thinking_face:');
            }
        } else if (words.length >= 3 && this.hasWord(words, 0, 'wake') && this.hasWord(words, 1, 'me') && this.hasWord(words, 2, 'up')) {
            var time = words.splice(0, 3);
            this.say(origin, 'I will wake up at ' + time.join(' '));
            // this.run(origin, { id:'wake-up', args: { time: time } });
        } else {
            this.say(origin, 'Sorry, I don\'t know what you mean.', ':thinking_face:');
        }
    };

    this.say = function (origin, text, icon_emoji) {
        if (!icon_emoji) {
            icon_emoji = ':grinning:';
        }
        this.emit('say', { message: { text: text, icon_emoji: icon_emoji }, origin: origin });
    };

    this.run = function (origin, cmd) {
        this.emit('run', { cmd: cmd, origin: origin });
    };
}

util.inherits(Ai, EventEmitter);

module.exports = Ai;
<|endoftext|>


<commit_before>module.exports = (() => {
"use strict"

const P = require('parsimmon')
const AST = require('../src/ast.js')
const unify = require('../src/unification.js')
const show = require('../src/typing-show.js')
const IndentationParser = require('../src/parse-indentation.js')
const PrecedenceParser = require('../src/parse-precedence.js')

const Indent = new IndentationParser(0)

//------------------------------------------------------------------------
// Terminals

// space = {' ' | TAB}
const space = P.regex(/[ \t]*/).desc('space')

// newline = '\n'
const newline = P.string('\n').desc('newline')

// exp_space = {' '}, [NL, {NL}, INDENT.>];
const exp_space = space.then((newline.atLeast(1).then(Indent.relative(Indent.gt))).atMost(1))

// lazy tokenization, consume trailing space
function token(tok) {
   return tok.skip(exp_space)
}

const comma = token(P.string(','))

function fix(f) {
   let lazy
   lazy = f(P.lazy(() => {return lazy}))
   return lazy
}

const paren_open = token(P.string('('))
const paren_close = token(P.string(')'))
function inParenthesis(exp) {
   return paren_open.then(exp).skip(paren_close)
}

//------------------------------------------------------------------------
// Types

const var_map = new Map()

const typeVariable = token(P.regexp(/[A-Z]+[A-Z0-9]*/)).map((n) => {
   let t = var_map.get(n)
   if (t === undefined) {
      t = new AST.TypeVariable(n)
      var_map.set(n, t)
   }
   return t
})

let texp
const typeConstructor = P.seqMap(
   token(P.regexp(/^(?=.*[a-z])[A-Z][a-zA-Z0-9]+/)),
   token(P.string('<')).then(P.sepBy(P.lazy(() => {return texp}), comma)).skip(token(P.string('>'))).or(P.succeed([])),
   (n, ps) => {return new AST.TypeConstructor(n, ps)}
)

const typeSubExpression = P.seqMap(
   typeConstructor.or(typeVariable).or(inParenthesis(P.lazy(() => {return texp}))),
   (token(P.string('as')).then(typeVariable)).or(P.succeed()),
   (texp, mu) => {
      if (mu !== undefined) {
         if (!unify.types(texp, mu)) {
            throw 'unification error when parsing recursive type'
         }
      }
      return texp
   }
)

const typeOperator = token(P.regex(/[->]+/))

const TypePrecedence = new PrecedenceParser.PrecedenceParser(typeOperator, typeSubExpression)
texp = TypePrecedence.parseExprWithMinimumPrecedence(0)

const typeExpression = P.succeed().chain(() => {
   var_map.clear()
   return texp
})

// Register type operators with precedence parser

function infixTypeOp([name, opInfo]) {
   TypePrecedence.ledop(opInfo.symbol, opInfo.associativity, opInfo.precedence, function(info, lhs) {
      return this.parseExprWithMinimumPrecedence(info.minimumPrecedence()).map((rhs) => {
         return new AST.TypeConstructor(name, [lhs, rhs])
      })
   })
}

for (let opEntry of AST.infixTypeOps.entries()) {
   infixTypeOp(opEntry)
}

//------------------------------------------------------------------------
// Values

// FLOAT
const literal_float = P.regexp(/[0-9]+\.[0-9]+/).map((n) => {
   return new AST.Literal_Float(parseFloat(n))
})

// STRING : Simple for now, fix later
const literal_string = P.regexp(/"[^"]"/).map((s) => {
   return new AST.Literal_String(s)
})

// definitions for recursive parsers
let expression_list_lazy
const expression_list = P.lazy(() => {return expression_list_lazy})

let expression_lazy
const expression = P.lazy(() => {return expression_lazy})

let block_lazy
const block = P.lazy(() => {return block_lazy})

let sub_expression_lazy
const sub_expression = P.lazy(() => {return sub_expression_lazy})

//------------------------------------------------------------------------
// Types

//------------------------------------------------------------------------
// Expressions

const thin_arrow = P.string('->').skip(space)
const fat_arrow = P.string('=>').skip(space)
const assign = P.string('=').skip(space)
const typeAnnotation = P.string(':').skip(space)
const identifier = P.regexp(/[a-z][a-zA-Z_0-9]*/).skip(exp_space)

// ID
const variable = identifier.map((id) => {
   return new AST.Variable(id)
})

// INT
const int_lit = P.regexp(/[0-9]+/).map((i) => {
   return new AST.LiteralInt(parseInt(i))
})

// arg_list = identifier, {comma, identifier}
const arg_list = P.sepBy(identifier, comma)

// typedIdentifier = identifier, [typeAnnotation, typeExpression]
const typedVariable = P.seqMap(variable,
   (typeAnnotation.then(typeExpression)).or(P.succeed()), (v, t) => {
      if (t !== undefined) {
         v.userType = t
      }
      return v;
   }
)

const typedArgList = P.sepBy(typedVariable, comma)

// literal_function = [identifier], '(', arg_list, ')', '=>' (NL, block | expression)
const literal_function = P.seqMap(
   identifier.or(P.succeed('')),
   inParenthesis(typedArgList).skip(fat_arrow),
   (newline.then(block)).or(expression.map((e) => {return new AST.Return(e)})),
   (name, args, body) => {
      return new AST.Fn(name, args, body)
   }
)

// expression_list = expression, {',', expression}
expression_list_lazy = P.sepBy(expression, comma)

// tuple = '(', expression_list, ')'
const tuple = inParenthesis(expression_list).map((exp_list) => {
   return new AST.LiteralTuple(exp_list)
})

const singleton = inParenthesis(variable.or(int_lit).or(inParenthesis(expression))).map((term) => {
   return new AST.LiteralTuple([term])
})

function application(exp1, exps) {
   return P.seqMap(exp1, exps.many(), (app, app_list) => {
      if (app_list.length > 0) {
         if (app instanceof AST.LiteralTuple && app.expressions.length == 1) {
            app = app.expressions[0]
         }
      }
      for (const a of app_list) {
         app = new AST.Application(app, a)
      }
      return app
   })
}

sub_expression_lazy = literal_function.or(variable).or(int_lit).or(tuple).skip(exp_space)

expression_lazy = application(sub_expression, sub_expression)

//------------------------------------------------------------------------
// Statements

// Assignments
const assignKeyword = P.string('let').then(exp_space)
const assignment = P.seqMap(
   assignKeyword.then(typedVariable).skip(assign),
   expression,
   (v, e) => {
      return new AST.Declaration(v, e)
   }
)

// Return
const returnKeyword = P.string('return')
const rtn = returnKeyword.then(space).then(expression).map((exp) => {
   return new AST.Return(exp)
})

const defineFunction = P.seqMap(
   identifier,
   inParenthesis(typedArgList).skip(fat_arrow),
   (newline.then(block)).or(expression.map((e) => {return new AST.Return(e)})),
   (name, args, body) => {
      return new AST.Declaration(new AST.Variable(name), new AST.Fn(name, args, body))
   }
)

// expression_list = expression, {',', expression}

const statement = rtn.or(defineFunction).or(assignment).or(expression).skip(space)

//------------------------------------------------------------------------
// Blocks

block_lazy = P.succeed({}).chain(() => {
   const indent = Indent.get()
   return P.seqMap(
      newline.many().then(Indent.relative(Indent.gt).map((i) => Indent.set(i))).then(statement),
      (newline.many().then(Indent.relative(Indent.eq).map((i) => Indent.set(i))).then(statement)).many(),
      (first, blk) => {
          blk.unshift(first)
          Indent.set(indent)
          return new AST.Block(blk)
      }
   )
})

//------------------------------------------------------------------------
// Program

// top_level = {NL}, {INDENT==0, statement, {NL}}
const topLevel = newline.many().then((Indent.absolute(0).map((i) => Indent.set(i)).
   then(statement).skip(newline.many())).many()).map((blk) => {return new AST.Block(blk)})

return {
   program(s) {
      return topLevel.parse(s)
   },

   type(s) {
      return typeExpression.parse(s)
   }
}

})()

<commit_msg>use const instead of let<commit_after>module.exports = (() => {
"use strict"

const P = require('parsimmon')
const AST = require('../src/ast.js')
const unify = require('../src/unification.js')
const show = require('../src/typing-show.js')
const IndentationParser = require('../src/parse-indentation.js')
const PrecedenceParser = require('../src/parse-precedence.js')

const Indent = new IndentationParser(0)

//------------------------------------------------------------------------
// Terminals

// space = {' ' | TAB}
const space = P.regex(/[ \t]*/).desc('space')

// newline = '\n'
const newline = P.string('\n').desc('newline')

// exp_space = {' '}, [NL, {NL}, INDENT.>];
const exp_space = space.then((newline.atLeast(1).then(Indent.relative(Indent.gt))).atMost(1))

// lazy tokenization, consume trailing space
function token(tok) {
   return tok.skip(exp_space)
}

const comma = token(P.string(','))

function fix(f) {
   let lazy
   lazy = f(P.lazy(() => {return lazy}))
   return lazy
}

const paren_open = token(P.string('('))
const paren_close = token(P.string(')'))
function inParenthesis(exp) {
   return paren_open.then(exp).skip(paren_close)
}

//------------------------------------------------------------------------
// Types

const var_map = new Map()

const typeVariable = token(P.regexp(/[A-Z]+[A-Z0-9]*/)).map((n) => {
   let t = var_map.get(n)
   if (t === undefined) {
      t = new AST.TypeVariable(n)
      var_map.set(n, t)
   }
   return t
})

let texp
const typeConstructor = P.seqMap(
   token(P.regexp(/^(?=.*[a-z])[A-Z][a-zA-Z0-9]+/)),
   token(P.string('<')).then(P.sepBy(P.lazy(() => {return texp}), comma)).skip(token(P.string('>'))).or(P.succeed([])),
   (n, ps) => {return new AST.TypeConstructor(n, ps)}
)

const typeSubExpression = P.seqMap(
   typeConstructor.or(typeVariable).or(inParenthesis(P.lazy(() => {return texp}))),
   (token(P.string('as')).then(typeVariable)).or(P.succeed()),
   (texp, mu) => {
      if (mu !== undefined) {
         if (!unify.types(texp, mu)) {
            throw 'unification error when parsing recursive type'
         }
      }
      return texp
   }
)

const typeOperator = token(P.regex(/[->]+/))

const TypePrecedence = new PrecedenceParser.PrecedenceParser(typeOperator, typeSubExpression)
texp = TypePrecedence.parseExprWithMinimumPrecedence(0)

const typeExpression = P.succeed().chain(() => {
   var_map.clear()
   return texp
})

// Register type operators with precedence parser

function infixTypeOp([name, opInfo]) {
   TypePrecedence.ledop(opInfo.symbol, opInfo.associativity, opInfo.precedence, function(info, lhs) {
      return this.parseExprWithMinimumPrecedence(info.minimumPrecedence()).map((rhs) => {
         return new AST.TypeConstructor(name, [lhs, rhs])
      })
   })
}

for (const opEntry of AST.infixTypeOps.entries()) {
   infixTypeOp(opEntry)
}

//------------------------------------------------------------------------
// Values

// FLOAT
const literal_float = P.regexp(/[0-9]+\.[0-9]+/).map((n) => {
   return new AST.Literal_Float(parseFloat(n))
})

// STRING : Simple for now, fix later
const literal_string = P.regexp(/"[^"]"/).map((s) => {
   return new AST.Literal_String(s)
})

// definitions for recursive parsers
let expression_list_lazy
const expression_list = P.lazy(() => {return expression_list_lazy})

let expression_lazy
const expression = P.lazy(() => {return expression_lazy})

let block_lazy
const block = P.lazy(() => {return block_lazy})

let sub_expression_lazy
const sub_expression = P.lazy(() => {return sub_expression_lazy})

//------------------------------------------------------------------------
// Types

//------------------------------------------------------------------------
// Expressions

const thin_arrow = P.string('->').skip(space)
const fat_arrow = P.string('=>').skip(space)
const assign = P.string('=').skip(space)
const typeAnnotation = P.string(':').skip(space)
const identifier = P.regexp(/[a-z][a-zA-Z_0-9]*/).skip(exp_space)

// ID
const variable = identifier.map((id) => {
   return new AST.Variable(id)
})

// INT
const int_lit = P.regexp(/[0-9]+/).map((i) => {
   return new AST.LiteralInt(parseInt(i))
})

// arg_list = identifier, {comma, identifier}
const arg_list = P.sepBy(identifier, comma)

// typedIdentifier = identifier, [typeAnnotation, typeExpression]
const typedVariable = P.seqMap(variable,
   (typeAnnotation.then(typeExpression)).or(P.succeed()), (v, t) => {
      if (t !== undefined) {
         v.userType = t
      }
      return v;
   }
)

const typedArgList = P.sepBy(typedVariable, comma)

// literal_function = [identifier], '(', arg_list, ')', '=>' (NL, block | expression)
const literal_function = P.seqMap(
   identifier.or(P.succeed('')),
   inParenthesis(typedArgList).skip(fat_arrow),
   (newline.then(block)).or(expression.map((e) => {return new AST.Return(e)})),
   (name, args, body) => {
      return new AST.Fn(name, args, body)
   }
)

// expression_list = expression, {',', expression}
expression_list_lazy = P.sepBy(expression, comma)

// tuple = '(', expression_list, ')'
const tuple = inParenthesis(expression_list).map((exp_list) => {
   return new AST.LiteralTuple(exp_list)
})

const singleton = inParenthesis(variable.or(int_lit).or(inParenthesis(expression))).map((term) => {
   return new AST.LiteralTuple([term])
})

function application(exp1, exps) {
   return P.seqMap(exp1, exps.many(), (app, app_list) => {
      if (app_list.length > 0) {
         if (app instanceof AST.LiteralTuple && app.expressions.length == 1) {
            app = app.expressions[0]
         }
      }
      for (const a of app_list) {
         app = new AST.Application(app, a)
      }
      return app
   })
}

sub_expression_lazy = literal_function.or(variable).or(int_lit).or(tuple).skip(exp_space)

expression_lazy = application(sub_expression, sub_expression)

//------------------------------------------------------------------------
// Statements

// Assignments
const assignKeyword = P.string('let').then(exp_space)
const assignment = P.seqMap(
   assignKeyword.then(typedVariable).skip(assign),
   expression,
   (v, e) => {
      return new AST.Declaration(v, e)
   }
)

// Return
const returnKeyword = P.string('return')
const rtn = returnKeyword.then(space).then(expression).map((exp) => {
   return new AST.Return(exp)
})

const defineFunction = P.seqMap(
   identifier,
   inParenthesis(typedArgList).skip(fat_arrow),
   (newline.then(block)).or(expression.map((e) => {return new AST.Return(e)})),
   (name, args, body) => {
      return new AST.Declaration(new AST.Variable(name), new AST.Fn(name, args, body))
   }
)

// expression_list = expression, {',', expression}

const statement = rtn.or(defineFunction).or(assignment).or(expression).skip(space)

//------------------------------------------------------------------------
// Blocks

block_lazy = P.succeed({}).chain(() => {
   const indent = Indent.get()
   return P.seqMap(
      newline.many().then(Indent.relative(Indent.gt).map((i) => Indent.set(i))).then(statement),
      (newline.many().then(Indent.relative(Indent.eq).map((i) => Indent.set(i))).then(statement)).many(),
      (first, blk) => {
          blk.unshift(first)
          Indent.set(indent)
          return new AST.Block(blk)
      }
   )
})

//------------------------------------------------------------------------
// Program

// top_level = {NL}, {INDENT==0, statement, {NL}}
const topLevel = newline.many().then((Indent.absolute(0).map((i) => Indent.set(i)).
   then(statement).skip(newline.many())).many()).map((blk) => {return new AST.Block(blk)})

return {
   program(s) {
      return topLevel.parse(s)
   },

   type(s) {
      return typeExpression.parse(s)
   }
}

})()

<|endoftext|>


<commit_before>/*
 * Copyright 2006 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/
if (typeof fleegix == 'undefined') { var fleegix = {}; }
fleegix.popup = new function() {

  var self = this;
  this.win = null;
  this.open = function(url, optParam) {
    var opts = optParam || {}
    var str = '';
    var propList = {
      'width':'',
      'height':'',
      'location':0,
      'menubar':0,
      'resizable':1,
      'scrollbars':0,
      'status':0,
      'titlebar':1,
      'toolbar':0
      };
    for (var prop in propList) {
      str += prop + '=';
      str += opts[prop] ? opts[prop] : propList[prop];
      str += ',';
    }
    var len = str.length;
    if (len) {
      str = str.substr(0, len-1);
    }
    if(!self.win || self.win.closed) {
      self.win = window.open(url, 'thePopupWin', str);
    }
    else {
      self.win.focus();
      self.win.document.location = url;
    }
  };
  this.close = function() {
    if (self.win) {
      self.win.window.close();
      self.win = null;
    }
  };
  this.goURLMainWin = function(url) {
    location = url;
    self.close();
  };
}
fleegix.popup.constructor = null;

<commit_msg>Cleaned up formatting in popup module, began process of changing 'self' references to '_this'.<commit_after>/*
 * Copyright 2006 Matthew Eernisse (mde@fleegix.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/
if (typeof fleegix == 'undefined') { var fleegix = {}; }
fleegix.popup = new function () {
  var _this = this;
  this.win = null;
  this.open = function (url, optParam) {
    var opts = optParam || {}
    var str = '';
    var propList = {
      'width': '',
      'height': '',
      'location': 0,
      'menubar': 0,
      'resizable': 1,
      'scrollbars': 0,
      'status': 0,
      'titlebar': 1,
      'toolbar': 0
      };
    for (var prop in propList) {
      str += prop + '=';
      str += opts[prop] ? opts[prop] : propList[prop];
      str += ',';
    }
    var len = str.length;
    if (len) {
      str = str.substr(0, len-1);
    }
    if(!_this.win || _this.win.closed) {
      _this.win = window.open(url, 'thePopupWin', str);
    }
    else {
      _this.win.focus();
      _this.win.document.location = url;
    }
  };
  this.close = function () {
    if (_this.win) {
      _this.win.window.close();
      _this.win = null;
    }
  };
  this.goURLMainWin = function (url) {
    location = url;
    _this.close();
  };
};

<|endoftext|>


<commit_before>goog.provide('annotorious.viewer');

goog.require('goog.style');
goog.require('goog.dom');
goog.require('goog.dom.query');

/**
 * A popup bubble widget to show annotation details.
 * @param {element} parentEl the DOM element to attach to
 * @param {annotorious.modules.image.ImageAnnotator} annotator reference to the annotator
 * @constructor
 */
annotorious.viewer.Popup = function(parentEl, annotator) {
  /** @private **/
  this._annotator = annotator;

  /** @private **/
  this._element = goog.soy.renderAsElement(annotorious.templates.popup);

  /** @private **/
  this._currentAnnotation;

  /** @private **/
  this._text = goog.dom.query('.yuma-popup-text', this._element)[0];

  /** @private **/
  this._buttons = goog.dom.query('.yuma-popup-action-delete', this._element)[0];

  /** @private **/
  this._popupHideTimer;

  /** @private **/
  this._buttonHideTimer;

  /** @private **/
  this._cancelHide = false;

  var btnDelete = goog.dom.query('.yuma-popup-action-delete', this._element)[0];

  var self = this;
  goog.events.listen(btnDelete, goog.events.EventType.CLICK, function(event) {
    goog.style.setOpacity(self._element, 0.0);
    annotator.fireEvent(annotorious.events.EventType.POPUP_BTN_DELETE,
      { annotation: self._currentAnnotation, mouseEvent: event });
  });

  goog.events.listen(this._buttons, goog.events.EventType.MOUSEOVER, function(event) {
    goog.style.setOpacity(self._buttons, 0.9);
  });

  goog.events.listen(this._buttons, goog.events.EventType.MOUSEOUT, function() {
    goog.style.setOpacity(self._buttons, 0.4);
  });

  goog.events.listen(this._element, goog.events.EventType.MOUSEOVER, function(event) {
    window.clearTimeout(self._buttonHideTimer);
    if (goog.style.getStyle(self._buttons, 'opacity') < 0.4)
      goog.style.setOpacity(self._buttons, 0.4);
    self.clearHideTimer();
  });

  goog.events.listen(this._element, goog.events.EventType.MOUSEOUT, function(event) {
    goog.style.setOpacity(self._buttons, 0);
    self.startHideTimer();
  });

  goog.style.setOpacity(this._buttons, 0.0);
  goog.style.setOpacity(this._element, 0.0);
  goog.dom.appendChild(parentEl, this._element);
}

/**
 * Start the popup hide timer.
 */
annotorious.viewer.Popup.prototype.startHideTimer = function() {
  this._cancelHide = false;
  if (!this._popupHideTimer) {
    var self = this;
    this._popupHideTimer = window.setTimeout(function() {
      self._annotator.fireEvent(annotorious.events.EventType.BEFORE_POPUP_HIDE);
      if (!self._cancelHide) {
        goog.style.setOpacity(self._element, 0.0);
        goog.style.setOpacity(self._buttons, 0.4);
        delete self._popupHideTimer;
      }
    }, 300);
  }
}

/**
 * Clear the popup hide timer.
 */
annotorious.viewer.Popup.prototype.clearHideTimer = function() {
  this._cancelHide = true;
  if (this._popupHideTimer) {
    window.clearTimeout(this._popupHideTimer);
    delete this._popupHideTimer;
  }
}

/**
 * Show the popup, loaded with the specified annotation, at the specified coordinates.
 * @param {Object} annotation the annotation
 * @param {number} x coordinate (relative to the image)
 * @param {number} y coordiante (relative to the image)
 */
annotorious.viewer.Popup.prototype.show = function(annotation, x, y) {
  this.clearHideTimer();

  if (annotation && x && y) {
    // New annotation and position - reset
    this._currentAnnotation = annotation;
    this._text.innerHTML = annotorious.viewer.Popup.toHTML(annotation.text);
    this.setPosition(x, y);

    if (this._buttonHideTimer)
      window.clearTimeout(this._buttonHideTimer);

    goog.style.setOpacity(this._buttons, 0.4);
    var self = this;
    this._buttonHideTimer = window.setTimeout(function() {
      goog.style.setOpacity(self._buttons, 0);
    }, 1000);
  }

  goog.style.setOpacity(this._element, 0.9);
}

/**
 * Set the position of the popup.
 * @param {number} x coordinate (relative to the image)
 * @param {number} y coordinate (realtive to the image)
 */
annotorious.viewer.Popup.prototype.setPosition = function(x, y) {
  goog.style.setPosition(this._element, new goog.math.Coordinate(x, y));
}

annotorious.viewer.Popup.toHTML = function(text) {
  var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
  return text.replace(exp,"$1");
}

<commit_msg>Popup pointer-events now set to 'none' on hide<commit_after>goog.provide('annotorious.viewer');

goog.require('goog.style');
goog.require('goog.dom');
goog.require('goog.dom.query');

/**
 * A popup bubble widget to show annotation details.
 * @param {element} parentEl the DOM element to attach to
 * @param {annotorious.modules.image.ImageAnnotator} annotator reference to the annotator
 * @constructor
 */
annotorious.viewer.Popup = function(parentEl, annotator) {
  /** @private **/
  this._annotator = annotator;

  /** @private **/
  this._element = goog.soy.renderAsElement(annotorious.templates.popup);

  /** @private **/
  this._currentAnnotation;

  /** @private **/
  this._text = goog.dom.query('.yuma-popup-text', this._element)[0];

  /** @private **/
  this._buttons = goog.dom.query('.yuma-popup-action-delete', this._element)[0];

  /** @private **/
  this._popupHideTimer;

  /** @private **/
  this._buttonHideTimer;

  /** @private **/
  this._cancelHide = false;

  var btnDelete = goog.dom.query('.yuma-popup-action-delete', this._element)[0];

  var self = this;
  goog.events.listen(btnDelete, goog.events.EventType.CLICK, function(event) {
    goog.style.setOpacity(self._element, 0.0);
    annotator.fireEvent(annotorious.events.EventType.POPUP_BTN_DELETE,
      { annotation: self._currentAnnotation, mouseEvent: event });
  });

  goog.events.listen(this._buttons, goog.events.EventType.MOUSEOVER, function(event) {
    goog.style.setOpacity(self._buttons, 0.9);
  });

  goog.events.listen(this._buttons, goog.events.EventType.MOUSEOUT, function() {
    goog.style.setOpacity(self._buttons, 0.4);
  });

  goog.events.listen(this._element, goog.events.EventType.MOUSEOVER, function(event) {
    window.clearTimeout(self._buttonHideTimer);
    if (goog.style.getStyle(self._buttons, 'opacity') < 0.4)
      goog.style.setOpacity(self._buttons, 0.4);
    self.clearHideTimer();
  });

  goog.events.listen(this._element, goog.events.EventType.MOUSEOUT, function(event) {
    goog.style.setOpacity(self._buttons, 0);
    self.startHideTimer();
  });

  goog.style.setOpacity(this._buttons, 0.0);
  goog.style.setOpacity(this._element, 0.0);
  goog.dom.appendChild(parentEl, this._element);
}

/**
 * Start the popup hide timer.
 */
annotorious.viewer.Popup.prototype.startHideTimer = function() {
  this._cancelHide = false;
  if (!this._popupHideTimer) {
    var self = this;
    this._popupHideTimer = window.setTimeout(function() {
      self._annotator.fireEvent(annotorious.events.EventType.BEFORE_POPUP_HIDE);
      if (!self._cancelHide) {
        goog.style.setOpacity(self._element, 0.0);
        goog.style.setStyle(self._element, 'pointer-events', 'none');
        goog.style.setOpacity(self._buttons, 0.4);
        delete self._popupHideTimer;
      }
    }, 300);
  }
}

/**
 * Clear the popup hide timer.
 */
annotorious.viewer.Popup.prototype.clearHideTimer = function() {
  this._cancelHide = true;
  if (this._popupHideTimer) {
    window.clearTimeout(this._popupHideTimer);
    delete this._popupHideTimer;
  }
}

/**
 * Show the popup, loaded with the specified annotation, at the specified coordinates.
 * @param {Object} annotation the annotation
 * @param {number} x coordinate (relative to the image)
 * @param {number} y coordiante (relative to the image)
 */
annotorious.viewer.Popup.prototype.show = function(annotation, x, y) {
  this.clearHideTimer();

  if (annotation && x && y) {
    // New annotation and position - reset
    this._currentAnnotation = annotation;
    this._text.innerHTML = annotorious.viewer.Popup.toHTML(annotation.text);
    this.setPosition(x, y);

    if (this._buttonHideTimer)
      window.clearTimeout(this._buttonHideTimer);

    goog.style.setOpacity(this._buttons, 0.4);
    var self = this;
    this._buttonHideTimer = window.setTimeout(function() {
      goog.style.setOpacity(self._buttons, 0);
    }, 1000);
  }

  goog.style.setOpacity(this._element, 0.9);
  goog.style.setStyle(self._element, 'pointer-events', 'auto');
}

/**
 * Set the position of the popup.
 * @param {number} x coordinate (relative to the image)
 * @param {number} y coordinate (realtive to the image)
 */
annotorious.viewer.Popup.prototype.setPosition = function(x, y) {
  goog.style.setPosition(this._element, new goog.math.Coordinate(x, y));
}

annotorious.viewer.Popup.toHTML = function(text) {
  var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
  return text.replace(exp,"$1");
}

<|endoftext|>


<commit_before>import authUtils from './authUtils';
import {BaseConfig}  from './baseConfig';
import {inject} from 'aurelia-framework';

@inject(BaseConfig)
export class Popup {
  constructor(config) {
    this.config      = config.current;
    this.popupWindow = null;
    this.polling     = null;
    this.url         = '';
  }

  open(url, windowName, options, redirectUri) {
    this.url          = url;
    let optionsString = this.stringifyOptions(this.prepareOptions(options || {}));

    this.popupWindow = window.open(url, windowName, optionsString);

    if (this.popupWindow && this.popupWindow.focus) {
      this.popupWindow.focus();
    }

    return this;
  }

  eventListener(redirectUri) {
    let self    = this;
    let promise = new Promise((resolve, reject) => {
      self.popupWindow.addEventListener('loadstart', (event) => {
        if (event.url.indexOf(redirectUri) !== 0) {
          return;
        }

        let parser  = document.createElement('a');
        parser.href = event.url;

        if (parser.search || parser.hash) {
          let queryParams = parser.search.substring(1).replace(/\/$/, '');
          let hashParams  = parser.hash.substring(1).replace(/\/$/, '');
          let hash        = authUtils.parseQueryString(hashParams);
          let qs          = authUtils.parseQueryString(queryParams);

          authUtils.extend(qs, hash);

          if (qs.error) {
            reject({
              error: qs.error
            });
          } else {
            resolve(qs);
          }

          self.popupWindow.close();
        }
      });

      popupWindow.addEventListener('exit', () => {
        reject({
          data: 'Provider Popup was closed'
        });
      });

      popupWindow.addEventListener('loaderror', () => {
        deferred.reject({
          data: 'Authorization Failed'
        });
      });
    });
    return promise;
  }

  pollPopup() {
    let self = this;

    return new Promise((resolve, reject) => {
      this.polling = setInterval(() => {
        let errorData;

        try {
          let documentOrigin    = document.location.host;
          let popupWindowOrigin = self.popupWindow.location.host;

          if (popupWindowOrigin === documentOrigin && (self.popupWindow.location.search || self.popupWindow.location.hash)) {
            let queryParams = self.popupWindow.location.search.substring(1).replace(/\/$/, '');
            let hashParams  = self.popupWindow.location.hash.substring(1).replace(/[\/$]/, '');
            let hash        = authUtils.parseQueryString(hashParams);
            let qs          = authUtils.parseQueryString(queryParams);

            authUtils.extend(qs, hash);

            if (qs.error) {
              reject({
                error: qs.error
              });
            } else {
              resolve(qs);
            }

            self.popupWindow.close();
            clearInterval(self.polling);
          }
        } catch (error) {
          errorData = error;
        }

        if (!self.popupWindow) {
          clearInterval(self.polling);
          reject({
            error: errorData,
            data: 'Provider Popup Blocked'
          });
        } else if (self.popupWindow.closed) {
          clearInterval(self.polling);
          reject({
            error: errorData,
            data: 'Problem poll popup'
          });
        }
      }, 35);
    });
  }

  prepareOptions(options) {
    let width  = options.width || 500;
    let height = options.height || 500;

    return authUtils.extend({
      width: width,
      height: height,
      left: window.screenX + ((window.outerWidth - width) / 2),
      top: window.screenY + ((window.outerHeight - height) / 2.5)
    }, options);
  }

  stringifyOptions(options) {
    let parts = [];
    authUtils.forEach(options, function(value, key) {
      parts.push(key + '=' + value);
    });
    return parts.join(',');
  }
}
<commit_msg>chore(popup): No need for self, replaced with this.<commit_after>import authUtils from './authUtils';
import {BaseConfig}  from './baseConfig';
import {inject} from 'aurelia-framework';

@inject(BaseConfig)
export class Popup {
  constructor(config) {
    this.config      = config.current;
    this.popupWindow = null;
    this.polling     = null;
    this.url         = '';
  }

  open(url, windowName, options, redirectUri) {
    this.url          = url;
    let optionsString = this.stringifyOptions(this.prepareOptions(options || {}));

    this.popupWindow = window.open(url, windowName, optionsString);

    if (this.popupWindow && this.popupWindow.focus) {
      this.popupWindow.focus();
    }

    return this;
  }

  eventListener(redirectUri) {
    let promise = new Promise((resolve, reject) => {
      this.popupWindow.addEventListener('loadstart', (event) => {
        if (event.url.indexOf(redirectUri) !== 0) {
          return;
        }

        let parser  = document.createElement('a');
        parser.href = event.url;

        if (parser.search || parser.hash) {
          let queryParams = parser.search.substring(1).replace(/\/$/, '');
          let hashParams  = parser.hash.substring(1).replace(/\/$/, '');
          let hash        = authUtils.parseQueryString(hashParams);
          let qs          = authUtils.parseQueryString(queryParams);

          authUtils.extend(qs, hash);

          if (qs.error) {
            reject({
              error: qs.error
            });
          } else {
            resolve(qs);
          }

          this.popupWindow.close();
        }
      });

      popupWindow.addEventListener('exit', () => {
        reject({
          data: 'Provider Popup was closed'
        });
      });

      popupWindow.addEventListener('loaderror', () => {
        deferred.reject({
          data: 'Authorization Failed'
        });
      });
    });

    return promise;
  }

  pollPopup() {
    return new Promise((resolve, reject) => {
      this.polling = setInterval(() => {
        let errorData;

        try {
          let documentOrigin    = document.location.host;
          let popupWindowOrigin = this.popupWindow.location.host;

          if (popupWindowOrigin === documentOrigin && (this.popupWindow.location.search || this.popupWindow.location.hash)) {
            let queryParams = this.popupWindow.location.search.substring(1).replace(/\/$/, '');
            let hashParams  = this.popupWindow.location.hash.substring(1).replace(/[\/$]/, '');
            let hash        = authUtils.parseQueryString(hashParams);
            let qs          = authUtils.parseQueryString(queryParams);

            authUtils.extend(qs, hash);

            if (qs.error) {
              reject({
                error: qs.error
              });
            } else {
              resolve(qs);
            }

            this.popupWindow.close();
            clearInterval(this.polling);
          }
        } catch (error) {
          errorData = error;
        }

        if (!this.popupWindow) {
          clearInterval(this.polling);
          reject({
            error: errorData,
            data: 'Provider Popup Blocked'
          });
        } else if (this.popupWindow.closed) {
          clearInterval(this.polling);
          reject({
            error: errorData,
            data: 'Problem poll popup'
          });
        }
      }, 35);
    });
  }

  prepareOptions(options) {
    let width  = options.width || 500;
    let height = options.height || 500;

    return authUtils.extend({
      width: width,
      height: height,
      left: window.screenX + ((window.outerWidth - width) / 2),
      top: window.screenY + ((window.outerHeight - height) / 2.5)
    }, options);
  }

  stringifyOptions(options) {
    let parts = [];
    authUtils.forEach(options, function(value, key) {
      parts.push(key + '=' + value);
    });
    return parts.join(',');
  }
}
<|endoftext|>


<commit_before>function openOptions() {
  if (window.chrome) {
    chrome.runtime.openOptionsPage(err => {
      console.error(`Error: ${err}`);
    });
  } else if (window.browser) {
    window.browser.runtime.openOptionsPage().catch(err => {
      console.error(`Error: ${err}`);
    });
  }
}

function getCurrentTab() {
  if (window.chrome) {
    return new Promise((resolve) => {
      chrome.tabs.query({ currentWindow:true, active:true }, tabs => {
        resolve(tabs[0])
      })
    })
  } else if (window.browser) {
    return browser.tabs.query({ currentWindow:true, active:true }).then(tabs => tabs[0])
  }
}

function executeAction(tabId, action, data) {
  if (window.chrome) {
    return new Promise((resolve) => {
      chrome.tabs.sendMessage(tabId, { action, data }, {}, resolve)
    })
  } else if (window.browser) {
    return browser.tabs.sendMessage(tabId, { action, data })
  }
}

function brushAccountId(val) {
  const r = val.match(/^(\d{4})\-(\d{4})\-(\d{4})$/);
  if (!r) return val;
  return r[1] + r[2] + r[3];
}

window.onload = function() {
  document.getElementById('openOptionsLink').onclick = function(e) {
    openOptions();
    return false;
  }

  document.getElementById('openUpdateNoticeLink').onclick = function(e) {
    chrome.tabs.create({ url: chrome.extension.getURL('updated.html')}, function(tab){});
    return false;
  }

  document.getElementById('openCreditsLink').onclick = function(e) {
    chrome.tabs.create({ url: chrome.extension.getURL('credits.html')}, function(tab){});
    return false;
  }

  main();
}

function main() {
  getCurrentTab()
    .then(tab => {
      const url = new URL(tab.url)
      if (url.host.endsWith('.aws.amazon.com')
       || url.host.endsWith('.amazonaws-us-gov.com')
       || url.host.endsWith('.amazonaws.cn')) {
        executeAction(tab.id, 'loadInfo', {}).then(userInfo => {
          loadFormList(url, userInfo, tab.id);
          document.querySelector('main').style.display = 'block';
        });
      }
    })
}

function loadFormList(currentUrl, userInfo, tabId) {
  chrome.storage.sync.get([
    'profiles', 'profiles_1', 'profiles_2', 'profiles_3', 'profiles_4',
    'hidesAccountId', 'showOnlyMatchingRoles',
  ], function(data) {
    const hidesAccountId = data.hidesAccountId || false;
    const showOnlyMatchingRoles = data.showOnlyMatchingRoles || false;

    if (data.profiles) {
      const dps = new DataProfilesSplitter();
      const profiles = dps.profilesFromDataSet(data);
      const {
        loginDisplayNameAccount, loginDisplayNameUser,
        roleDisplayNameAccount, roleDisplayNameUser, isGlobal
      } = userInfo;

      const baseAccount = brushAccountId(loginDisplayNameAccount);
      const filterByTargetRole = showOnlyMatchingRoles ? (roleDisplayNameUser || loginDisplayNameUser.split("/", 2)[0]) : null;
      const profileSet = new ProfileSet(profiles, baseAccount, { filterByTargetRole });

      const list = document.getElementById('roleList');
      loadProfiles(profileSet, tabId, list, currentUrl, isGlobal, hidesAccountId);
    }
  });
}

function loadProfiles(profileSet, tabId, list, currentUrl, isGlobal, hidesAccountId) {
  profileSet.destProfiles.forEach(item => {
    const color = item.color || 'aaaaaa';
    const li = document.createElement('li');
    const headSquare = document.createElement('span');
    headSquare.textContent = ' ';
    headSquare.className = 'headSquare';
    headSquare.style = `background-color: #${color}`
    if (item.image) {
      headSquare.style += `; background-image: url('${item.image.replace(/"/g, '')}');`
    }

    const anchor = document.createElement('a');
    anchor.href = "#";
    anchor.title = item.role_name + '@' + item.aws_account_id;
    anchor.dataset.profile = item.profile;
    anchor.dataset.rolename = item.role_name;
    anchor.dataset.account = item.aws_account_id;
    anchor.dataset.color = color;
    anchor.dataset.redirecturi = replaceRedirectURI(currentUrl, item.region, isGlobal);
    anchor.dataset.search = item.profile.toLowerCase() + ' ' + item.aws_account_id;

    anchor.appendChild(headSquare);
    anchor.appendChild(document.createTextNode(item.profile));

    if (hidesAccountId) {
      anchor.dataset.displayname = item.profile;
    } else {
      anchor.dataset.displayname = item.profile + '  |  ' + item.aws_account_id;

      const accountIdSpan = document.createElement('span');
      accountIdSpan.className = 'suffixAccountId';
      accountIdSpan.textContent = item.aws_account_id;
      anchor.appendChild(accountIdSpan);
    }

    li.appendChild(anchor);
    list.appendChild(li);
  });

  Array.from(list.querySelectorAll('li a')).forEach(anchor => {
    anchor.onclick = function() {
      const data = { ...this.dataset }; // do not directly refer DOM data in Firefox
      sendSwitchRole(tabId, data);
      return false;
    }
  });

  let AWSR_firstAnchor = null;
  document.getElementById('roleFilter').onkeyup = function(e) {
    const words = this.value.toLowerCase().split(' ');
    if (e.keyCode === 13) {
      if (AWSR_firstAnchor) {
        AWSR_firstAnchor.click()
      }
    } else {
      const lis = Array.from(document.querySelectorAll('#roleList > li'));
      let firstHitLi = null;
      lis.forEach(li => {
        const anchor = li.querySelector('a')
        const profileName = anchor.dataset.search;
        const hit = words.every(it => profileName.includes(it));
        li.style.display = hit ? 'block' : 'none';
        li.style.background = null;
        if (hit && firstHitLi === null) firstHitLi = li;
      });

      if (firstHitLi) {
        firstHitLi.style.background = '#f0f9ff';
        AWSR_firstAnchor = firstHitLi.querySelector('a');
      } else {
        AWSR_firstAnchor = null;
      }
    }
  }

  document.getElementById('roleFilter').focus()
}

function replaceRedirectURI(currentURL, destRegion, isGlobal) {
  if (!destRegion) return currentURL;

  let redirectUri = decodeURIComponent(currentURL);
  const md = currentURL.search.match(/region=([a-z\-1-9]+)/);
  if (md) {
    const currentRegion = md[1];
    if (currentRegion !== destRegion) {
      redirectUri = redirectUri.replace(new RegExp(currentRegion, 'g'), destRegion);
      if (!isGlobal) {
        if (currentRegion === 'us-east-1') {
          redirectUri = redirectUri.replace('://', `://${destRegion}.`);
        } else if (destRegion === 'us-east-1') {
          redirectUri = redirectUri.replace(/:\/\/[^.]+\./, '://');
        }
      }
    }
    redirectUri = encodeURIComponent(redirectUri);
  }
  return redirectUri;
}

function sendSwitchRole(tabId, data) {
  executeAction(tabId, 'switch', data).then(() => {
    window.close()
  });
}
<commit_msg>fix encoding redirect uri<commit_after>function openOptions() {
  if (window.chrome) {
    chrome.runtime.openOptionsPage(err => {
      console.error(`Error: ${err}`);
    });
  } else if (window.browser) {
    window.browser.runtime.openOptionsPage().catch(err => {
      console.error(`Error: ${err}`);
    });
  }
}

function getCurrentTab() {
  if (window.chrome) {
    return new Promise((resolve) => {
      chrome.tabs.query({ currentWindow:true, active:true }, tabs => {
        resolve(tabs[0])
      })
    })
  } else if (window.browser) {
    return browser.tabs.query({ currentWindow:true, active:true }).then(tabs => tabs[0])
  }
}

function executeAction(tabId, action, data) {
  if (window.chrome) {
    return new Promise((resolve) => {
      chrome.tabs.sendMessage(tabId, { action, data }, {}, resolve)
    })
  } else if (window.browser) {
    return browser.tabs.sendMessage(tabId, { action, data })
  }
}

function brushAccountId(val) {
  const r = val.match(/^(\d{4})\-(\d{4})\-(\d{4})$/);
  if (!r) return val;
  return r[1] + r[2] + r[3];
}

window.onload = function() {
  document.getElementById('openOptionsLink').onclick = function(e) {
    openOptions();
    return false;
  }

  document.getElementById('openUpdateNoticeLink').onclick = function(e) {
    chrome.tabs.create({ url: chrome.extension.getURL('updated.html')}, function(tab){});
    return false;
  }

  document.getElementById('openCreditsLink').onclick = function(e) {
    chrome.tabs.create({ url: chrome.extension.getURL('credits.html')}, function(tab){});
    return false;
  }

  main();
}

function main() {
  getCurrentTab()
    .then(tab => {
      const url = new URL(tab.url)
      if (url.host.endsWith('.aws.amazon.com')
       || url.host.endsWith('.amazonaws-us-gov.com')
       || url.host.endsWith('.amazonaws.cn')) {
        executeAction(tab.id, 'loadInfo', {}).then(userInfo => {
          loadFormList(url, userInfo, tab.id);
          document.querySelector('main').style.display = 'block';
        });
      }
    })
}

function loadFormList(currentUrl, userInfo, tabId) {
  chrome.storage.sync.get([
    'profiles', 'profiles_1', 'profiles_2', 'profiles_3', 'profiles_4',
    'hidesAccountId', 'showOnlyMatchingRoles',
  ], function(data) {
    const hidesAccountId = data.hidesAccountId || false;
    const showOnlyMatchingRoles = data.showOnlyMatchingRoles || false;

    if (data.profiles) {
      const dps = new DataProfilesSplitter();
      const profiles = dps.profilesFromDataSet(data);
      const {
        loginDisplayNameAccount, loginDisplayNameUser,
        roleDisplayNameAccount, roleDisplayNameUser, isGlobal
      } = userInfo;

      const baseAccount = brushAccountId(loginDisplayNameAccount);
      const filterByTargetRole = showOnlyMatchingRoles ? (roleDisplayNameUser || loginDisplayNameUser.split("/", 2)[0]) : null;
      const profileSet = new ProfileSet(profiles, baseAccount, { filterByTargetRole });

      const list = document.getElementById('roleList');
      loadProfiles(profileSet, tabId, list, currentUrl, isGlobal, hidesAccountId);
    }
  });
}

function loadProfiles(profileSet, tabId, list, currentUrl, isGlobal, hidesAccountId) {
  profileSet.destProfiles.forEach(item => {
    const color = item.color || 'aaaaaa';
    const li = document.createElement('li');
    const headSquare = document.createElement('span');
    headSquare.textContent = ' ';
    headSquare.className = 'headSquare';
    headSquare.style = `background-color: #${color}`
    if (item.image) {
      headSquare.style += `; background-image: url('${item.image.replace(/"/g, '')}');`
    }

    const anchor = document.createElement('a');
    anchor.href = "#";
    anchor.title = item.role_name + '@' + item.aws_account_id;
    anchor.dataset.profile = item.profile;
    anchor.dataset.rolename = item.role_name;
    anchor.dataset.account = item.aws_account_id;
    anchor.dataset.color = color;
    anchor.dataset.redirecturi = createRedirectURI(currentUrl, item.region, isGlobal);
    anchor.dataset.search = item.profile.toLowerCase() + ' ' + item.aws_account_id;

    anchor.appendChild(headSquare);
    anchor.appendChild(document.createTextNode(item.profile));

    if (hidesAccountId) {
      anchor.dataset.displayname = item.profile;
    } else {
      anchor.dataset.displayname = item.profile + '  |  ' + item.aws_account_id;

      const accountIdSpan = document.createElement('span');
      accountIdSpan.className = 'suffixAccountId';
      accountIdSpan.textContent = item.aws_account_id;
      anchor.appendChild(accountIdSpan);
    }

    li.appendChild(anchor);
    list.appendChild(li);
  });

  Array.from(list.querySelectorAll('li a')).forEach(anchor => {
    anchor.onclick = function() {
      const data = { ...this.dataset }; // do not directly refer DOM data in Firefox
      sendSwitchRole(tabId, data);
      return false;
    }
  });

  let AWSR_firstAnchor = null;
  document.getElementById('roleFilter').onkeyup = function(e) {
    const words = this.value.toLowerCase().split(' ');
    if (e.keyCode === 13) {
      if (AWSR_firstAnchor) {
        AWSR_firstAnchor.click()
      }
    } else {
      const lis = Array.from(document.querySelectorAll('#roleList > li'));
      let firstHitLi = null;
      lis.forEach(li => {
        const anchor = li.querySelector('a')
        const profileName = anchor.dataset.search;
        const hit = words.every(it => profileName.includes(it));
        li.style.display = hit ? 'block' : 'none';
        li.style.background = null;
        if (hit && firstHitLi === null) firstHitLi = li;
      });

      if (firstHitLi) {
        firstHitLi.style.background = '#f0f9ff';
        AWSR_firstAnchor = firstHitLi.querySelector('a');
      } else {
        AWSR_firstAnchor = null;
      }
    }
  }

  document.getElementById('roleFilter').focus()
}

function createRedirectURI(currentURL, destRegion, isGlobal) {
  if (!destRegion) return encodeURIComponent(currentURL.href);

  let redirectUri = currentURL.href;
  const md = currentURL.search.match(/region=([a-z\-1-9]+)/);
  if (md) {
    const currentRegion = md[1];
    if (currentRegion !== destRegion) {
      redirectUri = redirectUri.replace(new RegExp(currentRegion, 'g'), destRegion);
      if (!isGlobal) {
        if (currentRegion === 'us-east-1') {
          redirectUri = redirectUri.replace('://', `://${destRegion}.`);
        } else if (destRegion === 'us-east-1') {
          redirectUri = redirectUri.replace(/:\/\/[^.]+\./, '://');
        }
      }
    }
  }
  return encodeURIComponent(redirectUri);
}

function sendSwitchRole(tabId, data) {
  executeAction(tabId, 'switch', data).then(() => {
    window.close()
  });
}
<|endoftext|>


<commit_before>/*
 Inspired by pyson.js implementation in Sao.
 */
goog.provide('ng.tryton.PYSON');

goog.require('goog.object');
goog.require('goog.date.Interval');

goog.require('ng.tryton.fulfil');

goog.scope(function() {
  'use strict';

  var Fulfil = ng.tryton.fulfil;

  /**
   * Pyson Service.
   * @constructor
   */
  ng.tryton.PYSON = function() {};

  var PYSON = ng.tryton.PYSON;

  /**
   * This method accepts JSON parsed PYSON object.
   * Depending upon the class of the object, call relevant function.
   * @param pysonObj
   * @param context
   */
  PYSON.prototype.evaluate = function(pysonObj, context) {
    var cls = pysonObj.__class__;
    switch (cls) {
      case 'Eval':
        return PYSON.Eval.call(this, pysonObj, context);
      case 'Not':
        return PYSON.Not.call(this, pysonObj);
      case 'Bool':
        return PYSON.Bool.call(this, pysonObj);
      case 'And':
        return PYSON.And.call(this, pysonObj);
      case 'Or':
        return PYSON.Or.call(this, pysonObj);
      case 'Equal':
        return PYSON.Equal.call(this, pysonObj);
      case 'Greater':
        return PYSON.Greater.call(this, pysonObj);
      case 'Less':
        return PYSON.Less.call(this, pysonObj);
      case 'If':
        return PYSON.If.call(this, pysonObj);
      case 'Get':
        return PYSON.Get.call(this, pysonObj);
      case 'In':
        return PYSON.In.call(this, pysonObj);
      case 'date':
      case 'Date':
        return PYSON.Date.call(this, pysonObj);
      case 'datetime':
      case 'DateTime':
        return PYSON.DateTime.call(this, pysonObj);
      case 'Len':
        return PYSON.Len.call(this, pysonObj);
    }
  };

  /**
   * This method accept data object and convert that to pyson string
   * @param
   */
  PYSON.prototype.fromObject = function(dataObject) {
    return JSON.stringify(Fulfil.transformRequest(dataObject));
  };

  PYSON.Eval = function (value, context) {
    if (value.v in context) {
      return context[value.v];
    } else {
      return value.d;
    }
  };

  /**
   * @return {boolean}
   */
  PYSON.Not = function (value) {
    return !value.v;
  };

  /**
   * @return {boolean}
   */
  PYSON.Bool = function (value) {
    if (value.v instanceof Object) {
      return !goog.object.isEmpty(value.v);
    } else {
      return Boolean(value.v);
    }
  };

  PYSON.And = function (value) {
    var result = true;
    for (var i = 0, len = value.s.length; i < len; i++) {
      var statement = value.s[i];
      result = result && statement;
    }
    return result;
  };

  PYSON.Or = function (value) {
    var result = false;
    for (var i = 0, len = value.s.length; i < len; i++) {
      var statement = value.s[i];
      result = result || (
        goog.isObject(statement) ? !goog.object.isEmpty(statement) : !!statement
      );
    }
    return result;
  };

  /**
   * @return {boolean}
   */
  PYSON.Equal = function (value) {
    return value.s1 == value.s2;
  };

  /**
   * @return {boolean}
   */
  PYSON.Greater = function (v) {
    var value = {};
    goog.object.extend(value, v);
    value.s1 = Number(value.s1);
    value.s2 = Number(value.s2);
    if (value.e) {
      return value.s1 >= value.s2;
    } else {
      return value.s1 > value.s2;
    }
  };

  /**
   * @return {boolean}
   */
  PYSON.Less = function (v) {
    var value = {};
    goog.object.extend(value, v);
    value.s1 = Number(value.s1);
    value.s2 = Number(value.s2);
    if (value.e) {
      return value.s1 <= value.s2;
    } else {
      return value.s1 < value.s2;
    }
  };

  PYSON.If = function (value) {
    if (value.c) {
      return value.t;
    } else {
      return value.e;
    }
  };

  PYSON.Get = function (value) {
    if (value.k in value.v) {
      return value.v[value.k];
    } else {
      return value.d;
    }
  };

  PYSON.In = function (value) {
    if (goog.isArray(value.v)) {
      return goog.object.contains(value.v, value.k);
    } else {
      return goog.object.containsKey(value.v, value.k);
    }
  };

  PYSON.Date = function (value) {
    let interval = new goog.date.Interval();
    let month = value.M || value.month;

    let date = new Fulfil.datatype.Date(
      value.y || value.year,
      month && month - 1,
      value.d || value.day
    );

    if (value.dy) interval.years = value.dy;
    if (value.dM) interval.months = value.dM;
    if (value.dd) interval.days = value.dd;
    date.add(interval)
    return date;
  };

  PYSON.DateTime = function (value) {
    var interval = new goog.date.Interval();
    let month = value.M || value.month;
    let ms = value.ms || value.microsecond;
    var date = new Fulfil.datatype.DateTime(
      value.y || value.year,
      month && month - 1,
      value.d || value.day,
      value.h || value.hour,
      value.m || value.minute,
      value.s || value.second,
      ms && ms / 1000
    );

    if (value.dy) interval.years = value.dy;
    if (value.dM) interval.months = value.dM;
    if (value.dd) interval.days = value.dd;
    if (value.dh) interval.hours = value.dh;
    if (value.dm) interval.minutes = value.dm;
    if (value.ds) interval.seconds = value.ds;
    // TODO: Handle ms
    date.add(interval)
    return date;
  };

  PYSON.Len = function (value) {
    return goog.object.getCount(value.v);
  };
});   // goog.scope
<commit_msg>pyson datetime should be in utc<commit_after>/*
 Inspired by pyson.js implementation in Sao.
 */
goog.provide('ng.tryton.PYSON');

goog.require('goog.object');
goog.require('goog.date.Interval');

goog.require('ng.tryton.fulfil');

goog.scope(function() {
  'use strict';

  var Fulfil = ng.tryton.fulfil;

  /**
   * Pyson Service.
   * @constructor
   */
  ng.tryton.PYSON = function() {};

  var PYSON = ng.tryton.PYSON;

  /**
   * This method accepts JSON parsed PYSON object.
   * Depending upon the class of the object, call relevant function.
   * @param pysonObj
   * @param context
   */
  PYSON.prototype.evaluate = function(pysonObj, context) {
    var cls = pysonObj.__class__;
    switch (cls) {
      case 'Eval':
        return PYSON.Eval.call(this, pysonObj, context);
      case 'Not':
        return PYSON.Not.call(this, pysonObj);
      case 'Bool':
        return PYSON.Bool.call(this, pysonObj);
      case 'And':
        return PYSON.And.call(this, pysonObj);
      case 'Or':
        return PYSON.Or.call(this, pysonObj);
      case 'Equal':
        return PYSON.Equal.call(this, pysonObj);
      case 'Greater':
        return PYSON.Greater.call(this, pysonObj);
      case 'Less':
        return PYSON.Less.call(this, pysonObj);
      case 'If':
        return PYSON.If.call(this, pysonObj);
      case 'Get':
        return PYSON.Get.call(this, pysonObj);
      case 'In':
        return PYSON.In.call(this, pysonObj);
      case 'date':
      case 'Date':
        return PYSON.Date.call(this, pysonObj);
      case 'datetime':
      case 'DateTime':
        return PYSON.DateTime.call(this, pysonObj);
      case 'Len':
        return PYSON.Len.call(this, pysonObj);
    }
  };

  /**
   * This method accept data object and convert that to pyson string
   * @param
   */
  PYSON.prototype.fromObject = function(dataObject) {
    return JSON.stringify(Fulfil.transformRequest(dataObject));
  };

  PYSON.Eval = function (value, context) {
    if (value.v in context) {
      return context[value.v];
    } else {
      return value.d;
    }
  };

  /**
   * @return {boolean}
   */
  PYSON.Not = function (value) {
    return !value.v;
  };

  /**
   * @return {boolean}
   */
  PYSON.Bool = function (value) {
    if (value.v instanceof Object) {
      return !goog.object.isEmpty(value.v);
    } else {
      return Boolean(value.v);
    }
  };

  PYSON.And = function (value) {
    var result = true;
    for (var i = 0, len = value.s.length; i < len; i++) {
      var statement = value.s[i];
      result = result && statement;
    }
    return result;
  };

  PYSON.Or = function (value) {
    var result = false;
    for (var i = 0, len = value.s.length; i < len; i++) {
      var statement = value.s[i];
      result = result || (
        goog.isObject(statement) ? !goog.object.isEmpty(statement) : !!statement
      );
    }
    return result;
  };

  /**
   * @return {boolean}
   */
  PYSON.Equal = function (value) {
    return value.s1 == value.s2;
  };

  /**
   * @return {boolean}
   */
  PYSON.Greater = function (v) {
    var value = {};
    goog.object.extend(value, v);
    value.s1 = Number(value.s1);
    value.s2 = Number(value.s2);
    if (value.e) {
      return value.s1 >= value.s2;
    } else {
      return value.s1 > value.s2;
    }
  };

  /**
   * @return {boolean}
   */
  PYSON.Less = function (v) {
    var value = {};
    goog.object.extend(value, v);
    value.s1 = Number(value.s1);
    value.s2 = Number(value.s2);
    if (value.e) {
      return value.s1 <= value.s2;
    } else {
      return value.s1 < value.s2;
    }
  };

  PYSON.If = function (value) {
    if (value.c) {
      return value.t;
    } else {
      return value.e;
    }
  };

  PYSON.Get = function (value) {
    if (value.k in value.v) {
      return value.v[value.k];
    } else {
      return value.d;
    }
  };

  PYSON.In = function (value) {
    if (goog.isArray(value.v)) {
      return goog.object.contains(value.v, value.k);
    } else {
      return goog.object.containsKey(value.v, value.k);
    }
  };

  PYSON.Date = function (value) {
    let interval = new goog.date.Interval();
    let month = value.M || value.month;

    let date = new Fulfil.datatype.Date(
      value.y || value.year,
      month && month - 1,
      value.d || value.day
    );

    if (value.dy) interval.years = value.dy;
    if (value.dM) interval.months = value.dM;
    if (value.dd) interval.days = value.dd;
    date.add(interval)
    return date;
  };

  PYSON.DateTime = function (value) {
    var interval = new goog.date.Interval();
    let month = value.M || value.month;
    let ms = value.ms || value.microsecond;
    var date = new Fulfil.datatype.DateTime(
      value.y || value.year,
      month && month - 1,
      value.d || value.day,
      value.h || value.hour,
      value.m || value.minute,
      value.s || value.second,
      ms && ms / 1000,
      true
    );

    if (value.dy) interval.years = value.dy;
    if (value.dM) interval.months = value.dM;
    if (value.dd) interval.days = value.dd;
    if (value.dh) interval.hours = value.dh;
    if (value.dm) interval.minutes = value.dm;
    if (value.ds) interval.seconds = value.ds;
    // TODO: Handle ms
    date.add(interval)
    return date;
  };

  PYSON.Len = function (value) {
    return goog.object.getCount(value.v);
  };
});   // goog.scope
<|endoftext|>