<commit_before>/* Freifunk Knoten Liste
 * Copyright (c) 2015  Skruppy <skruppy@onmars.eu>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

var records = [];
var nodes = {};
var nodeCnt = 0;

// Try this: http://w2ui.com/web/demos/#!grid/grid-5
w2popup.lock('Loading', true);
var rawNodes = $.ajax({
	type: 'GET',
	url: nodelistconfig.dataPath + 'nodes.json',
	dataType: 'json',
	success: function() { },
	data: {},
	async: false,
}).responseJSON.nodes;

var rawGraph = $.ajax({
	type: 'GET',
	url: nodelistconfig.dataPath + 'graph.json',
	dataType: 'json',
	success: function() { },
	data: {},
	async: false,
}).responseJSON.batadv;

var enums = {gateways: {}, versions: {}, models: {}, sites: {}};

$.each(rawNodes, function(i, node){
	if(typeof node.nodeinfo == undefined) {
		//console.log('Useless node:', node);
		return;
	}
	if(i in nodes) {
		//console.log('Duplicate node:', nodes[i], node);
		return;
	}
	nodes[i] = node;
	nodes[i].recid = nodeCnt++;
	nodes[i].vpns = [];
	nodes[i].neighbours = [];
});

$.each(rawGraph.links, function(i, link) {
	var srcId = rawGraph.nodes[link.source].node_id;
	var dstId = rawGraph.nodes[link.target].node_id;

	if(!(srcId in nodes && dstId in nodes)) {
		return;
	}

	delete link.source;
	delete link.target;
	link.src = nodes[srcId].recid;
	link.dst = nodes[dstId].recid;

	if(link.vpn) {
		nodes[srcId].vpns.push(link);
		nodes[dstId].vpns.push(link);
	}
	else {
		nodes[srcId].neighbours.push(link);
		nodes[dstId].neighbours.push(link);
	}
});

$.each(nodes, function(i, node){
	var ni = node.nodeinfo;
	var row = {id: i, recid: node.recid, node: node};

	row.isOnline = node.flags.online;
	row.isGateway = node.flags.gateway;
	row.vpnCnt = node.vpns.length;
	row.neighbourCnt = node.neighbours.length;
	row.hasVpn = row.vpnCnt > 0;
	row.hasNeighbour = row.neighbourCnt > 0;
	if(row.isOnline == false) {
		row.style = 'background: #ff0000;'
	}
	row.hostname = ni.hostname;
	if(typeof ni.hardware != 'undefined') {
		row.model = ni.hardware.model;
		row.nproc = ni.hardware.nproc;
		enums.models[row.model] = true;
	}
	if(typeof ni.software != 'undefined') {
		var sw = ni.software;
		if(typeof sw.autoupdater != 'undefined') {
			row.autoupdater = sw.autoupdater.enabled ? sw.autoupdater.branch : 'Off';
		}
		row.version = sw.firmware.release;
		enums.versions[row.version] = true;
	}
	row.owner = typeof ni.owner != 'undefined' ? ni.owner.contact : undefined;
	if(typeof node.statistics != 'undefined') {
		var stats = node.statistics;
		row.clients = stats.clients;
		row.gateway = stats.gateway;
		enums.gateways[row.gateway] = true;
		if(row.isOnline) {
			row.uptime = ~~(stats.uptime);
			if (row.uptime == 0) {row.uptime = 999999999;}

			row.tramgrxu = ~~(row.tramgrx / row.uptime);
			row.tramgtxu = ~~(row.tramgtx / row.uptime);

			row.style = 'background: #ffff00;';

			if (row.uptime >= 3*60) {row.style = 'background: #ffff44;'}
			if (row.uptime >= 10*60) {row.style = 'background: #ffff88;'}
			if (row.uptime >= 30*60) {row.style = 'background: #ffffcc;'}
			if (row.uptime >= 1*3600) {row.style = 'background: #eeffee;'}
			if (row.uptime >= 3*3600) {row.style = 'background: #ddffdd;'}
			if (row.uptime >= 6*3600) {row.style = 'background: #ccffcc;'}
			if (row.uptime >= 12*3600) {row.style = 'background: #bbffbb;'}
			if (row.uptime >= 1*24*3600) {row.style = 'background: #aaffaa;'}
			if (row.uptime >= 3*24*3600) {row.style = 'background: #99ff99;'}
			if (row.uptime >= 10*24*3600) {row.style = 'background: #88ff88;'}
			if (row.uptime >= 30*24*3600) {row.style = 'background: #77ff77;'}
		}
		row.rootfsUsage = typeof stats.rootfs_usage != 'undefined' ? stats.rootfs_usage*100 : undefined;
		row.memoryUsage = typeof stats.memory_usage != 'undefined' ? stats.memory_usage*100 : undefined;
	}

	row.firstSeen = moment.utc(node.firstseen).local().toDate();
	row.lastSeen = moment.utc(node.lastseen).local().toDate();

	if(typeof ni.system != 'undefined') {
		row.site = ni.system.site_code;
		enums.sites[row.site] = true;
	}

	records.push(row);
});

var renderBool = function(record, ind, col_ind) {
	var val = record[this.columns[col_ind].field];
	if(typeof val == 'undefined') return;
	return val ? '✔' : '✘';
}

var renderPercent = function(record, ind, col_ind) {
	var val = record[this.columns[col_ind].field];
	if(typeof val == 'undefined') return;

	return '
' +
	       '

' +
	       '
' +
	       '
'+val.toFixed(1)+'%
';
}

var cols = [
	{ resizable: true, sortable: true, field: 'isOnline'    , caption: 'Online'      , size: '40px', render: renderBool, style: 'text-align: center;', hidden: true},
	{ resizable: true, sortable: true, field: 'isGateway'   , caption: 'Gateway'     , size: '40px', render: renderBool, style: 'text-align: center;', hidden: true},
	{ resizable: true, sortable: true, field: 'hasVpn'      , caption: 'VPN?'        , size: '40px', render: renderBool, style: 'text-align: center;'},
	{ resizable: true, sortable: true, field: 'hasNeighbour', caption: 'Link?'       , size: '40px', render: renderBool, style: 'text-align: center;', hidden: true},
	{ resizable: true, sortable: true, field: 'vpnCnt'      , caption: 'VPNs'        , size: '50px', style: 'text-align: right;', hidden: true},
	{ resizable: true, sortable: true, field: 'neighbourCnt', caption: 'Links'       , size: '50px', style: 'text-align: right;'},
	{ resizable: true, sortable: true, field: 'id'          , caption: 'Node ID'     , size: '100px', style: 'font-family: monospace;'},
	{ resizable: true, sortable: true, field: 'hostname'    , caption: 'Hostname'    , size: '10%'},
	{ resizable: true, sortable: true, field: 'owner'       , caption: 'Owner'       , size: '10%'},
	{ resizable: true, sortable: true, field: 'version'     , caption: 'Gluon Vers.' , size: '150px'},
	{ resizable: true, sortable: true, field: 'model'       , caption: 'HW model'    , size: '10%'},
	{ resizable: true, sortable: true, field: 'nproc'       , caption: 'Procs'       , size: '50px', style: 'text-align: right;', hidden: true},
	{ resizable: true, sortable: true, field: 'autoupdater' , caption: 'Updates'     , size: '100px'},
	{ resizable: true, sortable: true, field: 'clients'     , caption: 'Clients'     , size: '50px', style: 'text-align: right;'},
	{ resizable: true, sortable: true, field: 'gateway'     , caption: 'Gateway'     , size: '50px', style: 'text-align: right;'},
	{ resizable: true, sortable: true, field: 'site'        , caption: 'Site'        , size: '50px'},
	{ resizable: true, sortable: true, field: 'uptime'      , caption: 'Uptime'      , size: '80px', render: 'age'},
	{ resizable: true, sortable: true, field: 'firstSeen'   , caption: 'First seen'  , size: '80px', render: 'age'},
	{ resizable: true, sortable: true, field: 'lastSeen'    , caption: 'Last seen'   , size: '80px', render: 'age'},
	{ resizable: true, sortable: true, field: 'rootfsUsage' , caption: '% root'      , size: '50px', render: renderPercent, style: 'text-align: right; position: relative;'},
	{ resizable: true, sortable: true, field: 'memoryUsage' , caption: '% Mem'       , size: '50px', render: renderPercent, style: 'text-align: right; position: relative;'},
];

$('#grid').w2grid({
	name: 'grid',
	header: document.title = nodelistconfig.siteName + ' Knotenliste',
	show: {
		header:        true,
		toolbar:       true,
		toolbarReload: false,
		footer:        true,
		lineNumbers:   true,
	},
	toolbar: {
		items: [
			{ type: 'spacer' },
			{ type: 'button', id: 'about', caption: 'About', icon: 'w2ui-icon-question' },
		],
		onClick: function (target, data) {
			if(target.target == 'about') {
				$('#about-popup').w2popup();
			}
		},
	},
	multiSelect: false,
	reorderColumns: true,
	records: records,
	columns: cols,
	searches: [
//			{field: 'isOnline'    , caption: 'Is online?'},
//			{field: 'isGateway'   , caption: 'Is gateway?', type: 'combo', options: {items: [true, false]}},
		{field: 'vpnCnt'      , caption: 'VPNs'          , type: 'int',  options: {min: 0}},
		{field: 'neighbourCnt', caption: 'Links'         , type: 'int',  options: {min: 0}},
		{field: 'id'          , caption: 'Node ID'       , type: 'text'},
		{field: 'hostname'    , caption: 'Hostname'      , type: 'text'},
		{field: 'owner'       , caption: 'Owner'         , type: 'text'},
		{field: 'version'     , caption: 'Gluon Version' , type: 'enum', options: {items: Object.keys(enums.versions)}},
		{field: 'model'       , caption: 'Hardware model', type: 'enum', options: {items: Object.keys(enums.models)}},
		{field: 'nproc'       , caption: '# proc'        , type: 'int',  options: {min: 0}},
		{field: 'autoupdater' , caption: 'Updates'       , type: 'enum', options: {items: ['Off', 'stable', 'experimental']}},
		{field: 'clients'     , caption: '# clients'     , type: 'int',  options: {min: 0}},
		{field: 'gateway'     , caption: 'Gateway'       , type: 'enum', options: {items: Object.keys(enums.gateways)}},
		{field: 'site'        , caption: 'Site'          , type: 'enum', options: {items: Object.keys(enums.sites)}},
		{field: 'uptime'      , caption: 'Uptime'        , type: 'float'},
		{field: 'firstSeen'   , caption: 'First seen'    , type: 'date'},
		{field: 'lastSeen'    , caption: 'Last seen'     , type: 'date'},
		{field: 'rootfsUsage' , caption: 'Root FS usage' , type: 'percent', outTag: '%'},
		{field: 'memoryUsage' , caption: 'Memory usage'  , type: 'percent', outTag: '%'},
	],
	onExpand: function (event) {
		if (w2ui.hasOwnProperty('subgrid-' + event.recid)) w2ui['subgrid-' + event.recid].destroy();
		$('#'+ event.box_id).css({ margin: '0px', padding: '0px', width: '100%' }).animate({ height: '105px' }, 100);
		setTimeout(function () {
			subRecords = [];

			$.each(records[event.recid].node.neighbours, function(i, link) {
				subRecords.push(records[event.recid == link.src ? link.dst : link.src]);
			});

			$('#'+ event.box_id).w2grid({
				name: 'subgrid-' + event.recid,
				show: { columnHeaders: true },
				fixedBody: false,
				columns: cols,
				records: subRecords,
			});
			w2ui['subgrid-' + event.recid].resize();
		}, 300);
	},
	onDblClick: function(event) {
		window.open(nodelistconfig.mapPath + '/#!v:m;n:'+records[event.recid].id, '_blank');
	},
});

w2popup.unlock();
<commit_msg>activate search fields that were deactivated<commit_after>/* Freifunk Knoten Liste
 * Copyright (c) 2015  Skruppy <skruppy@onmars.eu>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

var records = [];
var nodes = {};
var nodeCnt = 0;

// Try this: http://w2ui.com/web/demos/#!grid/grid-5
w2popup.lock('Loading', true);
var rawNodes = $.ajax({
	type: 'GET',
	url: nodelistconfig.dataPath + 'nodes.json',
	dataType: 'json',
	success: function() { },
	data: {},
	async: false,
}).responseJSON.nodes;

var rawGraph = $.ajax({
	type: 'GET',
	url: nodelistconfig.dataPath + 'graph.json',
	dataType: 'json',
	success: function() { },
	data: {},
	async: false,
}).responseJSON.batadv;

var enums = {gateways: {}, versions: {}, models: {}, sites: {}};

$.each(rawNodes, function(i, node){
	if(typeof node.nodeinfo == undefined) {
		//console.log('Useless node:', node);
		return;
	}
	if(i in nodes) {
		//console.log('Duplicate node:', nodes[i], node);
		return;
	}
	nodes[i] = node;
	nodes[i].recid = nodeCnt++;
	nodes[i].vpns = [];
	nodes[i].neighbours = [];
});

$.each(rawGraph.links, function(i, link) {
	var srcId = rawGraph.nodes[link.source].node_id;
	var dstId = rawGraph.nodes[link.target].node_id;

	if(!(srcId in nodes && dstId in nodes)) {
		return;
	}

	delete link.source;
	delete link.target;
	link.src = nodes[srcId].recid;
	link.dst = nodes[dstId].recid;

	if(link.vpn) {
		nodes[srcId].vpns.push(link);
		nodes[dstId].vpns.push(link);
	}
	else {
		nodes[srcId].neighbours.push(link);
		nodes[dstId].neighbours.push(link);
	}
});

$.each(nodes, function(i, node){
	var ni = node.nodeinfo;
	var row = {id: i, recid: node.recid, node: node};

	row.isOnline = node.flags.online;
	row.isGateway = node.flags.gateway;
	row.vpnCnt = node.vpns.length;
	row.neighbourCnt = node.neighbours.length;
	row.hasVpn = row.vpnCnt > 0;
	row.hasNeighbour = row.neighbourCnt > 0;
	if(row.isOnline == false) {
		row.style = 'background: #ff0000;'
	}
	row.hostname = ni.hostname;
	if(typeof ni.hardware != 'undefined') {
		row.model = ni.hardware.model;
		row.nproc = ni.hardware.nproc;
		enums.models[row.model] = true;
	}
	if(typeof ni.software != 'undefined') {
		var sw = ni.software;
		if(typeof sw.autoupdater != 'undefined') {
			row.autoupdater = sw.autoupdater.enabled ? sw.autoupdater.branch : 'Off';
		}
		row.version = sw.firmware.release;
		enums.versions[row.version] = true;
	}
	row.owner = typeof ni.owner != 'undefined' ? ni.owner.contact : undefined;
	if(typeof node.statistics != 'undefined') {
		var stats = node.statistics;
		row.clients = stats.clients;
		row.gateway = stats.gateway;
		enums.gateways[row.gateway] = true;
		if(row.isOnline) {
			row.uptime = ~~(stats.uptime);
			if (row.uptime == 0) {row.uptime = 999999999;}

			row.tramgrxu = ~~(row.tramgrx / row.uptime);
			row.tramgtxu = ~~(row.tramgtx / row.uptime);

			row.style = 'background: #ffff00;';

			if (row.uptime >= 3*60) {row.style = 'background: #ffff44;'}
			if (row.uptime >= 10*60) {row.style = 'background: #ffff88;'}
			if (row.uptime >= 30*60) {row.style = 'background: #ffffcc;'}
			if (row.uptime >= 1*3600) {row.style = 'background: #eeffee;'}
			if (row.uptime >= 3*3600) {row.style = 'background: #ddffdd;'}
			if (row.uptime >= 6*3600) {row.style = 'background: #ccffcc;'}
			if (row.uptime >= 12*3600) {row.style = 'background: #bbffbb;'}
			if (row.uptime >= 1*24*3600) {row.style = 'background: #aaffaa;'}
			if (row.uptime >= 3*24*3600) {row.style = 'background: #99ff99;'}
			if (row.uptime >= 10*24*3600) {row.style = 'background: #88ff88;'}
			if (row.uptime >= 30*24*3600) {row.style = 'background: #77ff77;'}
		}
		row.rootfsUsage = typeof stats.rootfs_usage != 'undefined' ? stats.rootfs_usage*100 : undefined;
		row.memoryUsage = typeof stats.memory_usage != 'undefined' ? stats.memory_usage*100 : undefined;
	}

	row.firstSeen = moment.utc(node.firstseen).local().toDate();
	row.lastSeen = moment.utc(node.lastseen).local().toDate();

	if(typeof ni.system != 'undefined') {
		row.site = ni.system.site_code;
		enums.sites[row.site] = true;
	}

	records.push(row);
});

var renderBool = function(record, ind, col_ind) {
	var val = record[this.columns[col_ind].field];
	if(typeof val == 'undefined') return;
	return val ? '✔' : '✘';
}

var renderPercent = function(record, ind, col_ind) {
	var val = record[this.columns[col_ind].field];
	if(typeof val == 'undefined') return;

	return '
' +
	       '

' +
	       '
' +
	       '
'+val.toFixed(1)+'%
';
}

var cols = [
	{ resizable: true, sortable: true, field: 'isOnline'    , caption: 'Online'      , size: '40px', render: renderBool, style: 'text-align: center;', hidden: true},
	{ resizable: true, sortable: true, field: 'isGateway'   , caption: 'Gateway'     , size: '40px', render: renderBool, style: 'text-align: center;', hidden: true},
	{ resizable: true, sortable: true, field: 'hasVpn'      , caption: 'VPN?'        , size: '40px', render: renderBool, style: 'text-align: center;'},
	{ resizable: true, sortable: true, field: 'hasNeighbour', caption: 'Link?'       , size: '40px', render: renderBool, style: 'text-align: center;', hidden: true},
	{ resizable: true, sortable: true, field: 'vpnCnt'      , caption: 'VPNs'        , size: '50px', style: 'text-align: right;', hidden: true},
	{ resizable: true, sortable: true, field: 'neighbourCnt', caption: 'Links'       , size: '50px', style: 'text-align: right;'},
	{ resizable: true, sortable: true, field: 'id'          , caption: 'Node ID'     , size: '100px', style: 'font-family: monospace;'},
	{ resizable: true, sortable: true, field: 'hostname'    , caption: 'Hostname'    , size: '10%'},
	{ resizable: true, sortable: true, field: 'owner'       , caption: 'Owner'       , size: '10%'},
	{ resizable: true, sortable: true, field: 'version'     , caption: 'Gluon Vers.' , size: '150px'},
	{ resizable: true, sortable: true, field: 'model'       , caption: 'HW model'    , size: '10%'},
	{ resizable: true, sortable: true, field: 'nproc'       , caption: 'Procs'       , size: '50px', style: 'text-align: right;', hidden: true},
	{ resizable: true, sortable: true, field: 'autoupdater' , caption: 'Updates'     , size: '100px'},
	{ resizable: true, sortable: true, field: 'clients'     , caption: 'Clients'     , size: '50px', style: 'text-align: right;'},
	{ resizable: true, sortable: true, field: 'gateway'     , caption: 'Gateway'     , size: '50px', style: 'text-align: right;'},
	{ resizable: true, sortable: true, field: 'site'        , caption: 'Site'        , size: '50px'},
	{ resizable: true, sortable: true, field: 'uptime'      , caption: 'Uptime'      , size: '80px', render: 'age'},
	{ resizable: true, sortable: true, field: 'firstSeen'   , caption: 'First seen'  , size: '80px', render: 'age'},
	{ resizable: true, sortable: true, field: 'lastSeen'    , caption: 'Last seen'   , size: '80px', render: 'age'},
	{ resizable: true, sortable: true, field: 'rootfsUsage' , caption: '% root'      , size: '50px', render: renderPercent, style: 'text-align: right; position: relative;'},
	{ resizable: true, sortable: true, field: 'memoryUsage' , caption: '% Mem'       , size: '50px', render: renderPercent, style: 'text-align: right; position: relative;'},
];

$('#grid').w2grid({
	name: 'grid',
	header: document.title = nodelistconfig.siteName + ' Knotenliste',
	show: {
		header:        true,
		toolbar:       true,
		toolbarReload: false,
		footer:        true,
		lineNumbers:   true,
	},
	toolbar: {
		items: [
			{ type: 'spacer' },
			{ type: 'button', id: 'about', caption: 'About', icon: 'w2ui-icon-question' },
		],
		onClick: function (target, data) {
			if(target.target == 'about') {
				$('#about-popup').w2popup();
			}
		},
	},
	multiSelect: false,
	reorderColumns: true,
	records: records,
	columns: cols,
	searches: [
		{field: 'isOnline'    , caption: 'Is online?', type: 'combo', options: {items: [true, false]}},
		{field: 'isGateway'   , caption: 'Is gateway?', type: 'combo', options: {items: [true, false]}},
		{field: 'vpnCnt'      , caption: 'VPNs'          , type: 'int',  options: {min: 0}},
		{field: 'neighbourCnt', caption: 'Links'         , type: 'int',  options: {min: 0}},
		{field: 'id'          , caption: 'Node ID'       , type: 'text'},
		{field: 'hostname'    , caption: 'Hostname'      , type: 'text'},
		{field: 'owner'       , caption: 'Owner'         , type: 'text'},
		{field: 'version'     , caption: 'Gluon Version' , type: 'enum', options: {items: Object.keys(enums.versions)}},
		{field: 'model'       , caption: 'Hardware model', type: 'enum', options: {items: Object.keys(enums.models)}},
		{field: 'nproc'       , caption: '# proc'        , type: 'int',  options: {min: 0}},
		{field: 'autoupdater' , caption: 'Updates'       , type: 'enum', options: {items: ['Off', 'stable', 'experimental']}},
		{field: 'clients'     , caption: '# clients'     , type: 'int',  options: {min: 0}},
		{field: 'gateway'     , caption: 'Gateway'       , type: 'enum', options: {items: Object.keys(enums.gateways)}},
		{field: 'site'        , caption: 'Site'          , type: 'enum', options: {items: Object.keys(enums.sites)}},
		{field: 'uptime'      , caption: 'Uptime'        , type: 'float'},
		{field: 'firstSeen'   , caption: 'First seen'    , type: 'date'},
		{field: 'lastSeen'    , caption: 'Last seen'     , type: 'date'},
		{field: 'rootfsUsage' , caption: 'Root FS usage' , type: 'percent', outTag: '%'},
		{field: 'memoryUsage' , caption: 'Memory usage'  , type: 'percent', outTag: '%'},
	],
	onExpand: function (event) {
		if (w2ui.hasOwnProperty('subgrid-' + event.recid)) w2ui['subgrid-' + event.recid].destroy();
		$('#'+ event.box_id).css({ margin: '0px', padding: '0px', width: '100%' }).animate({ height: '105px' }, 100);
		setTimeout(function () {
			subRecords = [];

			$.each(records[event.recid].node.neighbours, function(i, link) {
				subRecords.push(records[event.recid == link.src ? link.dst : link.src]);
			});

			$('#'+ event.box_id).w2grid({
				name: 'subgrid-' + event.recid,
				show: { columnHeaders: true },
				fixedBody: false,
				columns: cols,
				records: subRecords,
			});
			w2ui['subgrid-' + event.recid].resize();
		}, 300);
	},
	onDblClick: function(event) {
		window.open(nodelistconfig.mapPath + '/#!v:m;n:'+records[event.recid].id, '_blank');
	},
});

w2popup.unlock();
<|endoftext|>


<commit_before>'use strict';

/**
 */
