<commit_before>'use strict';

const	EventEmitter	= require('events').EventEmitter,
	eventEmitter	= new EventEmitter(),
	dbmigration	= require('larvitdbmigration')({'tableName': 'product_db_version', 'migrationScriptsPath': __dirname + '/dbmigration'}),
	lUtils	= require('larvitutils'),
	async	= require('async'),
	db	= require('larvitdb');

let	readyInProgress	= false,
	isReady	= false;

function ready(cb) {
	const	tasks	= [];

	if (isReady === true) { cb(); return; }

	if (readyInProgress === true) {
		eventEmitter.on('ready', cb);
		return;
	}

	readyInProgress = true;

	// Migrate database
	tasks.push(function(cb) {
		dbmigration(function(err) {
			if (err) {
				log.error('larvitproduct: products.js: Database error: ' + err.message);
				return;
			}

			cb();
		});
	});

	async.series(tasks, function() {
		isReady	= true;
		eventEmitter.emit('ready');
		cb();
	});
}

function Products() {
	this.ready	= ready;
}

Products.prototype.get = function(cb) {
	const	tasks	= [],
		that	= this;

	let	productsCount	= 0,
		products	= {};

	// Make sure database is ready
	tasks.push(ready);

	// Get basic products
	tasks.push(function(cb) {
		let	countSql	= 'SELECT COUNT(*) AS products FROM product_products products WHERE 1',
			sql	= 'SELECT * FROM product_products products WHERE 1';

		that.generateWhere(function(where, dbFields) {
			where	+= ' ORDER BY created DESC';
			countSql	+= where;
			sql 	+= where;

			if (that.limit) {
				sql += ' LIMIT ' + parseInt(that.limit);
				if (that.offset) {
					sql += ' OFFSET ' + parseInt(that.offset);
				}
			}

			ready(function() {
				const	tasks	= [];

				tasks.push(function(cb) {
					db.query(sql, dbFields, function(err, rows) {
						if (err) { cb(err); return; }

						for (let i = 0; rows[i] !== undefined; i ++) {
							const	row	= rows[i],
								productUuid	= lUtils.formatUuid(row.uuid);

							products[productUuid]	= {};
							products[productUuid].uuid	= productUuid;
							products[productUuid].created	= row.created;
						}

						cb();
					});
				});

				tasks.push(function(cb) {
					db.query(countSql, dbFields, function(err, rows) {
						if (err) { cb(err); return; }

						productsCount = rows[0].products;
						cb();
					});
				});

				async.parallel(tasks, cb);
			});
		});
	});

	// Get fields
	tasks.push(function(cb) {
		const dbFields = [];

		let sql;

		if ((that.returnAllAttributes !== true && ! that.returnAttributes) || Object.keys(products).length === 0) {
			cb();
			return;
		}

		sql =  'SELECT productUuid, name AS attributeName, `data`\n';
		sql += 'FROM product_product_attributes JOIN product_attributes ON attributeUuid = uuid\n';
		sql += 'WHERE\n';
		sql += ' productUuid IN (';

		for (const productUuid of Object.keys(products)) {
			sql += '?,';
			dbFields.push(lUtils.uuidToBuffer(productUuid));
		}

		sql = sql.substring(0, sql.length - 1) + ')\n';

		if (that.returnAllAttributes !== true) {
			sql += '	AND name IN (';

			for (let i = 0; that.returnAttributes[i] !== undefined; i ++) {
				sql += '?,';
				dbFields.push(that.returnAttributes[i]);
			}

			sql = sql.substring(0, sql.length - 1) + ')\n';
		}

		db.query(sql, dbFields, function(err, rows) {
			if (err) { cb(err); return; }

			for (let i = 0; rows[i] !== undefined; i ++) {
				const row = rows[i];

				row.productUuid = lUtils.formatUuid(row.productUuid);

				if (products[row.productUuid].attributes === undefined) {
					products[row.productUuid].attributes = {};
				}

				if (products[row.productUuid].attributes[row.attributeName] === undefined) {
					products[row.productUuid].attributes[row.attributeName] = [];
				}

				products[row.productUuid].attributes[row.attributeName].push(row.data);
			}

			cb();
		});
	});

	async.series(tasks, function(err) {
		if (err) { cb(err); return; }

		cb(null, products, productsCount);
	});
};

Products.prototype.getUniqeAttributes = function(filters, cb) {
	const	attributes	= {},
		tasks	= [],
		that	= this;

	tasks.push(ready);

	tasks.push(function(cb) {
		let	sql;
		sql	=	'SELECT DISTINCT product_attributes.name, product_product_attributes.data\n';
		sql	+=	'FROM product_products as products\n';
		sql	+=	'JOIN product_product_attributes ON products.uuid = product_product_attributes.productUuid\n';
		sql	+=	'JOIN product_attributes ON product_product_attributes.attributeUuid = product_attributes.uuid WHERE 1\n';

		that.generateWhere(function(where, dbFields) {
			sql 	+= where;

			if (filters.length > 0) {
				sql += ' AND';
				for (let i = 0; filters[i] !== undefined; i ++) {
					sql += ' product_attributes.name = ? OR';
					dbFields.push(filters[i]);
				}
				sql = sql.substring(0, sql.length - 3);
			}

			ready(function() {
				db.query(sql, dbFields, function(err, rows) {
					for (let i = 0; rows[i] !== undefined; i ++) {
						if (attributes[rows[i].name] == undefined) {
							attributes[rows[i].name] = [rows[i].data];
						} else {
							attributes[rows[i].name].push(rows[i].data);
						}
					}
					cb();
				});
			});
		});
	});

	async.series(tasks, function(err) {
		if (err) { cb(err); return; }
		cb(null, attributes);
	});
};

Products.prototype.generateWhere = function(cb) {
	const	dbFields	= [],
		that	= this;

	let sql = '';

	if (that.uuids !== undefined) {
		if ( ! (that.uuids instanceof Array)) {
			that.uuids = [that.uuids];
		}

		if (that.uuids.length === 0) {
			sql += '	AND 0';
		} else {
			sql += '	AND products.uuid IN (';

			for (let i = 0; that.uuids[i] !== undefined; i ++) {
				sql += '?,';
				dbFields.push(lUtils.uuidToBuffer(that.uuids[i]));
			}

			sql = sql.substring(0, sql.length - 1) + ')';
		}
	}

	if (that.matchAllAttributes !== undefined) {
		for (const attributeName of Object.keys(that.matchAllAttributes)) {
			const	attributeValue	= that.matchAllAttributes[attributeName];

			sql += '	AND products.uuid IN (\n';
			sql += '		SELECT DISTINCT productUuid\n';
			sql += '		FROM product_product_attributes\n';

			dbFields.push(attributeName);
			if (attributeValue === undefined) {
				sql += '		WHERE attributeUuid = (SELECT uuid FROM product_attributes WHERE name = ?)\n';
			} else {
				sql += '		WHERE attributeUuid = (SELECT uuid FROM product_attributes WHERE name = ?) AND `data` = ?\n';
				dbFields.push(attributeValue);
			}

			sql += ')';

		}
	}

	if (that.searchString !== undefined && that.searchString !== '') {
		sql += '	AND products.uuid IN (\n';
		sql += '		SELECT DISTINCT productUuid\n';
		sql += '		FROM product_product_attributes\n';
		sql += ' WHERE data LIKE ?)\n';

		dbFields.push('%' + that.searchString.trim() + '%');
	}

	cb(sql, dbFields);
};

exports = module.exports = Products;
<commit_msg>Added possiblity to match multiple data in same attribute name<commit_after>'use strict';

const	EventEmitter	= require('events').EventEmitter,
	eventEmitter	= new EventEmitter(),
	dbmigration	= require('larvitdbmigration')({'tableName': 'product_db_version', 'migrationScriptsPath': __dirname + '/dbmigration'}),
	lUtils	= require('larvitutils'),
	async	= require('async'),
	db	= require('larvitdb');

let	readyInProgress	= false,
	isReady	= false;

function ready(cb) {
	const	tasks	= [];

	if (isReady === true) { cb(); return; }

	if (readyInProgress === true) {
		eventEmitter.on('ready', cb);
		return;
	}

	readyInProgress = true;

	// Migrate database
	tasks.push(function(cb) {
		dbmigration(function(err) {
			if (err) {
				log.error('larvitproduct: products.js: Database error: ' + err.message);
				return;
			}

			cb();
		});
	});

	async.series(tasks, function() {
		isReady	= true;
		eventEmitter.emit('ready');
		cb();
	});
}

function Products() {
	this.ready	= ready;
}

Products.prototype.get = function(cb) {
	const	tasks	= [],
		that	= this;

	let	productsCount	= 0,
		products	= {};

	// Make sure database is ready
	tasks.push(ready);

	// Get basic products
	tasks.push(function(cb) {
		let	countSql	= 'SELECT COUNT(*) AS products FROM product_products products WHERE 1',
			sql	= 'SELECT * FROM product_products products WHERE 1';

		that.generateWhere(function(where, dbFields) {
			where	+= ' ORDER BY created DESC';
			countSql	+= where;
			sql 	+= where;

			if (that.limit) {
				sql += ' LIMIT ' + parseInt(that.limit);
				if (that.offset) {
					sql += ' OFFSET ' + parseInt(that.offset);
				}
			}

			ready(function() {
				const	tasks	= [];

				tasks.push(function(cb) {
					db.query(sql, dbFields, function(err, rows) {
						if (err) { cb(err); return; }

						for (let i = 0; rows[i] !== undefined; i ++) {
							const	row	= rows[i],
								productUuid	= lUtils.formatUuid(row.uuid);

							products[productUuid]	= {};
							products[productUuid].uuid	= productUuid;
							products[productUuid].created	= row.created;
						}

						cb();
					});
				});

				tasks.push(function(cb) {
					db.query(countSql, dbFields, function(err, rows) {
						if (err) { cb(err); return; }

						productsCount = rows[0].products;
						cb();
					});
				});

				async.parallel(tasks, cb);
			});
		});
	});

	// Get fields
	tasks.push(function(cb) {
		const dbFields = [];

		let sql;

		if ((that.returnAllAttributes !== true && ! that.returnAttributes) || Object.keys(products).length === 0) {
			cb();
			return;
		}

		sql =  'SELECT productUuid, name AS attributeName, `data`\n';
		sql += 'FROM product_product_attributes JOIN product_attributes ON attributeUuid = uuid\n';
		sql += 'WHERE\n';
		sql += ' productUuid IN (';

		for (const productUuid of Object.keys(products)) {
			sql += '?,';
			dbFields.push(lUtils.uuidToBuffer(productUuid));
		}

		sql = sql.substring(0, sql.length - 1) + ')\n';

		if (that.returnAllAttributes !== true) {
			sql += '	AND name IN (';

			for (let i = 0; that.returnAttributes[i] !== undefined; i ++) {
				sql += '?,';
				dbFields.push(that.returnAttributes[i]);
			}

			sql = sql.substring(0, sql.length - 1) + ')\n';
		}

		db.query(sql, dbFields, function(err, rows) {
			if (err) { cb(err); return; }

			for (let i = 0; rows[i] !== undefined; i ++) {
				const row = rows[i];

				row.productUuid = lUtils.formatUuid(row.productUuid);

				if (products[row.productUuid].attributes === undefined) {
					products[row.productUuid].attributes = {};
				}

				if (products[row.productUuid].attributes[row.attributeName] === undefined) {
					products[row.productUuid].attributes[row.attributeName] = [];
				}

				products[row.productUuid].attributes[row.attributeName].push(row.data);
			}

			cb();
		});
	});

	async.series(tasks, function(err) {
		if (err) { cb(err); return; }

		cb(null, products, productsCount);
	});
};

