<commit_before>/**
 * This script is used to convert wikimedia dump of official wiki
 * to JSON format used in this extension.
 *
 * To get create xml files used by this file, you need to go to /Special:Export/,
 * select Functions or Commands category, then save them as functionsExport.xml and
 * operatorsExport.xml.
 *
 */

var fs = require('fs');
var xmldoc = require('xmldoc');

function fixSyntax(syntax) {
	syntax = removeMoreTags(syntax);

	var eq = syntax.indexOf('=');
	if (eq >= 0) {
		syntax = syntax.substr(eq + 1).trim();
	}

	return syntax.trim();
}

function parseValue(value, type) {
	if (type == "syntax") {
		return fixSyntax(value);
	}
	if (type == "return value") {
		var ret = value.trim();
		var dash = ret.indexOf("-");
		if (dash >= 0) {
			ret = ret.substr(0, dash - 1).trim();
		}
		return ret;
	}
}

function loadBlock(text, opening, closing, debug) {
	let bcount = -1;
	let index = 0;
	let result = '';
	while (index < text.length) {
		let chunk = 1;
		if (text.substr(index, opening.length) === opening) {
			bcount++;
			chunk = 2;
		}
		if (text.substr(index, closing.length) === closing) {
			bcount--;
			chunk = 2;

			if (bcount < 0) {
				break;
			}
		}
		result += text.substr(index, chunk);
		index += chunk;
	}
	return result;
}