angular.module('openolitor-kundenportal')
  .controller('OpenOlitorRootController', ['$scope', '$rootScope',
    'ServerService', 'ProjektService', 'gettextCatalog', 'amMoment',
    '$location', 'msgBus', 'checkSize', '$window', '$timeout', 'BUILD_NR',
    'ENV', 'VERSION',
    'ooAuthService', 'API_URL', '$cookies', 'dialogService',
    function($scope, $rootScope, ServerService, ProjektService,
      gettextCatalog, amMoment, $location, msgBus, checkSize, $window,
      $timeout, BUILD_NR, ENV, VERSION, ooAuthService, API_URL,
      $cookies, dialogService) {
      angular.element($window).bind('resize', function() {
        checkSize();
      });

      $scope.welcomeDisplayed = false;

      $scope.currentPathContains = function(pathJunk) {
        return $location.url().indexOf(pathJunk) !== -1;
      };

      //initial launch
      checkSize();

      $scope.connected = false;

      var unwatchLoggedIn = $scope.$watch(function() {
        return ooAuthService.getUser();
      }, function(user) {
        $scope.loggedIn = ooAuthService.isUserLoggedIn(user);
        $scope.user = user;

        if ($scope.loggedIn) {
          ProjektService.resolveProjekt(false).then(function(projekt) {
            $scope.projekt = projekt;
            $rootScope.projekt = projekt;
            $scope.checkWelcomeMessage();
          });
        } else {
          ProjektService.resolveProjekt(true).then(function(projekt) {
            $scope.projekt = projekt;
            $rootScope.projekt = projekt;
          });
        }
      });

      var unwatchStaticServerInfo = $scope.$watch(ServerService.getStaticServerInfo,
        function(info) {
          if (!angular.isUndefined(info)) {
            $scope.serverInfo = info;
            $scope.connected = true;
          }
        });

      $scope.buildNr = BUILD_NR;
      $scope.env = ENV;
      $scope.version = VERSION;

      msgBus.onMsg('WebSocketClosed', $rootScope, function(event, msg) {
        $scope.connected = false;
        $scope.messagingSocketClosedReason = msg.reason;
        $timeout(function() {
          $scope.showConnectionErrorMessage = true;
        }, 30000);
        $scope.$apply();
      });

      msgBus.onMsg('WebSocketOpen', $rootScope, function() {
        $scope.connected = true;
        $scope.showConnectionErrorMessage = false;
        $scope.messagingSocketClosedReason = '';
        $scope.$apply();
      });

      $scope.changeLang = function(lang) {
        if (!angular.isUndefined(lang)) {

          msgBus.emitMsg({
            type: 'ChangeLang',
            reason: lang
          });
          $scope.storeActiveLang(lang);
          $scope.$emit('languageChanged');
        }
      };

      msgBus.onMsg('ChangeLang', $rootScope, function(event, msg) {
        gettextCatalog.setCurrentLanguage(msg.reason);
      });

      $scope.activeLang = function() {
        return gettextCatalog.getCurrentLanguage();
      };

      $scope.storedActiveLang = function() {
        return $cookies.get('activeLang');
      };

      $scope.storeActiveLang = function(lang) {
        $cookies.put('activeLang', lang);
      };

      if (angular.isUndefined($scope.storedActiveLang())) {
        var lang = $window.navigator.language || $window.navigator.userLanguage;
        if (lang.indexOf('de-CH') >= 0) {
          $scope.changeLang('de_CH');
        } else if (lang.indexOf('de-DE') >= 0) {
          $scope.changeLang('de_DE');
        } else if (lang.indexOf('de-') >= 0) {
          $scope.changeLang('de_DE');
        } else if (lang.indexOf('fr-BE') >= 0) {
          $scope.changeLang('fr_BE');
        } else if (lang.indexOf('fr-CH') >= 0) {
          $scope.changeLang('fr_CH');
        } else if (lang.indexOf('fr-') >= 0) {
          $scope.changeLang('fr_CH');
        } else if (lang.indexOf('en-') >= 0) {
          $scope.changeLang('en');
        } else if (lang.indexOf('es-') >= 0) {
          $scope.changeLang('es');
        } else if (lang.indexOf('cs-') >= 0) {
          $scope.changeLang('cs');
        } else if (lang.indexOf('hu-') >= 0) {
          $scope.changeLang('hu');
        } else {
          $scope.changeLang('en');
        }
      } else {
        $scope.changeLang($scope.storedActiveLang());
      }

      $scope.checkWelcomeMessage = function() {
        if (!$scope.welcomeDisplayed) {
          $scope.welcomeDisplayed = true;
          if ($scope.projekt.welcomeMessage2) {
            dialogService.displayDialogOkAbort(
              $scope.projekt.welcomeMessage2,
              function() {},
              'Mitteilung',
              true,
              'Schliessen'
            );
          }
        }
      };

      $scope.$on('destroy', function() {
        unwatchLoggedIn();
        unwatchStaticServerInfo();
      });
    }
  ]);