Products.prototype.getUniqeAttributes = function(filters, cb) {
	const	attributes	= {},
		tasks	= [],
		that	= this;

	tasks.push(ready);

	tasks.push(function(cb) {
		let	sql;
		sql	=	'SELECT DISTINCT product_attributes.name, product_product_attributes.data\n';
		sql	+=	'FROM product_products as products\n';
		sql	+=	'JOIN product_product_attributes ON products.uuid = product_product_attributes.productUuid\n';
		sql	+=	'JOIN product_attributes ON product_product_attributes.attributeUuid = product_attributes.uuid WHERE 1\n';

		that.generateWhere(function(where, dbFields) {
			sql 	+= where;

			if (filters.length > 0) {
				sql += ' AND (';
				for (let i = 0; filters[i] !== undefined; i ++) {
					sql += ' product_attributes.name = ? OR';
					dbFields.push(filters[i]);
				}
				sql = sql.substring(0, sql.length - 3);
				sql += ')';
			}

			ready(function() {
				db.query(sql, dbFields, function(err, rows) {
					for (let i = 0; rows[i] !== undefined; i ++) {
						if (attributes[rows[i].name] == undefined) {
							attributes[rows[i].name] = [rows[i].data];
						} else {
							attributes[rows[i].name].push(rows[i].data);
						}
					}
					cb();
				});
			});
		});
	});

	async.series(tasks, function(err) {
		if (err) { cb(err); return; }
		cb(null, attributes);
	});
};

Products.prototype.generateWhere = function(cb) {
	const	dbFields	= [],
		that	= this;

	let sql = '';

	if (that.uuids !== undefined) {
		if ( ! (that.uuids instanceof Array)) {
			that.uuids = [that.uuids];
		}

		if (that.uuids.length === 0) {
			sql += '	AND 0';
		} else {
			sql += '	AND products.uuid IN (';

			for (let i = 0; that.uuids[i] !== undefined; i ++) {
				sql += '?,';
				dbFields.push(lUtils.uuidToBuffer(that.uuids[i]));
			}

			sql = sql.substring(0, sql.length - 1) + ')';
		}
	}

	if (that.matchAllAttributes !== undefined) {
		for (const attributeName of Object.keys(that.matchAllAttributes)) {
			const	attributeValue	= that.matchAllAttributes[attributeName];

			if (Array.isArray(attributeValue)) {
				for (let i = 0; attributeValue[i] !== undefined; i ++) {
					if (i === 0) {
						sql += '	AND ( products.uuid IN (\n';
					} else {
						sql += '	OR products.uuid IN (\n';
					}

					sql += '		SELECT DISTINCT productUuid\n';
					sql += '		FROM product_product_attributes\n';

					dbFields.push(attributeName);
					if (attributeValue[i] === undefined) {
						sql += '		WHERE attributeUuid = (SELECT uuid FROM product_attributes WHERE name = ?)\n';
					} else {
						sql += '		WHERE attributeUuid = (SELECT uuid FROM product_attributes WHERE name = ?) AND `data` = ?\n';
						dbFields.push(attributeValue[i]);
					}
					sql += ')';
				}
				sql += ')';
			} else {
				sql += '	AND products.uuid IN (\n';
				sql += '		SELECT DISTINCT productUuid\n';
				sql += '		FROM product_product_attributes\n';

				dbFields.push(attributeName);
				if (attributeValue === undefined) {
					sql += '		WHERE attributeUuid = (SELECT uuid FROM product_attributes WHERE name = ?)\n';
				} else {
					sql += '		WHERE attributeUuid = (SELECT uuid FROM product_attributes WHERE name = ?) AND `data` = ?\n';
					dbFields.push(attributeValue);
				}
				sql += ')';
			}

		}
	}

	if (that.searchString !== undefined && that.searchString !== '') {
		sql += '	AND products.uuid IN (\n';
		sql += '		SELECT DISTINCT productUuid\n';
		sql += '		FROM product_product_attributes\n';
		sql += ' WHERE data LIKE ?)\n';

		dbFields.push('%' + that.searchString.trim() + '%');
	}

	cb(sql, dbFields);
};

exports = module.exports = Products;
<|endoftext|>


<commit_before>(function() {
  "use strict";

  module("matches");

  var fixture1 = document.getElementById("fixture1");
  var fixture2 = document.getElementById("fixture2");

  test("id", function() {
    var set = new SelectorSet();
    set.add("#foo");
    set.add("#bar");
    set.add("#baz");

    var el = fixture1.querySelector("#foo");
    var results = set.matches(el);
    equal(results.length, 1);
    equal(results[0].selector, "#foo");

    el = fixture1.querySelector(".foo");
    results = set.matches(el);
    equal(results.length, 0);
  });

  test("id with tag", function() {
    var set = new SelectorSet();
    set.add("div#foo");
    set.add("span#foo");
    set.add("div#bar");

    var el = fixture1.querySelector("div#foo");
    var results = set.matches(el);
    equal(results.length, 1);
    equal(results[0].selector, "div#foo");

    el = fixture1.querySelector("div.foo");
    results = set.matches(el);
    equal(results.length, 0);
  });

  test("class", function() {
    var set = new SelectorSet();
    set.add(".foo");
    set.add(".bar");
    set.add(".baz");

    var el = fixture1.querySelector(".foo");
    var results = set.matches(el);
    equal(results.length, 1);
    equal(results[0].selector, ".foo");

    el = fixture1.querySelector("#foo");
    results = set.matches(el);
    equal(results.length, 0);
  });

  test("class with tag", function() {
    var set = new SelectorSet();
    set.add("div.foo");
    set.add("span.foo");
    set.add("div.bar");

    var el = fixture1.querySelector("div.foo");
    var results = set.matches(el);
    equal(results.length, 1);
    equal(results[0].selector, "div.foo");

    el = fixture1.querySelector("div#foo");
    results = set.matches(el);
    equal(results.length, 0);
  });

  test("tag", function() {
    var set = new SelectorSet();
    set.add("foo");
    set.add("bar");
    set.add("baz");

    var el = fixture1.querySelector("foo");
    var results = set.matches(el);
    equal(results.length, 1);
    equal(results[0].selector, "foo");

    el = fixture1.querySelector("bar");
    results = set.matches(el);
    equal(results.length, 0);
  });

  test("attribute", function() {
    var set = new SelectorSet();
    set.add("[foo=bar]");
    set.add("[foo=baz]");

    var el = fixture1.querySelector("[foo=bar]");
    var results = set.matches(el);
    equal(results.length, 1);
    equal(results[0].selector, "[foo=bar]");
  });

  test("id and class", function() {
    var set = new SelectorSet();
    set.add("#foo");
    set.add("#bar");
    set.add(".foo");
    set.add(".bar");

    var el = fixture1.querySelector("#foo");
    var results = set.matches(el);
    equal(results.length, 1);
    equal(results[0].selector, "#foo");

    el = fixture1.querySelector(".foo");
    results = set.matches(el);
    equal(results.length, 1);
    equal(results[0].selector, ".foo");

    el = fixture1.querySelector("foo");
    results = set.matches(el);
    equal(results.length, 0);
  });

  test("multiple id and class match", function() {
    var set = new SelectorSet();
    set.add("#foo");
    set.add("#bar");
    set.add(".foo");
    set.add(".bar");

    var el = fixture2.querySelector("foo");
    var results = set.matches(el);
    equal(results.length, 2);
    equal(results[0].selector, "#foo");
    equal(results[1].selector, ".foo");
  });

  test("multiple id and class match reverse", function() {
    var set = new SelectorSet();
    set.add(".foo");
    set.add(".bar");
    set.add("#foo");
    set.add("#bar");

    var el = fixture2.querySelector("foo");
    var results = set.matches(el);
    equal(results.length, 2);
    equal(results[0].selector, ".foo");
    equal(results[1].selector, "#foo");
  });

  test("multiple id, class and tag match", function() {
    var set = new SelectorSet();
    set.add("#foo");
    set.add("#bar");
    set.add(".foo");
    set.add(".bar");
    set.add("foo");
    set.add("bar");

    var el = fixture2.querySelector("foo");
    var results = set.matches(el);
    equal(results.length, 3);
    equal(results[0].selector, "#foo");
    equal(results[1].selector, ".foo");
    equal(results[2].selector, "foo");
  });

  test("multiple id, class and tag match reverse", function() {
    var set = new SelectorSet();
    set.add("foo");
    set.add("bar");
    set.add(".foo");
    set.add(".bar");
    set.add("#foo");
    set.add("#bar");

    var el = fixture2.querySelector("foo");
    var results = set.matches(el);
    equal(results.length, 3);
    equal(results[0].selector, "foo");
    equal(results[1].selector, ".foo");
    equal(results[2].selector, "#foo");
  });
})();
<commit_msg>Test multiple selectors<commit_after>(function() {
  "use strict";

  module("matches");

  var fixture1 = document.getElementById("fixture1");
  var fixture2 = document.getElementById("fixture2");

  test("id", function() {
    var set = new SelectorSet();
    set.add("#foo");
    set.add("#bar");
    set.add("#baz");

    var el = fixture1.querySelector("#foo");
    var results = set.matches(el);
    equal(results.length, 1);
    equal(results[0].selector, "#foo");

    el = fixture1.querySelector(".foo");
    results = set.matches(el);
    equal(results.length, 0);
  });

  test("id with tag", function() {
    var set = new SelectorSet();
    set.add("div#foo");
    set.add("span#foo");
    set.add("div#bar");

    var el = fixture1.querySelector("div#foo");
    var results = set.matches(el);
    equal(results.length, 1);
    equal(results[0].selector, "div#foo");

    el = fixture1.querySelector("div.foo");
    results = set.matches(el);
    equal(results.length, 0);
  });

  test("class", function() {
    var set = new SelectorSet();
    set.add(".foo");
    set.add(".bar");
    set.add(".baz");

    var el = fixture1.querySelector(".foo");
    var results = set.matches(el);
    equal(results.length, 1);
    equal(results[0].selector, ".foo");

    el = fixture1.querySelector("#foo");
    results = set.matches(el);
    equal(results.length, 0);
  });

  test("class with tag", function() {
    var set = new SelectorSet();
    set.add("div.foo");
    set.add("span.foo");
    set.add("div.bar");

    var el = fixture1.querySelector("div.foo");
    var results = set.matches(el);
    equal(results.length, 1);
    equal(results[0].selector, "div.foo");

    el = fixture1.querySelector("div#foo");
    results = set.matches(el);
    equal(results.length, 0);
  });

  test("tag", function() {
    var set = new SelectorSet();
    set.add("foo");
    set.add("bar");
    set.add("baz");

    var el = fixture1.querySelector("foo");
    var results = set.matches(el);
    equal(results.length, 1);
    equal(results[0].selector, "foo");

    el = fixture1.querySelector("bar");
    results = set.matches(el);
    equal(results.length, 0);
  });

  test("attribute", function() {
    var set = new SelectorSet();
    set.add("[foo=bar]");
    set.add("[foo=baz]");

    var el = fixture1.querySelector("[foo=bar]");
    var results = set.matches(el);
    equal(results.length, 1);
    equal(results[0].selector, "[foo=bar]");
  });

  test("id and class", function() {
    var set = new SelectorSet();
    set.add("#foo");
    set.add("#bar");
    set.add(".foo");
    set.add(".bar");

    var el = fixture1.querySelector("#foo");
    var results = set.matches(el);
    equal(results.length, 1);
    equal(results[0].selector, "#foo");

    el = fixture1.querySelector(".foo");
    results = set.matches(el);
    equal(results.length, 1);
    equal(results[0].selector, ".foo");

    el = fixture1.querySelector("foo");
    results = set.matches(el);
    equal(results.length, 0);
  });

  test("multiple id and class match", function() {
    var set = new SelectorSet();
    set.add("#foo");
    set.add("#bar");
    set.add(".foo");
    set.add(".bar");

    var el = fixture2.querySelector("foo");
    var results = set.matches(el);
    equal(results.length, 2);
    equal(results[0].selector, "#foo");
    equal(results[1].selector, ".foo");
  });

  test("multiple id and class match reverse", function() {
    var set = new SelectorSet();
    set.add(".foo");
    set.add(".bar");
    set.add("#foo");
    set.add("#bar");

    var el = fixture2.querySelector("foo");
    var results = set.matches(el);
    equal(results.length, 2);
    equal(results[0].selector, ".foo");
    equal(results[1].selector, "#foo");
  });

  test("component id and class match", function() {
    var set = new SelectorSet();
    set.add(".foo, .bar");
    set.add("#foo, #bar");
    set.add("#foo, .foo");
    set.add("#bar, .bar");

    var el = fixture2.querySelector("foo");
    var results = set.matches(el);
    equal(results.length, 3);
    equal(results[0].selector, ".foo, .bar");
    equal(results[1].selector, "#foo, #bar");
    equal(results[2].selector, "#foo, .bar");
  });

  test("multiple id, class and tag match", function() {
    var set = new SelectorSet();
    set.add("#foo");
    set.add("#bar");
    set.add(".foo");
    set.add(".bar");
    set.add("foo");
    set.add("bar");

    var el = fixture2.querySelector("foo");
    var results = set.matches(el);
    equal(results.length, 3);
    equal(results[0].selector, "#foo");
    equal(results[1].selector, ".foo");
    equal(results[2].selector, "foo");
  });

  test("multiple id, class and tag match reverse", function() {
    var set = new SelectorSet();
    set.add("foo");
    set.add("bar");
    set.add(".foo");
    set.add(".bar");
    set.add("#foo");
    set.add("#bar");

    var el = fixture2.querySelector("foo");
    var results = set.matches(el);
    equal(results.length, 3);
    equal(results[0].selector, "foo");
    equal(results[1].selector, ".foo");
    equal(results[2].selector, "#foo");
  });
})();
<|endoftext|>


