<commit_before>/*
 * mdashboard.js: Marlin dashboard implementation
 */

window.onload = mInit;

var mTableCommonConfig = {
    'bFilter': false,
    'bInfo': false,
    'bPaginate': false,
    'bLengthChange': false
};

var mTableConfig = {
    'supervisors': {
	'domid': 'mSupervisorTable',
	'autolink': [ 0 ],
	'config': {
	    'aoColumns': [
		{ 'sTitle': 'Supervisor', 'sClass': 'mUuid' },
		{ 'sTitle': 'Started' },
		{ 'sTitle': 'AuthP' },
		{ 'sTitle': 'LocsP' },
		{ 'sTitle': 'DelsP' },
		{ 'sTitle': 'LRq' },
		{ 'sTitle': 'LRs' },
		{ 'sTitle': 'ARq' },
		{ 'sTitle': 'ARs' }
	    ]
	}
    },
    'jobs': {
	'domid': 'mJobTable',
	'autolink': [ 0 ],
	'config': {
	    'aoColumns': [
		{ 'sTitle': 'Jobid', 'sClass': 'mUuid' },
		{ 'sTitle': 'User' },
		{ 'sTitle': 'Started' },
		{ 'sTitle': 'Asgn' },
		{ 'sTitle': 'InProc' },
		{ 'sTitle': 'TasksDisp' },
		{ 'sTitle': 'TasksComm' },
		{ 'sTitle': 'Out' },
		{ 'sTitle': 'Retries' },
		{ 'sTitle': 'Err' }
	    ]
	}
    },
    'groups': {
	'domid': 'mRunningGroupsTable',
	'autolink': [ 0 ],
	'config': {
	    'aoColumns': [
		{ 'sTitle': 'Server' },
		{ 'sTitle': 'Jobid', 'sClass': 'mUuid' },
		{ 'sTitle': 'Ph' },
		{ 'sTitle': 'Kind' },
		{ 'sTitle': 'Qd' },
		{ 'sTitle': 'Run' },
		{ 'sTitle': 'Zones' },
		{ 'sTitle': 'Share' }
	    ]
	}
    },
    'streams': {
	'domid': 'mStreamsTable',
	'autolink': [ 0, 1 ],
	'config': {
	    'aoColumns': [
		{ 'sTitle': 'Server' },
		{ 'sTitle': 'Jobid / phase / machine', 'sClass': 'mUuid' },
		{ 'sTitle': 'Last task started' }
	    ]
	}
    }
};

/* DOM elements */
var spanUpdateTime;				/* last updated time */
var divErrorContainer;				/* error alert */

/* Application configuration */
var mRefreshInterval = 5000;			/* ms between refreshes */
var mServerUrl;					/* kang proxy server location */
if (window.location.hash)
	mServerUrl = window.location.hash.substr(1);
else
	mServerUrl = window.location.origin;

/* Application state */
var mTables = {};
var mZoneStates;
var mUpdateTime;
var mSnapshot;
var mDetails;
var mRefreshOkay = true;

function mInit()
{
	var k;

	for (k in mTableConfig)
		mTables[k] = new mTable(k, mTableConfig[k]);
	mZoneStates = new mZoneStateWidget('zonegrid', 'mZoneStates');
	spanUpdateTime = document.getElementById('mUpdateTime');
	divErrorContainer = document.getElementById('mErrorContainer');

	$('#tabs').tab();
	$('#inner-tabs').tab();

	mRedrawWorld();
	mRefresh();
}

function mRedrawWorld()
{
	var k;

	for (k in mTables)
		mTables[k].redraw();

	mZoneStates.redraw();
	spanUpdateTime.firstChild.nodeValue = mFormatDate(mUpdateTime);
}