<commit_msg>OO changing indexof to startswith on the default language<commit_after>'use strict';

/**
 */
angular.module('openolitor-kundenportal')
  .controller('OpenOlitorRootController', ['$scope', '$rootScope',
    'ServerService', 'ProjektService', 'gettextCatalog', 'amMoment',
    '$location', 'msgBus', 'checkSize', '$window', '$timeout', 'BUILD_NR',
    'ENV', 'VERSION',
    'ooAuthService', 'API_URL', '$cookies', 'dialogService',
    function($scope, $rootScope, ServerService, ProjektService,
      gettextCatalog, amMoment, $location, msgBus, checkSize, $window,
      $timeout, BUILD_NR, ENV, VERSION, ooAuthService, API_URL,
      $cookies, dialogService) {
      angular.element($window).bind('resize', function() {
        checkSize();
      });

      $scope.welcomeDisplayed = false;

      $scope.currentPathContains = function(pathJunk) {
        return $location.url().indexOf(pathJunk) !== -1;
      };

      //initial launch
      checkSize();

      $scope.connected = false;

      var unwatchLoggedIn = $scope.$watch(function() {
        return ooAuthService.getUser();
      }, function(user) {
        $scope.loggedIn = ooAuthService.isUserLoggedIn(user);
        $scope.user = user;

        if ($scope.loggedIn) {
          ProjektService.resolveProjekt(false).then(function(projekt) {
            $scope.projekt = projekt;
            $rootScope.projekt = projekt;
            $scope.checkWelcomeMessage();
          });
        } else {
          ProjektService.resolveProjekt(true).then(function(projekt) {
            $scope.projekt = projekt;
            $rootScope.projekt = projekt;
          });
        }
      });

      var unwatchStaticServerInfo = $scope.$watch(ServerService.getStaticServerInfo,
        function(info) {
          if (!angular.isUndefined(info)) {
            $scope.serverInfo = info;
            $scope.connected = true;
          }
        });

      $scope.buildNr = BUILD_NR;
      $scope.env = ENV;
      $scope.version = VERSION;

      msgBus.onMsg('WebSocketClosed', $rootScope, function(event, msg) {
        $scope.connected = false;
        $scope.messagingSocketClosedReason = msg.reason;
        $timeout(function() {
          $scope.showConnectionErrorMessage = true;
        }, 30000);
        $scope.$apply();
      });

      msgBus.onMsg('WebSocketOpen', $rootScope, function() {
        $scope.connected = true;
        $scope.showConnectionErrorMessage = false;
        $scope.messagingSocketClosedReason = '';
        $scope.$apply();
      });

      $scope.changeLang = function(lang) {
        if (!angular.isUndefined(lang)) {

          msgBus.emitMsg({
            type: 'ChangeLang',
            reason: lang
          });
          $scope.storeActiveLang(lang);
          $scope.$emit('languageChanged');
        }
      };

      msgBus.onMsg('ChangeLang', $rootScope, function(event, msg) {
        gettextCatalog.setCurrentLanguage(msg.reason);
      });

      $scope.activeLang = function() {
        return gettextCatalog.getCurrentLanguage();
      };

      $scope.storedActiveLang = function() {
        return $cookies.get('activeLang');
      };

      $scope.storeActiveLang = function(lang) {
        $cookies.put('activeLang', lang);
      };

      if (angular.isUndefined($scope.storedActiveLang())) {
        var lang = $window.navigator.language || $window.navigator.userLanguage;
        if (lang.startsWith('de-CH')) {
          $scope.changeLang('de_CH');
        } else if (lang.startsWith('de-DE')) {
          $scope.changeLang('de_DE');
        } else if (lang.startsWith('de')) {
          $scope.changeLang('de_DE');
        } else if (lang.startsWith('fr-BE')) {
          $scope.changeLang('fr_BE');
        } else if (lang.startsWith('fr-CH')) {
          $scope.changeLang('fr_CH');
        } else if (lang.startsWith('fr')) {
          $scope.changeLang('fr_CH');
        } else if (lang.startsWith('en')) {
          $scope.changeLang('en');
        } else if (lang.startsWith('es')) {
          $scope.changeLang('es');
        } else if (lang.startsWith('cs')) {
          $scope.changeLang('cs');
        } else if (lang.startsWith('hu')) {
          $scope.changeLang('hu');
        } else {
          $scope.changeLang('en');
        }
      } else {
        $scope.changeLang($scope.storedActiveLang());
      }

      $scope.checkWelcomeMessage = function() {
        if (!$scope.welcomeDisplayed) {
          $scope.welcomeDisplayed = true;
          if ($scope.projekt.welcomeMessage2) {
            dialogService.displayDialogOkAbort(
              $scope.projekt.welcomeMessage2,
              function() {},
              'Mitteilung',
              true,
              'Schliessen'
            );
          }
        }
      };

      $scope.$on('destroy', function() {
        unwatchLoggedIn();
        unwatchStaticServerInfo();
      });
    }
  ]);