<commit_before>/*
Name:          readcsv.js
Description:   Parse CSV file with automatic format detection
Author:        Franklin van de Meent (https://frankl.in)
Source code:   https://github.com/fvdm/nodejs-readcsv
Feedback:      https://github.com/fvdm/nodejs-readcsv/issues
License:       Unlicense (Public Domain)
               (https://github.com/fvdm/nodejs-readcsv/raw/master/LICENSE)
*/

const fs = require ('fs');

/**
 * Read CSV file and parse to data
 *
 * @callback  callback

 * @param     {bool|array}  [head]    Read fieldnamed from first line (true) or custom
 * @param     {string}      file      Path the CSV file
 * @param     {function}    callback  `(err, data)`

 * @return    {void}
 */

module.exports = (head, file, callback) => {
  if (typeof file === 'function') {
    callback = file;
    file = head;
    head = null;
  }

  fs.readFile (file, { encoding: 'utf8' }, (err, data) => {
    let output = [];
    let sep = ',';
    let quotes = '\'';
    let linebreak;

    if (err) {
      callback (err);
      return;
    }

    linebreak = data.slice (-2) === '\r\n' ? '\r\n' : '\n';
    data = data
      .trim()
      .split (linebreak);

    if (data[0].match ('\',\'')) {
      sep = ',';
      quotes = '\'';
    } else if (data[0].match ('\';\'')) {
      sep = ';';
      quotes = '\'';
    } else if (data[0].match ('","')) {
      sep = ',';
      quotes = '"';
    } else if (data[0].match ('";"')) {
      sep = ';';
      quotes = '"';
    } else {
      callback (new Error ('cannot detect line format'));
      return;
    }

    data.forEach ((line, i) => {
      let tx = {};

      line = line.split (quotes + sep + quotes);
      line[0] = line[0].slice (1);
      line[line.length - 1] = line[line.length - 1].slice (0, -1);

      if (head === true && i === 0) {
        head = line;
      } else if (head) {
        head.forEach ((name, key) => {
          tx[name] = line[key];
        });

        output.push (tx);
      } else {
        output.push (line);
      }
    });

    callback (null, output);
  });
};

<commit_msg>Refactor(main): Rewrite to cleaner style<commit_after>/*
Name:          readcsv.js
Description:   Parse CSV file with automatic format detection
Author:        Franklin van de Meent (https://frankl.in)
Source code:   https://github.com/fvdm/nodejs-readcsv
Feedback:      https://github.com/fvdm/nodejs-readcsv/issues
License:       Unlicense (Public Domain)
               (https://github.com/fvdm/nodejs-readcsv/raw/master/LICENSE)
*/

const fs = require ('fs');

/**
 * Parse plain text to lines
 * and detect CSV format
 *
 * @param   {string}  data  Plain text document
 * @return  {object}        props: lines, sep, quotes
 */

function parseText (data) {
  const linebreak = data.slice (-2) === '\r\n' ? '\r\n' : '\n';

  let result = {};

  result.lines = data
    .trim()
    .split (linebreak);

  if (result.lines[0].match ('\',\'')) {
    result.sep = ',';
    result.quotes = '\'';
  } else if (result.lines[0].match ('\';\'')) {
    result.sep = ';';
    result.quotes = '\'';
  } else if (result.lines[0].match ('","')) {
    result.sep = ',';
    result.quotes = '"';
  } else if (result.lines[0].match ('";"')) {
    result.sep = ';';
    result.quotes = '"';
  } else {
    result = null;
  }

  return result;
}

/**
 * Parse each line to their fields
 *
 * @param   {object}      data
 * @param   {array}       data.lines   Lines from the text file
 * @param   {string}      data.quotes  `'` or `"`
 * @param   {string}      data.sep     `,` or `;`
 * @param   {bool|array}  head         Fields: true = use first line, array is custom
 *
 * @return  {array}                    Parsed lines
 */

function parseLines (data, head) {
  let output = [];

  data.lines.forEach ((line, i) => {
    let tx = {};

    line = line.split (data.quotes + data.sep + data.quotes);
    line[0] = line[0].slice (1);
    line[line.length - 1] = line[line.length - 1].slice (0, -1);

    if (head === true && i === 0) {
      head = line;
    } else if (head) {
      head.forEach ((name, key) => {
        tx[name] = line[key];
      });

      output.push (tx);
    } else {
      output.push (line);
    }
  });

  return output;
}

/**
 * Read CSV file and parse to data
 *
 * @callback  callback

 * @param     {bool|array}  [head]    Read fieldnamed from first line (true) or custom
 * @param     {string}      file      Path the CSV file
 * @param     {function}    callback  `(err, data)`

 * @return    {void}
 */

module.exports = (head, file, callback) => {
  if (typeof file === 'function') {
    callback = file;
    file = head;
    head = false;
  }

  fs.readFile (file, { encoding: 'utf8' }, (err, text) => {
    let error;
    let result;
    let data;

    if (err) {
      callback (err);
      return;
    }

    data = parseText (text);

    if (data) {
      result = parseLines (data, head);
      callback (null, result);
    } else {
      error = new Error ('cannot detect line format');
      callback (error);
    }
  });
};

<|endoftext|>


<commit_before>/*jslint es5: true, node: true, nomen: true */
/*global requireJsDefine: true, requirejs: true*/

/*
 * #%L
 * %%
 * Copyright (C) 2011 - 2017 BMW Car IT GmbH
 * %%
 * 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.
 * #L%
 */

function populateJoynrApi(joynr, api) {
    var key;
    for (key in api) {
        if (api.hasOwnProperty(key)) {
            joynr[key] = api[key];
        }
    }
}

function recursiveFreeze(object) {
    var property;
    var propertyKey = null;
    Object.freeze(object); // First freeze the object.
    for (propertyKey in object) {
        if (object.hasOwnProperty(propertyKey)) {
            property = object[propertyKey];
            if ((typeof property === 'object') && !Object.isFrozen(property)) {
                recursiveFreeze(property);
            }
        }
    }
}

function freeze(joynr, capabilitiesWritable) {
    Object.defineProperties(joynr, {
        "messaging" : {
            writable : false
        },
        "types" : {
            writable : false
        },
        "start" : {
            writable : false
        },
        "shutdown" : {
            writable : true
        },
        "typeRegistry" : {
            writable : false
        },
        "capabilities" : {
            writable : capabilitiesWritable === true
        },
        "proxy" : {
            writable : false
        },
        "proxyBuilder" : {
            writable : true
        },
        "providerBuilder" : {
            writable : false
        }
    });
}

var Promise;

/**
 * @name joynr
 * @class
 */