function removeMoreTags(value) {
	if (!value)
		return value;

	return value
		.replace(/
/ig, '')
		.replace(/''\(since \[.*?\]\)''/ig, '')
		.replace(/''\(since \[.*?\]\)''/ig, '')
		.replace(/\(''since .*?''\)/ig, '')
		.replace(/''since [^']*''/ig, '')
		.replace(/<nowiki>(.*?)<\/nowiki>/g, '$1')
		.replace(/'''(.*?)'''/g, '$1')
		.replace(/\[\[(.*?)\|(.*?)\]\]/g, '$2')
		.replace(/\[\[(.*?)\]\]/g, '$1')
		.replace(/ {2,}/g, ' ')
		.replace(/ /g, '');
}

function polishSyntaxArgument(item) {
	if (item.desc.length < 10) {
		return item.desc;
	}
	if (item.type.length > 10) {
		return item.type.substr(0, 8) + "..";
	}
	return item.type;
}

function findVariables(wiki, debug) {
	let buffer = '';
	let index = 0;
	let part = 0;
	let inside = -1;
	let variables = {};

	let previousIdent = null;
	let variableName = '';
	let variableValue = '';

	let skip = false;

	while (index < wiki.length) {
		let chunk = 1;

		if (skip && (wiki.substr(index, 7) === '' || wiki.substr(index, 9) === '</nowiki>')) {
			skip = false;
		}

		if (!skip) {
			if (wiki.substr(index, 6) === '' || wiki.substr(index, 8) === '<nowiki>') {
				skip = true;
			}

			if (wiki.substr(index, 2) === '{{' || wiki.substr(index, 2) === '[[') {
				inside++;
				chunk = 2;
			} else if (wiki.substr(index, 2) === '}}' || wiki.substr(index, 2) === ']]') {
				inside--;
				if (inside < 0) inside = 0;
				chunk = 2;
			}

			if (wiki.substr(index, 1) === '|') {
				if (inside === 0) {
					if (part === 0) {
						variableValue = buffer
							.replace(/[rpx][0-9]+=/, '')
							.trim();
						part = 1;
						buffer = '';
					} else if (part === 1) {
						variableName = buffer
							.replace(/=/g, '')
							.replace(/(\n|\r)/g, '')
							.replace(/_{2,}/, '')
							.toLowerCase()
							.trim();

						if (variableName === "") {
							if (previousIdent === 'description') {
								variableName = "syntax";
							}
							if (previousIdent === 'game version' || (!variables['description'] && Object.keys(variables).length > 2)) {
								variableName = 'description';
							}
						}

						if (variableName.indexOf("syntax") === 0) {
							variableName = "syntax";
						}

						// Fix this
						if (variableName == "returnvalue" || variableName.indexOf('return value') === 0) {
							variableName = "return value";
						}

						if (variableName == "syntax" || variableName == "return value") {
							if (!variables[variableName])
								variables[variableName] = [];
							variables[variableName].push(parseValue(variableValue, variableName));
						} else {
							variables[variableName] = variableValue;
						}

						previousIdent = variableName;

						part = 0;
						buffer = '';
						variableName = null;
						variableValue = null;
					}
				}
			} else {
				buffer += wiki.substr(index, chunk);
			}
		} else {
			buffer += wiki.substr(index, chunk);
		}

		index += chunk;
	}

	return variables;
}

function parseDocument(doc, type, extended) {
	var document = new xmldoc.XmlDocument(doc);
	var pages = document.childrenNamed("page");
	var findCommand = /{{(?:Command|Function)\|(?:Comments)?=((?:.|\n)*?\n?)}}/i;

	var findVariable = /\|\s*((?:.|\n)*?)\s*\|\s*=[\r\t\f\v ]*(.*)/g;
	var findVariable2 = /\|\s*((?:.|\n)*?)\s*\|\s*[\r\t\f\v ]*([^=]*)=/g;

	var results = extended || {};
	var placeHolder = /\/\*\s*Description:\s*([^]*)\s*(?:Parameters|Parameter\(s\)):\s*([^]*)\s*Returns:\s*([^]*)\s*\*\/\s*/i;
	var placeHolderWithExamples = /\/\*\s*Description:\s*([^]*)\s*(?:Parameters|Parameter\(s\)):\s*([^]*)\s*Returns:\s*([^]*)\s*Examples:\s*([^]*)\s*\*\/\s*/i;
	var placeHolderArgument = /([0-9]+):\s*([^-]*)-\s*(.*)/g;
	var placeHolderSingularArgument = /_this:\s*([^-]*)-\s*(.*)/i;
	var placeHolderReturn = /\s*([^-]*)(.*)/;
	var sqfTypes = ["bool", "string", "object", "task", "array", "scalar", "number", "side", "group", "boolean", "code", "config", "control", "display", "namespace"];

	var noInfo = [];

	for (var i in pages) {
		var page = pages[i];

		if (parseInt(page.childNamed("ns").val) == 0) {
			var title = page.childNamed("title").val;
			var text = page.valueWithPath("revision.text");

			title = title.replace(/ /g, '_');

			var debug = false && title === 'random';

			var match = findCommand.exec(text);
			if (match) {
				var info;
				var start = text.indexOf('{{Command');
				if (start < 0) start = text.indexOf('{{Function');
				if (start < 0) throw new Error(title + ' has no start');
				var wiki = loadBlock(text.substr(start), '{{', '}}', debug);
				var variables = {};

				if (title === 'random') {
					wiki = wiki.replace("|p1= x: [[Number]]\n", "")
				}

				var signatures = [];
				var description = {
					plain: "",
					formatted: ""
				};
				var returns = [];
				var syntax = [];

				var previousIdent = null;

				variables = findVariables(wiki, debug);
				for (let ident in variables) {
					let value = variables[ident];
					let valueStr = JSON.stringify(value);

					debug && console.log('Variable:', '`' + ident + '`');
					debug && console.log('Value:', '`' + valueStr.substr(0, valueStr.length > 10 ? 10 : valueStr.length).replace(/\n/g, '<NL>') + '`');
				}

				debug && console.log(variables);

				if (variables["return value"])
					returns = variables["return value"];

				if (variables["syntax"])
					syntax = variables["syntax"];

				syntax = syntax.map((item) => {
					var commented = //i.exec(item);
					if (commented) {
						item = commented[1].trim();
					}
					return item.replace(/''/g, '');
				});

				// Parse description
				if (variables["description"]) {
					var desc = variables["description"];
					match = placeHolderWithExamples.exec(desc);
					if (!match) {
						match = placeHolder.exec(desc);
					}

					if (match) {
						desc = match[1];

						if (match[2].trim()) {
							var args = [];
							var amatch = null;
							while (amatch = placeHolderArgument.exec(match[2])) {
								args.push({
									type: amatch[2].trim().replace(/(\s+)or(\s+)/g, "/"),
									desc: amatch[3].trim()
								});
							}

							if (args.length > 0) {
								syntax = ["[" + args.map((item) => polishSyntaxArgument(item)).join(", ") + "] call " + title];

								desc += "\r\nArguments:\r\n";
								args.forEach((arg, index) => {
									// desc += "\r\n - " + (index + 1) + ": " + arg.type + " - " + arg.desc
									desc += "\r\n " + index + ". " + arg.type + " - " + arg.desc;
								});
							} else {
								amatch = placeHolderSingularArgument.exec(match[2]);
								if (amatch) {
									var arg = { type: amatch[1].trim(), desc: amatch[2].trim() };
									syntax = [polishSyntaxArgument(arg) + " call " + title];
									desc += "\r\nArgument: _this: " + arg.type + " - " + arg.desc;
								}
							}
						}

						if (match[3].trim()) {
							match[3] = match[3].trim();
							var rmatch = placeHolderReturn.exec(match[3]);
							if (rmatch) {
								returns = [rmatch[1]];
								desc += "\r\n\r\nReturns: " + match[3];
							} else if (sqfTypes.indexOf(match[3].toLowerCase()) != -1) {
								returns = [match[3]];
							} else {
								returns = ["ANY"];
								desc += "\r\n\r\nReturns: " + match[3];
							}
						}
					} else {
						// Remove image tags
						desc = desc.replace(/\[\[Image(.*?)\]\]/g, '');

						// Only use first sentence
						var dot = desc.search(/[^\.]\.[^\.]/);
						if (dot >= 0) {
							desc = desc.substr(0, dot + 2);
						}
					}

					description.plain = fixSyntax(desc).trim();
					description.formatted = desc.replace(/'''(.*?)'''/g, '**$1**')
						.replace(/
/ig, '')
						.replace(/\[\[(.*?)\|(.*?)\]\]/g, '[$1](https://community.bistudio.com/wiki/$2)')
						.replace(/\[\[(.*?)\]\]/g, '[$1](https://community.bistudio.com/wiki/$1)')
						.trim();
				}

				if (description.plain.length === 0 || description.formatted.length === 0) {
					noInfo.push(title);
				}

				// Add more info to description
				if (variables["game version"] && variables["game name"]) {
					description.formatted += " _(" + variables["game name"] + " " + variables["game version"] + ")_";
				}

				description.formatted += " *([more info](https://community.bistudio.com/wiki/" + title + "))*\r\n\r\n";

				// Add signatures
				if (syntax.length > 0) {
					for(var s in syntax) {
						var signature = syntax[s].trim();
						var ret = returns[s];
						if (ret != null) ret = ret.trim();

						signatures.push({
							signature: removeMoreTags(signature),
							returns: removeMoreTags(ret || null)
						});
					}
				} else {
					signatures.push({
						signature: title
					});
				}

				results[title.toLowerCase()] = {
					type: type,
					title: title,
					description: description,
					signatures: signatures
				}
			} else {
				console.log("No match: " + title);
			}
		}
	}

	if (noInfo.length > 1) {
		console.log('No informations for ' + noInfo.length + ' commands/functions. Example: ' + noInfo[0]);
	}

	return results;
}

fs.readFile(__dirname + '/server/operatorsExport.xml', (err, data) => {
	if (err) throw err;

	var docs = parseDocument(data.toString(), "command");
	fs.readFile(__dirname + '/server/functionsExport.xml', (err, data) => {
		if (err) throw err;

		docs = parseDocument(data.toString(), "function", docs);
		fs.readFile(__dirname + '/../cba/cba.json', (err, data) => {
			if (err) throw err;

			var items = JSON.parse(data);
			for(var ident in items) {
				docs[ident] = items[ident];
			}

			fs.readFile(__dirname + '/../ace3/ace3.json', (err, data) => {
				if (err) throw err;

				var items = JSON.parse(data);
				for(var ident in items) {
					docs[ident] = items[ident];
				}

				fs.writeFile(__dirname + '/server/definitions/documentation.json', JSON.stringify(docs));
			});
		});
	});
});
<commit_msg>Fixed compatibility issue with newer nodejs.<commit_after>/**
 * This script is used to convert wikimedia dump of official wiki
 * to JSON format used in this extension.
 *
 * To get create xml files used by this file, you need to go to /Special:Export/,
 * select Functions or Commands category, then save them as functionsExport.xml and
 * operatorsExport.xml.
 *
 */

var fs = require('fs');
var xmldoc = require('xmldoc');

function fixSyntax(syntax) {
	syntax = removeMoreTags(syntax);

	var eq = syntax.indexOf('=');
	if (eq >= 0) {
		syntax = syntax.substr(eq + 1).trim();
	}

	return syntax.trim();
}

function parseValue(value, type) {
	if (type == "syntax") {
		return fixSyntax(value);
	}
	if (type == "return value") {
		var ret = value.trim();
		var dash = ret.indexOf("-");
		if (dash >= 0) {
			ret = ret.substr(0, dash - 1).trim();
		}
		return ret;
	}
}

function loadBlock(text, opening, closing, debug) {
	let bcount = -1;
	let index = 0;
	let result = '';
	while (index < text.length) {
		let chunk = 1;
		if (text.substr(index, opening.length) === opening) {
			bcount++;
			chunk = 2;
		}
		if (text.substr(index, closing.length) === closing) {
			bcount--;
			chunk = 2;

			if (bcount < 0) {
				break;
			}
		}
		result += text.substr(index, chunk);
		index += chunk;
	}
	return result;
}

function removeMoreTags(value) {
	if (!value)
		return value;

	return value
		.replace(/
/ig, '')
		.replace(/''\(since \[.*?\]\)''/ig, '')
		.replace(/''\(since \[.*?\]\)''/ig, '')
		.replace(/\(''since .*?''\)/ig, '')
		.replace(/''since [^']*''/ig, '')
		.replace(/<nowiki>(.*?)<\/nowiki>/g, '$1')
		.replace(/'''(.*?)'''/g, '$1')
		.replace(/\[\[(.*?)\|(.*?)\]\]/g, '$2')
		.replace(/\[\[(.*?)\]\]/g, '$1')
		.replace(/ {2,}/g, ' ')
		.replace(/ /g, '');
}

function polishSyntaxArgument(item) {
	if (item.desc.length < 10) {
		return item.desc;
	}
	if (item.type.length > 10) {
		return item.type.substr(0, 8) + "..";
	}
	return item.type;
}

function findVariables(wiki, debug) {
	let buffer = '';
	let index = 0;
	let part = 0;
	let inside = -1;
	let variables = {};

	let previousIdent = null;
	let variableName = '';
	let variableValue = '';

	let skip = false;

	while (index < wiki.length) {
		let chunk = 1;

		if (skip && (wiki.substr(index, 7) === '' || wiki.substr(index, 9) === '</nowiki>')) {
			skip = false;
		}

		if (!skip) {
			if (wiki.substr(index, 6) === '' || wiki.substr(index, 8) === '<nowiki>') {
				skip = true;
			}

			if (wiki.substr(index, 2) === '{{' || wiki.substr(index, 2) === '[[') {
				inside++;
				chunk = 2;
			} else if (wiki.substr(index, 2) === '}}' || wiki.substr(index, 2) === ']]') {
				inside--;
				if (inside < 0) inside = 0;
				chunk = 2;
			}

			if (wiki.substr(index, 1) === '|') {
				if (inside === 0) {
					if (part === 0) {
						variableValue = buffer
							.replace(/[rpx][0-9]+=/, '')
							.trim();
						part = 1;
						buffer = '';
					} else if (part === 1) {
						variableName = buffer
							.replace(/=/g, '')
							.replace(/(\n|\r)/g, '')
							.replace(/_{2,}/, '')
							.toLowerCase()
							.trim();

						if (variableName === "") {
							if (previousIdent === 'description') {
								variableName = "syntax";
							}
							if (previousIdent === 'game version' || (!variables['description'] && Object.keys(variables).length > 2)) {
								variableName = 'description';
							}
						}

						if (variableName.indexOf("syntax") === 0) {
							variableName = "syntax";
						}

						// Fix this
						if (variableName == "returnvalue" || variableName.indexOf('return value') === 0) {
							variableName = "return value";
						}

						if (variableName == "syntax" || variableName == "return value") {
							if (!variables[variableName])
								variables[variableName] = [];
							variables[variableName].push(parseValue(variableValue, variableName));
						} else {
							variables[variableName] = variableValue;
						}

						previousIdent = variableName;

						part = 0;
						buffer = '';
						variableName = null;
						variableValue = null;
					}
				}
			} else {
				buffer += wiki.substr(index, chunk);
			}
		} else {
			buffer += wiki.substr(index, chunk);
		}

		index += chunk;
	}

	return variables;
}

function parseDocument(doc, type, extended) {
	var document = new xmldoc.XmlDocument(doc);
	var pages = document.childrenNamed("page");
	var findCommand = /{{(?:Command|Function)\|(?:Comments)?=((?:.|\n)*?\n?)}}/i;

	var findVariable = /\|\s*((?:.|\n)*?)\s*\|\s*=[\r\t\f\v ]*(.*)/g;
	var findVariable2 = /\|\s*((?:.|\n)*?)\s*\|\s*[\r\t\f\v ]*([^=]*)=/g;

	var results = extended || {};
	var placeHolder = /\/\*\s*Description:\s*([^]*)\s*(?:Parameters|Parameter\(s\)):\s*([^]*)\s*Returns:\s*([^]*)\s*\*\/\s*/i;
	var placeHolderWithExamples = /\/\*\s*Description:\s*([^]*)\s*(?:Parameters|Parameter\(s\)):\s*([^]*)\s*Returns:\s*([^]*)\s*Examples:\s*([^]*)\s*\*\/\s*/i;
	var placeHolderArgument = /([0-9]+):\s*([^-]*)-\s*(.*)/g;
	var placeHolderSingularArgument = /_this:\s*([^-]*)-\s*(.*)/i;
	var placeHolderReturn = /\s*([^-]*)(.*)/;
	var sqfTypes = ["bool", "string", "object", "task", "array", "scalar", "number", "side", "group", "boolean", "code", "config", "control", "display", "namespace"];

	var noInfo = [];

	for (var i in pages) {
		var page = pages[i];

		if (parseInt(page.childNamed("ns").val) == 0) {
			var title = page.childNamed("title").val;
			var text = page.valueWithPath("revision.text");

			title = title.replace(/ /g, '_');

			var debug = false && title === 'random';

			var match = findCommand.exec(text);
			if (match) {
				var info;
				var start = text.indexOf('{{Command');
				if (start < 0) start = text.indexOf('{{Function');
				if (start < 0) throw new Error(title + ' has no start');
				var wiki = loadBlock(text.substr(start), '{{', '}}', debug);
				var variables = {};

				if (title === 'random') {
					wiki = wiki.replace("|p1= x: [[Number]]\n", "")
				}

				var signatures = [];
				var description = {
					plain: "",
					formatted: ""
				};
				var returns = [];
				var syntax = [];

				var previousIdent = null;

				variables = findVariables(wiki, debug);
				for (let ident in variables) {
					let value = variables[ident];
					let valueStr = JSON.stringify(value);

					debug && console.log('Variable:', '`' + ident + '`');
					debug && console.log('Value:', '`' + valueStr.substr(0, valueStr.length > 10 ? 10 : valueStr.length).replace(/\n/g, '<NL>') + '`');
				}

				debug && console.log(variables);

				if (variables["return value"])
					returns = variables["return value"];

				if (variables["syntax"])
					syntax = variables["syntax"];

				syntax = syntax.map((item) => {
					var commented = //i.exec(item);
					if (commented) {
						item = commented[1].trim();
					}
					return item.replace(/''/g, '');
				});

				// Parse description
				if (variables["description"]) {
					var desc = variables["description"];
					match = placeHolderWithExamples.exec(desc);
					if (!match) {
						match = placeHolder.exec(desc);
					}

					if (match) {
						desc = match[1];

						if (match[2].trim()) {
							var args = [];
							var amatch = null;
							while (amatch = placeHolderArgument.exec(match[2])) {
								args.push({
									type: amatch[2].trim().replace(/(\s+)or(\s+)/g, "/"),
									desc: amatch[3].trim()
								});
							}

							if (args.length > 0) {
								syntax = ["[" + args.map((item) => polishSyntaxArgument(item)).join(", ") + "] call " + title];

								desc += "\r\nArguments:\r\n";
								args.forEach((arg, index) => {
									// desc += "\r\n - " + (index + 1) + ": " + arg.type + " - " + arg.desc
									desc += "\r\n " + index + ". " + arg.type + " - " + arg.desc;
								});
							} else {
								amatch = placeHolderSingularArgument.exec(match[2]);
								if (amatch) {
									var arg = { type: amatch[1].trim(), desc: amatch[2].trim() };
									syntax = [polishSyntaxArgument(arg) + " call " + title];
									desc += "\r\nArgument: _this: " + arg.type + " - " + arg.desc;
								}
							}
						}

						if (match[3].trim()) {
							match[3] = match[3].trim();
							var rmatch = placeHolderReturn.exec(match[3]);
							if (rmatch) {
								returns = [rmatch[1]];
								desc += "\r\n\r\nReturns: " + match[3];
							} else if (sqfTypes.indexOf(match[3].toLowerCase()) != -1) {
								returns = [match[3]];
							} else {
								returns = ["ANY"];
								desc += "\r\n\r\nReturns: " + match[3];
							}
						}
					} else {
						// Remove image tags
						desc = desc.replace(/\[\[Image(.*?)\]\]/g, '');

						// Only use first sentence
						var dot = desc.search(/[^\.]\.[^\.]/);
						if (dot >= 0) {
							desc = desc.substr(0, dot + 2);
						}
					}

					description.plain = fixSyntax(desc).trim();
					description.formatted = desc.replace(/'''(.*?)'''/g, '**$1**')
						.replace(/
/ig, '')
						.replace(/\[\[(.*?)\|(.*?)\]\]/g, '[$1](https://community.bistudio.com/wiki/$2)')
						.replace(/\[\[(.*?)\]\]/g, '[$1](https://community.bistudio.com/wiki/$1)')
						.trim();
				}

				if (description.plain.length === 0 || description.formatted.length === 0) {
					noInfo.push(title);
				}

				// Add more info to description
				if (variables["game version"] && variables["game name"]) {
					description.formatted += " _(" + variables["game name"] + " " + variables["game version"] + ")_";
				}

				description.formatted += " *([more info](https://community.bistudio.com/wiki/" + title + "))*\r\n\r\n";

				// Add signatures
				if (syntax.length > 0) {
					for(var s in syntax) {
						var signature = syntax[s].trim();
						var ret = returns[s];
						if (ret != null) ret = ret.trim();

						signatures.push({
							signature: removeMoreTags(signature),
							returns: removeMoreTags(ret || null)
						});
					}
				} else {
					signatures.push({
						signature: title
					});
				}

				results[title.toLowerCase()] = {
					type: type,
					title: title,
					description: description,
					signatures: signatures
				}
			} else {
				console.log("No match: " + title);
			}
		}
	}

	if (noInfo.length > 1) {
		console.log('No informations for ' + noInfo.length + ' commands/functions. Example: ' + noInfo[0]);
	}

	return results;
}

fs.readFile(__dirname + '/server/operatorsExport.xml', (err, data) => {
	if (err) throw err;

	var docs = parseDocument(data.toString(), "command");
	fs.readFile(__dirname + '/server/functionsExport.xml', (err, data) => {
		if (err) throw err;

		docs = parseDocument(data.toString(), "function", docs);
		fs.readFile(__dirname + '/../cba/cba.json', (err, data) => {
			if (err) throw err;

			var items = JSON.parse(data);
			for(var ident in items) {
				docs[ident] = items[ident];
			}

			fs.readFile(__dirname + '/../ace3/ace3.json', (err, data) => {
				if (err) throw err;

				var items = JSON.parse(data);
				for(var ident in items) {
					docs[ident] = items[ident];
				}

				fs.writeFileSync(__dirname + '/server/definitions/documentation.json', JSON.stringify(docs));
			});
		});
	});
});
<|endoftext|>


<commit_before>import { CONST, TYPES } from '../core/Constants'
import AudioContext from '../AudioContext'
import Module from '../core/Module'

class Filter extends Module {

    constructor (props) {
        super(props);

        this.freq = +props.freq || 11000;
        this.q = +props.q || 10;
        //this.envelope = +props.envelope || 0;

        this.main = AudioContext.createBiquadFilter();
        this.main.type = props.type || CONST.FILTER_LOWPASS;

        ////TODO config envelope....introduce an Env Module!!!
        //this.env = {
        //    attack:  2,
        //    decay:   1,
        //    sustain: 100,
        //    release: 1
        //};

        this.setCutOff();
        this.setQ();
    }

    createGain () {
        return false;
    }

    setCutOff () {
        let cutOff = ((this.freq - 20) / (20000 - 20)) * (14.287712379549449 - 0) + 0;
        this.main.frequency.value = Math.pow(2, cutOff);
    }

    setQ () {
        let q = this.q % 21;
        this.main.Q.value = q;
    }

    //setEnvelope () {
    //    let now = AudioContext.currentTime,
    //        envelope = this.envelope % 101,
    //        filterAttackLevel = envelope * 72,  // Range: 0-7200: 6-octave range
    //        filterSustainLevel = filterAttackLevel * this.env.sustain / 100.0, // range: 0-7200
    //        filterAttackEnd = (this.env.attack / 20.0);
    //
    //    if (!filterAttackEnd) {
    //        filterAttackEnd = 0.05; // tweak to get target decay to work properly
    //    }
    //    //TODO let main parameter detune | frequency... configurable
    //    this.main.detune.setValueAtTime(0, now);
    //    this.main.detune.linearRampToValueAtTime(filterAttackLevel, now + filterAttackEnd);
    //    this.main.detune.setTargetAtTime(filterSustainLevel, now + filterAttackEnd, (this.env.decay / 100.0));
    //
    //}
    //
    //resetEnvelope () {
    //    let now = AudioContext.currentTime;
    //    //TODO let main parameter detune | frequency... configurable
    //    this.main.detune.cancelScheduledValues(now);
    //    this.main.detune.setTargetAtTime(0, now, (this.env.release / 100.0));
    //
    //}

    getLineIn (source) {
        if (source === TYPES.MODULATOR) {
            //TODO let main parameter detune | frequency... configurable
            return this.main.detune;
        } else {
            return this.main;
        }
    }

    getLineOut () {
        return this.main;
    }
}

export default Filter;
<commit_msg>filter cleaning<commit_after>import { CONST, TYPES } from '../core/Constants'
import AudioContext from '../AudioContext'
import Module from '../core/Module'

class Filter extends Module {

    constructor (props) {
        super(props);

        this.freq = +props.freq || 11000;
        this.q = +props.q || 10;

        this.main = AudioContext.createBiquadFilter();
        this.main.type = props.type || CONST.FILTER_LOWPASS;

        this.setCutOff();
        this.setQ();
    }

    createGain () {
        return false;
    }

    setCutOff () {
        let cutOff = ((this.freq - 20) / (20000 - 20)) * (14.287712379549449 - 0) + 0;
        this.main.frequency.value = Math.pow(2, cutOff);
    }

    setQ () {
        let q = this.q % 21;
        this.main.Q.value = q;
    }

    getLineIn (source) {
        if (source === TYPES.MODULATOR) {
            //TODO let main parameter detune | frequency... configurable
            return this.main.detune;
        } else {
            return this.main;
        }
    }

    getLineOut () {
        return this.main;
    }
}

export default Filter;
<|endoftext|>


<commit_before>/* eslint-disable */
'use strict';
var system = require('system');
var webpage = require('webpage');

var page;

var methods = {
  exit: function(callback) {
    callback(true);
  },
  open: function(url, callback) {
    page = webpage.create();
    page.open(url, function(status) {
      callback(status === 'success');
    });
  },
  evaluate: function(functionBody, callback) {
    callback(page.evaluate(new Function(functionBody)));
  },
  sendEvent: function() {
    var params = Array.prototype.slice.call(arguments, 0, -1);
    var callback = arguments[arguments.length - 1];
    page.sendEvent.apply(page, params);
    callback(true);
  }
};

function processRequest() {
  var request = JSON.parse(system.stdin.readLine());

  function callback(result) {
    system.stdout.writeLine(JSON.stringify({id: request.id, result: result}));
    if (request.method === 'exit') {
      phantom.exit(0);
    } else {
      processRequest();
    }
  }
  methods[request.method].apply(null, request.params.concat(callback));
}
processRequest();
<commit_msg>Allow PhantomJS process to free stack frames<commit_after>/* eslint-disable */
'use strict';
var system = require('system');
var webpage = require('webpage');

var page;

var methods = {
  exit: function(callback) {
    callback(true);
  },
  open: function(url, callback) {
    page = webpage.create();
    page.open(url, function(status) {
      callback(status === 'success');
    });
  },
  evaluate: function(functionBody, callback) {
    callback(page.evaluate(new Function(functionBody)));
  },
  sendEvent: function() {
    var params = Array.prototype.slice.call(arguments, 0, -1);
    var callback = arguments[arguments.length - 1];
    page.sendEvent.apply(page, params);
    callback(true);
  }
};

function processRequest() {
  var request = JSON.parse(system.stdin.readLine());

  function callback(result) {
    system.stdout.writeLine(JSON.stringify({id: request.id, result: result}));
    if (request.method === 'exit') {
      phantom.exit(0);
    } else {
      setTimeout(processRequest, 0);
    }
  }
  methods[request.method].apply(null, request.params.concat(callback));
}
processRequest();
<|endoftext|>


<commit_before>(function (app) {
    'use strict';

    app.Router.map(function (match) {
        this.resource('todos');
        this.route('settings');
    });
})(window.App);

<commit_msg>Base URL redirects to todos url [Fixes #1]<commit_after>(function (app) {
    'use strict';

    app.Router.map(function (match) {
        this.resource('todos');
        this.route('settings');
    });

    app.IndexRoute = Ember.Route.extend({
        redirect: function () {
            this.transitionTo('todos');
        }
    });
})(window.App);

<|endoftext|>


<commit_before>import Operation from "./operation"
import Expression from "./expression"
import {Context, ContextTypes, Doc, ParserStateOption} from "./node"
import * as PHPType from "../php-type"
import * as PHPError from "../php-error"
export default class Bin extends Operation {
    /** @type {string} */
    get type() {
        return this.node.type
    }
    /** @type {Expression} */
    get left() {
        return this.cacheNode("left")
    }
    /** @type {Expression} */
    get right() {
        return this.cacheNode("right")
    }
    /**
     * Checks that syntax seems ok
     * @param {Context} context
     * @param {Set<ParserStateOption.Base>} [parser_state]
     * @param {?Doc} [doc]
     * @returns {?ContextTypes} The set of types applicable to this value
     */
    check(context, parser_state = new Set(), doc = null) {
        super.check(context, parser_state, doc)
        let left_types = this.left.check(context, new Set(), null).expressionType
        let right_types = this.right.check(context, new Set(), null).expressionType
        let types = PHPType.Union.empty
        switch(this.type) {
            case "||":
            case "|":
            case "or":
                // Boolean (or)
                types = types.addTypesFrom(PHPType.Core.types.bool)
                break
            case "&":
            case "&&":
            case "and":
                // Boolean (and)
                types = types.addTypesFrom(PHPType.Core.types.bool)
                break
            case "*":
            case "/":
            case "-":
            case "%":
            case "**":
            case "<<":
            case ">>":
            case "^":
                // Numeric (1)
                types = types.addTypesFrom(PHPType.Core.types.float)
                break
            case "+":
                // Numeric (2)
                left_types.types.forEach(type => {
                    switch(type.typeSignature) {
                        case "null":
                            // Yes null casts to number.
                        case "int":
                        case "float":
                            types = types.addTypesFrom(PHPType.Core.types.float)
                            break
                        case "array":
                            types = types.addTypesFrom(PHPType.Core.types.array)
                            break
                        case "mixed":
                            types = types.addTypesFrom(type.union)
                            break
                        default:
                            this.throw(
                                new PHPError.BadTypeCast(`Possibly bad cast from type ${type} for +`),
                                context,
                                this.loc
                            )
                            types = types.addTypesFrom(PHPType.Core.types.float)
                    }
                })
                break
            case ".":
                // String
                types = types.addTypesFrom(PHPType.Core.types.string)
                break
            case "~":
            case "!~":
            case "=":
            case "!=":
            case "?":
            case "<":
            case "<=":
            case ">":
            case "=>":
            case ">=":
            case "==":
            case "!==":
            case "===":
            case "instanceof":
                // Comparison (boolean)
                types = types.addTypesFrom(PHPType.Core.types.bool)
                break
            case "??":
                // Left (not null) or right
                types = types.addTypesFrom(
                    left_types.excluding("null")
                ).addTypesFrom(right_types)
                break
            default:
                console.log(this.node)
                console.log(`Don't know how to parse operator type ${this.type}`)
                types = types.addTypesFrom(left_types)
                types = types.addTypesFrom(right_types)
        }
        return new ContextTypes(types)
    }
}
<commit_msg>Split and merge for binary ops<commit_after>import Operation from "./operation"
import Expression from "./expression"
import {Context, ContextTypes, Doc, ParserStateOption} from "./node"
import * as PHPType from "../php-type"
import * as PHPError from "../php-error"
export default class Bin extends Operation {
    /** @type {string} */
    get type() {
        return this.node.type
    }
    /** @type {Expression} */
    get left() {
        return this.cacheNode("left")
    }
    /** @type {Expression} */
    get right() {
        return this.cacheNode("right")
    }
    /**
     * Checks that syntax seems ok
     * @param {Context} context
     * @param {Set<ParserStateOption.Base>} [parser_state]
     * @param {?Doc} [doc]
     * @returns {?ContextTypes} The set of types applicable to this value
     */
    check(context, parser_state = new Set(), doc = null) {
        super.check(context, parser_state, doc)
        let left_context = context.childContext(false)
        left_context.importNamespaceFrom(context)
        let left_types = this.left.check(left_context, new Set(), null).expressionType
        let right_context = context.childContext(false)
        right_context.importNamespaceFrom(context)
        let right_types = this.right.check(right_context, new Set(), null).expressionType
        let types = PHPType.Union.empty
        switch(this.type) {
            case "||":
            case "|":
            case "or":
                // Boolean (or)
                types = types.addTypesFrom(PHPType.Core.types.bool)
                break
            case "&":
            case "&&":
            case "and":
                // Boolean (and)
                types = types.addTypesFrom(PHPType.Core.types.bool)
                break
            case "*":
            case "/":
            case "-":
            case "%":
            case "**":
            case "<<":
            case ">>":
            case "^":
                // Numeric (1)
                types = types.addTypesFrom(PHPType.Core.types.float)
                break
            case "+":
                // Numeric (2)
                left_types.types.forEach(type => {
                    switch(type.typeSignature) {
                        case "null":
                            // Yes null casts to number.
                        case "int":
                        case "float":
                            types = types.addTypesFrom(PHPType.Core.types.float)
                            break
                        case "array":
                            types = types.addTypesFrom(PHPType.Core.types.array)
                            break
                        case "mixed":
                            types = types.addTypesFrom(type.union)
                            break
                        default:
                            this.throw(
                                new PHPError.BadTypeCast(`Possibly bad cast from type ${type} for +`),
                                context,
                                this.loc
                            )
                            types = types.addTypesFrom(PHPType.Core.types.float)
                    }
                })
                break
            case ".":
                // String
                types = types.addTypesFrom(PHPType.Core.types.string)
                break
            case "~":
            case "!~":
            case "=":
            case "!=":
            case "?":
            case "<":
            case "<=":
            case ">":
            case "=>":
            case ">=":
            case "==":
            case "!==":
            case "===":
            case "instanceof":
                // Comparison (boolean)
                types = types.addTypesFrom(PHPType.Core.types.bool)
                break
            case "??":
                // Left (not null) or right
                types = types.addTypesFrom(
                    left_types.excluding("null")
                ).addTypesFrom(right_types)
                break
            default:
                console.log(this.node)
                console.log(`Don't know how to parse operator type ${this.type}`)
                types = types.addTypesFrom(left_types)
                types = types.addTypesFrom(right_types)
        }
        context.importNamespaceFrom(left_context)
        context.importNamespaceFrom(right_context)
        return new ContextTypes(types)
    }
}
<|endoftext|>


<commit_before>import XlsxUtil from './xlsx-util.js';

/**
 * Defines the file path in the XLSX.
 * @type {Object}
 */
const FilePaths = {
  WorkBook: 'xl/workbook.xml',
  SharedStrings: 'xl/sharedStrings.xml',
  SheetBase: 'xl/worksheets/sheet'
};

/**
 * The maximum number of sheets ( Excel 97 ).
 * @type {Number}
 */
const MaxSheets = 256;

/**
 * Extract the colums/rows from XLSX file.
 */
export default class XlsxExtractor {
  /**
   * Initialize instance.
   *
   * @param {String} path XLSX file path.
   *
   * @throws {Error} The specified XLSX file is invalid.
   */
  constructor( path ) {
    const zip = XlsxUtil.unzip( path );
    if( !( zip ) ) {
      throw new Error( 'Failed to expand the XLSX file.' );
    }

    const count = this._getSheetsCount( zip );
    if( count === 0 ) {
      throw new Error( 'Sheets was not found in XLSX file.' );
    }

    this._path  = path;
    this._count = count;
  }

  /**
   * Gets the number of sheets.
   *
   * @return {Number} The number of sheets.
   */
  get count() {
    return this._count;
  }

  /**
   * Extract a sheet.
   *
   * @param {Number} index Index of sheet. Range of from 1 to XlsxExtractor.count.
   *
   * @return {Promise} Extract task.
   */
  extract( index ) {
    return Promise
    .resolve()
    .then( () => {
      if( index < 1 || this._count < index ) {
        throw new Error( 'The index is out of range: ' + index );
      }

      return this._parseXML( index );
    } )
    .then( ( xmls ) => {
      return this._extract( index, xmls );
    } );
  }

  /**
   * Extract a sheet.
   *
   * @param {SheetData} data Sheet data.
   * @param {SheetData} data Sheet data.
   *
   * @return {Promise} Extract task.
   */
  _extract( id, data ) {
    return new Promise( ( resolve ) => {
      const cells   = XlsxUtil.getCells( data.sheet.worksheet.sheetData[ 0 ].row );
      const size    = XlsxUtil.getSheetSize( data.sheet, cells );
      const rows    = ( size.row.max - size.row.min ) + 1;
      const cols    = ( size.col.max - size.col.min ) + 1;
      const sheet   = XlsxUtil.createEmptyCells( rows, cols );
      const strings = data.strings.sst.si;

      cells.forEach( ( cell ) => {
        let value = cell.value;
        if( cell.type === 's' ) {
          const index  = parseInt( value, 10 );
          value = XlsxUtil.valueFromStrings( strings[ index ] );
        }

        sheet[ cell.row - size.row.min ][ cell.col - size.col.min ] = value;
      } );

      resolve( {
        id:    id,
        name:  data.name,
        cells: sheet
      } );
    } );
  }

  /**
   * Gets the number of sheets.
   *
   * @param {ZipObject} zip Zip object.
   *
   * @return {Number} The number of sheets.
   */
  _getSheetsCount( zip ) {
    let count = 0;
    for( let i = 1; i < MaxSheets; ++i ) {
      const path = FilePaths.SheetBase + i + '.xml';
      if( !( zip.files[ path ] ) ) {
        break;
      }

      ++count;
    }

    return count;
  }

  /**
   * Parses the XML.
   *
   * @param {Number} index Index of sheet. Range of from 1 to XlsxExtractor.count.
   *
   * @return {Promise} Parse task.
   */
  _parseXML( index ) {
    const zip    = XlsxUtil.unzip( this._path );
    const result = {};

    return Promise
    .resolve()
    .then( () => {
      const xml = zip.files[ FilePaths.WorkBook ].asText();
      return XlsxUtil.parseXML( xml );
    } )
    .then( ( root ) => {
      // Get a sheet name
      if( root && root.workbook && root.workbook.sheets && 0 < root.workbook.sheets.length && root.workbook.sheets[ 0 ].sheet ) {
        root.workbook.sheets[ 0 ].sheet.some( ( sheet ) => {
          const id = Number( sheet.$.sheetId );
          if( id === index ) {
            result.name = ( sheet.$.name || '' );
            return true;
          }

          return false;
        } );
      }

      const xml = zip.files[ FilePaths.SheetBase + index + '.xml' ].asText();
      return XlsxUtil.parseXML( xml );
    } )
    .then( ( sheet ) => {
      result.sheet = sheet;

      if( zip.files[ FilePaths.SharedStrings ] ) {
        const xml = zip.files[ FilePaths.SharedStrings ].asText();
        return XlsxUtil.parseXML( xml );
      }

      return Promise.resolve();
    } )
    .then( ( strings ) => {
      if( strings ) {
        result.strings = strings;
      }

      return result;
    } );
  }
}
<commit_msg>:bug: bugfix for the value of the row and column<commit_after>import XlsxUtil from './xlsx-util.js';

/**
 * Defines the file path in the XLSX.
 * @type {Object}
 */
const FilePaths = {
  WorkBook: 'xl/workbook.xml',
  SharedStrings: 'xl/sharedStrings.xml',
  SheetBase: 'xl/worksheets/sheet'
};

/**
 * The maximum number of sheets ( Excel 97 ).
 * @type {Number}
 */
const MaxSheets = 256;

/**
 * Extract the colums/rows from XLSX file.
 */
export default class XlsxExtractor {
  /**
   * Initialize instance.
   *
   * @param {String} path XLSX file path.
   *
   * @throws {Error} The specified XLSX file is invalid.
   */
  constructor( path ) {
    const zip = XlsxUtil.unzip( path );
    if( !( zip ) ) {
      throw new Error( 'Failed to expand the XLSX file.' );
    }

    const count = this._getSheetsCount( zip );
    if( count === 0 ) {
      throw new Error( 'Sheets was not found in XLSX file.' );
    }

    this._path  = path;
    this._count = count;
  }

  /**
   * Gets the number of sheets.
   *
   * @return {Number} The number of sheets.
   */
  get count() {
    return this._count;
  }

  /**
   * Extract a sheet.
   *
   * @param {Number} index Index of sheet. Range of from 1 to XlsxExtractor.count.
   *
   * @return {Promise} Extract task.
   */
  extract( index ) {
    return Promise
    .resolve()
    .then( () => {
      if( index < 1 || this._count < index ) {
        throw new Error( 'The index is out of range: ' + index );
      }

      return this._parseXML( index );
    } )
    .then( ( xmls ) => {
      return this._extract( index, xmls );
    } );
  }

  /**
   * Extract a sheet.
   *
   * @param {SheetData} data Sheet data.
   * @param {SheetData} data Sheet data.
   *
   * @return {Promise} Extract task.
   */
  _extract( id, data ) {
    return new Promise( ( resolve ) => {
      const cells   = XlsxUtil.getCells( data.sheet.worksheet.sheetData[ 0 ].row );
      const size    = XlsxUtil.getSheetSize( data.sheet, cells );
      const rows    = ( size.row.max - size.row.min ) + 1;
      const cols    = ( size.col.max - size.col.min ) + 1;
      const sheet   = XlsxUtil.createEmptyCells( rows, cols );
      const strings = data.strings.sst.si;

      cells.forEach( ( cell ) => {
        let value = cell.value;
        if( cell.type === 's' ) {
          const index  = parseInt( value, 10 );
          value = XlsxUtil.valueFromStrings( strings[ index ] );
        }

        let row = cell.row - size.row.min;
        row = (row >= 0) ? row : size.row.min;

        let col = cell.col - size.col.min;
        col = (col >= 0) ? col : size.col.min;

        sheet[ row ][ col ] = value;
      } );

      resolve( {
        id:    id,
        name:  data.name,
        cells: sheet
      } );
    } );
  }

  /**
   * Gets the number of sheets.
   *
   * @param {ZipObject} zip Zip object.
   *
   * @return {Number} The number of sheets.
   */
  _getSheetsCount( zip ) {
    let count = 0;
    for( let i = 1; i < MaxSheets; ++i ) {
      const path = FilePaths.SheetBase + i + '.xml';
      if( !( zip.files[ path ] ) ) {
        break;
      }

      ++count;
    }

    return count;
  }

  /**
   * Parses the XML.
   *
   * @param {Number} index Index of sheet. Range of from 1 to XlsxExtractor.count.
   *
   * @return {Promise} Parse task.
   */
  _parseXML( index ) {
    const zip    = XlsxUtil.unzip( this._path );
    const result = {};

    return Promise
    .resolve()
    .then( () => {
      const xml = zip.files[ FilePaths.WorkBook ].asText();
      return XlsxUtil.parseXML( xml );
    } )
    .then( ( root ) => {
      // Get a sheet name
      if( root && root.workbook && root.workbook.sheets && 0 < root.workbook.sheets.length && root.workbook.sheets[ 0 ].sheet ) {
        root.workbook.sheets[ 0 ].sheet.some( ( sheet ) => {
          const id = Number( sheet.$.sheetId );
          if( id === index ) {
            result.name = ( sheet.$.name || '' );
            return true;
          }

          return false;
        } );
      }

      const xml = zip.files[ FilePaths.SheetBase + index + '.xml' ].asText();
      return XlsxUtil.parseXML( xml );
    } )
    .then( ( sheet ) => {
      result.sheet = sheet;

      if( zip.files[ FilePaths.SharedStrings ] ) {
        const xml = zip.files[ FilePaths.SharedStrings ].asText();
        return XlsxUtil.parseXML( xml );
      }

      return Promise.resolve();
    } )
    .then( ( strings ) => {
      if( strings ) {
        result.strings = strings;
      }

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


<commit_before>/* @flow */
import omit from 'lodash.omit';

import type {
  MessagesState,
  MessageAction,
  MessageFetchCompleteAction,
  EventReactionAddAction,
  EventReactionRemoveAction,
  EventNewMessageAction,
  EventMessageDeleteAction,
  EventUpdateMessageAction,
} from '../types';
import {
  APP_REFRESH,
  LOGOUT,
  LOGIN_SUCCESS,
  ACCOUNT_SWITCH,
  MESSAGE_FETCH_COMPLETE,
  EVENT_NEW_MESSAGE,
  EVENT_MESSAGE_DELETE,
  EVENT_REACTION_ADD,
  EVENT_REACTION_REMOVE,
  EVENT_UPDATE_MESSAGE,
} from '../actionConstants';
import { NULL_ARRAY, NULL_OBJECT } from '../nullObjects';
import { groupItemsById } from '../utils/misc';

const initialState: MessagesState = NULL_OBJECT;

const messageFetchComplete = (
  state: MessagesState,
  action: MessageFetchCompleteAction,
): MessagesState => ({
  ...state,
  ...groupItemsById(action.messages.map(message => omit(message, 'flags'))),
});

const eventReactionAdd = (state: MessagesState, action: EventReactionAddAction): MessagesState => {
  const oldMessage = state[action.message_id];
  if (!oldMessage) {
    return state;
  }
  return {
    ...state,
    [action.message_id]: {
      ...oldMessage,
      reactions: oldMessage.reactions.concat({
        emoji_name: action.emoji_name,
        user: action.user,
      }),
    },
  };
};

const eventReactionRemove = (
  state: MessagesState,
  action: EventReactionRemoveAction,
): MessagesState => {
  const oldMessage = state[action.message_id];
  if (!oldMessage) {
    return state;
  }
  return {
    ...state,
    [action.message_id]: {
      ...oldMessage,
      reactions: oldMessage.reactions.filter(
        x => !(x.emoji_name === action.emoji_name && x.user.email === action.user.email),
      ),
    },
  };
};

const eventNewMessage = (state: MessagesState, action: EventNewMessageAction): MessagesState => {
  // TODO: Optimize -- Only update if the new message belongs to at least
  // one narrow that is caught up.
  if (state[action.message.id]) {
    return state;
  }
  return {
    ...state,
    [action.message.id]: omit(action.message, 'flags'),
  };
};

const eventMessageDelete = (
  state: MessagesState,
  action: EventMessageDeleteAction,
): MessagesState => {
  if (!state[action.messageId]) {
    return state;
  }
  return omit(state, action.messageId);
};

const eventUpdateMessage = (
  state: MessagesState,
  action: EventUpdateMessageAction,
): MessagesState => {
  const oldMessage = state[action.message_id];
  if (!oldMessage) {
    return state;
  }
  return {
    ...state,
    [action.message_id]: {
      ...oldMessage,
      content: action.rendered_content || oldMessage.content,
      subject: action.subject || oldMessage.subject,
      subject_links: action.subject_links || oldMessage.subject_links,
      edit_history: [
        action.orig_rendered_content
          ? action.orig_subject
            ? {
                prev_rendered_content: action.orig_rendered_content,
                prev_subject: oldMessage.subject,
                timestamp: action.edit_timestamp,
                prev_rendered_content_version: action.prev_rendered_content_version,
                user_id: action.user_id,
              }
            : {
                prev_rendered_content: action.orig_rendered_content,
                timestamp: action.edit_timestamp,
                prev_rendered_content_version: action.prev_rendered_content_version,
                user_id: action.user_id,
              }
          : {
              prev_subject: oldMessage.subject,
              timestamp: action.edit_timestamp,
              user_id: action.user_id,
            },
        ...(oldMessage.edit_history || NULL_ARRAY),
      ],
      last_edit_timestamp: action.edit_timestamp,
    },
  };
};

export default (state: MessagesState = initialState, action: MessageAction): MessagesState => {
  switch (action.type) {
    case APP_REFRESH:
    case LOGOUT:
    case LOGIN_SUCCESS:
    case ACCOUNT_SWITCH:
      return initialState;

    case MESSAGE_FETCH_COMPLETE:
      return messageFetchComplete(state, action);

    case EVENT_REACTION_ADD:
      return eventReactionAdd(state, action);

    case EVENT_REACTION_REMOVE:
      return eventReactionRemove(state, action);

    case EVENT_NEW_MESSAGE:
      return eventNewMessage(state, action);

    case EVENT_MESSAGE_DELETE:
      return eventMessageDelete(state, action);

    case EVENT_UPDATE_MESSAGE:
      return eventUpdateMessage(state, action);

    default:
      return state;
  }
};
<commit_msg>messageReducers: Store forgotten fields on a reaction event.<commit_after>/* @flow */
import omit from 'lodash.omit';

import type {
  MessagesState,
  MessageAction,
  MessageFetchCompleteAction,
  EventReactionAddAction,
  EventReactionRemoveAction,
  EventNewMessageAction,
  EventMessageDeleteAction,
  EventUpdateMessageAction,
} from '../types';
import {
  APP_REFRESH,
  LOGOUT,
  LOGIN_SUCCESS,
  ACCOUNT_SWITCH,
  MESSAGE_FETCH_COMPLETE,
  EVENT_NEW_MESSAGE,
  EVENT_MESSAGE_DELETE,
  EVENT_REACTION_ADD,
  EVENT_REACTION_REMOVE,
  EVENT_UPDATE_MESSAGE,
} from '../actionConstants';
import { NULL_ARRAY, NULL_OBJECT } from '../nullObjects';
import { groupItemsById } from '../utils/misc';

const initialState: MessagesState = NULL_OBJECT;

const messageFetchComplete = (
  state: MessagesState,
  action: MessageFetchCompleteAction,
): MessagesState => ({
  ...state,
  ...groupItemsById(action.messages.map(message => omit(message, 'flags'))),
});

const eventReactionAdd = (state: MessagesState, action: EventReactionAddAction): MessagesState => {
  const oldMessage = state[action.message_id];
  if (!oldMessage) {
    return state;
  }
  return {
    ...state,
    [action.message_id]: {
      ...oldMessage,
      reactions: oldMessage.reactions.concat({
        emoji_name: action.emoji_name,
        user: action.user,
        reaction_type: action.reaction_type,
        emoji_code: action.emoji_code,
      }),
    },
  };
};

const eventReactionRemove = (
  state: MessagesState,
  action: EventReactionRemoveAction,
): MessagesState => {
  const oldMessage = state[action.message_id];
  if (!oldMessage) {
    return state;
  }
  return {
    ...state,
    [action.message_id]: {
      ...oldMessage,
      reactions: oldMessage.reactions.filter(
        x => !(x.emoji_name === action.emoji_name && x.user.email === action.user.email),
      ),
    },
  };
};

const eventNewMessage = (state: MessagesState, action: EventNewMessageAction): MessagesState => {
  // TODO: Optimize -- Only update if the new message belongs to at least
  // one narrow that is caught up.
  if (state[action.message.id]) {
    return state;
  }
  return {
    ...state,
    [action.message.id]: omit(action.message, 'flags'),
  };
};

const eventMessageDelete = (
  state: MessagesState,
  action: EventMessageDeleteAction,
): MessagesState => {
  if (!state[action.messageId]) {
    return state;
  }
  return omit(state, action.messageId);
};

const eventUpdateMessage = (
  state: MessagesState,
  action: EventUpdateMessageAction,
): MessagesState => {
  const oldMessage = state[action.message_id];
  if (!oldMessage) {
    return state;
  }
  return {
    ...state,
    [action.message_id]: {
      ...oldMessage,
      content: action.rendered_content || oldMessage.content,
      subject: action.subject || oldMessage.subject,
      subject_links: action.subject_links || oldMessage.subject_links,
      edit_history: [
        action.orig_rendered_content
          ? action.orig_subject
            ? {
                prev_rendered_content: action.orig_rendered_content,
                prev_subject: oldMessage.subject,
                timestamp: action.edit_timestamp,
                prev_rendered_content_version: action.prev_rendered_content_version,
                user_id: action.user_id,
              }
            : {
                prev_rendered_content: action.orig_rendered_content,
                timestamp: action.edit_timestamp,
                prev_rendered_content_version: action.prev_rendered_content_version,
                user_id: action.user_id,
              }
          : {
              prev_subject: oldMessage.subject,
              timestamp: action.edit_timestamp,
              user_id: action.user_id,
            },
        ...(oldMessage.edit_history || NULL_ARRAY),
      ],
      last_edit_timestamp: action.edit_timestamp,
    },
  };
};

export default (state: MessagesState = initialState, action: MessageAction): MessagesState => {
  switch (action.type) {
    case APP_REFRESH:
    case LOGOUT:
    case LOGIN_SUCCESS:
    case ACCOUNT_SWITCH:
      return initialState;

    case MESSAGE_FETCH_COMPLETE:
      return messageFetchComplete(state, action);

    case EVENT_REACTION_ADD:
      return eventReactionAdd(state, action);

    case EVENT_REACTION_REMOVE:
      return eventReactionRemove(state, action);

    case EVENT_NEW_MESSAGE:
      return eventNewMessage(state, action);

    case EVENT_MESSAGE_DELETE:
      return eventMessageDelete(state, action);

    case EVENT_UPDATE_MESSAGE:
      return eventUpdateMessage(state, action);

    default:
      return state;
  }
};
<|endoftext|>


<commit_before>import {
  find,
  forEach,
  lensProp,
  map,
  none,
  over,
  reject,
} from 'ramda'
import {
  PATTERN_BEAT_CELL_CLICK,
  PATTERN_BEAT_PLAYING_START,
  PATTERN_BEAT_PLAYING_STOP,
  PATTERN_SYNTH_CELL_CLICK,
  PATTERN_SYNTH_PLAYING_START,
  PATTERN_SYNTH_PLAYING_STOP,
  patternActiveNotesSet,
  patternNextLoopEndTimeSet,
  PATTERNS_ALL_PLAYING_STOP,
} from '../actions'
import {cellId} from '../reducers/patterns'
import {forEachIndexed} from '../utils/helpers'
import {instrumentInstance} from '../utils/derivedData'
import audioContext from '../audioContext'
import patternPitchOffset from '../constants/patternPitchOffset'
import pitchFromScaleIndex from '../audioHelpers/pitchFromScaleIndex'
import pitchToFrequency from '../audioHelpers/pitchToFrequency'
import sampleNames from '../constants/sampleNames'
import scales from '../constants/scales'

// schema:
// {
//   [patternId]: new Set([
//     {id, sourceNode},
//   ]),
// }
let sourceNodes = {}
const timeoutIds = {}

const stopSynthPattern = ({activeNotes}, patternId) => {
  forEach(({id, instrumentObj}) => instrumentObj.noteStop(id), activeNotes)
  clearTimeout(timeoutIds[patternId])
  delete timeoutIds[patternId]
}

const sampleGain = audioContext.createGain()
sampleGain.gain.value = 0.5
sampleGain.connect(audioContext.destination)

const playSample = (id, buffer, startTime, patternId) => {
  const sourceNode = audioContext.createBufferSource()
  const stopTime = startTime + buffer.duration + 0.1
  const overPatternId = over(lensProp(patternId))

  sourceNode.buffer = buffer
  sourceNode.connect(sampleGain)
  sourceNode.start(startTime)
  sourceNode.stop(stopTime)

  const data = {id, sourceNode}

  sourceNodes = overPatternId(
    sources => sources ? sources.add(data) : new Set([data]),
    sourceNodes
  )

  window.setTimeout(
    () => {
      sourceNode.disconnect()
      sourceNodes = overPatternId(
        sources => (sources.delete(data), sources),
        sourceNodes
      )
    },
    (stopTime - audioContext.currentTime) * 1000
  )
}

export default store => next => action => {
  switch (action.type) {
    case PATTERN_BEAT_CELL_CLICK: {
      const {patternId, x, y} = action.payload
      const {
        patterns,
        samples,
        settings: {noteDuration},
      } = store.getState()
      const {
        nextLoopEndTime,
        playing,
        xLength,
        steps,
      } = patterns[patternId]
      if (!playing) break
      const patternDuration = xLength * noteDuration
      const isAddedNote = none(note => note.x === x && note.y === y, steps)
      const id = cellId(patternId, x, y)
      if (isAddedNote) {
        playSample(
          id,
          samples[sampleNames[y]],
          nextLoopEndTime - patternDuration + noteDuration * x,
          patternId
        )
      } else {
        const set = sourceNodes[patternId]
        const entry = find(a => a.id === id, [...set])
        const {sourceNode} = entry
        set.delete(entry)
        sourceNode.stop()
        sourceNode.disconnect()
      }
      break
    }
    case PATTERN_BEAT_PLAYING_START: {
      setTimeout(() => {
        const {currentTime, patternId} = action.payload

        store.dispatch(patternNextLoopEndTimeSet({patternId, value: currentTime}))

        const audioLoop = (i = 0) => {
          const {
            patterns,
            samples,
            settings: {noteDuration},
          } = store.getState()
          const {
            nextLoopEndTime,
            steps,
            xLength,
          } = patterns[patternId]
          const patternDuration = xLength * noteDuration
          const currentLoopEndTime = nextLoopEndTime
          const newLoopEndTime = nextLoopEndTime + patternDuration

          store.dispatch(patternNextLoopEndTimeSet({patternId, value: newLoopEndTime}))

          timeoutIds[patternId] = setTimeout(
            () => audioLoop(i + 1),
            (newLoopEndTime - audioContext.currentTime - 0.1) * 1000
          )

          forEach(
            ({x, y}) => playSample(
              cellId(patternId, x, y),
              samples[sampleNames[y]],
              currentLoopEndTime + noteDuration * x,
              patternId
            ),
            steps
          )
        }

        audioLoop()
      })
      break
    }
    case PATTERN_BEAT_PLAYING_STOP: {
      const patternId = action.payload
      forEach(key => {
        const sources = sourceNodes[key]
        forEach(({sourceNode}) => {
          sourceNode.stop()
          sourceNode.disconnect()
        }, sources)
        sources.clear()
      }, Object.keys(sourceNodes))
      clearTimeout(timeoutIds[patternId])
      delete timeoutIds[patternId]
      break
    }
    case PATTERN_SYNTH_CELL_CLICK: {
      const {patternId, x, y} = action.payload
      const {patterns, plugins, settings: {noteDuration, rootNote, selectedScale}} = store.getState()
      const {
        activeNotes,
        instrument,
        nextLoopEndTime,
        playing,
        steps,
        volume,
        xLength,
        yLength,
      } = patterns[patternId]
      if (!playing) break
      const isAddedNote = none(note => note.x === x && note.y === y, steps)
      if (isAddedNote) {
        const instrumentObj = instrumentInstance(instrument, plugins)
        const id = cellId(patternId, x, y)
        const note = {
          frequency: pitchToFrequency(pitchFromScaleIndex(
            scales[selectedScale],
            yLength - 1 - y
          ) + rootNote + patternPitchOffset),
          gain: volume,
          id,
          startTime: nextLoopEndTime + noteDuration * (x - xLength),
          stopTime: nextLoopEndTime + noteDuration * (x - xLength + 1),
        }
        instrumentObj.noteStart(note)
      } else {
        const {id, instrumentObj} = find(
          ({id}) => id.includes(cellId(patternId, x, y)),
          activeNotes
        )
        instrumentObj.noteStop(id)
      }
      break
    }
    case PATTERN_SYNTH_PLAYING_START: {
      setTimeout(() => {
        const {currentTime, patternId} = action.payload
        store.dispatch(patternNextLoopEndTimeSet({patternId, value: currentTime}))
        const audioLoop = (i = 0) => {
          const {
            patterns,
            plugins,
            settings: {noteDuration, rootNote, selectedScale},
          } = store.getState()
          const {
            activeNotes,
            instrument,
            nextLoopEndTime,
            steps,
            volume,
            xLength,
            yLength,
          } = patterns[patternId]
          const instrumentObj = instrumentInstance(instrument, plugins)
          const patternDuration = xLength * noteDuration
          const currentLoopEndTime = nextLoopEndTime
          const newLoopEndTime = nextLoopEndTime + patternDuration

          store.dispatch(patternNextLoopEndTimeSet({
            patternId,
            value: newLoopEndTime,
          }))

          timeoutIds[patternId] = setTimeout(
            () => audioLoop(i + 1),
            (newLoopEndTime - audioContext.currentTime - 0.1) * 1000
          )

          store.dispatch(patternActiveNotesSet({
            patternId,
            value: reject(({id}) => {
              for (const {x, y} of steps) {
                if (id.includes(cellId(patternId, x, y))) return true
              }
            }, activeNotes).concat(map(({x, y}) => ({
              id: `pattern-${patternId}-${x}-${y}-${i}`,
              instrumentObj,
            }), steps))}))

          const notes = map(({x, y}) => ({
            frequency: pitchToFrequency(pitchFromScaleIndex(
              scales[selectedScale],
              yLength - 1 - y
            ) + rootNote + patternPitchOffset),
            gain: volume,
            id: `pattern-${patternId}-${x}-${y}-${i}`,
            startTime: currentLoopEndTime + noteDuration * x,
            stopTime: currentLoopEndTime + noteDuration * (x + 1),
          }), steps)
          instrumentObj.notesStart(notes)
          i++
        }

        audioLoop()
      })
      break
    }
    case PATTERN_SYNTH_PLAYING_STOP: {
      const patternId = action.payload
      stopSynthPattern(store.getState().patterns[patternId], patternId)
      break
    }
    case PATTERNS_ALL_PLAYING_STOP:
      forEachIndexed(stopSynthPattern, store.getState().patterns)
  }
  next(action)
}
<commit_msg>fix:pattern clicking beat pattern cell after it has played caused error<commit_after>import {
  find,
  forEach,
  lensProp,
  map,
  none,
  over,
  reject,
} from 'ramda'
import {
  PATTERN_BEAT_CELL_CLICK,
  PATTERN_BEAT_PLAYING_START,
  PATTERN_BEAT_PLAYING_STOP,
  PATTERN_SYNTH_CELL_CLICK,
  PATTERN_SYNTH_PLAYING_START,
  PATTERN_SYNTH_PLAYING_STOP,
  patternActiveNotesSet,
  patternNextLoopEndTimeSet,
  PATTERNS_ALL_PLAYING_STOP,
} from '../actions'
import {cellId} from '../reducers/patterns'
import {forEachIndexed} from '../utils/helpers'
import {instrumentInstance} from '../utils/derivedData'
import audioContext from '../audioContext'
import patternPitchOffset from '../constants/patternPitchOffset'
import pitchFromScaleIndex from '../audioHelpers/pitchFromScaleIndex'
import pitchToFrequency from '../audioHelpers/pitchToFrequency'
import sampleNames from '../constants/sampleNames'
import scales from '../constants/scales'

// schema:
// {
//   [patternId]: new Set([
//     {id, sourceNode},
//   ]),
// }
let sourceNodes = {}
const timeoutIds = {}

const stopSynthPattern = ({activeNotes}, patternId) => {
  forEach(({id, instrumentObj}) => instrumentObj.noteStop(id), activeNotes)
  clearTimeout(timeoutIds[patternId])
  delete timeoutIds[patternId]
}

const sampleGain = audioContext.createGain()
sampleGain.gain.value = 0.5
sampleGain.connect(audioContext.destination)

const playSample = (id, buffer, startTime, patternId) => {
  const sourceNode = audioContext.createBufferSource()
  const stopTime = startTime + buffer.duration + 0.1
  const overPatternId = over(lensProp(patternId))

  sourceNode.buffer = buffer
  sourceNode.connect(sampleGain)
  sourceNode.start(startTime)
  sourceNode.stop(stopTime)

  const data = {id, sourceNode}

  sourceNodes = overPatternId(
    sources => sources ? sources.add(data) : new Set([data]),
    sourceNodes
  )

  window.setTimeout(
    () => {
      sourceNode.disconnect()
      sourceNodes = overPatternId(
        sources => (sources.delete(data), sources),
        sourceNodes
      )
    },
    (stopTime - audioContext.currentTime) * 1000
  )
}

export default store => next => action => {
  switch (action.type) {
    case PATTERN_BEAT_CELL_CLICK: {
      const {patternId, x, y} = action.payload
      const {
        patterns,
        samples,
        settings: {noteDuration},
      } = store.getState()
      const {
        nextLoopEndTime,
        playing,
        xLength,
        steps,
      } = patterns[patternId]
      if (!playing) break
      const patternDuration = xLength * noteDuration
      const isAddedNote = none(note => note.x === x && note.y === y, steps)
      const id = cellId(patternId, x, y)
      if (isAddedNote) {
        playSample(
          id,
          samples[sampleNames[y]],
          nextLoopEndTime - patternDuration + noteDuration * x,
          patternId
        )
      } else {
        const set = sourceNodes[patternId]
        const entry = find(a => a.id === id, [...set])

        if (entry) {
          const {sourceNode} = entry
          set.delete(entry)
          sourceNode.stop()
          sourceNode.disconnect()
        }
      }
      break
    }
    case PATTERN_BEAT_PLAYING_START: {
      setTimeout(() => {
        const {currentTime, patternId} = action.payload

        store.dispatch(patternNextLoopEndTimeSet({patternId, value: currentTime}))

        const audioLoop = (i = 0) => {
          const {
            patterns,
            samples,
            settings: {noteDuration},
          } = store.getState()
          const {
            nextLoopEndTime,
            steps,
            xLength,
          } = patterns[patternId]
          const patternDuration = xLength * noteDuration
          const currentLoopEndTime = nextLoopEndTime
          const newLoopEndTime = nextLoopEndTime + patternDuration

          store.dispatch(patternNextLoopEndTimeSet({patternId, value: newLoopEndTime}))

          timeoutIds[patternId] = setTimeout(
            () => audioLoop(i + 1),
            (newLoopEndTime - audioContext.currentTime - 0.1) * 1000
          )

          forEach(
            ({x, y}) => playSample(
              cellId(patternId, x, y),
              samples[sampleNames[y]],
              currentLoopEndTime + noteDuration * x,
              patternId
            ),
            steps
          )
        }

        audioLoop()
      })
      break
    }
    case PATTERN_BEAT_PLAYING_STOP: {
      const patternId = action.payload
      forEach(key => {
        const sources = sourceNodes[key]
        forEach(({sourceNode}) => {
          sourceNode.stop()
          sourceNode.disconnect()
        }, sources)
        sources.clear()
      }, Object.keys(sourceNodes))
      clearTimeout(timeoutIds[patternId])
      delete timeoutIds[patternId]
      break
    }
    case PATTERN_SYNTH_CELL_CLICK: {
      const {patternId, x, y} = action.payload
      const {patterns, plugins, settings: {noteDuration, rootNote, selectedScale}} = store.getState()
      const {
        activeNotes,
        instrument,
        nextLoopEndTime,
        playing,
        steps,
        volume,
        xLength,
        yLength,
      } = patterns[patternId]
      if (!playing) break
      const isAddedNote = none(note => note.x === x && note.y === y, steps)
      if (isAddedNote) {
        const instrumentObj = instrumentInstance(instrument, plugins)
        const id = cellId(patternId, x, y)
        const note = {
          frequency: pitchToFrequency(pitchFromScaleIndex(
            scales[selectedScale],
            yLength - 1 - y
          ) + rootNote + patternPitchOffset),
          gain: volume,
          id,
          startTime: nextLoopEndTime + noteDuration * (x - xLength),
          stopTime: nextLoopEndTime + noteDuration * (x - xLength + 1),
        }
        instrumentObj.noteStart(note)
      } else {
        const {id, instrumentObj} = find(
          ({id}) => id.includes(cellId(patternId, x, y)),
          activeNotes
        )
        instrumentObj.noteStop(id)
      }
      break
    }
    case PATTERN_SYNTH_PLAYING_START: {
      setTimeout(() => {
        const {currentTime, patternId} = action.payload
        store.dispatch(patternNextLoopEndTimeSet({patternId, value: currentTime}))
        const audioLoop = (i = 0) => {
          const {
            patterns,
            plugins,
            settings: {noteDuration, rootNote, selectedScale},
          } = store.getState()
          const {
            activeNotes,
            instrument,
            nextLoopEndTime,
            steps,
            volume,
            xLength,
            yLength,
          } = patterns[patternId]
          const instrumentObj = instrumentInstance(instrument, plugins)
          const patternDuration = xLength * noteDuration
          const currentLoopEndTime = nextLoopEndTime
          const newLoopEndTime = nextLoopEndTime + patternDuration

          store.dispatch(patternNextLoopEndTimeSet({
            patternId,
            value: newLoopEndTime,
          }))

          timeoutIds[patternId] = setTimeout(
            () => audioLoop(i + 1),
            (newLoopEndTime - audioContext.currentTime - 0.1) * 1000
          )

          store.dispatch(patternActiveNotesSet({
            patternId,
            value: reject(({id}) => {
              for (const {x, y} of steps) {
                if (id.includes(cellId(patternId, x, y))) return true
              }
            }, activeNotes).concat(map(({x, y}) => ({
              id: `pattern-${patternId}-${x}-${y}-${i}`,
              instrumentObj,
            }), steps))}))

          const notes = map(({x, y}) => ({
            frequency: pitchToFrequency(pitchFromScaleIndex(
              scales[selectedScale],
              yLength - 1 - y
            ) + rootNote + patternPitchOffset),
            gain: volume,
            id: `pattern-${patternId}-${x}-${y}-${i}`,
            startTime: currentLoopEndTime + noteDuration * x,
            stopTime: currentLoopEndTime + noteDuration * (x + 1),
          }), steps)
          instrumentObj.notesStart(notes)
          i++
        }

        audioLoop()
      })
      break
    }
    case PATTERN_SYNTH_PLAYING_STOP: {
      const patternId = action.payload
      stopSynthPattern(store.getState().patterns[patternId], patternId)
      break
    }
    case PATTERNS_ALL_PLAYING_STOP:
      forEachIndexed(stopSynthPattern, store.getState().patterns)
  }
  next(action)
}
<|endoftext|>