<|endoftext|>


<commit_before>/* Utilities */

Array.prototype.shuffle = function(){
    for(var j, x, i = this.length; i;
        j = Math.floor(Math.random() * i),
        x = this[--i], this[i] = this[j], this[j] = x);
    return this;
};

/* Color Prototype */

/**
 * Represents a single color, suitable for Set.
 */
function Color(r, g, b) {
    this.r = r;
    this.g = g;
    this.b = b;
}

/**
 * @param {Color} c
 * @returns {Number} the distance from c
 */
Color.prototype.dist = function(c) {
    /* Naive implementation! */
    return Math.abs(this.r - c.r) +
        Math.abs(this.g - c.g) +
        Math.abs(this.b - c.b);
};

/**
 * @returns {string} a string representation suitable for Set
 */
Color.prototype.toString = function() {
    return this.r + ',' + this.g + ',' + this.b;
};

/* Set Prototype */

/**
 * A set data structure that keys off of toString().
 * @param {Array} [elements]
 */
function Set(elements) {
    this.elements = {};
    this.length = 0;
    if (elements != null) {
        this.addAll(elements);
    }
}

/**
 * @param element to add/replace
 * @returns element
 */
Set.prototype.add = function(element) {
    if (!this.has(element)) {
        this.length++;
    }
    this.elements[element] = element;
    return element;
};

/**
 * @param {Array} elements to be added
 * @returns this
 */
Set.prototype.addAll = function(elements) {
    for (var i = 0; i < elements.length; i++) {
        this.add(elements[i]);
    }
    return this;
};

/**
 * @param element to be removed
 * @returns element
 */
Set.prototype.remove = function(element) {
    delete this.elements[element];
    this.length--;
    return element;
};

/**
 * @returns {boolean} true if element is in this set
 */
Set.prototype.has = function(element) {
    return element in this.elements;
};

/**
 * @param {Function} f called for each element
 */
Set.prototype.each = function(f) {
    for (var element in this.elements) {
        f(this.elements[element]);
    }
};

/**
 * @returns {Array} this set as an array
 */
Set.prototype.asArray = function() {
    var array = [];
    this.each(function(element) {
        array.push(element);
    });
    return array;
};

/**
 * @returns a random element
 */
Set.prototype.random = function() {
    var keys = Object.keys(this.elements);
    return this.elements[keys[Math.floor(Math.random() * keys.length)]];
};

/**
 * @returns a random element, removing it from the set
 */
Set.prototype.popRandom = function() {
    var e = this.random();
    this.remove(e);
    return e;
};

/* Pixel Prototype */

/**
 * Represents a pixel in the final result, suitable for Set.
 */
function Pixel(x, y, color) {
    this.x = Math.floor(x);
    this.y = Math.floor(y);
    this.color = color;
    this._neighbors = {};
}

/**
 * @returns {string} a string suitable for Set
 */
Pixel.prototype.toString = function() {
    return '(' + this.x + ',' + this.y + ')';
};

/**
 * @private
 */
Pixel.prototype._findneighbors = function(mx, my) {
    var pixels = [];
    for (var x = -1; x <= 1; x++) {
        for (var y = -1; y <= 1; y++) {
            if (x !== 0 || y !== 0) {
                var xx = this.x + x, yy = this.y + y;
                if (xx >= 0 && xx < mx && yy >= 0 && yy < my) {
                    pixels.push(new Pixel(xx, yy));
                }
            }
        }
    }
    return pixels;
};

/**
 * @param {Number} mx x-maximum
 * @param {Number} my y-maximum
 * @returns {Array} an array of this pixel's neighbors within bounds
 */
Pixel.prototype.neighbors = function(mx, my) {
    var key = [mx, my].toString();
    if (this._neighbors[key] != null) {
        return this._neighbors[key];
    } else {
        return this._neighbors[key] = this._findneighbors(mx, my);
    }
};

/* Color Lists */

/**
 * @param {Number} channelbits number of bits per channel
 * @returns {Set} all possible colors (2^(channelbits*3) total)
 */
function colors(channelbits) {
    var set = [];
    var s = Math.pow(2, 8 - channelbits);
    for (var r = 0; r < 256; r += s) {
        for (var g = 0; g < 256; g += s) {
            for (var b = 0; b < 256; b += s) {
                set.push(new Color(r, g, b));
            }
        }
    }
    return set;
}

/* Painter Prototype */

/**
 * Renders a fancy coloration across different interpeter turns.
 * @param {Number} w
 * @param {Number} h
 * @param {Number} channelbits
 */
function Painter(w, h, channelbits) {
    this.w = w;
    this.h = h;
    this.pixels = new Set();
    this.edges = new Set();
    this.colors = colors(channelbits).shuffle();
    this.count = 0;
    this.seed(w / 4 * 1, h / 4 * 1);
    this.seed(w / 4 * 3, h / 4 * 1);
    this.seed(w / 4 * 1, h / 4 * 3);
    this.seed(w / 4 * 3, h / 4 * 3);
}

Painter.prototype.seed = function(x, y) {
    this.edges.add(new Pixel(x, y, this.colors.pop()));
    this.count++;
};

/**
 * @returns {boolean} true if this Painter is complete
 */
Painter.prototype.isDone = function() {
    return this.count >= this.w * this.h;
};

/**
 * @param {Number} n number of pixels to render before returning
 */
Painter.prototype.render = function(n) {
    n = n || 1;
    console.log('Completed ' + this.count);
    for (var i = 0; i < n && !this.isDone(); i++) {
        this.count++;
        var next = this.colors.pop(),
            places = this.edges.asArray();
        places.forEach(function(p) {
            p._dist = p.color.dist(next);
        });
        places.sort(function(a, b) {
            return b._dist - a._dist;
        });
        var found = false;
        while (!found) {
            var best = places.pop();
            var neighbors = best.neighbors(this.w, this.h).shuffle();
            for (var j = 0; j < neighbors.length; j++) {
                var p = neighbors[j];
                if (!this.edges.has(p) && !this.pixels.has(p)) {
                    p.color = next;
                    this.edges.add(p);
                    found = true;
                    break;
                }
            }
            if (!found) {
                this.edges.remove(best);
                this.pixels.add(best);
            }
        }
    }
    if (this.isDone()) {
        var _this = this;
        this.edges.each(function(p) {
            _this.pixels.add(p);
        });
    }
};

/**
 * Draw the current results.
 * @param {CanvasRenderingContext2D} ctx
 */
Painter.prototype.draw = function(ctx) {
    var w = this.w, h = this.h, data = ctx.getImageData(0, 0, w, h);
    this.pixels.each(function(p) {
        var c = p.color;
        data.data[4 * w * p.y + 4 * p.x + 0] = c.r;
        data.data[4 * w * p.y + 4 * p.x + 1] = c.g;
        data.data[4 * w * p.y + 4 * p.x + 2] = c.b;
    });
    ctx.putImageData(data, 0, 0);
};

/**
 * Completely render this Painter across many turns.
 * @param {CanvasRenderingContext2D} ctx
 * @param {Numnber} [step=64] number of pixels to render per step
 */
Painter.prototype.run = function(ctx, step) {
    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, this.w, this.h);
    step = step || 64;
    var _this = this;
    var timer = window.setInterval(function() {
        try {
            _this.render(step);
            _this.draw(ctx);
        } catch (e) {
            window.clearInterval(timer);
            throw e;
        }
        if (_this.done) {
            window.clearInterval(timer);
        }
    }, 1);
};

window.addEventListener('load', function() {
    var n = 512;
    var canvas = document.querySelector('#canvas');
    var ctx = canvas.getContext('2d');
    new Painter(n, n, 6).run(ctx, 32);
});
<commit_msg>Fix completion typo.<commit_after>/* Utilities */

Array.prototype.shuffle = function(){
    for(var j, x, i = this.length; i;
        j = Math.floor(Math.random() * i),
        x = this[--i], this[i] = this[j], this[j] = x);
    return this;
};