var joynr = {
    loaded : false,
    /**
     * @name joynr#load
     * @function
     * @param provisioning
     * @param capabilitiesWriteable
     * @return Promise object being resolved in case all libjoynr dependencies are loaded
     */
    load : function load(provisioning, capabilitiesWritable) {
        return new Promise(function(resolve, reject) {
            joynr.loaded = true;
            requirejs([ 'libjoynr-deps' ], function(joynrapi) {
                var runtime;
                runtime = new joynrapi.Runtime(provisioning);
                runtime.start().then(function() {
                    populateJoynrApi(joynr, joynrapi);
                    //remove Runtime, as it is not required for the end user
                    delete joynr.Runtime;
                    populateJoynrApi(joynr, runtime);
                    freeze(joynr, capabilitiesWritable);
                    resolve(joynr);
                    return;
                }).catch(function(error) {
                    reject(error);
                    return error;
                });
            });
        });
    },
    /**
     * Adds a typeName to constructor entry in the type registry.
     *
     * @name joynr#addType
     * @function
     * @see TypeRegistry#addType
     *
     * @param {String}
     *            joynrTypeName - the joynr type name that is sent on the wire.
     * @param {Function}
     *            typeConstructor - the corresponding JavaScript constructor for this type.
     * @param {boolean}
     *            isEnum - optional flag if the added type is an enumeration type
     */
    addType : function registerType(name, type, isEnum) {
        requirejs([
                      "joynr/types/TypeRegistrySingleton"
                  ],
                  function(
                      TypeRegistrySingleton
                  ) {
            TypeRegistrySingleton.getInstance().addType(name, type, isEnum);
        });
    },
    JoynrObject : function JoynrObject() {}
};

var exports, module;
if (typeof define === 'function' && define.amd) {
    // expose joynr to requirejs in the event that requirejs is being
    // used natively, or by almond in the event that it has overwritten
    // define
    define([
               "global/Promise"
           ],
           function(
               PromiseDep
           ) {
        Promise = PromiseDep;
        return joynr;
    });
}

// using optimized joynr, never used with nodejs
if (typeof requireJsDefine === 'function' && requireJsDefine.amd) {
    // define has been mapped to almond's define
    requireJsDefine([
                        "global/Promise"
                    ],
                    function(
                        PromiseDep) {
        Promise = PromiseDep;
        return joynr;
    });

    // using joynr with native nodejs require
} else if (exports !== undefined) {
    requirejs = require("requirejs");
    joynr.selectRuntime = function selectRuntime(runtime) {
        if (joynr.loaded) {
            throw new Error("joynr.selectRuntime: this method must " +
                            "be invoked before calling joynr.load()");
        } else {
            // configuring requirejs
            var requirejsConfig = require("./require.config.node.js");
            requirejsConfig.paths["joynr/Runtime"] = "joynr/Runtime." + runtime;
            requirejs.config(requirejsConfig);
        }
    };
    joynr.selectRuntime("websocket.libjoynr");
    Promise = requirejs("global/Promise");
    if ((module !== undefined) && module.exports) {
        exports.joynr = module.exports = joynr;
    } else {
        // support CommonJS module 1.1.1 spec (`exports` cannot be a function)
        exports.joynr = joynr;
    }

    // not using AMD
} else if (typeof window === "object") {
    Promise = window.Promise;
    // export namespace fragment or module read-only to the parent namespace
    Object.defineProperty(window, "joynr", {
        readable : true,
        enumerable : true,
        configurable : false,
        writable : false,
        value : joynr
    });
}

/* jslint nomen: false */<commit_msg>[JS] Avoid multiple require of requirejs in joynr.js<commit_after>/*jslint es5: true, node: true, nomen: true */
/*global requireJsDefine: true, requirejs: true*/

/*
 * #%L
 * %%
 * Copyright (C) 2011 - 2017 BMW Car IT GmbH
 * %%
 * 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.
 * #L%
 */

function populateJoynrApi(joynr, api) {
    var key;
    for (key in api) {
        if (api.hasOwnProperty(key)) {
            joynr[key] = api[key];
        }
    }
}

function recursiveFreeze(object) {
    var property;
    var propertyKey = null;
    Object.freeze(object); // First freeze the object.
    for (propertyKey in object) {
        if (object.hasOwnProperty(propertyKey)) {
            property = object[propertyKey];
            if ((typeof property === 'object') && !Object.isFrozen(property)) {
                recursiveFreeze(property);
            }
        }
    }
}

function freeze(joynr, capabilitiesWritable) {
    Object.defineProperties(joynr, {
        "messaging" : {
            writable : false
        },
        "types" : {
            writable : false
        },
        "start" : {
            writable : false
        },
        "shutdown" : {
            writable : true
        },
        "typeRegistry" : {
            writable : false
        },
        "capabilities" : {
            writable : capabilitiesWritable === true
        },
        "proxy" : {
            writable : false
        },
        "proxyBuilder" : {
            writable : true
        },
        "providerBuilder" : {
            writable : false
        }
    });
}

var Promise;

/**
 * @name joynr
 * @class
 */
var joynr = {
    loaded : false,
    /**
     * @name joynr#load
     * @function
     * @param provisioning
     * @param capabilitiesWriteable
     * @return Promise object being resolved in case all libjoynr dependencies are loaded
     */
    load : function load(provisioning, capabilitiesWritable) {
        return new Promise(function(resolve, reject) {
            joynr.loaded = true;
            requirejs([ 'libjoynr-deps' ], function(joynrapi) {
                var runtime;
                runtime = new joynrapi.Runtime(provisioning);
                runtime.start().then(function() {
                    populateJoynrApi(joynr, joynrapi);
                    //remove Runtime, as it is not required for the end user
                    delete joynr.Runtime;
                    populateJoynrApi(joynr, runtime);
                    freeze(joynr, capabilitiesWritable);
                    resolve(joynr);
                    return;
                }).catch(function(error) {
                    reject(error);
                    return error;
                });
            });
        });
    },
    /**
     * Adds a typeName to constructor entry in the type registry.
     *
     * @name joynr#addType
     * @function
     * @see TypeRegistry#addType
     *
     * @param {String}
     *            joynrTypeName - the joynr type name that is sent on the wire.
     * @param {Function}
     *            typeConstructor - the corresponding JavaScript constructor for this type.
     * @param {boolean}
     *            isEnum - optional flag if the added type is an enumeration type
     */
    addType : function registerType(name, type, isEnum) {
        requirejs([
                      "joynr/types/TypeRegistrySingleton"
                  ],
                  function(
                      TypeRegistrySingleton
                  ) {
            TypeRegistrySingleton.getInstance().addType(name, type, isEnum);
        });
    },
    JoynrObject : function JoynrObject() {}
};

var exports, module;
if (typeof define === 'function' && define.amd) {
    // expose joynr to requirejs in the event that requirejs is being
    // used natively, or by almond in the event that it has overwritten
    // define
    define([
               "global/Promise"
           ],
           function(
               PromiseDep
           ) {
        Promise = PromiseDep;
        return joynr;
    });
}

// using optimized joynr, never used with nodejs
if (typeof requireJsDefine === 'function' && requireJsDefine.amd) {
    // define has been mapped to almond's define
    requireJsDefine([
                        "global/Promise"
                    ],
                    function(
                        PromiseDep) {
        Promise = PromiseDep;
        return joynr;
    });

    // using joynr with native nodejs require
} else if (exports !== undefined) {
    if (typeof requirejs !== 'function') {
        requirejs = require("requirejs");
    }
    joynr.selectRuntime = function selectRuntime(runtime) {
        if (joynr.loaded) {
            throw new Error("joynr.selectRuntime: this method must " +
                            "be invoked before calling joynr.load()");
        } else {
            // configuring requirejs
            var requirejsConfig = require("./require.config.node.js");
            requirejsConfig.paths["joynr/Runtime"] = "joynr/Runtime." + runtime;
            requirejs.config(requirejsConfig);
        }
    };
    joynr.selectRuntime("websocket.libjoynr");
    Promise = requirejs("global/Promise");
    if ((module !== undefined) && module.exports) {
        exports.joynr = module.exports = joynr;
    } else {
        // support CommonJS module 1.1.1 spec (`exports` cannot be a function)
        exports.joynr = joynr;
    }

    // not using AMD
} else if (typeof window === "object") {
    Promise = window.Promise;
    // export namespace fragment or module read-only to the parent namespace
    Object.defineProperty(window, "joynr", {
        readable : true,
        enumerable : true,
        configurable : false,
        writable : false,
        value : joynr
    });
}

/* jslint nomen: false */
<|endoftext|>


<commit_before>import { state } from './../store/index.js';
import external from './../externalModules.js';

const enable = function () {
  window.addEventListener('resize', resizeThrottler, false);
};

const disable = function () {
  window.addEventListener('resize', resizeThrottler, false);
};

let resizeTimeout;

function resizeThrottler () {
  // Ignore resize events as long as an actualResizeHandler execution is in the queue
  if (!resizeTimeout) {
    resizeTimeout = setTimeout(function () {
      resizeTimeout = null;
      actualResizeHandler();

      // The actualResizeHandler will execute at a rate of 15fps
    }, 66);
  }
}

function actualResizeHandler () {
  state.enabledElements.forEach((element) => {
    external.cornerstone.resize(element);
  });
}

export default {
  enable,
  disable,
  forceEnabledElementResize: actualResizeHandler
};
<commit_msg>Make sure disable removes<commit_after>import { state } from './../store/index.js';
import external from './../externalModules.js';

const enable = function () {
  window.addEventListener('resize', resizeThrottler, false);
};

const disable = function () {
  window.removeEventListener('resize', resizeThrottler, false);
};

let resizeTimeout;

function resizeThrottler () {
  // Ignore resize events as long as an actualResizeHandler execution is in the queue
  if (!resizeTimeout) {
    resizeTimeout = setTimeout(function () {
      resizeTimeout = null;
      actualResizeHandler();

      // The actualResizeHandler will execute at a rate of 15fps
    }, 66);
  }
}

function actualResizeHandler () {
  state.enabledElements.forEach((element) => {
    external.cornerstone.resize(element);
  });
}

export default {
  enable,
  disable,
  forceEnabledElementResize: actualResizeHandler
};
<|endoftext|>


<commit_before>/**
 *	@author Félix Girault <felix.girault@gmail.com>
 */
'use strict';

var co = require('co');
var should = require('should');
var Service = require('../lib/service');

/**
 *
 */
describe('Service', function() {
	var service = null;

	beforeEach(function() {
		service = new Service({
			map: {
				'old': 'new'
			}
		});
	});

	describe('#fetch', function() {
		it('should fail by default', function(done) {
			co(function *() {
				try {
					yield service.fetch('');
				} catch (e) {
					done();
				}
			})();
		});

		it('should prepare the URL', function(done) {
			service._prepareUrl = function(url, options) {
				done();
			};

			co(function *() {
				try {
					yield service.fetch('');
				} catch (e) {}
			})();
		});

		it('should complete the informations', function(done) {
			service._fetch = function *(url, options) {
				return {};
			};

			service._completeInfos = function(infos, options) {
				done();
			};

			co(function *() {
				yield service.fetch('');
			})();
		});

		it('should reindex the informations', function(done) {
			service._fetch = function *(url, options) {
				return {};
			};

			service._reindexInfos = function(infos, options) {
				done();
				return {};
			};

			service._completeInfos = function(infos, options) {
				return {};
			};

			co(function *() {
				yield service.fetch('');
			})();
		});
	});

	describe('#_prepareUrl', function() {
		it('should trim the URL', function() {
			service._prepareUrl(' url ').should.equal('url');
		});
	});

	describe('#_completeInfos', function() {

	});

	describe('#_reindexInfos', function() {
		it('should reindex the informations', function() {
			var infos = service._reindexInfos({'old' : 'foo'});

			infos.should.have.property('new', 'foo');
		});
	});
});
<commit_msg>Added tests for _completeInfos()<commit_after>/**
 *	@author Félix Girault <felix.girault@gmail.com>
 */