function mShowError(message, extra)
{
	var div = $('
' + message + '
');

	if (divErrorContainer.firstChild) {
		divErrorContainer.replaceChild(
		    divErrorContainer.firstChild, div);
	} else {
		divErrorContainer.appendChild(div[0]);
	}
}

function mHideError()
{
	if (divErrorContainer.firstChild)
		divErrorContainer.removeChild(divErrorContainer.firstChild);
}

function mRefresh()
{
	$.ajax({
	    'url': mServerUrl + '/marlin',
	    'dataType': 'json',
	    'success': function (data) {
		mHideError();
		mLoadData(data);
		mRedrawWorld();
		mRefreshOkay = true;
		setTimeout(mRefresh, mRefreshInterval);
	    },
	    'error': function (data) {
		if (mRefreshOkay) {
			mShowError('Failed to refresh dashboard (will retry)');
			mRefreshOkay = false;
		}

		setTimeout(mRefresh, mRefreshInterval);
		console.error('failed to refresh dashboard:', data);
	    }
	});
}

function mLoadData(data)
{
	mUpdateTime = new Date();
	mSnapshot = data;
	mDetails = {};

	var svcs, rows, k, o, r, i, e;
	var rowbyagent = {};
	var zonedata = {};

	svcs = data.cs_objects['service'];

	if (data.cs_objects['agent']) {
		for (k in data.cs_objects['agent']) {
			o = data.cs_objects['agent'][k][0];
			mDetails[svcs[o['origin']][0]['ident']] = o;
			r = [
			    svcs[o['origin']][0]['ident'],
			    data.cs_objects['stats'][o['origin']][0]['started'].
			        substr(5, 14),
			    o['nTasks'],
			    o['slopDiskUsed'] + ' / ' +
			        o['slopDiskTotal'] + 'GB',
			    o['slopMemUsed'] + ' / ' +
			        o['slopMemTotal'] + 'MB',
			    0,	/* total nzones */
			    0,	/* nbusy */
			    0,	/* ninit */
			    0 	/* ndisabled */
			];
			rowbyagent[o['origin']] = r;
		}
	}

	if (data.cs_objects['zone']) {
		for (k in data.cs_objects['zone']) {
			o = data.cs_objects['zone'][k][0];
			r = rowbyagent[o['origin']];
			if (!r)
				continue;

			r[5]++;
			if (o['state'] == 'busy')
				r[6]++;
			else if (o['state'] == 'uninit')
				r[7]++;
			else if (o['state'] == 'disabled')
				r[8]++;
		}

		for (k in rowbyagent) {
			zonedata[k] = {
			    'i': 0,
			    'values': rowbyagent[k],
			    'data': new Array(rowbyagent[k][6]),
			    'label': svcs[k][0]['ident']
			};
		}

		for (k in data.cs_objects['zone']) {
			o = data.cs_objects['zone'][k][0];
			r = rowbyagent[o['origin']];
			if (!r)
				continue;

			e = zonedata[o['origin']];
			e['data'][e['i']++] = [ k, o['state'][0] ];
		}

		mZoneStates.zs_data = zonedata;
	}

	rows = [];
	if (data.cs_objects['worker']) {
		for (k in data.cs_objects['worker']) {
			o = data.cs_objects['worker'][k][0];
			mDetails[o['conf']['instanceUuid']] = o;
			r = [
			    o['conf']['instanceUuid'],
			    data.cs_objects['stats'][o['origin']][0]['started'].
			        substr(5, 14),
			    o['nLocs'],
			    o['nLocIn'],
			    o['nLocOut'],
			    o['nAuths'],
			    o['nAuthsIn'],
			    o['nAuthsOut'],
			    o['nDels']
			];
			delete (o['conf']);
			rows.push(r);
		}
	}
	mTables['supervisors'].t_rows = rows;

	rows = [];
	if (data.cs_objects['job']) {
		for (k in data.cs_objects['job']) {
			/* Find the worker "job" record, not the agent ones. */
			for (i = 0; i < data.cs_objects['job'][k].length; i++) {
				o = data.cs_objects['job'][k][i];
				if (svcs[o['origin']][0]['component'] ==
				    'jobworker')
					break;
			}

			if (svcs[o['origin']][0]['component'] != 'jobworker')
				continue;

			mDetails[o['record']['jobId']] = o['record'];

			r = [
			    o['record']['jobId'],
			    o['record']['auth']['login'],
			    o['record']['timeCreated'].substr(5, 14),
			    o['record']['stats']['nAssigns'],
			    o['record']['stats']['nInputsRead'],
			    o['record']['stats']['nTasksDispatched'],
			    o['record']['stats']['nTasksCommittedOk'] +
			        o['record']['stats']['nTasksCommittedFail'],
			    o['record']['stats']['nJobOutputs'],
			    o['record']['stats']['nRetries'],
			    o['record']['stats']['nErrors']
			];

			rows.push(r);
		}
	}
	mTables['jobs'].t_rows = rows;

	rows = [];
	if (data.cs_objects['taskgroup']) {
		for (k in data.cs_objects['taskgroup']) {
			data.cs_objects['taskgroup'][k].forEach(function (g) {
				o = g;
				if (!svcs[o['origin']])
					return;
				r = [
				    svcs[o['origin']][0]['ident'],
				    o['jobid'],
				    o['phasei'],
				    o['phase'] ?
					(o['phase']['type'] +
				        (o['phase']['type'] == 'reduce' ?
				        ' (' + (o['phase']['count'] || 1) +
					')' : '')) : 'unknown',
				    o['ntasks'],
				    o['nrunning'],
				    o['nstreams'],
				    o['share']
				];
				rows.push(r);
			});
		}
	}
	mTables['groups'].t_rows = rows;

	rows = [];
	if (data.cs_objects['taskstream']) {
		for (k in data.cs_objects['taskstream']) {
			data.cs_objects['taskstream'][k].forEach(function (g) {
				o = g;
				if (!svcs[o['origin']])
					return;
				r = [
				    svcs[o['origin']][0]['ident'],
				    o['id'],
				    o['taskStart'] || 'N/A'
				];
				rows.push(r);
				mDetails[o['id']] = o;
			});
		}
	}
	mTables['streams'].t_rows = rows;
}

function mTable(key, conf)
{
	this.t_key = key;
	this.t_elt = document.getElementById(conf['domid']);
	this.t_rows = [];
	this.t_config = {};
	this.t_drawn = false;
	this.t_autolink = conf['autolink'] || [];

	var k;
	for (k in mTableCommonConfig)
		this.t_config[k] = mTableCommonConfig[k];
	for (k in conf['config'])
		this.t_config[k] = conf['config'][k];
}

mTable.prototype.redraw = function ()
{
	if (this.t_drawn)
		$(this.t_elt).dataTable().fnDestroy();

	var t = this;
	this.t_autolink.forEach(function (col) {
		t.t_rows.forEach(function (row) {
			row[col] = '' + row[col] + '';
		});
	});

	var conf = {};
	for (var k in this.t_config)
		conf[k] = this.t_config[k];
	conf['aaData'] = this.t_rows;
	$(this.t_elt).dataTable(conf);
	this.t_drawn = true;

};

function mFormatDate(time)
{
	if (!time)
		return ('never');

	var h, m, s;
	h = time.getHours();
	m = time.getMinutes();
	if (m < 10)
		m = '0' + m;
	s = time.getSeconds();
	if (s < 10)
		s = '0' + s;

	return (h + ':' + m + ':' + s);
}

function mZoneStateWidget(key, domid)
{
	this.zs_data = {};
	this.zs_table = new mTable(key, {
	    'domid': domid,
	    'autolink': [ 0 ],
	    'config': {
	        'aoColumns': [
		    { 'sTitle': 'Agent' },
		    { 'sTitle': 'Started' },
		    { 'sTitle': 'Tasks' },
		    { 'sTitle': 'Disk slop used' },
		    { 'sTitle': 'Mem slop used' },
		    { 'sTitle': 'Z' },
		    { 'sTitle': 'B' },
		    { 'sTitle': 'R' },
		    { 'sTitle': 'D' },
		    { 'sTitle': 'Zones' }
		]
	    }
	});
}

mZoneStateWidget.prototype.redraw = function ()
{
	var data = this.zs_data;
	var rows, div, wrap, k, row;

	rows = [];

	for (k in data) {
		wrap = document.createElement('div');
		div = wrap.appendChild(document.createElement('div'));
		div.className = 'mZoneRow';

		data[k]['data'].forEach(function (s, i) {
			var elt = div.appendChild(
			    document.createElement('div'));
			elt.className = 'mZoneState' + s[1].toUpperCase();
			elt.title = s[0];

			if (i % 32 == 31) {
				div = wrap.appendChild(
				    document.createElement('div'));
				div.className = 'mZoneRow';
			}
		});

		row = data[k]['values'].slice(0);
		row.push(wrap.innerHTML);
		rows.push(row);
	}

	this.zs_table.t_rows = rows;
	this.zs_table.redraw();
};

function mDetailShow(id)
{
	var html = [
	    '
',
	    '
',
	    '<button type="button" class="close" data-dismiss="modal" ' +
	        'aria-hidden="true">×</button>',
	    '
Details for ' + id + '
',
	    '
',
	    '
',
	    '
',
	    JSON.stringify(mDetails[id], false, 4),
	    '
',
	    '
',
	    '
',
	    '<button class="btn" data-dismiss="modal" aria-hidden="true">' +
	        'Close</button>',
	    '
',
	    '
'
	].join('\n');

	$(html).modal({ 'show': true });
}
<commit_msg>add count of zones used by each job<commit_after>/*
 * mdashboard.js: Marlin dashboard implementation
 */

window.onload = mInit;

var mTableCommonConfig = {
    'bFilter': false,
    'bInfo': false,
    'bPaginate': false,
    'bLengthChange': false
};

var mTableConfig = {
    'supervisors': {
	'domid': 'mSupervisorTable',
	'autolink': [ 0 ],
	'config': {
	    'aoColumns': [
		{ 'sTitle': 'Supervisor', 'sClass': 'mUuid' },
		{ 'sTitle': 'Started' },
		{ 'sTitle': 'AuthP' },
		{ 'sTitle': 'LocsP' },
		{ 'sTitle': 'DelsP' },
		{ 'sTitle': 'LRq' },
		{ 'sTitle': 'LRs' },
		{ 'sTitle': 'ARq' },
		{ 'sTitle': 'ARs' }
	    ]
	}
    },
    'jobs': {
	'domid': 'mJobTable',
	'autolink': [ 0 ],
	'config': {
	    'aoColumns': [
		{ 'sTitle': 'Jobid', 'sClass': 'mUuid' },
		{ 'sTitle': 'User' },
		{ 'sTitle': 'Started' },
		{ 'sTitle': 'Zones' },
		{ 'sTitle': 'Asgn' },
		{ 'sTitle': 'InProc' },
		{ 'sTitle': 'TasksDisp' },
		{ 'sTitle': 'TasksComm' },
		{ 'sTitle': 'Out' },
		{ 'sTitle': 'Retries' },
		{ 'sTitle': 'Err' }
	    ]
	}
    },
    'groups': {
	'domid': 'mRunningGroupsTable',
	'autolink': [ 0 ],
	'config': {
	    'aoColumns': [
		{ 'sTitle': 'Server' },
		{ 'sTitle': 'Jobid', 'sClass': 'mUuid' },
		{ 'sTitle': 'Ph' },
		{ 'sTitle': 'Kind' },
		{ 'sTitle': 'Qd' },
		{ 'sTitle': 'Run' },
		{ 'sTitle': 'Zones' },
		{ 'sTitle': 'Share' }
	    ]
	}
    },
    'streams': {
	'domid': 'mStreamsTable',
	'autolink': [ 0, 1 ],
	'config': {
	    'aoColumns': [
		{ 'sTitle': 'Server' },
		{ 'sTitle': 'Jobid / phase / machine', 'sClass': 'mUuid' },
		{ 'sTitle': 'Last task started' }
	    ]
	}
    }
};

/* DOM elements */
var spanUpdateTime;				/* last updated time */
var divErrorContainer;				/* error alert */

/* Application configuration */
var mRefreshInterval = 5000;			/* ms between refreshes */
var mServerUrl;					/* kang proxy server location */
if (window.location.hash)
	mServerUrl = window.location.hash.substr(1);
else
	mServerUrl = window.location.origin;

/* Application state */
var mTables = {};
var mZoneStates;
var mUpdateTime;
var mSnapshot;
var mDetails;
var mRefreshOkay = true;

function mInit()
{
	var k;

	for (k in mTableConfig)
		mTables[k] = new mTable(k, mTableConfig[k]);
	mZoneStates = new mZoneStateWidget('zonegrid', 'mZoneStates');
	spanUpdateTime = document.getElementById('mUpdateTime');
	divErrorContainer = document.getElementById('mErrorContainer');

	$('#tabs').tab();
	$('#inner-tabs').tab();

	mRedrawWorld();
	mRefresh();
}

function mRedrawWorld()
{
	var k;

	for (k in mTables)
		mTables[k].redraw();

	mZoneStates.redraw();
	spanUpdateTime.firstChild.nodeValue = mFormatDate(mUpdateTime);
}

function mShowError(message, extra)
{
	var div = $('
' + message + '
');

	if (divErrorContainer.firstChild) {
		divErrorContainer.replaceChild(
		    divErrorContainer.firstChild, div);
	} else {
		divErrorContainer.appendChild(div[0]);
	}
}

function mHideError()
{
	if (divErrorContainer.firstChild)
		divErrorContainer.removeChild(divErrorContainer.firstChild);
}

function mRefresh()
{
	$.ajax({
	    'url': mServerUrl + '/marlin',
	    'dataType': 'json',
	    'success': function (data) {
		mHideError();
		mLoadData(data);
		mRedrawWorld();
		mRefreshOkay = true;
		setTimeout(mRefresh, mRefreshInterval);
	    },
	    'error': function (data) {
		if (mRefreshOkay) {
			mShowError('Failed to refresh dashboard (will retry)');
			mRefreshOkay = false;
		}

		setTimeout(mRefresh, mRefreshInterval);
		console.error('failed to refresh dashboard:', data);
	    }
	});
}

function mLoadData(data)
{
	mUpdateTime = new Date();
	mSnapshot = data;
	mDetails = {};

	var svcs, rows, k, o, r, i, e;
	var rowbyagent = {};
	var zonedata = {};
	var rowbyjob = {};

	svcs = data.cs_objects['service'];

	if (data.cs_objects['agent']) {
		for (k in data.cs_objects['agent']) {
			o = data.cs_objects['agent'][k][0];
			mDetails[svcs[o['origin']][0]['ident']] = o;
			r = [
			    svcs[o['origin']][0]['ident'],
			    data.cs_objects['stats'][o['origin']][0]['started'].
			        substr(5, 14),
			    o['nTasks'],
			    o['slopDiskUsed'] + ' / ' +
			        o['slopDiskTotal'] + 'GB',
			    o['slopMemUsed'] + ' / ' +
			        o['slopMemTotal'] + 'MB',
			    0,	/* total nzones */
			    0,	/* nbusy */
			    0,	/* ninit */
			    0 	/* ndisabled */
			];
			rowbyagent[o['origin']] = r;
		}
	}

	if (data.cs_objects['zone']) {
		for (k in data.cs_objects['zone']) {
			o = data.cs_objects['zone'][k][0];
			r = rowbyagent[o['origin']];
			if (!r)
				continue;

			r[5]++;
			if (o['state'] == 'busy')
				r[6]++;
			else if (o['state'] == 'uninit')
				r[7]++;
			else if (o['state'] == 'disabled')
				r[8]++;
		}

		for (k in rowbyagent) {
			zonedata[k] = {
			    'i': 0,
			    'values': rowbyagent[k],
			    'data': new Array(rowbyagent[k][6]),
			    'label': svcs[k][0]['ident']
			};
		}

		for (k in data.cs_objects['zone']) {
			o = data.cs_objects['zone'][k][0];
			r = rowbyagent[o['origin']];
			if (!r)
				continue;

			e = zonedata[o['origin']];
			e['data'][e['i']++] = [ k, o['state'][0] ];
		}

		mZoneStates.zs_data = zonedata;
	}

	rows = [];
	if (data.cs_objects['worker']) {
		for (k in data.cs_objects['worker']) {
			o = data.cs_objects['worker'][k][0];
			mDetails[o['conf']['instanceUuid']] = o;
			r = [
			    o['conf']['instanceUuid'],
			    data.cs_objects['stats'][o['origin']][0]['started'].
			        substr(5, 14),
			    o['nLocs'],
			    o['nLocIn'],
			    o['nLocOut'],
			    o['nAuths'],
			    o['nAuthsIn'],
			    o['nAuthsOut'],
			    o['nDels']
			];
			delete (o['conf']);
			rows.push(r);
		}
	}
	mTables['supervisors'].t_rows = rows;

	rows = [];
	if (data.cs_objects['job']) {
		for (k in data.cs_objects['job']) {
			/* Find the worker "job" record, not the agent ones. */
			for (i = 0; i < data.cs_objects['job'][k].length; i++) {
				o = data.cs_objects['job'][k][i];
				if (svcs[o['origin']][0]['component'] ==
				    'jobworker')
					break;
			}

			if (svcs[o['origin']][0]['component'] != 'jobworker')
				continue;

			mDetails[o['record']['jobId']] = o['record'];

			r = [
			    o['record']['jobId'],
			    o['record']['auth']['login'],
			    o['record']['timeCreated'].substr(5, 14),
			    0,
			    o['record']['stats']['nAssigns'],
			    o['record']['stats']['nInputsRead'],
			    o['record']['stats']['nTasksDispatched'],
			    o['record']['stats']['nTasksCommittedOk'] +
			        o['record']['stats']['nTasksCommittedFail'],
			    o['record']['stats']['nJobOutputs'],
			    o['record']['stats']['nRetries'],
			    o['record']['stats']['nErrors']
			];
			rowbyjob[o['record']['jobId']] = r;

			rows.push(r);
		}
	}
	mTables['jobs'].t_rows = rows;

	rows = [];
	if (data.cs_objects['taskgroup']) {
		for (k in data.cs_objects['taskgroup']) {
			data.cs_objects['taskgroup'][k].forEach(function (g) {
				o = g;
				if (!svcs[o['origin']])
					return;
				r = [
				    svcs[o['origin']][0]['ident'],
				    o['jobid'],
				    o['phasei'],
				    o['phase'] ?
					(o['phase']['type'] +
				        (o['phase']['type'] == 'reduce' ?
				        ' (' + (o['phase']['count'] || 1) +
					')' : '')) : 'unknown',
				    o['ntasks'],
				    o['nrunning'],
				    o['nstreams'],
				    o['share']
				];
				rows.push(r);
				rowbyjob[o['jobid']][3] += o['nrunning'];
			});
		}
	}
	mTables['groups'].t_rows = rows;

	rows = [];
	if (data.cs_objects['taskstream']) {
		for (k in data.cs_objects['taskstream']) {
			data.cs_objects['taskstream'][k].forEach(function (g) {
				o = g;
				if (!svcs[o['origin']])
					return;
				r = [
				    svcs[o['origin']][0]['ident'],
				    o['id'],
				    o['taskStart'] || 'N/A'
				];
				rows.push(r);
				mDetails[o['id']] = o;
			});
		}
	}
	mTables['streams'].t_rows = rows;
}

function mTable(key, conf)
{
	this.t_key = key;
	this.t_elt = document.getElementById(conf['domid']);
	this.t_rows = [];
	this.t_config = {};
	this.t_drawn = false;
	this.t_autolink = conf['autolink'] || [];

	var k;
	for (k in mTableCommonConfig)
		this.t_config[k] = mTableCommonConfig[k];
	for (k in conf['config'])
		this.t_config[k] = conf['config'][k];
}

mTable.prototype.redraw = function ()
{
	if (this.t_drawn)
		$(this.t_elt).dataTable().fnDestroy();

	var t = this;
	this.t_autolink.forEach(function (col) {
		t.t_rows.forEach(function (row) {
			row[col] = '' + row[col] + '';
		});
	});

	var conf = {};
	for (var k in this.t_config)
		conf[k] = this.t_config[k];
	conf['aaData'] = this.t_rows;
	$(this.t_elt).dataTable(conf);
	this.t_drawn = true;

};

function mFormatDate(time)
{
	if (!time)
		return ('never');

	var h, m, s;
	h = time.getHours();
	m = time.getMinutes();
	if (m < 10)
		m = '0' + m;
	s = time.getSeconds();
	if (s < 10)
		s = '0' + s;

	return (h + ':' + m + ':' + s);
}

function mZoneStateWidget(key, domid)
{
	this.zs_data = {};
	this.zs_table = new mTable(key, {
	    'domid': domid,
	    'autolink': [ 0 ],
	    'config': {
	        'aoColumns': [
		    { 'sTitle': 'Agent' },
		    { 'sTitle': 'Started' },
		    { 'sTitle': 'Tasks' },
		    { 'sTitle': 'Disk slop used' },
		    { 'sTitle': 'Mem slop used' },
		    { 'sTitle': 'Z' },
		    { 'sTitle': 'B' },
		    { 'sTitle': 'R' },
		    { 'sTitle': 'D' },
		    { 'sTitle': 'Zones' }
		]
	    }
	});
}

mZoneStateWidget.prototype.redraw = function ()
{
	var data = this.zs_data;
	var rows, div, wrap, k, row;

	rows = [];

	for (k in data) {
		wrap = document.createElement('div');
		div = wrap.appendChild(document.createElement('div'));
		div.className = 'mZoneRow';

		data[k]['data'].forEach(function (s, i) {
			var elt = div.appendChild(
			    document.createElement('div'));
			elt.className = 'mZoneState' + s[1].toUpperCase();
			elt.title = s[0];

			if (i % 32 == 31) {
				div = wrap.appendChild(
				    document.createElement('div'));
				div.className = 'mZoneRow';
			}
		});

		row = data[k]['values'].slice(0);
		row.push(wrap.innerHTML);
		rows.push(row);
	}

	this.zs_table.t_rows = rows;
	this.zs_table.redraw();
};

function mDetailShow(id)
{
	var html = [
	    '
',
	    '
',
	    '<button type="button" class="close" data-dismiss="modal" ' +
	        'aria-hidden="true">×</button>',
	    '
Details for ' + id + '
',
	    '
',
	    '
',
	    '
',
	    JSON.stringify(mDetails[id], false, 4),
	    '
',
	    '
',
	    '
',
	    '<button class="btn" data-dismiss="modal" aria-hidden="true">' +
	        'Close</button>',
	    '
',
	    '
'
	].join('\n');

	$(html).modal({ 'show': true });
}
<|endoftext|>


<commit_before>/**
 * @author      Emma Barme
 * @version     0.1
 */

/*
 * The array holding the Slots representing the beverages in the consumer view
 */
var beveragesSlots = [];

/*
 * A dictionnary of the distinct beverages present in the machine with name of the beverage as key and id as value
 */
var availableBevIds = {};

/**
 * Constructor for the Slot object
 * Represents a slot in the machine, with the information necessary for its display, attached to it's corresponding html object
 */
function Slot(beer_id, name, price, amount, category, alcohol, displayBlock) {
    'use strict';

    var beerId = beer_id, //The id of the beverage occupying the slot
        name = name, //The screen name of the beverage
        price = price, //The price of the beverage
        amount = amount, //The amount of bottles of the beverages in the slot
        type = category, //The type of the beverage,
        alcohol = alcohol, //True if the drink contains alcohol
        display = displayBlock, //The DOM element corresponding to the slot
        available = true; //Whether the drink is currently available or filtered out

    /**
     * Getter of alcohol
     * @return alcohol
     */
    this.isAlcoholic = function() {
        return alcohol;
    }

    /**
     * @return true is amount is 0
     */
    this.isEmpty = function() {
        return amount == 0;
    }

    /**
     * Display the information for the beverage
     */
    this.displayInfo = function() {
        var infoDisplay = display.getElementsByClassName("slotLeft")[0];
        infoDisplay.getElementsByClassName("drinkName")[0].textContent = name;
        infoDisplay.getElementsByClassName("price")[0].textContent = price + " kr";
        infoDisplay.getElementsByClassName("stock")[0].textContent = amount;
        this.fetchImage();
    }

    /**
     * Fetch the image corresponding to the beverage category and set it as the display child image
     */
    this.fetchImage = function() {
        display.getElementsByClassName("slotElement")[0].style.backgroundImage = 'url("./../resources/img/' + category + '.png")';
        display.getElementsByClassName("slotElement")[0].title = category.replace("_", " "); //Information pop-up + voice-over
    }

    /**
     * Make the drink available is the consumer view by ungreying it and making it draggable
     */
    this.makeAvailable = function() {
        available = true;
        $(display).fadeTo(0, 1);
        $($(display).find("div")[0]).draggable().draggable('enable');
    }

    /**
     * Make the drink unavailable is the consumer view by greying it and making it non-draggable
     */
    this.makeUnavailable = function() {
        available = false;
        $(display).fadeTo(0, 0.4);
        $($(display).find("div")[0]).draggable().draggable('disable');
    }

    /**
     * Update the quantity of a beverage
     * @param newQuantity The new quantity of beverage in this slot
     */
    this.updateSlotQuantity = function(newQuantity) {
        var infoDisplay = display.getElementsByClassName("slotLeft")[0];
        amount = newQuantity;
        infoDisplay.getElementsByClassName("stock")[0].textContent = amount;

        //If the slot becomes empty
        if (this.isEmpty()) {
            this.makeUnavailable();
        } else {
            this.makeAvailable();
        }
    }

    /**
     * Wire the slot display to react to a double click and be added to the cart
     */
    display.ondblclick = function() {
        addProduct(beerId, name, price);
    }
}

/**
 * Populate the table of beverages in the consumer view
 * @param displayAlco True if the alcoholic drinks should be available
 * @param displaySoft True if the non-acoholic drinks should be available
 */
function populateSlotsConsumer(displayAlco, displaySoft) {
    'use strict';
    var beveragesTable = document.getElementById("beveragesTable"), //The table to populate with the drinks
        api = new APIConnect, //The api connection to get the machine content
        i, //Loop index
        j, //Loop index
        rows, //Rows of beveragesTable
        cells, //Cells of the beveragesTable
        displayBlock, //DOM-element in which to display the slot info
        allBevButton = document.getElementById("allBevButton"), //Filter button to make all drinks available
        alcoholOnlyButton = document.getElementById("alcoDrinksButton"), //Filter button to make only alcoholic drinks available
        softOnlyButton = document.getElementById("softDrinksButton"); //Filter button to make only soft available

    //Fetch the content and create a Slot object to hold it
    api.fetchContent(function(machineContent) {
        var beverage,
            beverageInfo,
            index;

        //For each slot in the machine
        rows = beveragesTable.getElementsByTagName("tr");
        for (i = 0; i < rows.length; i++) {
            cells = rows[i].getElementsByTagName("td");
            //  cells = rows[i].getElementsByClassName("product");
            //  console.log(cells);
            for (j = 0; j < cells.length; j++) {
                index = i * cells.length + j;
                beverageInfo = machineContent[index];
                beverage = new Slot(beverageInfo.beer_id, beverageInfo.name, beverageInfo.price, beverageInfo.amount, beverageInfo.category, beverageInfo.alcoholic, cells[j]);
                //Send the information to the display
                beverage.displayInfo();
                //    console.log(beverage);
                beveragesSlots.push(beverage);
                //Add the beverage in the dictionnary of available distinct drinks
                availableBevIds[beverageInfo.name] = beverageInfo.beer_id;
            }
        }
    });
    //Initial filter of drinks
    filterAlcoholDrinks(displayAlco, displaySoft);

    //Wire the filter buttons
    allBevButton.onclick = function() {
        console.log("All");
        filterAlcoholDrinks(true, true);
    };
    softOnlyButton.onclick = function() {
        console.log("Soft only");
        filterAlcoholDrinks(false, true);
    };
    alcoholOnlyButton.onclick = function() {
        console.log("Alco only");
        filterAlcoholDrinks(true, false);
    };
}

/**
 * Extract the category of the beverage from the information provided by the database
 * @param providedInfo The drink category string
 * @return An array with a string with the category of the beverage and a boolean indicating whether or not the drink is alcoholic
 * Possible values for the category: beer | lager | stout | ale | soft | cider | white_wine | red_wine
 */
function beverageCategory(providedInfo) {
    'use strict';
    var category = "beer",
        patterns = { red_wine: /rött vin/i, white_wine: /vitt vin/i, ale: /ale/i, stout: /stout/i, lager: /lager/i, cider: /cider/i, beer: /öl/i, soft: /alkoholfritt/i },
        key,
        alcoholFreePattern = /alkoholfritt/i,
        alcoholic = !alcoholFreePattern.test(providedInfo);

    for (key in patterns) {
        if (patterns.hasOwnProperty(key)) {
            if (patterns[key].test(providedInfo)) {
                category = key;
                break;
            }
        }
    }

    return [category, alcoholic];
}

/**
 * Manages the filtering out of drinks in the consumer view
 * @param displayAlco True if the alcoholic drinks should be available
 * @param displaySoft True if the non-acoholic drinks should be available
 */
function filterAlcoholDrinks(displayAlco, displaySoft) {
    'use strict';
    var i = 0,
        slot;
    //For each beverage
    for (; i < beveragesSlots.length; i++) {
        slot = beveragesSlots[i];
        //If the slot is empty
        if (slot.isEmpty()) {
            slot.makeUnavailable();
            continue;
        }

        //If the beverage is alcoholic
        if (slot.isAlcoholic()) {
            if (displayAlco) {
                slot.makeAvailable();
            } else {
                slot.makeUnavailable();
            }
            continue;
        }

        //If the beverage is non-acoholic
        if (displaySoft) {
            slot.makeAvailable();
        } else {
            slot.makeUnavailable();
        }
    }

    //Highlight the selected filtering button
    if (displayAlco) {
        if (displaySoft) {
            document.getElementById("allBevButton").className = "selected";
            document.getElementById("alcoDrinksButton").className = "";
            document.getElementById("softDrinksButton").className = "";
        } else {
            document.getElementById("allBevButton").className = "";
            document.getElementById("alcoDrinksButton").className = "selected";
            document.getElementById("softDrinksButton").className = "";
        }
    } else {
        document.getElementById("allBevButton").className = "";
        document.getElementById("alcoDrinksButton").className = "";
        document.getElementById("softDrinksButton").className = "selected";
    }
}<commit_msg>When beverage is filtered it isn't double clickable<commit_after>/**
 * @author      Emma Barme
 * @version     0.1
 */

/*
 * The array holding the Slots representing the beverages in the consumer view
 */
var beveragesSlots = [];

/*
 * A dictionnary of the distinct beverages present in the machine with name of the beverage as key and id as value
 */
var availableBevIds = {};

/**
 * Constructor for the Slot object
 * Represents a slot in the machine, with the information necessary for its display, attached to it's corresponding html object
 */
function Slot(beer_id, name, price, amount, category, alcohol, displayBlock) {
    'use strict';

    var beerId = beer_id, //The id of the beverage occupying the slot
        name = name, //The screen name of the beverage
        price = price, //The price of the beverage
        amount = amount, //The amount of bottles of the beverages in the slot
        type = category, //The type of the beverage,
        alcohol = alcohol, //True if the drink contains alcohol
        display = displayBlock, //The DOM element corresponding to the slot
        available = true; //Whether the drink is currently available or filtered out

    /**
     * Getter of alcohol
     * @return alcohol
     */
    this.isAlcoholic = function() {
        return alcohol;
    }

    /**
     * @return true is amount is 0
     */
    this.isEmpty = function() {
        return amount == 0;
    }

    /**
     * Display the information for the beverage
     */
    this.displayInfo = function() {
        var infoDisplay = display.getElementsByClassName("slotLeft")[0];
        infoDisplay.getElementsByClassName("drinkName")[0].textContent = name;
        infoDisplay.getElementsByClassName("price")[0].textContent = price + " kr";
        infoDisplay.getElementsByClassName("stock")[0].textContent = amount;
        this.fetchImage();
    }

    /**
     * Fetch the image corresponding to the beverage category and set it as the display child image
     */
    this.fetchImage = function() {
        display.getElementsByClassName("slotElement")[0].style.backgroundImage = 'url("./../resources/img/' + category + '.png")';
        display.getElementsByClassName("slotElement")[0].title = category.replace("_", " "); //Information pop-up + voice-over
    }

    /**
     * Make the drink available is the consumer view by ungreying it and making it draggable
     */
    this.makeAvailable = function() {
        available = true;
        $(display).fadeTo(0, 1);
        $($(display).find("div")[0]).draggable().draggable('enable');
    }

    /**
     * Make the drink unavailable is the consumer view by greying it and making it non-draggable
     */
    this.makeUnavailable = function() {
        available = false;
        $(display).fadeTo(0, 0.4);
        $($(display).find("div")[0]).draggable().draggable('disable');
    }

    /**
     * Update the quantity of a beverage
     * @param newQuantity The new quantity of beverage in this slot
     */
    this.updateSlotQuantity = function(newQuantity) {
        var infoDisplay = display.getElementsByClassName("slotLeft")[0];
        amount = newQuantity;
        infoDisplay.getElementsByClassName("stock")[0].textContent = amount;

        //If the slot becomes empty
        if (this.isEmpty()) {
            this.makeUnavailable();
        } else {
            this.makeAvailable();
        }
    }

    /**
     * Wire the slot display to react to a double click and be added to the cart
     */
    display.ondblclick = function() {
        if (available) {
            addProduct(beerId, name, price);
        }
    }
}

/**
 * Populate the table of beverages in the consumer view
 * @param displayAlco True if the alcoholic drinks should be available
 * @param displaySoft True if the non-acoholic drinks should be available
 */
function populateSlotsConsumer(displayAlco, displaySoft) {
    'use strict';
    var beveragesTable = document.getElementById("beveragesTable"), //The table to populate with the drinks
        api = new APIConnect, //The api connection to get the machine content
        i, //Loop index
        j, //Loop index
        rows, //Rows of beveragesTable
        cells, //Cells of the beveragesTable
        displayBlock, //DOM-element in which to display the slot info
        allBevButton = document.getElementById("allBevButton"), //Filter button to make all drinks available
        alcoholOnlyButton = document.getElementById("alcoDrinksButton"), //Filter button to make only alcoholic drinks available
        softOnlyButton = document.getElementById("softDrinksButton"); //Filter button to make only soft available

    //Fetch the content and create a Slot object to hold it
    api.fetchContent(function(machineContent) {
        var beverage,
            beverageInfo,
            index;

        //For each slot in the machine
        rows = beveragesTable.getElementsByTagName("tr");
        for (i = 0; i < rows.length; i++) {
            cells = rows[i].getElementsByTagName("td");
            //  cells = rows[i].getElementsByClassName("product");
            //  console.log(cells);
            for (j = 0; j < cells.length; j++) {
                index = i * cells.length + j;
                beverageInfo = machineContent[index];
                beverage = new Slot(beverageInfo.beer_id, beverageInfo.name, beverageInfo.price, beverageInfo.amount, beverageInfo.category, beverageInfo.alcoholic, cells[j]);
                //Send the information to the display
                beverage.displayInfo();
                //    console.log(beverage);
                beveragesSlots.push(beverage);
                //Add the beverage in the dictionnary of available distinct drinks
                availableBevIds[beverageInfo.name] = beverageInfo.beer_id;
            }
        }
    });
    //Initial filter of drinks
    filterAlcoholDrinks(displayAlco, displaySoft);

    //Wire the filter buttons
    allBevButton.onclick = function() {
        console.log("All");
        filterAlcoholDrinks(true, true);
    };
    softOnlyButton.onclick = function() {
        console.log("Soft only");
        filterAlcoholDrinks(false, true);
    };
    alcoholOnlyButton.onclick = function() {
        console.log("Alco only");
        filterAlcoholDrinks(true, false);
    };
}

/**
 * Extract the category of the beverage from the information provided by the database
 * @param providedInfo The drink category string
 * @return An array with a string with the category of the beverage and a boolean indicating whether or not the drink is alcoholic
 * Possible values for the category: beer | lager | stout | ale | soft | cider | white_wine | red_wine
 */
function beverageCategory(providedInfo) {
    'use strict';
    var category = "beer",
        patterns = { red_wine: /rött vin/i, white_wine: /vitt vin/i, ale: /ale/i, stout: /stout/i, lager: /lager/i, cider: /cider/i, beer: /öl/i, soft: /alkoholfritt/i },
        key,
        alcoholFreePattern = /alkoholfritt/i,
        alcoholic = !alcoholFreePattern.test(providedInfo);

    for (key in patterns) {
        if (patterns.hasOwnProperty(key)) {
            if (patterns[key].test(providedInfo)) {
                category = key;
                break;
            }
        }
    }

    return [category, alcoholic];
}

/**
 * Manages the filtering out of drinks in the consumer view
 * @param displayAlco True if the alcoholic drinks should be available
 * @param displaySoft True if the non-acoholic drinks should be available
 */
function filterAlcoholDrinks(displayAlco, displaySoft) {
    'use strict';
    var i = 0,
        slot;
    //For each beverage
    for (; i < beveragesSlots.length; i++) {
        slot = beveragesSlots[i];
        //If the slot is empty
        if (slot.isEmpty()) {
            slot.makeUnavailable();
            continue;
        }

        //If the beverage is alcoholic
        if (slot.isAlcoholic()) {
            if (displayAlco) {
                slot.makeAvailable();
            } else {
                slot.makeUnavailable();
            }
            continue;
        }

        //If the beverage is non-acoholic
        if (displaySoft) {
            slot.makeAvailable();
        } else {
            slot.makeUnavailable();
        }
    }

    //Highlight the selected filtering button
    if (displayAlco) {
        if (displaySoft) {
            document.getElementById("allBevButton").className = "selected";
            document.getElementById("alcoDrinksButton").className = "";
            document.getElementById("softDrinksButton").className = "";
        } else {
            document.getElementById("allBevButton").className = "";
            document.getElementById("alcoDrinksButton").className = "selected";
            document.getElementById("softDrinksButton").className = "";
        }
    } else {
        document.getElementById("allBevButton").className = "";
        document.getElementById("alcoDrinksButton").className = "";
        document.getElementById("softDrinksButton").className = "selected";
    }
}<|endoftext|>


<commit_before>/**
 * @author      Emma Barme
 * @version     0.1
 */

/*
 * The array holding the Slots representing the beverages in the consumer view.
 */
var beveragesSlots = [];

/**
 * Constructor for the Slot object
 * Represents a slot in the machine, with the information necessary for its display, attached to it's corresponding html object
 */
function Slot(beer_id, name, price, amount, category, alcohol, displayBlock) {
    'use strict';

    var beerId = beer_id, //The id of the beverage occupying the slot
        name = name, //The screen name of the beverage
        price = price, //The price of the beverage
        amount = amount, //The amount of bottles of the beverages in the slot
        type = category, //The type of the beverage,
        alcohol = alcohol, //True if the drink contains alcohol
        display = displayBlock, //The DOM element corresponding to the slot
        available = true; //Whether the drink is currently available or filtered out

    /**
     * Getter of alcohol
     * @return alcohol
     */
    this.isAlcoholic = function() {
        return alcohol;
    }

    /**
     * Display the information for the beverage
     */
    this.displayInfo = function() {
        var infoDisplay = display.getElementsByClassName("slotLeft")[0];
        infoDisplay.getElementsByClassName("drinkName")[0].textContent = name;
        infoDisplay.getElementsByClassName("price")[0].textContent = price;
        infoDisplay.getElementsByClassName("stock")[0].textContent = amount;
        this.fetchImage();
    }

    /**
     * Fetch the image corresponding to the beverage category and set it as the display child image
     */
    this.fetchImage = function() {
        display.getElementsByClassName("slotElement")[0].style.backgroundImage = 'url("./../resources/img/' + category + '.png")';
    }

    /**
     * Make the drink available is the consumer view by ungreying it and making it draggable
     */
    this.makeAvailable = function() {
        display.style.opacity = 1;
        display.style.filter = "alpha(opacity = 100)";
        available = true;
        $(display).draggable('enable');
    }

    /**
     * Make the drink unavailable is the consumer view by greying it and making it non-draggable
     */
    this.makeUnavailable = function() {
        display.style.opacity = 0.4;
        display.style.filter = "alpha(opacity = 40)";
        available = false;
        $(display).draggable('disable');
    }
}

/**
 * Populate the table of beverages in the consumer view
 */
function populateSlotsConsumer() {
    'use strict';
    var beveragesTable = document.getElementById("beveragesTable"),
        api = new APIConnect,
        i,
        j,
        rows,
        cells,
        i = 0,
        displayBlock,
        allBevButton = document.getElementById("allBevButton"),
        alcoholOnlyButton = document.getElementById("alcoDrinksButton"),
        softOnlyButton = document.getElementById("softDrinksButton");

    //Fetch the content and create a Slot object to hold it
    api.fetchContent(function(machineContent) {
        var beverage,
            beverageInfo,
            index;

        //For each slot in the machine
        rows = beveragesTable.getElementsByTagName("tr");
        for (i = 0; i < rows.length; i++) {
            cells = rows[i].getElementsByTagName("td");
            for (j = 0; j < cells.length; j++) {
                index = i * cells.length + j;
                beverageInfo = machineContent[index];
                beverage = new Slot(beverageInfo.beer_id, beverageInfo.name, beverageInfo.price, beverageInfo.amount, beverageInfo.category, beverageInfo.alcoholic, cells[j]);
                //Send the information to the display
                beverage.displayInfo();
                beveragesSlots.push(beverage);
            }
        }
    });

    //Wire the filter buttons
    allBevButton.onclick = function() {
        console.log("All");
        filterAlcoholDrinks(true, true);
    };
    softOnlyButton.onclick = function() {
        console.log("Soft only");
        filterAlcoholDrinks(false, true);
    };
    alcoholOnlyButton.onclick = function() {
        console.log("Alco only");
        filterAlcoholDrinks(true, false);
    };
}

/**
 * Extract the category of the beverage from the information provided by the database
 * @param providedInfo
 * @return An array with a string with the category of the beverage and a boolean indicating whether or not the drink is alcoholic
 * Possible values for the category: beer | lager | stout | ale | soft | cider | white_wine | red_wine
 */
function beverageCategory(providedInfo) {
    'use strict';
    var category = "beer",
        patterns = { red_wine: /rött vin/i, white_wine: /vitt vin/i, ale: /ale/i, stout: /stout/i, lager: /lager/i, cider: /cider/i, beer: /öl/i, soft: /alkoholfritt/i },
        key,
        alcoholFreePattern = /alkoholfritt/i,
        alcoholic = !alcoholFreePattern.test(providedInfo);

    for (key in patterns) {
        if (patterns.hasOwnProperty(key)) {
            if (patterns[key].test(providedInfo)) {
                category = key;
                break;
            }
        }
    }

    return [category, alcoholic];
}

/**
 * Function to execute drag 'n drop functionality
 */
function dragNDrop() {
    'use strict';
    var sidebar = document.getElementById("sidebar");
    var productsData = { "totalAmountOfDifferentProducts": 0, "products": [] };
    var totalCost = 0;

    /**
     * Function to change the id of the sidebar
     * @param elementToBeChanged is the element which id is changed
     */
    function changeSidebarId(elementToBeChanged) {
        if (elementToBeChanged.id == "sidebar") {
            elementToBeChanged.setAttribute('id', 'sidebarHighlighted');
        } else {
            elementToBeChanged.id = "sidebar";
        }
    }

    /**
     * Function which handles writing into shoppingcart.
     * @param an array of product objects
     */
    function writeIntoTable(productsArray) {
        var tbody = $('#tableBody');
        $('#tableBody').empty();
        for (var i = 0; i < productsArray.length; i++) {
            var tr = $('
').appendTo(tbody);
            var currentProduct = productsArray[i];
            // Loops through all the own properties of the object
            for (var property in currentProduct) {
                if (currentProduct.hasOwnProperty(property)) {
                    tr.append('' + currentProduct[property] + '	');
                }
            }
        }
    }

    /**
     * Function which adds a product to the shoppingcart table and adjusts the total
     * @param name - the name of the beverage
     * @param price - the price of the beverage
     */
    function addProduct(name, price) {
        /**
         * Function which forms an array of products which have been placed in the shoppingcart.
         * @return An array with product objects as elements
         */
        function updateProductsArray() {
            var updatedProductsArray = productsData.products;
            /**
             * Loops through the total amount of separate products and checks if a certain
             * product already is added to the products array. If so the quantity of the
             * product is just incremented by 1.
             */
            for (var i = 0; i < productsData.totalAmountOfDifferentProducts; i++) {
                var row = productsData.products[i];
                if (row.name == name) {
                    row.quantity += 1;
                    return updatedProductsArray;
                }
            }
            productsData.totalAmountOfDifferentProducts += 1;
            /**
             * Adding a new product into the array of products
             */
            productsData.products.push({
                name: name,
                quantity: 1,
                price: price
            });
            return updatedProductsArray;
        }

        var updatedProductsArray = updateProductsArray();
        writeIntoTable(updatedProductsArray);
        totalCost += price;
        $('#total').html('Total: $' + totalCost);
    }

    /**
     * Clear the table
     */
    function clearTable() {
        productsData = { "totalAmountOfDifferentProducts": 0, "products": [] };
        totalCost = 0;
        writeIntoTable([]);
        $('#total').html('Total: $' + totalCost);
    }

    /**
     * Function that does the actual dragging and dropping
     */
    $(function() {
        $(".product").draggable({
            revert: "invalid", // when not dropped, the item will revert back to its initial position
            containment: "document",
            helper: function(e, ui) {
                console.log(ui);
                console.log(e);
                console.log($(this).clone());
                return $(this).clone();
            },
            cursor: "grab",
            start: function(event, ui) {
                // Function call to highlight the sidebar once dragging starts
                //    changeSidebarId(sidebar);
            },
            stop: function(event, ui) {
                // Function call to stop highlighting of the sidebar once dragging stops
                //    changeSidebarId(sidebar);
            }

        });

        $("#sidebar").droppable({
            //      accept: ".product",
            drop: function(event, ui) {
                // console.log(ui.draggable);
                var name = $(ui.draggable).find('p:eq(0)').html();
                var price = $(ui.draggable).find('p:eq(1)').html();
                addProduct(name, parseFloat(price));
            },
            out: function(event, ui) {
                // Not sure if this is needed in the project work.
            }
        });
    });

    /*
     * Wire the Clear Table and Purchase button
     */
    $(function() {
        $('#clearAllCart').click(function() {
            clearTable();
        });
        $('#purchaseCart').click(function() {
            console.log("ADD PURCHASE");
            clearTable();
        });
    });
}

/**
 * Manages the filtering out of drinks in the consumer view
 * @param displayAlco True if the alcoholic drinks should be available
 * @param displaySoft True if the non-acoholic drinks should be available
 */
function filterAlcoholDrinks(displayAlco, displaySoft) {
    'use strict';
    var i = 0,
        slot;
    //For each beverage
    for (; i < beveragesSlots.length; i++) {
        slot = beveragesSlots[i];
        //If the beverage is alcoholic
        if (slot.isAlcoholic()) {
            if (displayAlco) {
                slot.makeAvailable();
            } else {
                slot.makeUnavailable();
            }
            continue;
        }

        //If the beverage is non-acoholic
        if (displaySoft) {
            slot.makeAvailable();
        } else {
            slot.makeUnavailable();
        }
    }
}<commit_msg>Comment out some console texts<commit_after>/**
 * @author      Emma Barme
 * @version     0.1
 */

/*
 * The array holding the Slots representing the beverages in the consumer view.
 */
var beveragesSlots = [];

/**
 * Constructor for the Slot object
 * Represents a slot in the machine, with the information necessary for its display, attached to it's corresponding html object
 */
function Slot(beer_id, name, price, amount, category, alcohol, displayBlock) {
    'use strict';

    var beerId = beer_id, //The id of the beverage occupying the slot
        name = name, //The screen name of the beverage
        price = price, //The price of the beverage
        amount = amount, //The amount of bottles of the beverages in the slot
        type = category, //The type of the beverage,
        alcohol = alcohol, //True if the drink contains alcohol
        display = displayBlock, //The DOM element corresponding to the slot
        available = true; //Whether the drink is currently available or filtered out

    /**
     * Getter of alcohol
     * @return alcohol
     */
    this.isAlcoholic = function() {
        return alcohol;
    }

    /**
     * Display the information for the beverage
     */
    this.displayInfo = function() {
        var infoDisplay = display.getElementsByClassName("slotLeft")[0];
        infoDisplay.getElementsByClassName("drinkName")[0].textContent = name;
        infoDisplay.getElementsByClassName("price")[0].textContent = price;
        infoDisplay.getElementsByClassName("stock")[0].textContent = amount;
        this.fetchImage();
    }

    /**
     * Fetch the image corresponding to the beverage category and set it as the display child image
     */
    this.fetchImage = function() {
        display.getElementsByClassName("slotElement")[0].style.backgroundImage = 'url("./../resources/img/' + category + '.png")';
    }

    /**
     * Make the drink available is the consumer view by ungreying it and making it draggable
     */
    this.makeAvailable = function() {
        display.style.opacity = 1;
        display.style.filter = "alpha(opacity = 100)";
        available = true;
        $(display).draggable('enable');
    }

    /**
     * Make the drink unavailable is the consumer view by greying it and making it non-draggable
     */
    this.makeUnavailable = function() {
        display.style.opacity = 0.4;
        display.style.filter = "alpha(opacity = 40)";
        available = false;
        $(display).draggable('disable');
    }
}

/**
 * Populate the table of beverages in the consumer view
 */
function populateSlotsConsumer() {
    'use strict';
    var beveragesTable = document.getElementById("beveragesTable"),
        api = new APIConnect,
        i,
        j,
        rows,
        cells,
        i = 0,
        displayBlock,
        allBevButton = document.getElementById("allBevButton"),
        alcoholOnlyButton = document.getElementById("alcoDrinksButton"),
        softOnlyButton = document.getElementById("softDrinksButton");

    //Fetch the content and create a Slot object to hold it
    api.fetchContent(function(machineContent) {
        var beverage,
            beverageInfo,
            index;

        //For each slot in the machine
        rows = beveragesTable.getElementsByTagName("tr");
        for (i = 0; i < rows.length; i++) {
            cells = rows[i].getElementsByTagName("td");
            for (j = 0; j < cells.length; j++) {
                index = i * cells.length + j;
                beverageInfo = machineContent[index];
                beverage = new Slot(beverageInfo.beer_id, beverageInfo.name, beverageInfo.price, beverageInfo.amount, beverageInfo.category, beverageInfo.alcoholic, cells[j]);
                //Send the information to the display
                beverage.displayInfo();
                beveragesSlots.push(beverage);
            }
        }
    });

    //Wire the filter buttons
    allBevButton.onclick = function() {
        console.log("All");
        filterAlcoholDrinks(true, true);
    };
    softOnlyButton.onclick = function() {
        console.log("Soft only");
        filterAlcoholDrinks(false, true);
    };
    alcoholOnlyButton.onclick = function() {
        console.log("Alco only");
        filterAlcoholDrinks(true, false);
    };
}

/**
 * Extract the category of the beverage from the information provided by the database
 * @param providedInfo
 * @return An array with a string with the category of the beverage and a boolean indicating whether or not the drink is alcoholic
 * Possible values for the category: beer | lager | stout | ale | soft | cider | white_wine | red_wine
 */
function beverageCategory(providedInfo) {
    'use strict';
    var category = "beer",
        patterns = { red_wine: /rött vin/i, white_wine: /vitt vin/i, ale: /ale/i, stout: /stout/i, lager: /lager/i, cider: /cider/i, beer: /öl/i, soft: /alkoholfritt/i },
        key,
        alcoholFreePattern = /alkoholfritt/i,
        alcoholic = !alcoholFreePattern.test(providedInfo);

    for (key in patterns) {
        if (patterns.hasOwnProperty(key)) {
            if (patterns[key].test(providedInfo)) {
                category = key;
                break;
            }
        }
    }

    return [category, alcoholic];
}

/**
 * Function to execute drag 'n drop functionality
 */
function dragNDrop() {
    'use strict';
    var sidebar = document.getElementById("sidebar");
    var productsData = { "totalAmountOfDifferentProducts": 0, "products": [] };
    var totalCost = 0;

    /**
     * Function to change the id of the sidebar
     * @param elementToBeChanged is the element which id is changed
     */
    function changeSidebarId(elementToBeChanged) {
        if (elementToBeChanged.id == "sidebar") {
            elementToBeChanged.setAttribute('id', 'sidebarHighlighted');
        } else {
            elementToBeChanged.id = "sidebar";
        }
    }

    /**
     * Function which handles writing into shoppingcart.
     * @param an array of product objects
     */
    function writeIntoTable(productsArray) {
        var tbody = $('#tableBody');
        $('#tableBody').empty();
        for (var i = 0; i < productsArray.length; i++) {
            var tr = $('
').appendTo(tbody);
            var currentProduct = productsArray[i];
            // Loops through all the own properties of the object
            for (var property in currentProduct) {
                if (currentProduct.hasOwnProperty(property)) {
                    tr.append('' + currentProduct[property] + '	');
                }
            }
        }
    }

    /**
     * Function which adds a product to the shoppingcart table and adjusts the total
     * @param name - the name of the beverage
     * @param price - the price of the beverage
     */
    function addProduct(name, price) {
        /**
         * Function which forms an array of products which have been placed in the shoppingcart.
         * @return An array with product objects as elements
         */
        function updateProductsArray() {
            var updatedProductsArray = productsData.products;
            /**
             * Loops through the total amount of separate products and checks if a certain
             * product already is added to the products array. If so the quantity of the
             * product is just incremented by 1.
             */
            for (var i = 0; i < productsData.totalAmountOfDifferentProducts; i++) {
                var row = productsData.products[i];
                if (row.name == name) {
                    row.quantity += 1;
                    return updatedProductsArray;
                }
            }
            productsData.totalAmountOfDifferentProducts += 1;
            /**
             * Adding a new product into the array of products
             */
            productsData.products.push({
                name: name,
                quantity: 1,
                price: price
            });
            return updatedProductsArray;
        }

        var updatedProductsArray = updateProductsArray();
        writeIntoTable(updatedProductsArray);
        totalCost += price;
        $('#total').html('Total: $' + totalCost);
    }

    /**
     * Clear the table
     */
    function clearTable() {
        productsData = { "totalAmountOfDifferentProducts": 0, "products": [] };
        totalCost = 0;
        writeIntoTable([]);
        $('#total').html('Total: $' + totalCost);
    }

    /**
     * Function that does the actual dragging and dropping
     */
    $(function() {
        $(".product").draggable({
            revert: "invalid", // when not dropped, the item will revert back to its initial position
            containment: "document",
            helper: function(e, ui) {
                /*    console.log(ui);
                    console.log(e);
                    console.log($(this).clone());*/
                return $(this).clone();
            },
            cursor: "grab",
            start: function(event, ui) {
                // Function call to highlight the sidebar once dragging starts
                //    changeSidebarId(sidebar);
            },
            stop: function(event, ui) {
                // Function call to stop highlighting of the sidebar once dragging stops
                //    changeSidebarId(sidebar);
            }

        });

        $("#sidebar").droppable({
            //      accept: ".product",
            drop: function(event, ui) {
                // console.log(ui.draggable);
                var name = $(ui.draggable).find('p:eq(0)').html();
                var price = $(ui.draggable).find('p:eq(1)').html();
                addProduct(name, parseFloat(price));
            },
            out: function(event, ui) {
                // Not sure if this is needed in the project work.
            }
        });
    });

    /*
     * Wire the Clear Table and Purchase button
     */
    $(function() {
        $('#clearAllCart').click(function() {
            clearTable();
        });
        $('#purchaseCart').click(function() {
            console.log("ADD PURCHASE");
            clearTable();
        });
    });
}

/**
 * Manages the filtering out of drinks in the consumer view
 * @param displayAlco True if the alcoholic drinks should be available
 * @param displaySoft True if the non-acoholic drinks should be available
 */
function filterAlcoholDrinks(displayAlco, displaySoft) {
    'use strict';
    var i = 0,
        slot;
    //For each beverage
    for (; i < beveragesSlots.length; i++) {
        slot = beveragesSlots[i];
        //If the beverage is alcoholic
        if (slot.isAlcoholic()) {
            if (displayAlco) {
                slot.makeAvailable();
            } else {
                slot.makeUnavailable();
            }
            continue;
        }

        //If the beverage is non-acoholic
        if (displaySoft) {
            slot.makeAvailable();
        } else {
            slot.makeUnavailable();
        }
    }
}<|endoftext|>


<commit_before>const glob = require('glob');
const fs = require('fs');
const path = require('path');
const marked = require('marked');
const renderer = new marked.Renderer();
const camelCase = require('camel-case');
const titleCase = require('title-case');
const shell = require('shelljs');
const getFileNameFromPath = require('@lukeboyle/get-filename-from-path');

renderer.heading = function(code, level) {
	if (level === 1) {
		return `

${code}

		`;
	} else {
		return `<h${level}>${code}</h${level}>`;
	}
};

renderer.code = function(code, language) {
	return `

		${code.split('\n').map(codeBlock => {
		const codeWithEscapedQuotes = codeBlock.split('"').join('\\"');
		const codeWithEscapedHashes = codeWithEscapedQuotes.split('#').join('\\#');
		return `<span>{"${codeWithEscapedHashes}"}</span>`;
	}).join('')}

`;
};

renderer.image = function(href, title, text) {
	return `<img src="${href}" alt="${text}"/>`;
};

function getMarkupFromMarkdown(markdownString) {
	return `<article className="blog-post">${marked(markdownString, {renderer: renderer, gfm: true})}</article>`;
}

function generateComponent(post) {
	const fileName = getFileNameFromPath(post.path);
	const camelCaseName = camelCase(fileName);

	return {
		path: post.path,
		fileName,
		componentName: camelCaseName[0].toUpperCase() + camelCaseName.slice(1),
		component: `
			import React from 'react';
			import Helmet from 'react-helmet';

			export default class ${camelCaseName} extends React.Component {
				componentDidMount() {
					const heading = this.rootNode.querySelector('h1');

					heading.innerHTML = '' + heading.innerText + '';
				}

				render() {
					return (

 this.rootNode = el}
						>
							<Helmet>
								<title>${titleCase(fileName)} | Luke Boyle</title>
							</Helmet>
							${getMarkupFromMarkdown(post.contents)}

					);
				}
			}
		`
	};
}

(() => {
	glob('blog-posts/**/*.md', {}, (err, files) => {
		const blogPosts = files.reduce((acc, curr) => {
			acc.push({
				path: curr,
				contents: fs.readFileSync(curr, {encoding: 'utf-8'})
			});

			return acc;
		}, []);

		const components = blogPosts.map(generateComponent);

		shell.rm('-rf', path.resolve(`${__dirname}/../src/pages/blog-posts`));

		components.forEach(component => {
			shell.mkdir('-p', path.resolve(`${__dirname}/../src/pages/${component.path.replace(`/${component.fileName}.md`, '')}`));
			fs.writeFileSync(path.resolve(`${__dirname}/../src/pages/${component.path.replace('.md', '.jsx')}`), component.component);
		});

		const reversedComponents = components.reverse();

		const blogPage = `
			import React from 'react';
			import Helmet from 'react-helmet';
			${reversedComponents.reduce((acc, curr) => {
				return acc + `import ${curr.componentName} from './${curr.path.replace('.md', '.jsx')}';\n`;
			}, '')}

			export default class Blog extends React.Component {
				render() {
					return (

							<Helmet>
								<title>Blog | Luke Boyle</title>
							</Helmet>
							${reversedComponents.reduce((acc, curr) => {
								return acc + `<${curr.componentName} isBlogPage={true}/>\n`;
							}, '')}

					);
				}
			}
		`;

		fs.writeFileSync(path.resolve(`${__dirname}/../src/pages/blog.jsx`), blogPage);
	});
})();<commit_msg>refactor: add blog header<commit_after>const glob = require('glob');
const fs = require('fs');
const path = require('path');
const marked = require('marked');
const renderer = new marked.Renderer();
const camelCase = require('camel-case');
const titleCase = require('title-case');
const shell = require('shelljs');
const getFileNameFromPath = require('@lukeboyle/get-filename-from-path');

renderer.heading = function(code, level) {
	if (level === 1) {
		return `

${code}

		`;
	} else {
		return `<h${level}>${code}</h${level}>`;
	}
};

renderer.code = function(code, language) {
	return `

		${code.split('\n').map(codeBlock => {
		const codeWithEscapedQuotes = codeBlock.split('"').join('\\"');
		const codeWithEscapedHashes = codeWithEscapedQuotes.split('#').join('\\#');
		return `<span>{"${codeWithEscapedHashes}"}</span>`;
	}).join('')}

`;
};

renderer.image = function(href, title, text) {
	return `<img src="${href}" alt="${text}"/>`;
};

function getMarkupFromMarkdown(markdownString) {
	return `<article className="blog-post">${marked(markdownString, {renderer: renderer, gfm: true})}</article>`;
}

function generateComponent(post) {
	const fileName = getFileNameFromPath(post.path);
	const camelCaseName = camelCase(fileName);

	return {
		path: post.path,
		fileName,
		componentName: camelCaseName[0].toUpperCase() + camelCaseName.slice(1),
		component: `
			import React from 'react';
			import Helmet from 'react-helmet';

			export default class ${camelCaseName} extends React.Component {
				componentDidMount() {
					const heading = this.rootNode.querySelector('h1');

					heading.innerHTML = '' + heading.innerText + '';
				}

				render() {
					return (

 this.rootNode = el}
						>
							<Helmet>
								<title>${titleCase(fileName)} | Luke Boyle</title>
							</Helmet>
							${getMarkupFromMarkdown(post.contents)}

					);
				}
			}
		`
	};
}

(() => {
	glob('blog-posts/**/*.md', {}, (err, files) => {
		const blogPosts = files.reduce((acc, curr) => {
			acc.push({
				path: curr,
				contents: fs.readFileSync(curr, {encoding: 'utf-8'})
			});

			return acc;
		}, []);

		const components = blogPosts.map(generateComponent);

		shell.rm('-rf', path.resolve(`${__dirname}/../src/pages/blog-posts`));

		components.forEach(component => {
			shell.mkdir('-p', path.resolve(`${__dirname}/../src/pages/${component.path.replace(`/${component.fileName}.md`, '')}`));
			fs.writeFileSync(path.resolve(`${__dirname}/../src/pages/${component.path.replace('.md', '.jsx')}`), component.component);
		});

		const reversedComponents = components.reverse();

		const blogPage = `
			import React from 'react';
			import Helmet from 'react-helmet';
			${reversedComponents.reduce((acc, curr) => {
				return acc + `import ${curr.componentName} from './${curr.path.replace('.md', '.jsx')}';\n`;
			}, '')}

			export default class Blog extends React.Component {
				render() {
					return (

							<Helmet>
								<title>Blog | Luke Boyle</title>
							</Helmet>

									Boyleing Point

									7/11 was an inside job

								${reversedComponents.reduce((acc, curr) => {
									return acc + `<${curr.componentName} isBlogPage={true}/>\n`;
								}, '')}

					);
				}
			}
		`;

		fs.writeFileSync(path.resolve(`${__dirname}/../src/pages/blog.jsx`), blogPage);
	});
})();<|endoftext|>


<commit_before>var
Player = require('./Player'),
SocketPlayer = function(options, player) {
var socket = options.socket,
    filterPlayerObject = function(fullPlayer) {
        var filteredPlayer = {}
            ;

        filteredPlayer.name = fullPlayer.name;

        return filteredPlayer;
    },

    filterCardObject = function(fullCard) {
        var filteredCard = {}
            ;

        filteredCard.color = fullCard.color;
        filteredCard.value = fullCard.value;

        if (fullCard.pickedColor) {
            filteredCard.pickedColor = fullCard.pickedColor;
        }

        return filteredCard;
    }
    ;

    player = player || {};
    Player(options, player);

    player.on('playerJoined', function(data) {
        var event = {};
        event.player = filterPlayerObject(data.player);

        socket.emit('playerJoined', event);
    });

    player.on('gameStarted', function(data) {
        var event = {};
        event.firstCard = filterCardObject(data.firstCard);

        socket.emit('gameStarted', event);
    });

    player.on('gameEnded', function(data) {
        var event = {};
        event.winner = filterPlayerObject(data.winner);

        socket.emit('gameEnded', event);
    });

    player.on('cardAdded', function(data) {
        var event = {};
        event.card = filterCardObject(data.card);

        socket.emit('cardAdded', event);
    });

    player.on('cardPlayed', function(data) {
        var event = {};
        event.card = filterCardObject(data.card);
        event.player = filterPlayerObject(data.player);

        socket.emit('cardPlayed', event);
    });

    player.on('playerGotCard', function(data) {
        var event = {};
        event.player = filterPlayerObject(data.player);
        event.cardCount = data.cardCount;

        socket.emit('playerGotCard', event);
    });

    player.on('playerTurn', function(data) {
        var event = {};
        event.player = filterPlayerObject(data.player);

        socket.emit('playerTurn', event);
    });

    return player;
}

module.exports = SocketPlayer;
<commit_msg>SocketPlayer: handle playCard event<commit_after>var
Player = require('./Player'),
SocketPlayer = function(options, player) {
var socket = options.socket,
    filterPlayerObject = function(fullPlayer) {
        var filteredPlayer = {}
            ;

        filteredPlayer.name = fullPlayer.name;

        return filteredPlayer;
    },

    filterCardObject = function(fullCard) {
        var filteredCard = {}
            ;

        filteredCard.color = fullCard.color;
        filteredCard.value = fullCard.value;

        if (fullCard.pickedColor) {
            filteredCard.pickedColor = fullCard.pickedColor;
        }

        return filteredCard;
    }
    ;

    player = player || {};
    Player(options, player);

    player.on('playerJoined', function(data) {
        var event = {};
        event.player = filterPlayerObject(data.player);

        socket.emit('playerJoined', event);
    });

    player.on('gameStarted', function(data) {
        var event = {};
        event.firstCard = filterCardObject(data.firstCard);

        socket.emit('gameStarted', event);
    });

    player.on('gameEnded', function(data) {
        var event = {};
        event.winner = filterPlayerObject(data.winner);

        socket.emit('gameEnded', event);
    });

    player.on('cardAdded', function(data) {
        var event = {};
        event.card = filterCardObject(data.card);

        socket.emit('cardAdded', event);
    });

    player.on('cardPlayed', function(data) {
        var event = {};
        event.card = filterCardObject(data.card);
        event.player = filterPlayerObject(data.player);

        socket.emit('cardPlayed', event);
    });

    player.on('playerGotCard', function(data) {
        var event = {};
        event.player = filterPlayerObject(data.player);
        event.cardCount = data.cardCount;

        socket.emit('playerGotCard', event);
    });

    player.on('playerTurn', function(data) {
        var event = {};
        event.player = filterPlayerObject(data.player);

        socket.emit('playerTurn', event);
    });

    socket.on('playCard', function(data) {
        var card = data.card,
            pickedCard,
            successfulPlay = false,
            i
            ;

        // remove card from hand
        for (i = 0; i < player.hand.length; i++) {
            if (player.hand[i].color === card.color
            &&  player.hand[i].value === card.value) {
                // the card object sent through the socket
                // is only a partial copy of the original card
                pickedCard = player.hand.splice(i,1)[0];

                if (card.pickedColor) {
                    pickedCard.pickedColor = card.pickedColor;
                }

                // we want remove one card only
                // there may be same cards in hand
                break;
            }
        }

        successfulPlay = player.game.playCard(player, pickedCard);
        if (!successfulPlay) {
            player.hand.push(pickedCard);
        }
    });

    socket.on('playDraw', function() {
        player.game.playDraw(player);
    });

    socket.on('sayUno', function() {
        player.uno(true);
    });

    return player;
}

module.exports = SocketPlayer;
<|endoftext|>


<commit_before>import ModelService from '../services/ModelService';
import AdhocModelService from '../services/AdhocModelService';
import AuthService from '../services/AuthService';
import models from '../models';

const userModel = models.User;

/**
 * @class GroupController
 */
class UserController {
  /**
   * Check if authentication request is valid
   * @method
   * @memberof UserController
   * @static
   * @return {function} Express middleware function that checks
   * request method and payload for every authentication request
   */
  static validateRequest() {
    return (req, res, next) => {
      if (req.method !== 'POST') {
        res.status(400).send('POST request method expected');
      } else if (!req.body.username || !req.body.password) {
        res.status(400).send('non-empty username and password expected');
      } else {
        next();
      }
    };
  }

  /**
   * Authenticate a user with username and password
   * @method
   * @memberof UserController
   * @static
   * @return {function} Express middleware function that
   * validates username and password  and sends token to client
   */
  static authenticateUser() {
    return (req, res) => {
      const username = req.body.username;
      ModelService.getModelInstance(userModel, { username })
      .then((user) => {
        user.verifyPassword(req.body.password)
        .then((passwordIsValid) => {
          if (passwordIsValid) {
            const rsaKey = process.env.PRIVATE_KEY;
            return AuthService.generateToken(user, rsaKey);
          }
          return res.status(401).send('Invalid Password');
        })
        .then((token) => {
          return res.status(200).json(token);
        })
        .catch((err) => {
          return res.status(401).send(err.message);
        });
      })
      .catch((err) => {
        return res.status(401).send(err.message);
      });
    };
  }

  /**
   * Create a user
   * @method
   * @memberof UserController
   * @static
   * @return {function} Express middleware function that creates
   * new user and sends response to client
   */
  static createUser() {
    return (req, res) => {
      ModelService.createModelInstance(userModel, req.body)
      .then((user) => {
        if (user) {
          res.status(201).send(user);
        }
      })
      .catch((err) => {
        res.status(400).send(err.message);
      });
    };
  }

  /**
   * Delete a user
   * @method
   * @memberof ModelService
   * @static
   * @returns {function} Express middleware function which deletes
   * user and sends response to client
   */
  static deleteUser() {
    return (req, res) => {
      ModelService.deleteModelInstance(userModel, {
        username: req.params.username
      })
      .then(() => {
        res.sendStatus(204);
      })
      .catch((err) => {
        res.status(400).send(err.message);
      });
    };
  }

  /**
   * Update user profile
   * @method
   * @memberof ModelService
   * @static
   * @returns {function} Express middleware function which updates
   * user details and sends response to client
   */
  static updateUser() {
    return (req, res) => {
      ModelService.updateModelInstance(userModel, {
        username: req.params.username
      }, req.body)
      .then((user) => {
        res.status(200).send(user);
      })
      .catch((err) => {
        res.status(400).send(err.message);
      });
    };
  }

  /**
   * Get a user
   * @method
   * @memberof ModelService
   * @static
   * @returns {function} Express middleware function which gets
   * a user and sends response to client
   */
  static getUser() {
    return (req, res) => {
      ModelService.getModelInstance(userModel, {
        username: req.params.username
      })
      .then((user) => {
        res.status(200).send(user);
      })
      .catch((err) => {
        res.status(400).send(err.message);
      });
    };
  }

  /**
   * Get all the groups a user belong to
   * @method
   * @memberof ModelService
   * @static
   * @returns {function} Express middleware function which gets
   * user's groups and sends response to client
   */
  static getUserGroups() {
    return (req, res) => {
      AdhocModelService.getUserGroups(req.params.username)
      .then((groups) => {
        res.status(200).send(groups);
      })
      .catch((err) => {
        res.status(400).send(err.message);
      });
    };
  }
}
export default UserController;
<commit_msg>[feat]: Authorize client for every request to the API<commit_after>import ModelService from '../services/ModelService';
import AdhocModelService from '../services/AdhocModelService';
import AuthService from '../services/AuthService';
import models from '../models';

const userModel = models.User;

/**
 * @class GroupController
 */
class UserController {
  /**
   * Check if authentication request is valid
   * @method
   * @memberof UserController
   * @static
   * @return {function} Express middleware function that checks
   * request method and payload for every authentication request
   */
  static validateRequest() {
    return (req, res, next) => {
      if (req.method !== 'POST') {
        res.status(400).send('POST request method expected');
      } else if (!req.body.username || !req.body.password) {
        res.status(400).send('non-empty username and password expected');
      } else {
        next();
      }
    };
  }

  /**
   * Get Token sent in client request
   * @method
   * @memberof UserController
   * @static
   * @return {function} Express middleware function that gets
   * authorization token from request body, query, header or cookies
   * and passes request to next middleware function.
   */
  static getClientAuthToken() {
    return (req, res, next) => {
      const token = req.get('Authorization') || req.body.token
      || req.cookies.token || req.query.token;
      if (!token) {
        return res.status(400).send('No Access token provided!');
      }
      const matched = /^Bearer (\S+)$/.exec(token);
      req.token = (matched) ? matched[1] : token;
      next();
    };
  }

  /**
   * Authenticate a user with username and password
   * @method
   * @memberof UserController
   * @static
   * @return {function} Express middleware function that
   * validates username and password  and sends token to client
   */
  static authenticateUser() {
    return (req, res) => {
      const username = req.body.username;
      ModelService.getModelInstance(userModel, { username })
      .then((user) => {
        user.verifyPassword(req.body.password)
        .then((passwordIsValid) => {
          if (passwordIsValid) {
            const rsaKey = process.env.PRIVATE_KEY;
            return AuthService.generateToken(user, rsaKey);
          }
          return res.status(401).send('Invalid Password');
        })
        .then((token) => {
          return res.status(200).json(token);
        })
        .catch((err) => {
          return res.status(401).send(err.message);
        });
      })
      .catch((err) => {
        return res.status(401).send(err.message);
      });
    };
  }

  /**
   * Verify user token and authorize user to access requested route
   * @method
   * @memberof UserController
   * @static
   * @return {function} Express middleware function that
   * validates user token and pass request to route handler
   */
  static authorizeUser() {
    return (req, res, next) => {
      const rsaKey = process.env.PUBLIC_KEY;
      return AuthService.verifyTokenGetPayload(req.token, rsaKey)
      .then((decodedPayload) => {
        req.username = decodedPayload.key;
        next();
      })
      .catch((err) => {
        return res.status(401).send(err.message);
      });
    };
  }

  /**
   * Create a user
   * @method
   * @memberof UserController
   * @static
   * @return {function} Express middleware function that creates
   * new user and sends response to client
   */
  static createUser() {
    return (req, res) => {
      ModelService.createModelInstance(userModel, req.body)
      .then((user) => {
        if (user) {
          res.status(201).send(user);
        }
      })
      .catch((err) => {
        res.status(400).send(err.message);
      });
    };
  }

  /**
   * Delete a user
   * @method
   * @memberof ModelService
   * @static
   * @returns {function} Express middleware function which deletes
   * user and sends response to client
   */
  static deleteUser() {
    return (req, res) => {
      ModelService.deleteModelInstance(userModel, {
        username: req.params.username
      })
      .then(() => {
        res.sendStatus(204);
      })
      .catch((err) => {
        res.status(400).send(err.message);
      });
    };
  }

  /**
   * Update user profile
   * @method
   * @memberof ModelService
   * @static
   * @returns {function} Express middleware function which updates
   * user details and sends response to client
   */
  static updateUser() {
    return (req, res) => {
      ModelService.updateModelInstance(userModel, {
        username: req.params.username
      }, req.body)
      .then((user) => {
        res.status(200).send(user);
      })
      .catch((err) => {
        res.status(400).send(err.message);
      });
    };
  }

  /**
   * Get a user
   * @method
   * @memberof ModelService
   * @static
   * @returns {function} Express middleware function which gets
   * a user and sends response to client
   */
  static getUser() {
    return (req, res) => {
      ModelService.getModelInstance(userModel, {
        username: req.params.username
      })
      .then((user) => {
        res.status(200).send(user);
      })
      .catch((err) => {
        res.status(400).send(err.message);
      });
    };
  }

  /**
   * Get all the groups a user belong to
   * @method
   * @memberof ModelService
   * @static
   * @returns {function} Express middleware function which gets
   * user's groups and sends response to client
   */
  static getUserGroups() {
    return (req, res) => {
      AdhocModelService.getUserGroups(req.params.username)
      .then((groups) => {
        res.status(200).send(groups);
      })
      .catch((err) => {
        res.status(400).send(err.message);
      });
    };
  }
}
export default UserController;
<|endoftext|>


<commit_before>module.exports = function (usersData, productsData) {
    var contoller = {
        getCart: function (req, res, next) {
            if (!req.user) {
                res.redirect('/');
            }
            else {
                usersData.getUserByIdWithCart(req.user._id, function (err, user) {
                    if (err) {
                        console.log('Users could not be loaded: ' + err);
                    }
                    var cart = user.cart;
                    cart['total'] = user.calculateCartSum(cart);
                    res.render('cart/cart', { currentUser: req.user, cart: cart });
                });
            }
        },
        addItemToCart: function (req, res, next) {
            var newProductData = req.body;
            newProductData.user = req.user._id;
            usersData.updateUser({ _id: req.user._id }, { $push: { cart: newProductData.itemId } }, function (err, user) {
                if (err) {
                    console.log("ERROR", err);
                    req.session.error = 'Unable to add to cart';
                    return;
                }
                console.log('Updated!!!', user);
                res.redirect('/cart');
            });
        },
        removeItemFromCart: function (req, res, next) {
            var newProductData = req.body;
            newProductData.user = req.user._id;
            usersData.updateUser({ _id: req.user._id }, { $pop: { cart: newProductData.itemId } }, function (err, user) {
                if (err) {
                    console.log("ERROR", err);
                    req.session.error = 'Unable to remove product';
                    return;
                }
                console.log('Updated!!!', user);
                res.redirect('/cart');
            });
        },
        removeFromCart: function (req, res, next) {
            if (!req.user) {
                res.redirect('/');
            } else {
                var product = req.params.id ? { id: req.params.id } : {};
                console.log(product);
                productsData.getProductById(product.id, function (err, product) {
                    if (err) {
                        console.log('Product could not be loaded: ' + err);
                        return;
                    }
                    res.render('products/productDetails', { currentUser: req.user, product: product, inCart: true });
                });
            }
        }
    };

    return contoller;
};
<commit_msg>Fixed removing an item from the Cart bug<commit_after>module.exports = function (usersData, productsData) {
    var contoller = {
        getCart: function (req, res, next) {
            if (!req.user) {
                res.redirect('/');
            }
            else {
                usersData.getUserByIdWithCart(req.user._id, function (err, user) {
                    if (err) {
                        console.log('Users could not be loaded: ' + err);
                    }
                    var cart = user.cart;
                    cart['total'] = user.calculateCartSum(cart);
                    res.render('cart/cart', { currentUser: req.user, cart: cart });
                });
            }
        },
        addItemToCart: function (req, res, next) {
            var newProductData = req.body;
            newProductData.user = req.user._id;
            usersData.updateUser({ _id: req.user._id }, { $push: { cart: newProductData.itemId } }, function (err, user) {
                if (err) {
                    console.log("ERROR", err);
                    req.session.error = 'Unable to add to cart';
                    return;
                }
                console.log('Updated!!!', user);
                res.redirect('/cart');
            });
        },
        removeItemFromCart: function (req, res, next) {
            var newProductData = req.body;
            newProductData.user = req.user._id;
            usersData.updateUser({ _id: req.user._id }, { $pull: { cart: newProductData.itemId } }, function (err, user) {
                if (err) {
                    console.log("ERROR", err);
                    req.session.error = 'Unable to remove product';
                    return;
                }
                console.log('Updated!!!', user);
                res.redirect('/cart');
            });
        },
        removeFromCart: function (req, res, next) {
            if (!req.user) {
                res.redirect('/');
            } else {
                var product = req.params.id ? { id: req.params.id } : {};
                console.log(product);
                productsData.getProductById(product.id, function (err, product) {
                    if (err) {
                        console.log('Product could not be loaded: ' + err);
                        return;
                    }
                    res.render('products/productDetails', { currentUser: req.user, product: product, inCart: true });
                });
            }
        }
    };

    return contoller;
};
<|endoftext|>


<commit_before>var User = require('../db/dbconfig').User;

module.exports = {
  getAllUsers: function (req, res) {
    User.findAll({})
    .then(function (users) {
      //res.send(users);
      res.status(200).json(users);
    })
    .catch(function (err) {
      console.error (err);
    });
  },

  getUser: function (req, res) {
    var userId = req.params.googid;
    User.findById(userId)
    .then(function (user) {
      res.status(200).json(user);
    })
    .catch(function (err) {
      console.error (err);
    });
  },

  findOrCreateUser: function (body, next) {
    var userData = {
      googid: body.id,
      firstName: body.given_name,
      lastName: body.family_name,
      email: body.email,
      picture: body.picture
    };
    User.findOrCreate({where: userData})
    .then(function (user) {
      //send data back to /callback route
      next(user);
    })
    .catch(function (err) {
      console.error(err);
    });
  },

  //this will add a relationship to the UserFriends table
  addFriend: function (req, res) {
    var googId = req.session.user;
    var friendId = req.body.friendGoogId;
    User.find({where: {googid: googId}})
    .then(function (user) {
      user.addFriends(friendId);
      res.status(201).json(user);
    })
    .catch(function (err) {
      console.error(err);
    });
  },

  // getAllFriendsAllPlurbs: function (req, res) {
  //   var googId = req.session.user;
  //   res.status(200).json(plurbs);
  // }
};
<commit_msg>(refactor) Remove commented code<commit_after>var User = require('../db/dbconfig').User;

module.exports = {
  getAllUsers: function (req, res) {
    User.findAll({})
    .then(function (users) {
      //res.send(users);
      res.status(200).json(users);
    })
    .catch(function (err) {
      console.error (err);
    });
  },

  getUser: function (req, res) {
    var userId = req.params.googid;
    User.findById(userId)
    .then(function (user) {
      res.status(200).json(user);
    })
    .catch(function (err) {
      console.error (err);
    });
  },

  findOrCreateUser: function (body, next) {
    var userData = {
      googid: body.id,
      firstName: body.given_name,
      lastName: body.family_name,
      email: body.email,
      picture: body.picture
    };
    User.findOrCreate({where: userData})
    .then(function (user) {
      //send data back to /callback route
      next(user);
    })
    .catch(function (err) {
      console.error(err);
    });
  },

  //this will add a relationship to the UserFriends table
  addFriend: function (req, res) {
    var googId = req.session.user;
    var friendId = req.body.friendGoogId;
    User.find({where: {googid: googId}})
    .then(function (user) {
      user.addFriends(friendId);
      res.status(201).json(user);
    })
    .catch(function (err) {
      console.error(err);
    });
  }
};
<|endoftext|>


<commit_before>var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));

var config = require('./../config/config');
var UserSettings = require('./../models/UserSettings');
var emoticonService = require('./../services/emoticonService');

//module.exports.version = function (req, res) {
//	versionService.version()
//		.then(res.ok)
//		.catch(res.serverError);
//};

module.exports.index = function (req, res) {
	var userId = _.isString(req.session.userId) ? req.session.userId.toObjectId() : req.session.userId;

	Promise.join(
		emoticonService.getEmoticonNamesFromDisk(),
		UserSettings.findOne({user: userId}),
		fs.readdirAsync('./assets/bundled').catch(empty)
	)
		.spread(function (emoticons, settings, bundledFiles) {
			//var templates = _.find(bundledFiles, function (file) {
			//	return _.includes(file, 'templates');
			//});

			// no template caching for dev
			//if (!config.useJavascriptBundle) {
			//	templates = null;
			//}

			var templates = null;
			var isProd = false;

			res.render(isProd ? 'index-prod' :'index', {
				templates: templates,
				userId: userId,
				isProduction: isProd,
				emoticons: emoticons,
				loadingEmote: emoticonService.getLoadScreenEmoticon(),
				debugging: settings.showDebugging
			});
		})
		.catch(res.serverError);
};

module.exports.login = function (req, res) {
	res.render('login', {
		clientID: config.google.clientID
	});
};

module.exports.logout = function (req, res) {
	req.session.destroy();
	res.redirect('/login');
};

function empty() {

}
<commit_msg>production bundling<commit_after>var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));