/* Color Prototype */

/**
 * Represents a single color, suitable for Set.
 */
function Color(r, g, b) {
    this.r = r;
    this.g = g;
    this.b = b;
}

/**
 * @param {Color} c
 * @returns {Number} the distance from c
 */
Color.prototype.dist = function(c) {
    /* Naive implementation! */
    return Math.abs(this.r - c.r) +
        Math.abs(this.g - c.g) +
        Math.abs(this.b - c.b);
};

/**
 * @returns {string} a string representation suitable for Set
 */
Color.prototype.toString = function() {
    return this.r + ',' + this.g + ',' + this.b;
};

/* Set Prototype */

/**
 * A set data structure that keys off of toString().
 * @param {Array} [elements]
 */
function Set(elements) {
    this.elements = {};
    this.length = 0;
    if (elements != null) {
        this.addAll(elements);
    }
}

/**
 * @param element to add/replace
 * @returns element
 */
Set.prototype.add = function(element) {
    if (!this.has(element)) {
        this.length++;
    }
    this.elements[element] = element;
    return element;
};

/**
 * @param {Array} elements to be added
 * @returns this
 */
Set.prototype.addAll = function(elements) {
    for (var i = 0; i < elements.length; i++) {
        this.add(elements[i]);
    }
    return this;
};

/**
 * @param element to be removed
 * @returns element
 */
Set.prototype.remove = function(element) {
    delete this.elements[element];
    this.length--;
    return element;
};

/**
 * @returns {boolean} true if element is in this set
 */
Set.prototype.has = function(element) {
    return element in this.elements;
};

/**
 * @param {Function} f called for each element
 */
Set.prototype.each = function(f) {
    for (var element in this.elements) {
        f(this.elements[element]);
    }
};

/**
 * @returns {Array} this set as an array
 */
Set.prototype.asArray = function() {
    var array = [];
    this.each(function(element) {
        array.push(element);
    });
    return array;
};

/**
 * @returns a random element
 */
Set.prototype.random = function() {
    var keys = Object.keys(this.elements);
    return this.elements[keys[Math.floor(Math.random() * keys.length)]];
};

/**
 * @returns a random element, removing it from the set
 */
Set.prototype.popRandom = function() {
    var e = this.random();
    this.remove(e);
    return e;
};

/* Pixel Prototype */

/**
 * Represents a pixel in the final result, suitable for Set.
 */
function Pixel(x, y, color) {
    this.x = Math.floor(x);
    this.y = Math.floor(y);
    this.color = color;
    this._neighbors = {};
}

/**
 * @returns {string} a string suitable for Set
 */
Pixel.prototype.toString = function() {
    return '(' + this.x + ',' + this.y + ')';
};

/**
 * @private
 */
Pixel.prototype._findneighbors = function(mx, my) {
    var pixels = [];
    for (var x = -1; x <= 1; x++) {
        for (var y = -1; y <= 1; y++) {
            if (x !== 0 || y !== 0) {
                var xx = this.x + x, yy = this.y + y;
                if (xx >= 0 && xx < mx && yy >= 0 && yy < my) {
                    pixels.push(new Pixel(xx, yy));
                }
            }
        }
    }
    return pixels;
};

/**
 * @param {Number} mx x-maximum
 * @param {Number} my y-maximum
 * @returns {Array} an array of this pixel's neighbors within bounds
 */
Pixel.prototype.neighbors = function(mx, my) {
    var key = [mx, my].toString();
    if (this._neighbors[key] != null) {
        return this._neighbors[key];
    } else {
        return this._neighbors[key] = this._findneighbors(mx, my);
    }
};

/* Color Lists */

/**
 * @param {Number} channelbits number of bits per channel
 * @returns {Set} all possible colors (2^(channelbits*3) total)
 */
function colors(channelbits) {
    var set = [];
    var s = Math.pow(2, 8 - channelbits);
    for (var r = 0; r < 256; r += s) {
        for (var g = 0; g < 256; g += s) {
            for (var b = 0; b < 256; b += s) {
                set.push(new Color(r, g, b));
            }
        }
    }
    return set;
}

/* Painter Prototype */

/**
 * Renders a fancy coloration across different interpeter turns.
 * @param {Number} w
 * @param {Number} h
 * @param {Number} channelbits
 */
function Painter(w, h, channelbits) {
    this.w = w;
    this.h = h;
    this.pixels = new Set();
    this.edges = new Set();
    this.colors = colors(channelbits).shuffle();
    this.count = 0;
    this.seed(w / 4 * 1, h / 4 * 1);
    this.seed(w / 4 * 3, h / 4 * 1);
    this.seed(w / 4 * 1, h / 4 * 3);
    this.seed(w / 4 * 3, h / 4 * 3);
}

Painter.prototype.seed = function(x, y) {
    this.edges.add(new Pixel(x, y, this.colors.pop()));
    this.count++;
};

/**
 * @returns {boolean} true if this Painter is complete
 */
Painter.prototype.isDone = function() {
    return this.count >= this.w * this.h;
};

/**
 * @param {Number} n number of pixels to render before returning
 */
Painter.prototype.render = function(n) {
    n = n || 1;
    console.log('Completed ' + this.count);
    for (var i = 0; i < n && !this.isDone(); i++) {
        this.count++;
        var next = this.colors.pop(),
            places = this.edges.asArray();
        places.forEach(function(p) {
            p._dist = p.color.dist(next);
        });
        places.sort(function(a, b) {
            return b._dist - a._dist;
        });
        var found = false;
        while (!found) {
            var best = places.pop();
            var neighbors = best.neighbors(this.w, this.h).shuffle();
            for (var j = 0; j < neighbors.length; j++) {
                var p = neighbors[j];
                if (!this.edges.has(p) && !this.pixels.has(p)) {
                    p.color = next;
                    this.edges.add(p);
                    found = true;
                    break;
                }
            }
            if (!found) {
                this.edges.remove(best);
                this.pixels.add(best);
            }
        }
    }
    if (this.isDone()) {
        var _this = this;
        this.edges.each(function(p) {
            _this.pixels.add(p);
        });
    }
};

/**
 * Draw the current results.
 * @param {CanvasRenderingContext2D} ctx
 */
Painter.prototype.draw = function(ctx) {
    var w = this.w, h = this.h, data = ctx.getImageData(0, 0, w, h);
    this.pixels.each(function(p) {
        var c = p.color;
        data.data[4 * w * p.y + 4 * p.x + 0] = c.r;
        data.data[4 * w * p.y + 4 * p.x + 1] = c.g;
        data.data[4 * w * p.y + 4 * p.x + 2] = c.b;
    });
    ctx.putImageData(data, 0, 0);
};

/**
 * Completely render this Painter across many turns.
 * @param {CanvasRenderingContext2D} ctx
 * @param {Numnber} [step=64] number of pixels to render per step
 */
Painter.prototype.run = function(ctx, step) {
    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, this.w, this.h);
    step = step || 64;
    var _this = this;
    var timer = window.setInterval(function() {
        try {
            _this.render(step);
            _this.draw(ctx);
        } catch (e) {
            window.clearInterval(timer);
            throw e;
        }
        if (_this.isDone()) {
            window.clearInterval(timer);
        }
    }, 1);
};

window.addEventListener('load', function() {
    var n = 512;
    var canvas = document.querySelector('#canvas');
    var ctx = canvas.getContext('2d');
    new Painter(n, n, 6).run(ctx, 32);
});
<|endoftext|>


<commit_before><commit_msg>checkFreshness works for tags<commit_after><|endoftext|>


<commit_before>const request = require('../requests');
const async = require('async');

/**
 * The service which provides build information by using the VSTS REST API
 *  v2.0. Although the naming of the variables suggest usage with VSTS, the
 *  code is compatible with both VSTS and TFS. The naming is simply the
 *  artifact of making the code generalised for both after initial usage
 *  with VSTS.
 * @public
 * @constructor
 * @see https://www.visualstudio.com/en-us/docs/integrate/api/build/overview
 */