'use strict';

var co = require('co');
var should = require('should');
var Service = require('../lib/service');

/**
 *
 */
describe('Service', function() {
	var service = null;

	beforeEach(function() {
		service = new Service({
			map: {
				'old': 'new'
			}
		});
	});

	describe('#fetch', function() {
		it('should fail by default', function(done) {
			co(function *() {
				try {
					yield service.fetch('');
				} catch (e) {
					done();
				}
			})();
		});

		it('should prepare the URL', function(done) {
			service._prepareUrl = function(url, options) {
				done();
			};

			co(function *() {
				try {
					yield service.fetch('');
				} catch (e) {}
			})();
		});

		it('should complete the informations', function(done) {
			service._fetch = function *(url, options) {
				return {};
			};

			service._completeInfos = function(infos, options) {
				done();
			};

			co(function *() {
				yield service.fetch('');
			})();
		});

		it('should reindex the informations', function(done) {
			service._fetch = function *(url, options) {
				return {};
			};

			service._reindexInfos = function(infos, options) {
				done();
				return {};
			};

			service._completeInfos = function(infos, options) {
				return {};
			};

			co(function *() {
				yield service.fetch('');
			})();
		});
	});

	describe('#_prepareUrl', function() {
		it('should trim the URL', function() {
			service._prepareUrl(' url ').should.equal('url');
		});
	});

	describe('#_completeInfos', function() {
		it('should build an HTML code for photos', function() {
			var infos = service._completeInfos({
				type: 'photo',
				url: 'url',
				title: 'title',
				description: 'description',
				width: 800,
				height: 600
			}, {});

			infos.html.should.equal(
				''
			);
		});

		it('should build an HTML code for videos', function() {
			var infos = service._completeInfos({
				type: 'video',
				url: 'url',
				title: 'title',
				description: 'description',
				width: 800,
				height: 600
			}, {});

			infos.html.should.equal(
				''
			);
		});

		it('should build an HTML code for unhandled types', function() {
			var infos = service._completeInfos({
				url: 'url',
				title: 'title',
				description: 'description'
			}, {});

			infos.html.should.equal(
				'title'
			);
		});
	});

	describe('#_reindexInfos', function() {
		it('should reindex the informations', function() {
			var infos = service._reindexInfos({'old' : 'foo'});

			infos.should.have.property('new', 'foo');
		});
	});
});
<|endoftext|>


<commit_before>/*
 * This file is part of Cockpit.
 *
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Cockpit is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * Cockpit is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
 */

define([
    "jquery",
    "base1/cockpit",
    "kubernetes/angular",
    "kubernetes/d3",
    "kubernetes/client",
], function($, cockpit, angular, d3, client) {
    "use strict";

    var _ = cockpit.gettext;
    var module = { };

    var colors = [
        "#0099d3",
        "#67d300",
        "#d39e00",
        "#d3007c",
        "#00d39f",
        "#00d1d3",
        "#00618a",
        "#4c8a00",
        "#8a6600",
        "#9b005b",
        "#008a55",
        "#008a8a",
        "#00b9ff",
        "#7dff00",
        "#ffbe00",
        "#ff0096",
        "#00ffc0",
        "#00fdff",
        "#023448",
        "#264802",
        "#483602",
        "#590034",
        "#024830",
        "#024848"
    ];

    function ServiceMap(kube) {
        var self = this;

        self.hosts = { };
        self.containers = { };

        function update() {
            var changed = false;

            /* Lookup all the services */
            kube.services.forEach(function(service) {
                var spec = service.spec;
                var meta = service.metadata;
                var name = meta.name;

                if (!spec || !spec.selector || name === "kubernetes" || name === "kubernetes-ro")
                    return;

                var uid = meta.uid;

                /* Lookup all the pods for each service */
                kube.select(spec.selector, meta.namespace, "Pod").forEach(function(pod) {
                    var status = pod.status || { };
                    var spec = pod.spec || { };
                    var host = spec.host;
                    var containers = status.containerStatuses || [];

                    if (host && !self.hosts[host]) {
                        self.hosts[host] = host;
                        $(self).triggerHandler("host", host);
                    }

                    /* Note all the containers for that pod */
                    containers.forEach(function(container) {
                        var id = container.containerID;
                        if (id && id.indexOf("docker://") === 0) {
                            id = id.substring(9);
                            var mapped = self.containers[uid];
                            if (!mapped) {
                                mapped = self.containers[uid] = { };
                                $(self).triggerHandler("service", uid);
                            }
                            if (!mapped[id]) {
                                mapped[id] = id;
                                changed = true;
                            }
                        }
                    });
                });
            });

            /* Notify for all rows */
            if (changed)
                $(self).triggerHandler("changed");
        }

        $(kube).on("services pods", update);

        self.close = function close() {
            $(kube).off("services pods", update);
        };
    }

    function service_grid() {
        var self = cockpit.grid(1000, 0, 0);

        /* The various rows being shown, override */
        self.rows = [ ];
        self.events = [ ];

        var kube = client.k8client();

        var change_queued = false;
        var current_metric = null;

        var services = [ ];
        var rows = {
            cpu: { },
            memory: { },
            network: { }
        };

        var container_cpu = { };
        var container_mem = { };
        var container_rx = { };
        var container_tx = { };

        /* All the cadvisors that have been opened */
        var cadvisors = [ ];

        /* Gives us events when kube does something */
        var service_map = new ServiceMap(kube);
        $(service_map)
            .on("host", function(ev, ip) {
                var host = ip;
                var cadvisor = client.cadvisor(host);
                $(cadvisor).on("container", function(ev, id) {
                    var cpu = self.add(this, [ id, "cpu", "usage", "total" ]);
                    container_cpu[id] = self.add(function(row, x, n) {
                        row_delta(cpu, row, x, n);
                    }, true);

                    container_mem[id] = self.add(this, [ id, "memory", "usage" ]);

                    var rx = self.add(this, [ id, "network", "rx_bytes" ]);
                    container_rx[id] = self.add(function(row, x, n) {
                        row_delta(rx, row, x, n);
                    }, true);

                    var tx = self.add(this, [ id, "network", "tx_bytes" ]);
                    container_tx[id] = self.add(function(row, x, n) {
                        row_delta(tx, row, x, n);
                    }, true);

                    self.sync();
                });

                /* In order to even know which containers we have, ask cadvisor to fetch */
                cadvisor.fetch(self.beg, self.end);

                /* TODO: Handle cadvisor failure somehow */
                cadvisors.push(cadvisor);
            })
            .on("service", function(ev, uid) {
                services.push(uid);

                /* CPU needs summing of containers, and then delta between them */
                rows.cpu[uid] = self.add(function(row, x, n) {
                    containers_sum(uid, container_cpu, row, x, n);
                });

                /* Memory row is pretty simple, just sum containers */
                rows.memory[uid] = self.add(function(row, x, n) {
                    containers_sum(uid, container_mem, row, x, n);
                });

                /* Network sums containers, then sum tx and rx, and then delta */
                var tx = self.add(function(row, x, n) {
                    containers_sum(uid, container_tx, row, x, n);
                });
                var rx = self.add(function(row, x, n) {
                    containers_sum(uid, container_rx, row, x, n);
                });
                rows.network[uid] = self.add(function(row, x, n) {
                    rows_sum([tx, rx], row, x, n);
                });

                change_queued = true;
            })
            .on("changed", function(ev) {
                self.sync();
            });

        $(self).on("notify", function() {
            if (change_queued) {
                change_queued = false;
                self.metric(current_metric);
            }
        });

        function rows_sum(input, row, x, n) {
            var max = row.maximum || 0;
            var value, i, v, j, len = input.length;

            /* Calculate the sum of the rows */
            for (i = 0; i < n; i++) {
                value = undefined;
                for (j = 0; j < len; j++) {
                    v = input[j][x + i];
                    if (v !== undefined) {
                        if (value === undefined)
                            value = v;
                        else
                            value += v;
                    }
                }

                if (value !== undefined && value > max) {
                    row.maximum = max = value;
                    change_queued = true;
                }

                row[x + i] = value;
            }
        }

        function row_delta(input, row, x, n) {
            var i, last, res, value;
            if (x > 0)
                last = input[x - 1];

            var max = row.maximum || 1;
            for (i = 0; i < n; i++) {
                value = input[x + i];
                if (last === undefined || value === undefined) {
                    res = undefined;
                } else {
                    res = (value - last);
                    if (res > max) {
                        row.maximum = max = res;
                        change_queued = true;
                    }
                }
                row[x + i] = res;
                last = value;
            }
        }

        function containers_sum(service, input, row, x, n) {
            var id, rowc, subset = [];
            var mapped = service_map.containers[service];
            if (mapped) {
                for(id in mapped) {
                    rowc = input[id];
                    if (rowc)
                        subset.push(rowc);
                }
            }
            rows_sum(subset, row, x, n);
        }

        self.metric = function metric(type) {
            if (type === undefined)
                return current_metric;
            if (rows[type] === undefined)
                throw "unsupported metric type";

            self.rows = [];
            current_metric = type;

            var row, i, len = services.length;
            for (i = 0; i < len; i++) {
                row = rows[type][services[i]];
                if (row !== undefined)
                    self.rows.push(row);
            }

            $(self).triggerHandler("changed");
        };

        var base_close = self.close;
        self.close = function close() {
            cadvisors.forEach(function(cadvisor) {
                cadvisor.close();
            });

            service_map.close();
            kube.close();
            base_close.apply(self);
        };

        return self;
    }

    function service_graph(selector) {
        var grid = service_grid();

        var outer = d3.select(selector);

        /* Various tabs */

        var tabs = {
            cpu: {
                label: _("CPU"),
                step: 1000 * 1000 * 1000,
                formatter: function(v) { return (v / (10 * 1000 * 1000)) + "%"; }
            },
            memory: {
                label: _("Memory"),
                step: 1024 * 1024 * 64,
                formatter: function(v) { return cockpit.format_bytes(v); }
            },
            network: {
                label: _("Network"),
                step: 1000 * 1000,
                formatter: function(v) { return cockpit.format_bits_per_sec(v, "Mbps"); }
            }
        };

        outer.append("ul")
            .attr("class", "nav nav-tabs")
            .selectAll("li")
                .data(Object.keys(tabs))
              .enter().append("li")
                .attr("data-metric", function(d) { return d; })
              .append("a")
                .text(function(d) { return tabs[d].label; });

        function metric_tab(tab) {
            outer.selectAll("ul li")
                .attr("class", function(d) { return tab === d ? "active": null; });
            grid.metric(tab);
        }

        outer.selectAll("ul li")
            .on("click", function() {
                metric_tab(d3.select(this).attr("data-metric"));
            });

        metric_tab("cpu");

        /* The main svg graph stars here */

        var margins = {
            top: 12,
            right: 10,
            bottom: 40,
            left: 60
        };

        var element = d3.select(selector).append("svg");
        var stage = element.append("g")
            .attr("transform", "translate(" + margins.left + "," + margins.top + ")");

        var y = d3.scale.linear();
        var y_axis = d3.svg.axis()
            .scale(y)
            .ticks(5)
            .orient("left");
        var y_group = stage.append("g")
            .attr("class", "y axis");

        var x = d3.scale.linear();
        var x_axis = d3.svg.axis()
            .scale(x)
            .orient("bottom");
        var x_group = stage.append("g")
            .attr("class", "x axis");

        var offset = 0;

        var line = d3.svg.line()
            .defined(function(d) { return d !== undefined; })
            .x(function(d, i) { return x((grid.beg + i) - offset); })
            .y(function(d, i) { return y(d); });

        /* Initial display: 1024 px is 5 minutes of data */
        var factor = 300000 / 1024;
        var width = 300;
        var height = 300;

        var changing = false;

        var rendered = false;
        window.setTimeout(function() {
            rendered = true;
            adjust();
        }, 1);

        function ceil(value, step) {
            var d = value % step;
            if (value === 0 || d !== 0)
                value += (step - d);
            return value;
        }

        function jump() {
            var interval = grid.interval;
            var w = (width - margins.right) - margins.left;

            /* This doesn't yet work for an arbitary ponit in time */
            var end = Math.floor($.now() / interval);
            var beg = end - Math.floor((factor * w) / interval);
            offset = beg;
            grid.move(beg, end);
        }

        function adjust() {
            if (!rendered)
                return;

            element
                .attr("width", width)
                .attr("height", height);

            var w = (width - margins.right) - margins.left;
            var h = (height - margins.top) - margins.bottom;

            var metric = grid.metric();
            var interval = grid.interval;

            /* Calculate our maximum value, hopefully rows are tracking this for us */
            var rows = grid.rows, maximum = 0;
            var i, max, len = rows.length;
            for (i = 0; i < len; i++) {
                if (rows[i].maximum !== undefined)
                    max = rows[i].maximum;
                else
                    max = d3.max(rows[i]);
                if (max > maximum)
                    maximum = Math.ceil(max);
            }

            /* This doesn't yet work for an arbitary ponit in time */
            var end = Math.floor((factor * w) / interval);
            x.domain([0, end]).range([0, w]);
            y.domain([0, ceil(maximum, tabs[metric].step)]).range([h, 0]);

            /* The ticks are inverted backwards */
            var tsc = d3.scale.linear().domain([0, end]).range([end, 0]);

            /* Calculate ticks every 60 seconds in past */
            var ticks = [];
            for (i = 60; i < end; i += 60)
                ticks.push(Math.round(tsc(i)));

            /* Make x-axis ticks into grid of right width */
            x_axis
                .tickValues(ticks)
                .tickSize(-h, -h)
                .tickFormat(function(d) {
                    d = Math.round(tsc.invert(d));
                    return (d / 60) + " min";
                });

            /* Re-render the X axis. Note that we also
             * bump down the labels a bit. */
            x_group
                .attr("transform", "translate(0," + h + ")")
                .call(x_axis)
              .selectAll("text")
                .attr("y", "10px");

            /* Turn the Y axis ticks into a grid */
            y_axis
                .tickSize(-w, -w)
                .tickFormat(tabs[metric].formatter);

            y_group
                .call(y_axis)
              .selectAll("text")
                .attr("x", "-10px");

            jump();
        }

        function notified(ev, x, n) {
            var rows = grid.rows;

            var series = stage.selectAll("path.line")
                .data(rows, function(d, i) { return i; });

            var trans = series
                .style("stroke", function(d, i) { return colors[i % colors.length]; })
                .interrupt().transition().attr("d", function(d) { return line(d); });

            series.enter().append("path")
                .attr("class", "line");
            series.exit().remove();
        }

        $(grid).on("changed", adjust);
        $(grid).on("notify changed", notified);

        function resized() {
            width = $(selector).innerWidth();
            adjust();
        }

        $(window).on('resize', resized);
        resized();

        var timer = window.setInterval(jump, grid.interval);

        return {
            close: function close() {
                $(window).off('resize', resized);
                window.clearInterval(timer);
                grid.close();
            }
        };
    }

    return angular.module('kubernetes.graph', [])
        .directive('kubernetesServiceGraph', function() {
            return {
                restrict: 'E',
                link: function($scope, element, attributes) {
                    var graph = service_graph(element[0]);
                    element.on('$destroy', function() {
                        graph.close();
                    });
                }
            };
        });
});
<commit_msg>kubernetes: Delta values in graphs should never be negative<commit_after>/*
 * This file is part of Cockpit.
 *
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Cockpit is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * Cockpit is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
 */