var config = require('./../config/config');
var UserSettings = require('./../models/UserSettings');
var emoticonService = require('./../services/emoticonService');

//module.exports.version = function (req, res) {
//	versionService.version()
//		.then(res.ok)
//		.catch(res.serverError);
//};

module.exports.index = function (req, res) {
	var userId = _.isString(req.session.userId) ? req.session.userId.toObjectId() : req.session.userId;

	Promise.join(
		emoticonService.getEmoticonNamesFromDisk(),
		UserSettings.findOne({user: userId}),
		fs.readdirAsync('./assets/bundled').catch(empty)
	)
		.spread(function (emoticons, settings, bundledFiles) {
			var templates = _.find(bundledFiles, function (file) {
				return _.includes(file, 'templates');
			});

			 //no template caching for dev
			if (!config.useJavascriptBundle) {
				templates = null;
			}

			res.render(config.isProduction ? 'index-prod' :'index', {
				templates: templates,
				userId: userId,
				isProduction: config.isProduction,
				emoticons: emoticons,
				loadingEmote: emoticonService.getLoadScreenEmoticon(),
				debugging: settings.showDebugging
			});
		})
		.catch(res.serverError);
};

module.exports.login = function (req, res) {
	res.render('login', {
		clientID: config.google.clientID
	});
};

module.exports.logout = function (req, res) {
	req.session.destroy();
	res.redirect('/login');
};

function empty() {

}
<|endoftext|>