function VSTSRestBuilds() {
  let basicAuth = null;
  let instance = null;
  let project = null;
  let collection = null;
  let params = null;
  let includeQueued = null;
  let previousBuildsToGet = [];
  let apiVersion = null;
  let showBuildStep = null;

  /**
   * This object is the representation of resultFilter mentioned in the docs
   * @private
   * @see https://www.visualstudio.com/en-us/docs/integrate/api/build/builds
   */
  const resultFilter = Object.freeze({
    succeeded:          'succeeded',
    partiallySucceeded: 'partiallySucceeded',
    failed:             'failed',
    canceled:           'canceled'
  });

  /** This object is the representation of statusFilter mentioned in the docs
   * @private
   * @see https://www.visualstudio.com/en-us/docs/integrate/api/build/builds
   */
  const statusFilter = Object.freeze({
    inProgress: 'inProgress',
    completed:  'completed',
    cancelling: 'cancelling',
    postponed:  'postponed',
    notStarted: 'notStarted',
    all:        'all',
  });

  const timelineRecordState = Object.freeze({
    completed:  'completed',
    inProgress: 'inProgress',
    pending:    'pending'
  })

  /**
   * This object defines the color scheme used.
   * @private
   */
  const colorScheme = Object.freeze({
    succeeded:          'Green',
    partiallySucceeded: '#F8A800',
    failed:             'Red',
    canceled:           'Gray',
    inProgress:         '#0078D7',
    completed:          'Green',
    cancelling:         '#0078D7',
    postponed:          'Gray',
    notStarted:         'Gray',
    all:                'Gray'
  });

  /**
   * This object defines the compatable api versions that we are allowed to use
   * @private
   */
  const allowedAPIVersions = Object.freeze({
    '2.0':      '2.0',
    undefined:  '2.0'
  });

  /**
   * @typedef {Object} Build
   * @property {string} definition Build definition id
   * @property {Date} startedAt Build start time
   * @property {Date} finishedAt Build finish time
   * @property {boolean} hasErrors Does the resulting build have errors?
   * @property {boolean} hasWarnings Did the build give some warnings?
   * @property {boolean} isRunning Is the build currently running?
   * @property {boolean} isQueued Is the build currently waiting in the queue?
   * @property {string} id Unique ID of the build
   * @property {string} number Build number
   * @property {string} project Name of the project
   * @property {string} reason Reason for building the project
   * @property {string} requestedFor Name of the Requester
   * @property {string} status The color to be used for displaying
   * @property {string} statusText The status of the build
   * @property {string} url URL of the project
   */

  /**
   * It is a node-style callback.
   * @callback buildsInfoRequestCallback
   * @param {Error|null} err It is an instance of Error
   * @param {Array<Build>} listOfBuilds It is an array of {@link Build}
   */

  /**
   * It exposes the API needed by the application to check the status of builds.
   * @name check
   * @function
   * @public
   * @instance
   * @memberOf VSTSRestBuilds
   * @param {buildsInfoRequestCallback} cb Callback which handles the
   *  requested build information
   */
  this.check = (callback) => {
    if (basicAuth && instance && project) {
      getListOfBuilds(callback);
      return;
    }
    callback('incomplete configuration');
    return;
  };

  /**
   * @typedef {Object} VSTSRestBuildsConfiguration
   * @property {string} instance VS Team Services account
   *  ({account}.visualstudio.com) or TFS server ({server:port}).
   * @property {string} project Team project ID or name
   * @property {string} queryparams Additional queryparams to filter the data
   *  and provide additional options
   * @property {string} username Username
   * @property {string} pat Personal Access Token with access to Builds
   *  information
   * @property {boolean} includeQueued Show queued builds
   * @property {string} apiVersion The api version to use
   * @property {boolean} showBuildStep Adds the current build step to the statusString of the build variable
   */

  /**
   * It exposes the API needed by the application to provide
   *  configuration parameters.
   * @name check
   * @function
   * @public
   * @instance
   * @memberOf VSTSRestBuilds
   * @param {VSTSRestBuildsConfiguration} config Configuration parameters
   */
  this.configure = (config) => {
    /**
     * It exposes the configuration passed to the
     * configure instance method.
     * @public
     * @instance
     */
    this.configuration = config;
    basicAuth = new Buffer(`${config.username}:${config.pat}`)
      .toString('base64');
    instance = config.instance;
    params = config.queryparams;
    project = config.project;
    collection = config.collection || 'DefaultCollection';
    includeQueued = config.includeQueued || false;
    apiVersion = allowedAPIVersions[config.apiVersion] || '2.0';
    showBuildStep = config.showBuildStep || false;

    console.log(config,apiVersion);
  };

  /**
   * @private
   * @param {buildsInfoRequestCallback} cb Callback which handles the
   *  requested build information
   */
  const getListOfBuilds = (callback) => {
    const url = `https://${instance}/${collection}/${project}/_apis/build/builds?api-version=${apiVersion}${params}`;
    let options = {
      url,
      headers: {
        Authorization: `Basic ${basicAuth}`,
      },
    };

    // Set up our dependency tree, similar to angular
    // https://caolan.github.io/async/docs.html#autoInject
    async.autoInject({
      // ### 1. Get the list of builds ###
      get_builds: (callback) => {
        request.makeRequest(options, (err, body) => {
          transformData(err, body, callback);
        });
      },
      // ### 2. Get any previous builds ###
      get_previous_builds: (get_builds, callback) => {
        // No builds to get ? then nothing to add
        if (previousBuildsToGet.length === 0) { callback(null); return; }
        getPreviousBuilds(get_builds, callback);
      },
      // ### 3. Merge our 2 arrays ###
      merge_builds: (get_builds, get_previous_builds, callback) => {
        let builds = [];
        if (get_previous_builds && get_previous_builds.shift()) {
          builds = [...new Set([...get_builds.shift(),...get_previous_builds.shift()])]; // Merge arrays, removing duplicates
        } else {
          builds = get_builds;
        }
        callback(null, builds);
      },
      // ### 4. Get the current build steps/stage for each build ###
      get_build_steps: (merge_builds, callback) => {
        // Only get the build step if we are allowed to
        if (!showBuildStep) { callback(null, merge_builds); return; }
        async.map(merge_builds, (build, callback) => {
          getLatestBuildStep(build, callback);
        }, (err, results) => {
          callback(null, results);
        });
      }
    }, (err, results) => {
      // Pass back to the monitor app
      callback(err, results.get_build_steps);
    });

    /**
     * Transforms the data received from the request to VSTS REST API
     * @private
     * @param {any} err If the value is truthy, it indicates an error has
     *  occurred.
     * @param {object} body It contains the response body from VSTS REST API
     * @param {buildsInfoRequestCallback} cb Callback which handles the
     *  requested build information
     * @return {null}
     */
    const transformData = (err, body, callback) => {
      if (err) {
        callback(err);
        return;
      }
      if (!(body && body.value)) {
        callback('No values found');
        return;
      }
      // Filter out any dummy empty objects
      const transformedData = body.value.map(transformer).filter((val) => { return Object.keys(val).length; });

      callback(null, transformedData);
    };

    /**
     * The function transforms the data from VSTS API to
     *  the accepted by callback
     * @name transformer
     * @private
     * @param {object} build individual build information object returned
     *  from API
     * @returns {Build} the object is in the format accepted by the application
     */
    const transformer = (build) => {
      let color = colorScheme[
        build.result ?
          resultFilter[build.result] :
          (build.status === statusFilter.notStarted ?
            statusFilter[statusFilter.notStarted] :
            statusFilter[statusFilter.inProgress]
          )
      ];

      let text = build.result ?
      build.result :
        (build.status === statusFilter.notStarted ?
          statusFilter.notStarted :
          statusFilter.inProgress
        );

      let webUrl = build._links ?
        (build._links.web ? build._links.web.href : build.url) :
        build.url;

      let result = {
        definition: build.definition.id,
        finishedAt: build.finishTime ? new Date(build.finishTime) : new Date(),
        hasErrors: build.result === resultFilter.failed,
        hasWarnings: build.result === resultFilter.partiallySucceeded,
        id: build.id,
        isRunning: build.status === statusFilter.inProgress,
        isQueued: build.status === statusFilter.notStarted,
        number: build.buildNumber,
        project: build.definition.name,
        queuedAt: build.queueTime ? new Date(build.queueTime) : new Date(),
        reason: build.reason,
        requestedFor: build.requestedFor ? build.requestedFor.displayName : '',
        startedAt: new Date(build.startTime),
        status: color,
        statusText: text,
        timeline: build._links.timeline ? build._links.timeline.href : '',
        url: webUrl
      };

      // Only show queued builds if we're told to
      if (result.isQueued && !includeQueued) {
        previousBuildsToGet.push(result);
        return {};  // Return a dummy empty object (that we will remove later)
      }

      return result;
    };
  };

  /**
   * This function makes an individual API call for each build we need
   *  to get the previous version for
   * @private
   * @param {buildsInfoRequsetCallback} callback Callback which handles the
   *  requested build information
   */
  const getPreviousBuilds = (currentBuilds, callback) => {
    async.map(previousBuildsToGet, (build, callback) => {
      let def = build.definition;

      // If we already have a previous build, then we don't need to get another one
      const hasPreviousBuild = (val) => { return (val.definition === build.definition) && (val.project === build.project); };
      if (currentBuilds.some(hasPreviousBuild)) {
        callback(null);
        return;
      }

      // Get the second to last build instead
      options.url = `https://${instance}/${collection}/${project}/_apis/build/builds?api-version=${apiVersion}&definitions=${def}&$top=2`;
      request.makeRequest(options, (err, body) => {
        if (err) { callback(err); return; }
        if (!(body && body.value)) {
          console.log('No previous builds found'); // Don't break the rest of the builds if we can't get a previous one
          callback(null);
          return;
         }

        let prevBuild = [body.value[1]];

        if (prevBuild) {
          const transformedData = prevBuild.map(transformer);
          callback(null, transformedData[0]);
          return;
        }

        console.log('Unable to fetch previous build');  // Don't break the rest of the builds if we can't get a previous one
        callback(null);

      });

    }, (err, results) => {
      callback(null, results);
      previousBuildsToGet = [];
    });
  }

  /**
   * This function gets the most recent timeline record (aka step) for a build
   * @private
   * @param {string} timelineURL the url to the timeline VSTS API
   */
  const getLatestBuildStep = (build, callback) => {
    const timelineURL = build.timeline;
    if (!timelineURL || timelineURL === '') {
      console.log("no timeline url");
      callback(null);
      return;
    }

    const url = timelineURL;
    const options = {
      url,
      headers: {
        Authorization: `Basic ${basicAuth}`,
      },
    };

    request.makeRequest(options, (err, body) => {
      if (err) { callback(null); return; }
      if (!(body && body.records)) { callback(null); return; }

      // As of API version 2.0 there is no better way of doing this, we *have* to retrieve everything
      let records = body.records.sort( (a, b) => {
        return a.order - b.order;
      });

      for (let key in records) {
        let record = records[key];
        if (record.state === timelineRecordState.inProgress) {
          build.statusText += ' - ' + record.name;
        }
      }
      callback(null, build);
    });
  };
}

module.exports = VSTSRestBuilds;
<commit_msg>Fixed missing semicolons<commit_after>const request = require('../requests');
const async = require('async');

/**
 * The service which provides build information by using the VSTS REST API
 *  v2.0. Although the naming of the variables suggest usage with VSTS, the
 *  code is compatible with both VSTS and TFS. The naming is simply the
 *  artifact of making the code generalised for both after initial usage
 *  with VSTS.
 * @public
 * @constructor
 * @see https://www.visualstudio.com/en-us/docs/integrate/api/build/overview
 */