define([
    "jquery",
    "base1/cockpit",
    "kubernetes/angular",
    "kubernetes/d3",
    "kubernetes/client",
], function($, cockpit, angular, d3, client) {
    "use strict";

    var _ = cockpit.gettext;
    var module = { };

    var colors = [
        "#0099d3",
        "#67d300",
        "#d39e00",
        "#d3007c",
        "#00d39f",
        "#00d1d3",
        "#00618a",
        "#4c8a00",
        "#8a6600",
        "#9b005b",
        "#008a55",
        "#008a8a",
        "#00b9ff",
        "#7dff00",
        "#ffbe00",
        "#ff0096",
        "#00ffc0",
        "#00fdff",
        "#023448",
        "#264802",
        "#483602",
        "#590034",
        "#024830",
        "#024848"
    ];

    function ServiceMap(kube) {
        var self = this;

        self.hosts = { };
        self.containers = { };

        function update() {
            var changed = false;

            /* Lookup all the services */
            kube.services.forEach(function(service) {
                var spec = service.spec;
                var meta = service.metadata;
                var name = meta.name;

                if (!spec || !spec.selector || name === "kubernetes" || name === "kubernetes-ro")
                    return;

                var uid = meta.uid;

                /* Lookup all the pods for each service */
                kube.select(spec.selector, meta.namespace, "Pod").forEach(function(pod) {
                    var status = pod.status || { };
                    var spec = pod.spec || { };
                    var host = spec.host;
                    var containers = status.containerStatuses || [];

                    if (host && !self.hosts[host]) {
                        self.hosts[host] = host;
                        $(self).triggerHandler("host", host);
                    }

                    /* Note all the containers for that pod */
                    containers.forEach(function(container) {
                        var id = container.containerID;
                        if (id && id.indexOf("docker://") === 0) {
                            id = id.substring(9);
                            var mapped = self.containers[uid];
                            if (!mapped) {
                                mapped = self.containers[uid] = { };
                                $(self).triggerHandler("service", uid);
                            }
                            if (!mapped[id]) {
                                mapped[id] = id;
                                changed = true;
                            }
                        }
                    });
                });
            });

            /* Notify for all rows */
            if (changed)
                $(self).triggerHandler("changed");
        }

        $(kube).on("services pods", update);

        self.close = function close() {
            $(kube).off("services pods", update);
        };
    }

    function service_grid() {
        var self = cockpit.grid(1000, 0, 0);

        /* The various rows being shown, override */
        self.rows = [ ];
        self.events = [ ];

        var kube = client.k8client();

        var change_queued = false;
        var current_metric = null;

        var services = [ ];
        var rows = {
            cpu: { },
            memory: { },
            network: { }
        };

        var container_cpu = { };
        var container_mem = { };
        var container_rx = { };
        var container_tx = { };

        /* All the cadvisors that have been opened */
        var cadvisors = [ ];

        /* Gives us events when kube does something */
        var service_map = new ServiceMap(kube);
        $(service_map)
            .on("host", function(ev, ip) {
                var host = ip;
                var cadvisor = client.cadvisor(host);
                $(cadvisor).on("container", function(ev, id) {
                    var cpu = self.add(this, [ id, "cpu", "usage", "total" ]);
                    container_cpu[id] = self.add(function(row, x, n) {
                        row_delta(cpu, row, x, n);
                    }, true);

                    container_mem[id] = self.add(this, [ id, "memory", "usage" ]);

                    var rx = self.add(this, [ id, "network", "rx_bytes" ]);
                    container_rx[id] = self.add(function(row, x, n) {
                        row_delta(rx, row, x, n);
                    }, true);

                    var tx = self.add(this, [ id, "network", "tx_bytes" ]);
                    container_tx[id] = self.add(function(row, x, n) {
                        row_delta(tx, row, x, n);
                    }, true);

                    self.sync();
                });

                /* In order to even know which containers we have, ask cadvisor to fetch */
                cadvisor.fetch(self.beg, self.end);

                /* TODO: Handle cadvisor failure somehow */
                cadvisors.push(cadvisor);
            })
            .on("service", function(ev, uid) {
                services.push(uid);

                /* CPU needs summing of containers, and then delta between them */
                rows.cpu[uid] = self.add(function(row, x, n) {
                    containers_sum(uid, container_cpu, row, x, n);
                });

                /* Memory row is pretty simple, just sum containers */
                rows.memory[uid] = self.add(function(row, x, n) {
                    containers_sum(uid, container_mem, row, x, n);
                });

                /* Network sums containers, then sum tx and rx, and then delta */
                var tx = self.add(function(row, x, n) {
                    containers_sum(uid, container_tx, row, x, n);
                });
                var rx = self.add(function(row, x, n) {
                    containers_sum(uid, container_rx, row, x, n);
                });
                rows.network[uid] = self.add(function(row, x, n) {
                    rows_sum([tx, rx], row, x, n);
                });

                change_queued = true;
            })
            .on("changed", function(ev) {
                self.sync();
            });

        $(self).on("notify", function() {
            if (change_queued) {
                change_queued = false;
                self.metric(current_metric);
            }
        });

        function rows_sum(input, row, x, n) {
            var max = row.maximum || 0;
            var value, i, v, j, len = input.length;

            /* Calculate the sum of the rows */
            for (i = 0; i < n; i++) {
                value = undefined;
                for (j = 0; j < len; j++) {
                    v = input[j][x + i];
                    if (v !== undefined) {
                        if (value === undefined)
                            value = v;
                        else
                            value += v;
                    }
                }

                if (value !== undefined && value > max) {
                    row.maximum = max = value;
                    change_queued = true;
                }

                row[x + i] = value;
            }
        }

        function row_delta(input, row, x, n) {
            var i, last, res, value;
            if (x > 0)
                last = input[x - 1];

            var max = row.maximum || 1;
            for (i = 0; i < n; i++) {
                value = input[x + i];
                if (last === undefined || value === undefined) {
                    res = undefined;
                } else {
                    res = (value - last);
                    if (res < 0) {
                        res = undefined;
                    } else if (res > max) {
                        row.maximum = max = res;
                        change_queued = true;
                    }
                }
                row[x + i] = res;
                last = value;
            }
        }

        function containers_sum(service, input, row, x, n) {
            var id, rowc, subset = [];
            var mapped = service_map.containers[service];
            if (mapped) {
                for(id in mapped) {
                    rowc = input[id];
                    if (rowc)
                        subset.push(rowc);
                }
            }
            rows_sum(subset, row, x, n);
        }

        self.metric = function metric(type) {
            if (type === undefined)
                return current_metric;
            if (rows[type] === undefined)
                throw "unsupported metric type";

            self.rows = [];
            current_metric = type;

            var row, i, len = services.length;
            for (i = 0; i < len; i++) {
                row = rows[type][services[i]];
                if (row !== undefined)
                    self.rows.push(row);
            }

            $(self).triggerHandler("changed");
        };

        var base_close = self.close;
        self.close = function close() {
            cadvisors.forEach(function(cadvisor) {
                cadvisor.close();
            });

            service_map.close();
            kube.close();
            base_close.apply(self);
        };

        return self;
    }

    function service_graph(selector) {
        var grid = service_grid();

        var outer = d3.select(selector);

        /* Various tabs */

        var tabs = {
            cpu: {
                label: _("CPU"),
                step: 1000 * 1000 * 1000,
                formatter: function(v) { return (v / (10 * 1000 * 1000)) + "%"; }
            },
            memory: {
                label: _("Memory"),
                step: 1024 * 1024 * 64,
                formatter: function(v) { return cockpit.format_bytes(v); }
            },
            network: {
                label: _("Network"),
                step: 1000 * 1000,
                formatter: function(v) { return cockpit.format_bits_per_sec(v, "Mbps"); }
            }
        };

        outer.append("ul")
            .attr("class", "nav nav-tabs")
            .selectAll("li")
                .data(Object.keys(tabs))
              .enter().append("li")
                .attr("data-metric", function(d) { return d; })
              .append("a")
                .text(function(d) { return tabs[d].label; });

        function metric_tab(tab) {
            outer.selectAll("ul li")
                .attr("class", function(d) { return tab === d ? "active": null; });
            grid.metric(tab);
        }

        outer.selectAll("ul li")
            .on("click", function() {
                metric_tab(d3.select(this).attr("data-metric"));
            });

        metric_tab("cpu");

        /* The main svg graph stars here */

        var margins = {
            top: 12,
            right: 10,
            bottom: 40,
            left: 60
        };

        var element = d3.select(selector).append("svg");
        var stage = element.append("g")
            .attr("transform", "translate(" + margins.left + "," + margins.top + ")");

        var y = d3.scale.linear();
        var y_axis = d3.svg.axis()
            .scale(y)
            .ticks(5)
            .orient("left");
        var y_group = stage.append("g")
            .attr("class", "y axis");

        var x = d3.scale.linear();
        var x_axis = d3.svg.axis()
            .scale(x)
            .orient("bottom");
        var x_group = stage.append("g")
            .attr("class", "x axis");

        var offset = 0;

        var line = d3.svg.line()
            .defined(function(d) { return d !== undefined; })
            .x(function(d, i) { return x((grid.beg + i) - offset); })
            .y(function(d, i) { return y(d); });

        /* Initial display: 1024 px is 5 minutes of data */
        var factor = 300000 / 1024;
        var width = 300;
        var height = 300;

        var changing = false;

        var rendered = false;
        window.setTimeout(function() {
            rendered = true;
            adjust();
        }, 1);

        function ceil(value, step) {
            var d = value % step;
            if (value === 0 || d !== 0)
                value += (step - d);
            return value;
        }

        function jump() {
            var interval = grid.interval;
            var w = (width - margins.right) - margins.left;

            /* This doesn't yet work for an arbitary ponit in time */
            var end = Math.floor($.now() / interval);
            var beg = end - Math.floor((factor * w) / interval);
            offset = beg;
            grid.move(beg, end);
        }

        function adjust() {
            if (!rendered)
                return;

            element
                .attr("width", width)
                .attr("height", height);

            var w = (width - margins.right) - margins.left;
            var h = (height - margins.top) - margins.bottom;

            var metric = grid.metric();
            var interval = grid.interval;

            /* Calculate our maximum value, hopefully rows are tracking this for us */
            var rows = grid.rows, maximum = 0;
            var i, max, len = rows.length;
            for (i = 0; i < len; i++) {
                if (rows[i].maximum !== undefined)
                    max = rows[i].maximum;
                else
                    max = d3.max(rows[i]);
                if (max > maximum)
                    maximum = Math.ceil(max);
            }

            /* This doesn't yet work for an arbitary ponit in time */
            var end = Math.floor((factor * w) / interval);
            x.domain([0, end]).range([0, w]);
            y.domain([0, ceil(maximum, tabs[metric].step)]).range([h, 0]);

            /* The ticks are inverted backwards */
            var tsc = d3.scale.linear().domain([0, end]).range([end, 0]);

            /* Calculate ticks every 60 seconds in past */
            var ticks = [];
            for (i = 60; i < end; i += 60)
                ticks.push(Math.round(tsc(i)));

            /* Make x-axis ticks into grid of right width */
            x_axis
                .tickValues(ticks)
                .tickSize(-h, -h)
                .tickFormat(function(d) {
                    d = Math.round(tsc.invert(d));
                    return (d / 60) + " min";
                });

            /* Re-render the X axis. Note that we also
             * bump down the labels a bit. */
            x_group
                .attr("transform", "translate(0," + h + ")")
                .call(x_axis)
              .selectAll("text")
                .attr("y", "10px");

            /* Turn the Y axis ticks into a grid */
            y_axis
                .tickSize(-w, -w)
                .tickFormat(tabs[metric].formatter);

            y_group
                .call(y_axis)
              .selectAll("text")
                .attr("x", "-10px");

            jump();
        }

        function notified(ev, x, n) {
            var rows = grid.rows;

            var series = stage.selectAll("path.line")
                .data(rows, function(d, i) { return i; });

            var trans = series
                .style("stroke", function(d, i) { return colors[i % colors.length]; })
                .interrupt().transition().attr("d", function(d) { return line(d); });

            series.enter().append("path")
                .attr("class", "line");
            series.exit().remove();
        }

        $(grid).on("changed", adjust);
        $(grid).on("notify changed", notified);

        function resized() {
            width = $(selector).innerWidth();
            adjust();
        }

        $(window).on('resize', resized);
        resized();

        var timer = window.setInterval(jump, grid.interval);

        return {
            close: function close() {
                $(window).off('resize', resized);
                window.clearInterval(timer);
                grid.close();
            }
        };
    }

    return angular.module('kubernetes.graph', [])
        .directive('kubernetesServiceGraph', function() {
            return {
                restrict: 'E',
                link: function($scope, element, attributes) {
                    var graph = service_graph(element[0]);
                    element.on('$destroy', function() {
                        graph.close();
                    });
                }
            };
        });
});
<|endoftext|>