function VSTSRestBuilds() {
  let basicAuth = null;
  let instance = null;
  let project = null;
  let collection = null;
  let params = null;
  let includeQueued = null;
  let previousBuildsToGet = [];
  let apiVersion = null;
  let showBuildStep = null;

  /**
   * This object is the representation of resultFilter mentioned in the docs
   * @private
   * @see https://www.visualstudio.com/en-us/docs/integrate/api/build/builds
   */
  const resultFilter = Object.freeze({
    succeeded:          'succeeded',
    partiallySucceeded: 'partiallySucceeded',
    failed:             'failed',
    canceled:           'canceled'
  });

  /** This object is the representation of statusFilter mentioned in the docs
   * @private
   * @see https://www.visualstudio.com/en-us/docs/integrate/api/build/builds
   */
  const statusFilter = Object.freeze({
    inProgress: 'inProgress',
    completed:  'completed',
    cancelling: 'cancelling',
    postponed:  'postponed',
    notStarted: 'notStarted',
    all:        'all',
  });

  const timelineRecordState = Object.freeze({
    completed:  'completed',
    inProgress: 'inProgress',
    pending:    'pending'
  });

  /**
   * This object defines the color scheme used.
   * @private
   */
  const colorScheme = Object.freeze({
    succeeded:          'Green',
    partiallySucceeded: '#F8A800',
    failed:             'Red',
    canceled:           'Gray',
    inProgress:         '#0078D7',
    completed:          'Green',
    cancelling:         '#0078D7',
    postponed:          'Gray',
    notStarted:         'Gray',
    all:                'Gray'
  });

  /**
   * This object defines the compatable api versions that we are allowed to use
   * @private
   */
  const allowedAPIVersions = Object.freeze({
    '2.0':      '2.0',
    undefined:  '2.0'
  });

  /**
   * @typedef {Object} Build
   * @property {string} definition Build definition id
   * @property {Date} startedAt Build start time
   * @property {Date} finishedAt Build finish time
   * @property {boolean} hasErrors Does the resulting build have errors?
   * @property {boolean} hasWarnings Did the build give some warnings?
   * @property {boolean} isRunning Is the build currently running?
   * @property {boolean} isQueued Is the build currently waiting in the queue?
   * @property {string} id Unique ID of the build
   * @property {string} number Build number
   * @property {string} project Name of the project
   * @property {string} reason Reason for building the project
   * @property {string} requestedFor Name of the Requester
   * @property {string} status The color to be used for displaying
   * @property {string} statusText The status of the build
   * @property {string} url URL of the project
   */

  /**
   * It is a node-style callback.
   * @callback buildsInfoRequestCallback
   * @param {Error|null} err It is an instance of Error
   * @param {Array<Build>} listOfBuilds It is an array of {@link Build}
   */

  /**
   * It exposes the API needed by the application to check the status of builds.
   * @name check
   * @function
   * @public
   * @instance
   * @memberOf VSTSRestBuilds
   * @param {buildsInfoRequestCallback} cb Callback which handles the
   *  requested build information
   */
  this.check = (callback) => {
    if (basicAuth && instance && project) {
      getListOfBuilds(callback);
      return;
    }
    callback('incomplete configuration');
    return;
  };

  /**
   * @typedef {Object} VSTSRestBuildsConfiguration
   * @property {string} instance VS Team Services account
   *  ({account}.visualstudio.com) or TFS server ({server:port}).
   * @property {string} project Team project ID or name
   * @property {string} queryparams Additional queryparams to filter the data
   *  and provide additional options
   * @property {string} username Username
   * @property {string} pat Personal Access Token with access to Builds
   *  information
   * @property {boolean} includeQueued Show queued builds
   * @property {string} apiVersion The api version to use
   * @property {boolean} showBuildStep Adds the current build step to the statusString of the build variable
   */

  /**
   * It exposes the API needed by the application to provide
   *  configuration parameters.
   * @name check
   * @function
   * @public
   * @instance
   * @memberOf VSTSRestBuilds
   * @param {VSTSRestBuildsConfiguration} config Configuration parameters
   */
  this.configure = (config) => {
    /**
     * It exposes the configuration passed to the
     * configure instance method.
     * @public
     * @instance
     */
    this.configuration = config;
    basicAuth = new Buffer(`${config.username}:${config.pat}`)
      .toString('base64');
    instance = config.instance;
    params = config.queryparams;
    project = config.project;
    collection = config.collection || 'DefaultCollection';
    includeQueued = config.includeQueued || false;
    apiVersion = allowedAPIVersions[config.apiVersion] || '2.0';
    showBuildStep = config.showBuildStep || false;

    console.log(config,apiVersion);
  };

  /**
   * @private
   * @param {buildsInfoRequestCallback} cb Callback which handles the
   *  requested build information
   */
  const getListOfBuilds = (callback) => {
    const url = `https://${instance}/${collection}/${project}/_apis/build/builds?api-version=${apiVersion}${params}`;
    let options = {
      url,
      headers: {
        Authorization: `Basic ${basicAuth}`,
      },
    };

    // Set up our dependency tree, similar to angular
    // https://caolan.github.io/async/docs.html#autoInject
    async.autoInject({
      // ### 1. Get the list of builds ###
      get_builds: (callback) => {
        request.makeRequest(options, (err, body) => {
          transformData(err, body, callback);
        });
      },
      // ### 2. Get any previous builds ###
      get_previous_builds: (get_builds, callback) => {
        // No builds to get ? then nothing to add
        if (previousBuildsToGet.length === 0) { callback(null); return; }
        getPreviousBuilds(get_builds, callback);
      },
      // ### 3. Merge our 2 arrays ###
      merge_builds: (get_builds, get_previous_builds, callback) => {
        let builds = [];
        if (get_previous_builds && get_previous_builds.shift()) {
          builds = [...new Set([...get_builds.shift(),...get_previous_builds.shift()])]; // Merge arrays, removing duplicates
        } else {
          builds = get_builds;
        }
        callback(null, builds);
      },
      // ### 4. Get the current build steps/stage for each build ###
      get_build_steps: (merge_builds, callback) => {
        // Only get the build step if we are allowed to
        if (!showBuildStep) { callback(null, merge_builds); return; }
        async.map(merge_builds, (build, callback) => {
          getLatestBuildStep(build, callback);
        }, (err, results) => {
          callback(null, results);
        });
      }
    }, (err, results) => {
      // Pass back to the monitor app
      callback(err, results.get_build_steps);
    });

    /**
     * Transforms the data received from the request to VSTS REST API
     * @private
     * @param {any} err If the value is truthy, it indicates an error has
     *  occurred.
     * @param {object} body It contains the response body from VSTS REST API
     * @param {buildsInfoRequestCallback} cb Callback which handles the
     *  requested build information
     * @return {null}
     */
    const transformData = (err, body, callback) => {
      if (err) {
        callback(err);
        return;
      }
      if (!(body && body.value)) {
        callback('No values found');
        return;
      }
      // Filter out any dummy empty objects
      const transformedData = body.value.map(transformer).filter((val) => { return Object.keys(val).length; });

      callback(null, transformedData);
    };

    /**
     * The function transforms the data from VSTS API to
     *  the accepted by callback
     * @name transformer
     * @private
     * @param {object} build individual build information object returned
     *  from API
     * @returns {Build} the object is in the format accepted by the application
     */
    const transformer = (build) => {
      let color = colorScheme[
        build.result ?
          resultFilter[build.result] :
          (build.status === statusFilter.notStarted ?
            statusFilter[statusFilter.notStarted] :
            statusFilter[statusFilter.inProgress]
          )
      ];

      let text = build.result ?
      build.result :
        (build.status === statusFilter.notStarted ?
          statusFilter.notStarted :
          statusFilter.inProgress
        );

      let webUrl = build._links ?
        (build._links.web ? build._links.web.href : build.url) :
        build.url;

      let result = {
        definition: build.definition.id,
        finishedAt: build.finishTime ? new Date(build.finishTime) : new Date(),
        hasErrors: build.result === resultFilter.failed,
        hasWarnings: build.result === resultFilter.partiallySucceeded,
        id: build.id,
        isRunning: build.status === statusFilter.inProgress,
        isQueued: build.status === statusFilter.notStarted,
        number: build.buildNumber,
        project: build.definition.name,
        queuedAt: build.queueTime ? new Date(build.queueTime) : new Date(),
        reason: build.reason,
        requestedFor: build.requestedFor ? build.requestedFor.displayName : '',
        startedAt: new Date(build.startTime),
        status: color,
        statusText: text,
        timeline: build._links.timeline ? build._links.timeline.href : '',
        url: webUrl
      };

      // Only show queued builds if we're told to
      if (result.isQueued && !includeQueued) {
        previousBuildsToGet.push(result);
        return {};  // Return a dummy empty object (that we will remove later)
      }

      return result;
    };
  };

  /**
   * This function makes an individual API call for each build we need
   *  to get the previous version for
   * @private
   * @param {buildsInfoRequsetCallback} callback Callback which handles the
   *  requested build information
   */
  const getPreviousBuilds = (currentBuilds, callback) => {
    async.map(previousBuildsToGet, (build, callback) => {
      let def = build.definition;

      // If we already have a previous build, then we don't need to get another one
      const hasPreviousBuild = (val) => { return (val.definition === build.definition) && (val.project === build.project); };
      if (currentBuilds.some(hasPreviousBuild)) {
        callback(null);
        return;
      }

      // Get the second to last build instead
      options.url = `https://${instance}/${collection}/${project}/_apis/build/builds?api-version=${apiVersion}&definitions=${def}&$top=2`;
      request.makeRequest(options, (err, body) => {
        if (err) { callback(err); return; }
        if (!(body && body.value)) {
          console.log('No previous builds found'); // Don't break the rest of the builds if we can't get a previous one
          callback(null);
          return;
         }

        let prevBuild = [body.value[1]];

        if (prevBuild) {
          const transformedData = prevBuild.map(transformer);
          callback(null, transformedData[0]);
          return;
        }

        console.log('Unable to fetch previous build');  // Don't break the rest of the builds if we can't get a previous one
        callback(null);

      });

    }, (err, results) => {
      callback(null, results);
      previousBuildsToGet = [];
    });
  };

  /**
   * This function gets the most recent timeline record (aka step) for a build
   * @private
   * @param {string} timelineURL the url to the timeline VSTS API
   */
  const getLatestBuildStep = (build, callback) => {
    const timelineURL = build.timeline;
    if (!timelineURL || timelineURL === '') {
      console.log("no timeline url");
      callback(null);
      return;
    }

    const url = timelineURL;
    const options = {
      url,
      headers: {
        Authorization: `Basic ${basicAuth}`,
      },
    };

    request.makeRequest(options, (err, body) => {
      if (err) { callback(null); return; }
      if (!(body && body.records)) { callback(null); return; }

      // As of API version 2.0 there is no better way of doing this, we *have* to retrieve everything
      let records = body.records.sort( (a, b) => {
        return a.order - b.order;
      });

      for (let key in records) {
        let record = records[key];
        if (record.state === timelineRecordState.inProgress) {
          build.statusText += ' - ' + record.name;
        }
      }
      callback(null, build);
    });
  };
}

module.exports = VSTSRestBuilds;
<|endoftext|>