<commit_before>var accounts = new (require('./SocketToAccountMap'))();

const delimiter = '\n';

/**
 * Starts the TCP server and adds event listeners to the HTTP server.
 * @param {object} server
 */
function main(server, app) {

  // start http/socket.io server code

  var io = require('socket.io')(server);
  var config = require('../config/config');

  var passportSocketIo = require('passport.socketio');
  io.use(passportSocketIo.authorize({
    store: app.get('sessionStore'),
    success: onAuthorizeSuccess,
    fail: onAuthorizeFail,
    secret: config.expressSessionSecret
  }));

  function onAuthorizeSuccess(data, accept){
    console.log('successful connection to socket.io');
    console.dir(data.user)
    accept();
  }

  function onAuthorizeFail(data, message, error, accept){
    console.log('failed connection to socket.io:', message);
    if (error) {throw new Error(message);}
    else {accept(new Error(message));}
  }

  io.on('connection', function (socket) {

    accounts.addClient(socket.request.user.username, socket);
    console.log(socket.request.user.username + " account connected");

    socket.on('message', (data)=>{
      console.log(data);
      socket.send('pong');
    });
    socket.on('disconnect', function () {
      accounts.removeClient(socket.request.user.username, socket);
      console.log(socket.request.user.username + " account disconnected");
    });

    // relay commands to the tcp server
    socket.on('command', (data)=>{
      console.dir(data);
      accounts.sendToRobot(socket.request.user.username, data.robot, data.command);
    });

  });

  // end http/socket.io server code

  // start tcp server code

  // listen for tcp connections from robots
  var net = require('net');

  var tcpServer = net.createServer((tcpSocket)=>{

  	console.log("unidentified robot connected");

    // test write
  	tcpSocket.write(
      JSON.stringify({name: 'message', parameters: ["hello, it's the tcp server!"]}) + delimiter
    );

  	// relay command results from robot to web server
  	tcpSocket.on('data', (data)=>{
      if (!tcpSocket.remainder) {tcpSocket.remainder = '';}
      const parsedTCP = parseTCPData(data.toString(), tcpSocket.remainder);
      tcpSocket.remainder = parsedTCP.remainder;
      const dataJSONList = parsedTCP.messages.map(JSON.parse);

  		// separate tcp data into various messages
      for (var dataJSON of dataJSONList) {
        for (var key in dataJSON) {
          console.log(key, dataJSON[key]);
          if (key == 'id') {
            tcpSocket.id = dataJSON[key];
            if (accounts.getRobot(tcpSocket.id.account, tcpSocket.id.robot)) {
              var errorString = 'a robot called ' + tcpSocket.id.robot +
                ' is already connected to account ' + tcpSocket.id.account;
              disconnectRobot(tcpSocket, errorString);
            }
            else {
              accounts.setRobot(tcpSocket.id.account, tcpSocket.id.robot, tcpSocket);
              console.log("robot " + tcpSocket.id.robot + " identified for account " + tcpSocket.id.account);
            }
          }
          else if (tcpSocket.id && tcpSocket.id.account && tcpSocket.id.robot) {
            accounts.sendToClients(tcpSocket.id.account, key, {data: dataJSON[key], robot: tcpSocket.id.robot});
          }
          else {
            var errorString = 'unidentified robots cannot send messages';
            disconnectRobot(tcpSocket, errorString);
          }
        }
      }

    });

    /**
     * Used to piece together any tcp messages which got broken up,
     * and find any pieces we don't have the end of yet.
     * @param {string} tcpString
     * @param {string} tcpRemainder
     */
    function parseTCPData(tcpString, tcpRemainder) {
      let completeMessages = [];
      const tcpMessageRegExp = new RegExp('.*?' + delimiter + '|.+?$', 'g');
      const tcpMessages = tcpString.match(tcpMessageRegExp) || [];
      for (let tcpMessage of tcpMessages) {

        const assembledTCPMessage = tcpRemainder ? tcpRemainder + tcpMessage : tcpMessage;
        tcpRemainder = '';

        const containsDelimiter = assembledTCPMessage.indexOf(delimiter) != -1;
        if (!containsDelimiter) {
          tcpRemainder = assembledTCPMessage;
        }
        else {
          completeMessages.push(assembledTCPMessage);
        }

      }
      let result = {
        messages: completeMessages,
        remainder: tcpRemainder,
      };
      return result;
    }

    /**
     * Disconnects a robot without notifying the web client.
     * Used for unidentified robots and duplicate identifiers.
     * @param {object} robotSocket
     * @param {string} errorString
     */
    function disconnectRobot(robotSocket, errorString) {
      console.log(errorString);
      robotSocket.write(JSON.stringify({name: 'message', parameters: [errorString]}) + delimiter);
      robotSocket.endedByServer = true;
      robotSocket.end();
    }

    /**
     * Tells the web client a robot has disconnected.
     * @param {object} robotSocket
     */
    function notifyOfDisconnect(robotSocket) {
      if (robotSocket.id) {
        accounts.setRobot(robotSocket.id.account, robotSocket.id.robot);
        accounts.sendToClients(robotSocket.id.account, "listen end", {robot: robotSocket.id.robot})
        console.log("robot " + robotSocket.id.robot + " for account " + robotSocket.id.account + " disconnected");
      }
    }

  	tcpSocket.on('error', (error)=>{
      console.dir(error);
      notifyOfDisconnect(tcpSocket);
    });

  	// Remove the client from the list when it leaves
  	tcpSocket.on('end', ()=>{
      if (!tcpSocket.endedByServer) {
        notifyOfDisconnect(tcpSocket);
      }
  	});

  	 tcpSocket.on('close', ()=>{console.log('closed');});

  }).listen(3001);

  // end tcp server code

}