<commit_before>(function cometSpawner() {
  var $body = document.getElementsByTagName('body')[0],
      game, loop,
      resizeRunning = false;

  /******************
    Utility Object
  *******************/
  var utility = {
    htmlSize: function htmlSize() {
      return {
        width: document.documentElement.clientWidth,
        height: document.documentElement.clientHeight
      };
    },

    weightElements: function weightElements() {
      var items = document.body.getElementsByTagName('*');

      for (let i = 0, itemsLen = items.length; i < itemsLen; i += 1) {

      }
    }
  };

  /******************
    Game Prototype
  *******************/
  function Game() {
    this.ignoredElements = ['html', 'head', 'body', 'script', 'style', 'link', 'meta', 'br', 'hr'];
    this.fps = 50;
    this.comets = [];
    this.canvas = document.createElement('canvas');

    var keysPressed = {
      'Alt': false,
      'Win': false, 'OS': false,
      'c': false,
      'success': false
    };
    var eventsFuncs = {
      keydownEvent: function keydown(e) {
        if (e.key in Object.keys(keysPressed)) {
          keysPressed[e.key] = true;
          if (keysPressed['Alt'] &&
              (keysPressed['Win'] || keysPressed['OS']) &&
              keysPressed['c']) {
            keysPressed['success'] = true;
          }
        }
      },
      keyupEvent: function keyup(e) {
        if (e.key in Object.keys(keysPressed)) {
          keysPressed[e.key] = false;
          if (keysPressed['success']) {
            keysPressed['success'] = false;
          }
        }
      },
      clickEvent: function click(e) {
        if (e.button === 0) {
          if (keysPressed['success']) {
            var comet = new Comet(e.clientX, e.clientY);
            comet.spawn();
          }
        } else if (e.button === 2) {
          for (let i = 0, cometsLen = game.comets.length; i < cometsLen; i += 1) {
            if (game.comets[i].isCollide({ left: e.clientX, right: e.clientX, top: e.clientY, bottom: e.clientY })) {
              game.comets.splice(i, 1);
              break;
            }
          }
        }
      },
      resizeEvent: function resize(e) {
        function resizeCanvas() {
          this.canvas.style.display = 'none';
          var htmlSize = utility.htmlSize();
          this.canvas.setAttribute('width', htmlSize.width);
          this.canvas.setAttribute('height', htmlSize.height);
          resizeRunning = false;
        }

        if (!resizeRunning) {
          resizeRunning = true;
          if (window.requestAnimationFrame) {
            window.requestAnimationFrame(resizeCanvas);
          }
        }
      }
    };

    this.events = new Map();
    events.set('keydown', eventsFuncs.keydownEvent);
    events.set('keyup', eventsFuncs.keyupEvent);
    events.set('click', eventsFuncs.clickEvent);
    events.set('resize', eventsFuncs.resizeEvent);
  }

  Game.prototype = {
    init: function init() {
      for (let [eventName, eventFunc] of this.events.entries()) {
        $body.addEventListener(eventName, eventFunc);
      }
    },

    start: function start() {
      var htmlSize = utility.htmlSize();
      this.canvas.setAttribute('width', htmlSize.width);
      this.canvas.setAttribute('height', htmlSize.height);
      this.canvas.style.width = htmlSize.width + 'px';
      this.canvas.style.height = htmlSize.height + 'px';
      this.canvas.className = 'comet-canvas';
      document.body.appendChild(this.canvas);
      loop = setInterval(game.update, 1000 / this.fps);
    },

    update: function update() {
      this.comets.forEach(function (comet) {
        comet.move();
      });
    },

    end: function end() {
      clearInterval(loop);
      for (let [eventName, eventFunc] of this.events.entries()) {
        $body.removeEventListener(eventName, eventFunc);
      }
      document.body.removeChild(this.canvas);
    }
  };

  /******************
    Comet Prototype
  *******************/
  function Comet(x, y) {
    this.x = x;
    this.y = y;
    this.size = 5;
    this.shape = new Path2D();
  }

  Comet.prototype = {
    length: function length(point) {
      var xDiff, yDiff;
      xDiff = point.x - this.x;
      yDiff = point.y - this.y;
      return Math.sqrt(xDiff * xDiff + yDiff * yDiff);
    },

    angle: function angle(point) {
      var xDiff, yDiff;
      xDiff = point.x - this.x;
      yDiff = point.y - this.y;
      return Math.atan2(yDiff, xDiff);
    },

    move: function move() {
      // move towards denser elements

    },

    trail: function trail(rect) {
      //var rect = element.getBoundingClientRect();
      var isCollide =
        this.x > rect.left && this.y > rect.top &&
        this.x < rect.right + rect.width && this.y < rect.bottom + rect.height;

      if (isCollide) {
        // render comet trails by consuming colliding element (increase size and add colors)
      }
    },

    spawn: function spawn() {
      this.shape.arc(100, 35, 25, 0, 2 * Math.PI);
      this.shape.moveTo(this.x, this.y);
      game.comets.push(this);
    }
  };

  /******************
    Game Runner
  *******************/
  game = new Game();
  game.init();
  game.start();
}());
<commit_msg>revert es6 for compatability with chrome versions<commit_after>(function cometSpawner() {
  var $body = document.getElementsByTagName('body')[0],
      game, loop,
      resizeRunning = false;

  /******************
    Utility Object
  *******************/
  var utility = {
    htmlSize: function htmlSize() {
      return {
        width: document.documentElement.clientWidth,
        height: document.documentElement.clientHeight
      };
    },

    weightElements: function weightElements() {
      var items = document.body.getElementsByTagName('*'),
          i, itemsLen;

      for (i = 0, itemsLen = items.length; i < itemsLen; i += 1) {

      }
    }
  };

  /******************
    Game Prototype
  *******************/
  function Game() {
    this.ignoredElements = ['html', 'head', 'body', 'script', 'style', 'link', 'meta', 'br', 'hr'];
    this.fps = 50;
    this.comets = [];
    this.canvas = document.createElement('canvas');

    var keysPressed = {
      'Alt': false,
      'Win': false, 'OS': false,
      'c': false,
      'success': false
    };
    var eventsFuncs = {
      keydownEvent: function keydown(e) {
        if (e.key in Object.keys(keysPressed)) {
          keysPressed[e.key] = true;
          if (keysPressed['Alt'] &&
              (keysPressed['Win'] || keysPressed['OS']) &&
              keysPressed['c']) {
            keysPressed['success'] = true;
          }
        }
      },
      keyupEvent: function keyup(e) {
        if (e.key in Object.keys(keysPressed)) {
          keysPressed[e.key] = false;
          if (keysPressed['success']) {
            keysPressed['success'] = false;
          }
        }
      },
      clickEvent: function click(e) {
        if (e.button === 0) {
          if (keysPressed['success']) {
            var comet = new Comet(e.clientX, e.clientY);
            comet.spawn();
          }
        } else if (e.button === 2) {
          var i, cometsLen;
          for (i = 0, cometsLen = game.comets.length; i < cometsLen; i += 1) {
            if (game.comets[i].isCollide({ left: e.clientX, right: e.clientX, top: e.clientY, bottom: e.clientY })) {
              game.comets.splice(i, 1);
              break;
            }
          }
        }
      },
      resizeEvent: function resize(e) {
        function resizeCanvas() {
          this.canvas.style.display = 'none';
          var htmlSize = utility.htmlSize();
          this.canvas.setAttribute('width', htmlSize.width);
          this.canvas.setAttribute('height', htmlSize.height);
          resizeRunning = false;
        }

        if (!resizeRunning) {
          resizeRunning = true;
          if (window.requestAnimationFrame) {
            window.requestAnimationFrame(resizeCanvas);
          }
        }
      }
    };

    this.events = {
      'keydown': eventsFuncs.keydownEvent,
      'keyup': eventsFuncs.keyupEvent,
      'click': eventsFuncs.clickEvent,
      'resize': eventsFuncs.resizeEvent
    };
  }

  Game.prototype = {
    init: function init() {
      var eventsKeys = Object.keys(this.events),
          i, eventsLen, eventsKey;

      for (i = 0, eventsLen = eventsKeys.length; i < eventsLen; i += 1) {
        eventsKey = eventsKeys[i];
        $body.addEventListener(eventsKey, this.events[eventsKey]);
      }
    },

    start: function start() {
      var htmlSize = utility.htmlSize();
      this.canvas.setAttribute('width', htmlSize.width);
      this.canvas.setAttribute('height', htmlSize.height);
      this.canvas.style.width = htmlSize.width + 'px';
      this.canvas.style.height = htmlSize.height + 'px';
      this.canvas.className = 'comet-canvas';
      document.body.appendChild(this.canvas);
      //loop = setInterval(this.update, 1000 / this.fps);
    },

    update: function update() {
      this.comets.forEach(function (comet) {
        comet.move();
      });
    },

    end: function end() {
      clearInterval(loop);
      var eventsKeys = Object.keys(this.events),
          i, eventsLen, eventsKey;

      for (i = 0, eventsLen = eventsKeys.length; i < eventsLen; i += 1) {
        eventsKey = eventsKeys[i];
        $body.removeEventListener(eventsKey, this.events[eventsKey]);
      }
      document.body.removeChild(this.canvas);
    }
  };

  /******************
    Comet Prototype
  *******************/
  function Comet(x, y) {
    this.x = x;
    this.y = y;
    this.size = 5;
    this.shape = new Path2D();
  }

  Comet.prototype = {
    length: function length(point) {
      var xDiff, yDiff;
      xDiff = point.x - this.x;
      yDiff = point.y - this.y;
      return Math.sqrt(xDiff * xDiff + yDiff * yDiff);
    },

    angle: function angle(point) {
      var xDiff, yDiff;
      xDiff = point.x - this.x;
      yDiff = point.y - this.y;
      return Math.atan2(yDiff, xDiff);
    },

    move: function move() {
      // move towards denser elements

    },

    trail: function trail(rect) {
      //var rect = element.getBoundingClientRect();
      var isCollide =
        this.x > rect.left && this.y > rect.top &&
        this.x < rect.right + rect.width && this.y < rect.bottom + rect.height;

      if (isCollide) {
        // render comet trails by consuming colliding element (increase size and add colors)
      }
    },

    spawn: function spawn() {
      this.shape.arc(100, 35, 25, 0, 2 * Math.PI);
      this.shape.moveTo(this.x, this.y);
      game.comets.push(this);
    }
  };

  /******************
    Game Runner
  *******************/
  game = new Game();
  game.init();
  game.start();
}());
<|endoftext|>