module.exports = main;
<commit_msg>parseTCPData seems to not be working right anymore<commit_after>var accounts = new (require('./SocketToAccountMap'))();

const delimiter = '\n';

/**
 * Starts the TCP server and adds event listeners to the HTTP server.
 * @param {object} server
 */
function main(server, app) {

  // start http/socket.io server code

  var io = require('socket.io')(server);
  var config = require('../config/config');

  var passportSocketIo = require('passport.socketio');
  io.use(passportSocketIo.authorize({
    store: app.get('sessionStore'),
    success: onAuthorizeSuccess,
    fail: onAuthorizeFail,
    secret: config.expressSessionSecret
  }));

  function onAuthorizeSuccess(data, accept){
    console.log('successful connection to socket.io');
    console.dir(data.user)
    accept();
  }

  function onAuthorizeFail(data, message, error, accept){
    console.log('failed connection to socket.io:', message);
    if (error) {throw new Error(message);}
    else {accept(new Error(message));}
  }

  io.on('connection', function (socket) {

    accounts.addClient(socket.request.user.username, socket);
    console.log(socket.request.user.username + " account connected");

    socket.on('message', (data)=>{
      console.log(data);
      socket.send('pong');
    });
    socket.on('disconnect', function () {
      accounts.removeClient(socket.request.user.username, socket);
      console.log(socket.request.user.username + " account disconnected");
    });

    // relay commands to the tcp server
    socket.on('command', (data)=>{
      console.dir(data);
      accounts.sendToRobot(socket.request.user.username, data.robot, data.command);
    });

  });

  // end http/socket.io server code

  // start tcp server code

  // listen for tcp connections from robots
  var net = require('net');

  var tcpServer = net.createServer((tcpSocket)=>{

  	console.log("unidentified robot connected");

    // test write
  	tcpSocket.write(
      JSON.stringify({name: 'message', parameters: ["hello, it's the tcp server!"]}) + delimiter
    );

  	// relay command results from robot to web server
  	tcpSocket.on('data', (data)=>{
      if (!tcpSocket.remainder) {tcpSocket.remainder = '';}
      const parsedTCP = parseTCPData(data.toString(), tcpSocket.remainder);
      console.log(parsedTCP);
      tcpSocket.remainder = parsedTCP.remainder;
      const dataJSONList = parsedTCP.messages.map(JSON.parse);

  		// separate tcp data into various messages
      for (var dataJSON of dataJSONList) {
        for (var key in dataJSON) {
          console.log(key, dataJSON[key]);
          if (key == 'id') {
            tcpSocket.id = dataJSON[key];
            if (accounts.getRobot(tcpSocket.id.account, tcpSocket.id.robot)) {
              var errorString = 'a robot called ' + tcpSocket.id.robot +
                ' is already connected to account ' + tcpSocket.id.account;
              disconnectRobot(tcpSocket, errorString);
            }
            else {
              accounts.setRobot(tcpSocket.id.account, tcpSocket.id.robot, tcpSocket);
              console.log("robot " + tcpSocket.id.robot + " identified for account " + tcpSocket.id.account);
            }
          }
          else if (tcpSocket.id && tcpSocket.id.account && tcpSocket.id.robot) {
            accounts.sendToClients(tcpSocket.id.account, key, {data: dataJSON[key], robot: tcpSocket.id.robot});
          }
          else {
            var errorString = 'unidentified robots cannot send messages';
            disconnectRobot(tcpSocket, errorString);
          }
        }
      }

    });

    /**
     * Used to piece together any tcp messages which got broken up,
     * and find any pieces we don't have the end of yet.
     * @param {string} tcpString
     * @param {string} tcpRemainder
     */
    function parseTCPData(tcpString, tcpRemainder) {
      console.log(tcpString);
      console.log(tcpRemainder);
      let completeMessages = [];
      const tcpMessageRegExp = new RegExp(`.+' + delimiter + '?`, 'g');
      const tcpMessages = tcpString.match(tcpMessageRegExp) || [];
      for (let tcpMessage of tcpMessages) {

        const assembledTCPMessage = tcpRemainder ? tcpRemainder + tcpMessage : tcpMessage;
        tcpRemainder = '';

        const containsDelimiter = assembledTCPMessage.indexOf(delimiter) != -1;
        if (!containsDelimiter) {
          tcpRemainder = assembledTCPMessage;
        }
        else {
          completeMessages.push(assembledTCPMessage);
        }

      }
      let result = {
        messages: completeMessages,
        remainder: tcpRemainder,
      };
      return result;
    }

    /**
     * Disconnects a robot without notifying the web client.
     * Used for unidentified robots and duplicate identifiers.
     * @param {object} robotSocket
     * @param {string} errorString
     */
    function disconnectRobot(robotSocket, errorString) {
      console.log(errorString);
      robotSocket.write(JSON.stringify({name: 'message', parameters: [errorString]}) + delimiter);
      robotSocket.endedByServer = true;
      robotSocket.end();
    }

    /**
     * Tells the web client a robot has disconnected.
     * @param {object} robotSocket
     */
    function notifyOfDisconnect(robotSocket) {
      if (robotSocket.id) {
        accounts.setRobot(robotSocket.id.account, robotSocket.id.robot);
        accounts.sendToClients(robotSocket.id.account, "listen end", {robot: robotSocket.id.robot})
        console.log("robot " + robotSocket.id.robot + " for account " + robotSocket.id.account + " disconnected");
      }
    }

  	tcpSocket.on('error', (error)=>{
      console.dir(error);
      notifyOfDisconnect(tcpSocket);
    });

  	// Remove the client from the list when it leaves
  	tcpSocket.on('end', ()=>{
      if (!tcpSocket.endedByServer) {
        notifyOfDisconnect(tcpSocket);
      }
  	});

  	 tcpSocket.on('close', ()=>{console.log('closed');});

  }).listen(3001);

  // end tcp server code

}

module.exports = main;
<|endoftext|>


<commit_before>// Copyright 2021, University of Colorado Boulder

/**
 * WARNING: This is using a prototype trait called Voicing with speech synthesis. Do not use in production code yet.
 *
 * RichText that composes Voicing, supporting speech synthesis. VoicingRichText is added to the focus
 * order when Voicing is enabled, and a highlight will surround it that indicates it can be clicked on
 * to hear the text with speech synthesis.
 *
 * @author Jesse Greenberg (PhET Interactive Simulations)
 */

import merge from '../../../../phet-core/js/merge.js';
import ReadingBlock from '../../../../scenery/js/accessibility/speaker/ReadingBlock.js';
import RichText from '../../../../scenery/js/nodes/RichText.js';
import sceneryPhet from '../../sceneryPhet.js';
import VoicingHighlight from './VoicingHighlight.js';

class VoicingRichText extends RichText {

  /**
   * @param {string} text
   * @param {Object} [options]
   */
  constructor( text, options ) {
    options = merge( {

      // {string|null} - if provided, alternative text that will be read that is different from the
      // visually displayed text
      readingBlockContent: null,

      // pdom
      innerContent: text,

      // voicing
      // default tag name for a ReadingBlock, but there are cases where you may want to override this (such as
      // RichText links)
      readingBlockTagName: 'button'
    }, options );

    super( text, options );

    this.initializeReadingBlock();

    this.mutate( {

      readingBlockContent: options.voicingText || text,

      // unique highlight for non-interactive components
      focusHighlight: new VoicingHighlight( this ),

      readingBlockTagName: options.readingBlockTagName
    } );
  }
}

ReadingBlock.compose( VoicingRichText );

sceneryPhet.register( 'VoicingRichText', VoicingRichText );
export default VoicingRichText;
<commit_msg>Fix incorrect option name, see https://github.com/phetsims/joist/issues/715<commit_after>// Copyright 2021, University of Colorado Boulder

/**
 * WARNING: This is using a prototype trait called Voicing with speech synthesis. Do not use in production code yet.
 *
 * RichText that composes Voicing, supporting speech synthesis. VoicingRichText is added to the focus
 * order when Voicing is enabled, and a highlight will surround it that indicates it can be clicked on
 * to hear the text with speech synthesis.
 *
 * @author Jesse Greenberg (PhET Interactive Simulations)
 */

import merge from '../../../../phet-core/js/merge.js';
import ReadingBlock from '../../../../scenery/js/accessibility/speaker/ReadingBlock.js';
import RichText from '../../../../scenery/js/nodes/RichText.js';
import sceneryPhet from '../../sceneryPhet.js';
import VoicingHighlight from './VoicingHighlight.js';

class VoicingRichText extends RichText {

  /**
   * @param {string} text
   * @param {Object} [options]
   */
  constructor( text, options ) {
    options = merge( {

      // {string|null} - if provided, alternative text that will be read that is different from the
      // visually displayed text
      readingBlockContent: null,

      // pdom
      innerContent: text,

      // voicing
      // default tag name for a ReadingBlock, but there are cases where you may want to override this (such as
      // RichText links)
      readingBlockTagName: 'button'
    }, options );

    super( text, options );

    this.initializeReadingBlock();

    this.mutate( {

      readingBlockContent: options.readingBlockContent || text,

      // unique highlight for non-interactive components
      focusHighlight: new VoicingHighlight( this ),

      readingBlockTagName: options.readingBlockTagName
    } );
  }
}

ReadingBlock.compose( VoicingRichText );

sceneryPhet.register( 'VoicingRichText', VoicingRichText );
export default VoicingRichText;
<|endoftext|>