<commit_before>const jsLibs = new ((() => { 'use strict';

class JSLibs
{

    get Require()
    { let self = this;
        return Require;
    }

    constructor()
    { let self = this;
        self._packages = {};
    }

    exportModule(package_name, module_path, module_init_fn)
    { let self = this;
        if (!(package_name in self._packages))
            self._packages[package_name] = new Package(self, package_name);

        self._packages[package_name].addModule(module_path, module_init_fn);
    }

    importModule(package_name, module_path)
    { let self = this;
        if (!(package_name in self._packages)) {
            throw new Error('Package `' + package_name + '` does not exist.');
        }

        return self._packages[package_name].importModule(module_path);
    }

    require(package_name)
    { let self = this;
        return self.importModule(package_name, 'index');
    }

    _parsePackagePath(package_path)
    { let self = this;
        return package_path;
    }

}

class Module
{

    get instanceModule()
    { let self = this;
        if (self._instanceModule === null) {
            let require = new Require(self._package.jsLibs, self._package.name,
                    self._path);
            self._instanceModule = {
                exports: Module.ExportNotImplemented,
            };

            self._initFn(require.fn, self._instanceModule);

            if (self._instanceModule.exports === Module.ExportNotImplemented) {
                throw new Error('No `exports` found in module `' +
                        self._package.name + '/' + self._path + '`.');
            }
        }

        return self._instanceModule;
    }

    constructor(js_lib_package, module_path, init_fn)
    { let self = this;
        self._package = js_lib_package;
        self._path = module_path;
        self._initFn = init_fn;

        self._instanceModule = null;
    }

}
Object.defineProperties(Module, {

    DoesNotExists: { value:
    new class Module_DoesNotExist {

    }()},

    ExportNotImplemented: { value: {}, },

});

class Package
{

    constructor(js_libs, package_name)
    { let self = this;
        self.jsLibs = js_libs;
        self.name = package_name;

        self._modules = {};
    }

    addModule(module_path, module_init_fn)
    { let self = this;
        self._modules[module_path] = new Module(self, module_path, module_init_fn);
    }

    importModule(module_path)
    { let self = this;
        if (module_path in self._modules)
            return self._modules[module_path].instanceModule.exports;

        module_path += '/index';
        if (module_path in self._modules)
            return self._modules[module_path].instanceModule.exports;

        return Module.DoesNotExist;
    }

}

class Require
{

    get fn()
    { let self = this;
        return self._fn;
    }

    constructor(js_libs, package_name = null, current_path = null)
    { let self = this;
        self._packageName = package_name;
        self._currentPath_Array = null;

        self._fn = (import_path) => {
            let import_info = self._resolveImportInfo(import_path);

            let module = js_libs.importModule(import_info.packageName,
                    import_info.modulePath);
            if (module === Module.DoesNotExist) {
                throw new Error('Module `' + import_path + '` (`' +
                        import_info.packageName + ':' + import_info.modulePath +
                        '`) does not exist.');
            }

            return module;
        };

        if (current_path !== null) {
            self._currentPath_Array = current_path.split('/');
            self._currentPath_Array.pop();
        }
    }

    _resolveImportInfo(import_path)
    { let self = this;
        let import_path_array = import_path.split('/');

        /* Import Package */
        if (import_path_array[0] !== '.' && import_path_array[0] !== '..') {
            return {
                packageName: import_path_array[0],
                modulePath: 'index',
            };
        }

        if (self._packageName === null)
            throw new Error('Cannot import module outside of package.');

        /* Import Module */
        let module_path_array = self._currentPath_Array.slice();

        for (let i = 0; i < import_path_array.length; i++) {
            if (import_path_array[i] === '.')
                continue;

            if (import_path_array[i] === '..') {
                module_path_array.pop();
                continue;
            }

            module_path_array.push(import_path_array[i]);
        }

        return {
            packageName: self._packageName,
            modulePath: module_path_array.join('/'),
        };
    }

}

return JSLibs;

})())();

const require = (new jsLibs.Require(jsLibs)).fn;
<commit_msg><commit_after>const jsLibs = new ((() => { 'use strict';

class JSLibs
{

    get Require()
    { let self = this;
        return Require;
    }

    constructor()
    { let self = this;
        self._packages = {};
    }

    exportModule(package_name, module_path, module_init_fn)
    { let self = this;
        if (!(package_name in self._packages))
            self._packages[package_name] = new Package(self, package_name);

        self._packages[package_name].addModule(module_path, module_init_fn);
    }

    importModule(package_name, module_path)
    { let self = this;
        if (!(package_name in self._packages)) {
            throw new Error('Package `' + package_name + '` does not exist.');
        }

        return self._packages[package_name].importModule(module_path);
    }

    require(package_name)
    { let self = this;
        let module = self.importModule(package_name, 'index');
        if (module === Module.DoesNotExist) {
            throw new Error('Module `' + package_name + '` (`' +
                    package_name + ':' + 'index' +
                    '`) does not exist.');
        }

        return module;
    }

    _parsePackagePath(package_path)
    { let self = this;
        return package_path;
    }

}

class Module
{

    get instanceModule()
    { let self = this;
        if (self._instanceModule === null) {
            let require = new Require(self._package.jsLibs, self._package.name,
                    self._path);
            self._instanceModule = {
                exports: Module.ExportNotImplemented,
            };

            self._initFn(require.fn, self._instanceModule);

            if (self._instanceModule.exports === Module.ExportNotImplemented) {
                throw new Error('No `exports` found in module `' +
                        self._package.name + '/' + self._path + '`.');
            }
        }

        return self._instanceModule;
    }

    constructor(js_lib_package, module_path, init_fn)
    { let self = this;
        self._package = js_lib_package;
        self._path = module_path;
        self._initFn = init_fn;

        self._instanceModule = null;
    }

}
Object.defineProperties(Module, {

    DoesNotExists: { value:
    new class Module_DoesNotExist {

    }()},

    ExportNotImplemented: { value: {}, },

});

class Package
{

    constructor(js_libs, package_name)
    { let self = this;
        self.jsLibs = js_libs;
        self.name = package_name;

        self._modules = {};
    }

    addModule(module_path, module_init_fn)
    { let self = this;
        self._modules[module_path] = new Module(self, module_path, module_init_fn);
    }

    importModule(module_path)
    { let self = this;
        if (module_path in self._modules)
            return self._modules[module_path].instanceModule.exports;

        module_path += '/index';
        if (module_path in self._modules)
            return self._modules[module_path].instanceModule.exports;

        return Module.DoesNotExist;
    }

}

class Require
{

    get fn()
    { let self = this;
        return self._fn;
    }

    constructor(js_libs, package_name = null, current_path = null)
    { let self = this;
        self._packageName = package_name;
        self._currentPath_Array = null;

        self._fn = (import_path) => {
            let import_info = self._resolveImportInfo(import_path);

            let module = js_libs.importModule(import_info.packageName,
                    import_info.modulePath);
            if (module === Module.DoesNotExist) {
                throw new Error('Module `' + import_path + '` (`' +
                        import_info.packageName + ':' + import_info.modulePath +
                        '`) does not exist.');
            }

            return module;
        };

        if (current_path !== null) {
            self._currentPath_Array = current_path.split('/');
            self._currentPath_Array.pop();
        }
    }

    _resolveImportInfo(import_path)
    { let self = this;
        let import_path_array = import_path.split('/');

        /* Import Package */
        if (import_path_array[0] !== '.' && import_path_array[0] !== '..') {
            return {
                packageName: import_path_array[0],
                modulePath: 'index',
            };
        }

        if (self._packageName === null)
            throw new Error('Cannot import module outside of package.');

        /* Import Module */
        let module_path_array = self._currentPath_Array.slice();

        for (let i = 0; i < import_path_array.length; i++) {
            if (import_path_array[i] === '.')
                continue;

            if (import_path_array[i] === '..') {
                module_path_array.pop();
                continue;
            }

            module_path_array.push(import_path_array[i]);
        }

        return {
            packageName: self._packageName,
            modulePath: module_path_array.join('/'),
        };
    }

}

return JSLibs;

})())();

const require = (new jsLibs.Require(jsLibs)).fn;
<|endoftext|>


<commit_before>/*
 * kartvid.js: exports parseKartvid, which parses kartvid output
 */

exports.parseKartvid = parseKartvid;
exports.summarize = summarize;

/*
 * Returns a function that takes individual lines of kartvid output and parses
 * it, storing the results in video.races.
 */
function parseKartvid(video)
{
	var log = video.log;
	var races = video.races = [];
	var i = 0;
	var race, segment, entry;

	return (function (line) {
		++i;

		if (line.length === 0)
			return;

		try {
			entry = JSON.parse(line);
		} catch (ex) {
			log.warn(ex, 'ignoring bad JSON object at line %s', i);
			return;
		}

		if (i == 1 && entry.nframes) {
			video.maxframes = entry.nframes;
			video.crtime = parseDate(entry.crtime);
			return;
		}

		video.frame = entry.frame;

		if (entry.start) {
			if (race !== undefined) {
				log.warn('ignoring race aborted at %s',
				    entry.source);
				segment = undefined;
			}

			race = {
				'vstart': entry.time,
				'vend': undefined,
				'mode': trackMode(entry.track),
				'track': trackName(entry.track),
				'players': entry.players,
				'start_source': entry.source,
				'segments': []
			};
			return;
		}

		if (race === undefined) {
			log.warn('ignoring line %d (not in a race)');
			return;
		}

		if (!entry.done) {
			if (segment !== undefined) {
				segment.vend = entry.time;
				race['segments'].push(segment);
			}

			segment = {
				'vstart': entry.time,
				'players': entry.players.map(function (p) {
					return ({
				            'rank': p.position,
					    'lap': p.lap
					});
				}),
				'source': entry['source']
			};

			return;
		}

		if (segment !== undefined) {
			segment.vend = entry.time;
			race['segments'].push(segment);
			segment = undefined;
		}

		race.vend = entry.time;
		race.end_source = entry.source;
		race.players.forEach(function (p, j) {
			p['rank'] = entry.players[j]['position'];
		});
		races.push(race);
		race = undefined;
	});
}

var trackNames = {
    'banshee': 'Banshee Boardwalk',
    'beach': 'Koopa Troopa Beach',
    'bowser': 'Bowser\'s Castle',
    'choco': 'Choco Mountain',
    'desert': 'Kalimari Desert',
    'dk': 'DK\'s Jungle Parkway',
    'frappe': 'Frappe Snowland',
    'luigi': 'Luigi Raceway',
    'mario': 'Mario Raceway',
    'moo': 'Moo Moo Farm',
    'rainbow': 'Rainbow Road',
    'royal': 'Royal Raceway',
    'sherbet': 'Sherbet Land',
    'toad': 'Toad\'s Turnpike',
    'wario': 'Wario Raceway',
    'yoshi': 'Yoshi Valley'
};

function trackName(trackid)
{
	if (trackNames.hasOwnProperty(trackid))
		return (trackNames[trackid]);

	return ('Unknown Track');
}

function trackMode(trackid)
{
	return ('VS');
}

/*
 * The input here is the "creation_time" metadata written by our capture device,
 * which has the from YYYY-MM-DD HH:MM:SS and is written in UTC.  Amazingly,
 * V8's Date.parse() handles this format directly.
 */
function parseDate(when)
{
	if (!when)
		return (when);

	return (Date.parse(when));
}

/*
 * Converts an individual race to an English summary.
 */
function summarize(race)
{
	var entries = [];
	var time = kDuration(race['vstart']);
	var players = race['players'].slice(0);
	var last, seg;

	entries.push(race['players'].length + 'P ' + race['mode'] + ' on ' +
	    race['track'] + ' (starts at ' + time + ' in video)');
	entries.push('    Racers: ' + players.map(
	    function (p) { return (ucfirst(p['character'])); }).join(', '));

	if (race['segments'].length > 0) {
		seg = race['segments'][0];

		seg.players.forEach(function (p, i) {
			entries.push('    ' + kDuration(seg['vstart']) + ': ' +
			    ucfirst(players[i]['character']) + ' is in ' +
			    ordinal(p['rank']));
		});

		last = seg;

		race['segments'].slice(1).forEach(function (segment) {
			compareSegments(race, last, segment, function (text) {
				entries.push('    ' + kDuration(
				    segment['vstart']) + ': ' + text);
			});

			last = segment;
		});
	}

	entries.push('    ' + kDuration(race['vend']) + ': The race is over.');

	players.sort(function (p1, p2) {
		return (p1['rank'] - p2['rank']);
	});

	entries.push('    Result: ' + players.map(
	    function (p) { return (ucfirst(p['character'])); }).join(', '));

	return (entries);
}

/* XXX this is closely duplicated in the web code */
function compareSegments(race, last, next, emit)
{
	var cn, lp, np;
	var i, j, inr, ilr, jnr, jlr;

	cn = race['players'].map(
	    function (p) { return (ucfirst(p['character'])); });
	lp = last['players'];
	np = next['players'];

	for (i = 0; i < np.length; i++) {
		inr = np[i]['rank'];
		ilr = lp[i]['rank'];

		if (!inr || !ilr)
			continue;

		for (j = i + 1; j < np.length; j++) {
			jnr = np[j]['rank'];
			jlr = lp[j]['rank'];

			if (!jnr || !jlr)
				continue;

			if (inr < jnr && ilr > jlr) {
				emit(cn[i] + ' passes ' + cn[j]);
			} else if (inr > jnr && ilr < jlr) {
				emit(cn[j] + ' passes ' + cn[i]);
			}
		}
	}

	for (i = 0; i < np.length; i++) {
		if (np[i]['lap'] == 4 && np[i]['lap'] != lp[i]['lap'])
			emit(cn[i] + ' finishes');
	}
}

/* XXX copied/pasted from www/resources/js/kart.js */
function kDuration(ms, showmillis)
{
	var hour, min, sec, rv;

	/* compute totals in each unit */
	sec = Math.floor(ms / 1000);
	min = Math.floor(sec / 60);
	hour = Math.floor(min / 60);

	/* compute offsets for each unit */
	ms %= 1000;
	sec %= 60;
	min %= 60;

	rv = '';
	if (hour > 0)
		rv += hour + ':';

	if (hour > 0 || min > 0) {
		if (hour > 0 && min < 10)
			rv += '0' + min + ':';
		else
			rv += min + ':';
	}

	if ((hour > 0 || min > 0) && sec < 10)
		rv += '0' + sec;
	else
		rv += sec;

	if (!showmillis)
		return (rv + 's');

	if (ms < 10)
		rv += '.00' + ms;
	else if (ms < 100)
		rv += '.0' + ms;
	else
		rv += '.' + ms;

	return (rv);
}

/* XXX ditto */
function ucfirst(str)
{
	return (str[0].toUpperCase() + str.substr(1));
}

/* XXX ditto */
function ordinal(num)
{
	if (num == 1)
		return ('1st');
	if (num == 2)
		return ('2nd');
	if (num == 3)
		return ('3rd');
	if (num == 4)
		return ('4th');

	return (num);
}
<commit_msg>update kartvid JS library to process item state<commit_after>/*
 * kartvid.js: exports parseKartvid, which parses kartvid output
 */

exports.parseKartvid = parseKartvid;
exports.summarize = summarize;

/*
 * Returns a function that takes individual lines of kartvid output and parses
 * it, storing the results in video.races.
 */
function parseKartvid(video)
{
	var log = video.log;
	var races = video.races = [];
	var i = 0;
	var race, segment, entry;
	var itemstates;
	var lastp;

	return (function (line) {
		var k;

		++i;

		if (line.length === 0)
			return;

		try {
			entry = JSON.parse(line);
		} catch (ex) {
			log.warn(ex, 'ignoring bad JSON object at line %s', i);
			return;
		}

		if (i == 1 && entry.nframes) {
			video.maxframes = entry.nframes;
			video.crtime = parseDate(entry.crtime);
			return;
		}

		video.frame = entry.frame;

		if (entry.start) {
			if (race !== undefined) {
				log.warn('ignoring race aborted at %s',
				    entry.source);
				segment = undefined;
			}

			race = {
				'vstart': entry.time,
				'vend': undefined,
				'mode': trackMode(entry.track),
				'track': trackName(entry.track),
				'players': entry.players,
				'start_source': entry.source,
				'itemstates': [],
				'segments': []
			};
			itemstates = new Array(entry.players.length);
			lastp = null;
			for (k = 0; k < entry.players.length; k++)
				race['itemstates'].push([]);
			return;
		}

		if (race === undefined) {
			log.warn('ignoring line %d (not in a race)');
			return;
		}

		entry.players.forEach(function (p, j) {
			if (p['itemstate'] == 'slotmachine') {
				if (itemstates[j])
					return;
				itemstates[j] = {
				    'r0': p['position'],
				    'v0': entry.time,
				    's0': entry.source
				};
			} else if (p['itemstate']) {
				if (!itemstates[j])
					return;

				itemstates[j]['r1'] = p['position'];
				itemstates[j]['v1'] = entry.time;
				itemstates[j]['s1'] = entry.source;
				itemstates[j]['item'] = p['itemstate'];
				race['itemstates'][j].push(itemstates[j]);
				itemstates[j] = null;
			} else if (itemstates[j]) {
				log.warn('p%d: no itemstate after slotmachine',
				    j + 1);
				itemstates[j] = null;
			}
		});

		if (!entry.done) {
			/*
			 * If all players' ranks are the same, this isn't a new
			 * segment.
			 */
			if (lastp) {
				for (k = 0; k < entry.players.length; k++) {
					if (lastp[k]['position'] !=
					    entry.players['position'] ||
					    lastp[k]['lap'] !=
					    entry.players['lap'])
						break;
				}

				if (k == entry.players.length) {
					lastp = entry.players;
					return;
				}
			}

			lastp = entry.players;

			if (segment !== undefined) {
				segment.vend = entry.time;
				race['segments'].push(segment);
			}

			segment = {
				'vstart': entry.time,
				'players': entry.players.map(function (p) {
					return ({
				            'rank': p.position,
					    'lap': p.lap
					});
				}),
				'source': entry['source']
			};

			return;
		}

		if (segment !== undefined) {
			segment.vend = entry.time;
			race['segments'].push(segment);
			segment = undefined;
		}

		race.vend = entry.time;
		race.end_source = entry.source;
		race.players.forEach(function (p, j) {
			p['rank'] = entry.players[j]['position'];
		});
		races.push(race);
		race = undefined;
	});
}

var trackNames = {
    'banshee': 'Banshee Boardwalk',
    'beach': 'Koopa Troopa Beach',
    'bowser': 'Bowser\'s Castle',
    'choco': 'Choco Mountain',
    'desert': 'Kalimari Desert',
    'dk': 'DK\'s Jungle Parkway',
    'frappe': 'Frappe Snowland',
    'luigi': 'Luigi Raceway',
    'mario': 'Mario Raceway',
    'moo': 'Moo Moo Farm',
    'rainbow': 'Rainbow Road',
    'royal': 'Royal Raceway',
    'sherbet': 'Sherbet Land',
    'toad': 'Toad\'s Turnpike',
    'wario': 'Wario Raceway',
    'yoshi': 'Yoshi Valley'
};

function trackName(trackid)
{
	if (trackNames.hasOwnProperty(trackid))
		return (trackNames[trackid]);

	return ('Unknown Track');
}

function trackMode(trackid)
{
	return ('VS');
}

/*
 * The input here is the "creation_time" metadata written by our capture device,
 * which has the from YYYY-MM-DD HH:MM:SS and is written in UTC.  Amazingly,
 * V8's Date.parse() handles this format directly.
 */
function parseDate(when)
{
	if (!when)
		return (when);

	return (Date.parse(when));
}

/*
 * Converts an individual race to an English summary.
 */
function summarize(race)
{
	var entries = [];
	var time = kDuration(race['vstart']);
	var players = race['players'].slice(0);
	var last, seg;
	var itemsbyr0 = {}, itemsbyr1 = {}, allitems = {};
	var items, ranks;

	entries.push(race['players'].length + 'P ' + race['mode'] + ' on ' +
	    race['track'] + ' (starts at ' + time + ' in video)');
	entries.push('    Racers: ' + players.map(
	    function (p) { return (ucfirst(p['character'])); }).join(', '));

	if (race['segments'].length > 0) {
		seg = race['segments'][0];

		seg.players.forEach(function (p, i) {
			entries.push('    ' + kDuration(seg['vstart']) + ': ' +
			    ucfirst(players[i]['character']) + ' is in ' +
			    ordinal(p['rank']));
		});

		last = seg;

		race['segments'].slice(1).forEach(function (segment) {
			compareSegments(race, last, segment, function (text) {
				entries.push('    ' + kDuration(
				    segment['vstart']) + ': ' + text);
			});

			last = segment;
		});
	}

	entries.push('    ' + kDuration(race['vend']) + ': The race is over.');

	players.sort(function (p1, p2) {
		return (p1['rank'] - p2['rank']);
	});

	entries.push('    Result: ' + players.map(
	    function (p) { return (ucfirst(p['character'])); }).join(', '));

	entries.push('');
	entries.push('Item events');

	race['itemstates'].forEach(function (events, j) {
		entries.push('Player ' + (j + 1));
		entries.push('    R0  R1  ITEM');
		events.forEach(function (evt) {
			allitems[evt['item']] = true;

			if (!itemsbyr0[evt['r0']])
				itemsbyr0[evt['r0']] = {};
			if (!itemsbyr0[evt['r0']][evt['item']])
				itemsbyr0[evt['r0']][evt['item']] = 0;
			itemsbyr0[evt['r0']][evt['item']]++;

			if (!itemsbyr1[evt['r1']])
				itemsbyr1[evt['r1']] = {};
			if (!itemsbyr1[evt['r1']][evt['item']])
				itemsbyr1[evt['r1']][evt['item']] = 0;
			itemsbyr1[evt['r1']][evt['item']]++;

			entries.push('     ' + evt['r0'] + '   ' + evt['r1'] +
			    '  ' + evt['item'] + ' (' + evt['s0'] + ' to ' +
			    evt['s1'] + ')');
		});
	});

	items = Object.keys(allitems).sort();
	ranks = Object.keys(itemsbyr0).sort();
	entries.push('');
	entries.push('Items by rank when player hits item block');
	entries.push(ranks.map(function (r) { return (' ' + r); }).join(' ') +
	    '  Item');
	items.forEach(function (it) {
		entries.push(
		    ranks.map(function (r) {
			return (' ' + (itemsbyr0[r][it] || 0));
		    }).join(' ') + '  ' + it);
	});

	entries.push('');
	entries.push('Items by rank when player gets item');
	entries.push(ranks.map(function (r) { return (' ' + r); }).join(' ') +
	    '  Item');
	items.forEach(function (it) {
		entries.push(
		    ranks.map(function (r) {
			return (' ' + (itemsbyr1[r][it] || 0));
		    }).join(' ') + '  ' + it);
	});

	return (entries);
}

/* XXX this is closely duplicated in the web code */
function compareSegments(race, last, next, emit)
{
	var cn, lp, np;
	var i, j, inr, ilr, jnr, jlr;

	cn = race['players'].map(
	    function (p) { return (ucfirst(p['character'])); });
	lp = last['players'];
	np = next['players'];

	for (i = 0; i < np.length; i++) {
		inr = np[i]['rank'];
		ilr = lp[i]['rank'];

		if (!inr || !ilr)
			continue;

		for (j = i + 1; j < np.length; j++) {
			jnr = np[j]['rank'];
			jlr = lp[j]['rank'];

			if (!jnr || !jlr)
				continue;

			if (inr < jnr && ilr > jlr) {
				emit(cn[i] + ' passes ' + cn[j]);
			} else if (inr > jnr && ilr < jlr) {
				emit(cn[j] + ' passes ' + cn[i]);
			}
		}
	}

	for (i = 0; i < np.length; i++) {
		if (np[i]['lap'] == 4 && np[i]['lap'] != lp[i]['lap'])
			emit(cn[i] + ' finishes');
	}
}

/* XXX copied/pasted from www/resources/js/kart.js */
function kDuration(ms, showmillis)
{
	var hour, min, sec, rv;

	/* compute totals in each unit */
	sec = Math.floor(ms / 1000);
	min = Math.floor(sec / 60);
	hour = Math.floor(min / 60);

	/* compute offsets for each unit */
	ms %= 1000;
	sec %= 60;
	min %= 60;

	rv = '';
	if (hour > 0)
		rv += hour + ':';

	if (hour > 0 || min > 0) {
		if (hour > 0 && min < 10)
			rv += '0' + min + ':';
		else
			rv += min + ':';
	}

	if ((hour > 0 || min > 0) && sec < 10)
		rv += '0' + sec;
	else
		rv += sec;

	if (!showmillis)
		return (rv + 's');

	if (ms < 10)
		rv += '.00' + ms;
	else if (ms < 100)
		rv += '.0' + ms;
	else
		rv += '.' + ms;

	return (rv);
}

/* XXX ditto */
function ucfirst(str)
{
	return (str[0].toUpperCase() + str.substr(1));
}

/* XXX ditto */
function ordinal(num)
{
	if (num == 1)
		return ('1st');
	if (num == 2)
		return ('2nd');
	if (num == 3)
		return ('3rd');
	if (num == 4)
		return ('4th');

	return (num);
}
<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright 2012 Intel Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

/*****************************************************************************/

var nfc = window.nfc = {};

nfc.reset = function() {
	nfc.busName = "org.neard";
	nfc.bus = null;
	nfc.uri = null;
	nfc.manager = null;
	nfc.adapter = null;
	nfc.defaultAdapter = null;
};

nfc.init = function(uri, manifest, successCB, errorCB) {
	nfc.reset();

	function onAdapterPropsOk(props) {
		nfc.adapter.props = props;
		nfc.defaultAdapter = new nfc.NFCAdapter(nfc.adapter);
		if (successCB)
			successCB();
	}

	function onAdapterOk() {
		nfc.adapter.GetProperties(onAdapterPropsOk, errorCB);
	}

	function onManagerPropsOk(props) {
		if (props.Adapters.length == 0)
			errorCB("No NFC Adapter found");
		else {
			nfc.adapter = nfc.bus.getObject(nfc.busName,
					props.Adapters[0],
					onAdapterOk,
					errorCB);
		}
	}

	function onManagerOk() {
		nfc.manager.GetProperties(onManagerPropsOk, errorCB);
	}

	function onConnectOk() {
		nfc.bus = cloudeebus.SystemBus();
		nfc.uri = uri;
		nfc.manager = nfc.bus.getObject(nfc.busName, "/", onManagerOk, errorCB);
	}

	cloudeebus.connect(uri, manifest, onConnectOk, errorCB);
};

nfc.getDefaultAdapter = function() {
	return nfc.defaultAdapter;
};

/*****************************************************************************/

nfc.NFCAdapter = function(proxy) {
	this.proxy = proxy;
	if (proxy) {
		this.id = proxy.objectPath;
		this.powered = proxy.props.Powered ? true : false;
		this.polling = proxy.props.Polling ? true : false;
	}
	this.tagListener = this.peerListener = null;
	return this;
};

nfc.NFCAdapter.prototype.setPowered = function(state, successCB, errorCB) {

	var self = this;

	function onPoweredOk() {
		self.powered = state;
		if (successCB)
			successCB();
	}

	self.proxy.SetProperty("Powered", state, onPoweredOk, errorCB);
};

nfc.NFCAdapter.prototype.setPolling = function(state, successCB, errorCB) {

	var self = this;

	function onPollingOk() {
		self.polling = state;
		if (successCB)
			successCB();
	}

	if (state)
		self.proxy.StartPollLoop("Initiator", onPollingOk, errorCB);
	else
		self.proxy.StopPollLoop(onPollingOk, errorCB);
};

nfc.NFCAdapter.prototype.setListener = function(listenerKey, errorCB) {

	var self = this;

	var tag = null;
	var peer = null;

	function onTagPropsOk(props) {
		tag.props = props;
		tag.type = props.Type;
		if (self.tagListener)
			self.tagListener.onattach(tag);
	}

	function onPeerPropsOk(props) {
		peer.props = props;
		if (self.peerListener)
			self.peerListener.onattach(peer);
	}

	function onTagFound(tagId) {
		if (tag) /* trigger "found" callback only once */
			return;
		tag = new nfc.NFCTag(nfc.bus.getObject(nfc.busName, tagId));
		tag.proxy.callMethod("org.neard.Tag", "GetProperties",
				[], onTagPropsOk, errorCB);
	}

	function onPeerFound(deviceId) {
		if (peer) /* trigger "found" callback only once */
			return;
		peer = new nfc.NFCPeer(nfc.bus.getObject(nfc.busName, deviceId));
		peer.proxy.callMethod("org.neard.Device", "GetProperties",
				[], onPeerPropsOk, errorCB);
	}

	function onPropertyChanged(key, table) {
		if (key == "Tags") {
			if (table.length == 0) {
				tag = null;
				if (self.tagListener)
					self.tagListener.ondetach();
				self.setPolling(true);
			}
			else
				onTagFound(table[0]);
		}
		if (key == "Devices") {
			if (table.length == 0) {
				peer = null;
				if (self.peerListener)
					self.peerListener.ondetach();
				self.setPolling(true);
			}
			else
				onPeerFound(table[0]);
		}
	}

	if (!self.connected) {
		self.proxy.connectToSignal("org.neard.Adapter","PropertyChanged",
				onPropertyChanged);
		self.connected = true;
	}
};

nfc.NFCAdapter.prototype.setTagListener = function(detectCB, errorCB, tagFilter) {
	this.tagListener = detectCB;
	return this.setListener("Tags", errorCB);
};

nfc.NFCAdapter.prototype.setPeerListener = function(detectCB, errorCB) {
	this.peerListener = detectCB;
	return this.setListener("Devices", errorCB);
};

nfc.NFCAdapter.prototype.unsetListener = function() {
	if (this.tagListener || this.peerListener)
		return;
	this.proxy.disconnectSignal("org.neard.Adapter","PropertyChanged");
	this.connected = false;
};

nfc.NFCAdapter.prototype.unsetTagListener = function() {
	this.tagListener = null;
	return this.unsetListener();
};

nfc.NFCAdapter.prototype.unsetPeerListener = function() {
	this.peerListener = null;
	return this.unsetListener();
};

/*****************************************************************************/

nfc.NFCPeer = function(proxy) {
	this.proxy = proxy;
	if (proxy) {
		this.id = proxy.objectPath;
	}
	this.isConnected = false;
	return this;
};

nfc.NFCPeer.prototype.setReceiveNDEFListener = function(receiveCB, errorCB) {

	var self = this;

	if (!self.props)
		return errorCB("Peer properties unknown.");

	var records = [];

	function onRecPropsOk(props) {
		records.push(nfc.NDEFRecordForProps(props));
		if (records.length == self.props.Records.length && receiveCB)
			receiveCB(new NDEFMessage(records));
	}

	function NDEFMessageForRecords() {
		records = [];
		for (var i=0; i<self.props.Records.length; i++) {
			var recProxy = nfc.bus.getObject(nfc.busName, self.props.Records[i]);
			recProxy.callMethod("org.neard.Record", "GetProperties",
					[], onRecPropsOk, errorCB);
		}
	}

	function onPropertyChanged(key, table) {
		if (key == "Records") {
			self.props.Records = table;
			NDEFMessageForRecords();
		}
	}

	if (!self.isConnected) {
		self.proxy.connectToSignal("org.neard.Device","PropertyChanged",
				onPropertyChanged);
		self.isConnected = true;
		NDEFMessageForRecords();
	}
};

nfc.NFCPeer.prototype.unsetReceiveNDEFListener = function() {
	this.proxy.disconnectSignal("org.neard.Device","PropertyChanged");
	this.isConnected = false;
};

nfc.NFCPeer.prototype.sendNDEF = function(ndefMessage, successCB, errorCB) {
	for (var i=0; i< ndefMessage.records.length; i++) {
		var ndefRecord = ndefMessage.records[i];
		var rec = ndefRecord.neardRecord();
		this.proxy.callMethod("org.neard.Device", "Push",
			[rec], successCB, errorCB);
	}
};

nfc.NFCPeer.prototype.startHandover = function(type, successCB, errorCB) {
	this.proxy.callMethod("org.neard.Device", "Push",
		[{
			 Type: "Handover",
			 Carrier: type
		}],
		successCB, errorCB);
};

/*****************************************************************************/

nfc.NFCTag = function(proxy) {
	this.proxy = proxy;
	this.type = "GENERIC_TARGET";
	if (proxy) {
		this.id = proxy.objectPath;
	}
	return this;
};

nfc.NFCTag.prototype.readNDEF = function(readCB, errorCB) {

	var self = this;

	if (!self.props)
		return errorCB("Tag properties unknown.");

	var records = [];

	function onRecPropsOk(props) {
		records.push(nfc.NDEFRecordForProps(props));
		if (records.length == self.props.Records.length && readCB)
			readCB(new NDEFMessage(records));
	}

	for (var i=0; i<self.props.Records.length; i++) {
		var recProxy = nfc.bus.getObject(nfc.busName, self.props.Records[i]);
		recProxy.callMethod("org.neard.Record", "GetProperties",
				[], onRecPropsOk, errorCB);
	}
};

nfc.NFCTag.prototype.writeNDEF = function(ndefMessage, successCB, errorCB) {
	var ndefRecord = ndefMessage.records[0];
	var rec = ndefRecord.neardRecord();
	this.proxy.callMethod("org.neard.Tag", "Write",
			[rec], successCB, errorCB);
};

/*****************************************************************************/

nfc.NDEFMessage = function() {
	return this;
};

NDEFMessage = function(records) {
	nfc.NDEFMessage.call(this);
	this.records = records;
	return this;
};

NDEFMessage.prototype = new nfc.NDEFMessage();
NDEFMessage.prototype.constructor = NDEFMessage;

/*****************************************************************************/

nfc.NDEFRecord = function(props) {
	return this;
};

nfc.NDEFRecord.prototype.neardRecord = function() {
	return {};
};

/*****************************************************************************/

nfc.NDEFRecordText = function(props) {
	nfc.NDEFRecord.call(this,props);
	if (props) {
		this.text = props.Representation;
		this.languageCode = props.Language;
		this.encoding = props.Encoding;
	}
	return this;
};

nfc.NDEFRecordText.prototype = new nfc.NDEFRecord();
nfc.NDEFRecordText.prototype.constructor = nfc.NDEFRecordText;

nfc.NDEFRecordText.prototype.neardRecord = function() {
	return {
		Type: "Text",
		Representation: this.text,
		Language: this.languageCode,
		Encoding: this.encoding
	};
};

NDEFRecordText = function(text, languageCode, encoding) {
	nfc.NDEFRecordText.call(this);
	this.text = text;
	this.languageCode = languageCode ? languageCode : "en-US";
	this.encoding = encoding ? encoding : "UTF-8";
	return this;
};

NDEFRecordText.prototype = new nfc.NDEFRecordText();
NDEFRecordText.prototype.constructor = NDEFRecordText;

/*****************************************************************************/

nfc.NDEFRecordURI = function(props) {
	nfc.NDEFRecord.call(this,props);
	if (props) {
		this.uri = props.URI;
	}
	return this;
};

nfc.NDEFRecordURI.prototype = new nfc.NDEFRecord();
nfc.NDEFRecordURI.prototype.constructor = nfc.NDEFRecordURI;

nfc.NDEFRecordURI.prototype.neardRecord = function() {
	return {
		Type: "URI",
		URI: this.uri
	};
};

NDEFRecordURI = function(uri) {
	nfc.NDEFRecordURI.call(this);
	this.uri = uri;
	return this;
};

NDEFRecordURI.prototype = new nfc.NDEFRecordURI();
NDEFRecordURI.prototype.constructor = NDEFRecordURI;

/*****************************************************************************/

nfc.NDEFRecordForProps = function(props) {
	if (props.Type == "Text")
		return new nfc.NDEFRecordText(props);
	if (props.Type == "URI")
		return new nfc.NDEFRecordURI(props);
	return new nfc.NDEFRecord(props);
};

<commit_msg>rewrite all callMethod calls to pass callbacks with the request .then method<commit_after>/******************************************************************************
 * Copyright 2012 Intel Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

/*****************************************************************************/

var nfc = window.nfc = {};

nfc.reset = function() {
	nfc.busName = "org.neard";
	nfc.bus = null;
	nfc.uri = null;
	nfc.manager = null;
	nfc.adapter = null;
	nfc.defaultAdapter = null;
};

nfc.init = function(uri, manifest, successCB, errorCB) {
	nfc.reset();

	function onAdapterPropsOk(props) {
		nfc.adapter.props = props;
		nfc.defaultAdapter = new nfc.NFCAdapter(nfc.adapter);
		if (successCB)
			successCB();
	}

	function onAdapterOk() {
		nfc.adapter.GetProperties(onAdapterPropsOk, errorCB);
	}

	function onManagerPropsOk(props) {
		if (props.Adapters.length == 0)
			errorCB("No NFC Adapter found");
		else {
			nfc.adapter = nfc.bus.getObject(nfc.busName,
					props.Adapters[0],
					onAdapterOk,
					errorCB);
		}
	}

	function onManagerOk() {
		nfc.manager.GetProperties(onManagerPropsOk, errorCB);
	}

	function onConnectOk() {
		nfc.bus = cloudeebus.SystemBus();
		nfc.uri = uri;
		nfc.manager = nfc.bus.getObject(nfc.busName, "/", onManagerOk, errorCB);
	}

	cloudeebus.connect(uri, manifest, onConnectOk, errorCB);
};

nfc.getDefaultAdapter = function() {
	return nfc.defaultAdapter;
};

/*****************************************************************************/

nfc.NFCAdapter = function(proxy) {
	this.proxy = proxy;
	if (proxy) {
		this.id = proxy.objectPath;
		this.powered = proxy.props.Powered ? true : false;
		this.polling = proxy.props.Polling ? true : false;
	}
	this.tagListener = this.peerListener = null;
	return this;
};

nfc.NFCAdapter.prototype.setPowered = function(state, successCB, errorCB) {

	var self = this;

	function onPoweredOk() {
		self.powered = state;
		if (successCB)
			successCB();
	}

	self.proxy.SetProperty("Powered", state, onPoweredOk, errorCB);
};

nfc.NFCAdapter.prototype.setPolling = function(state, successCB, errorCB) {

	var self = this;

	function onPollingOk() {
		self.polling = state;
		if (successCB)
			successCB();
	}

	if (state)
		self.proxy.StartPollLoop("Initiator", onPollingOk, errorCB);
	else
		self.proxy.StopPollLoop(onPollingOk, errorCB);
};

nfc.NFCAdapter.prototype.setListener = function(listenerKey, errorCB) {

	var self = this;

	var tag = null;
	var peer = null;

	function onTagPropsOk(props) {
		tag.props = props;
		tag.type = props.Type;
		if (self.tagListener)
			self.tagListener.onattach(tag);
	}

	function onPeerPropsOk(props) {
		peer.props = props;
		if (self.peerListener)
			self.peerListener.onattach(peer);
	}

	function onTagFound(tagId) {
		if (tag) /* trigger "found" callback only once */
			return;
		tag = new nfc.NFCTag(nfc.bus.getObject(nfc.busName, tagId));
		tag.proxy.callMethod("org.neard.Tag", "GetProperties",
				[]).then(onTagPropsOk, errorCB);
	}

	function onPeerFound(deviceId) {
		if (peer) /* trigger "found" callback only once */
			return;
		peer = new nfc.NFCPeer(nfc.bus.getObject(nfc.busName, deviceId));
		peer.proxy.callMethod("org.neard.Device", "GetProperties",
				[]).then(onPeerPropsOk, errorCB);
	}

	function onPropertyChanged(key, table) {
		if (key == "Tags") {
			if (table.length == 0) {
				tag = null;
				if (self.tagListener)
					self.tagListener.ondetach();
				self.setPolling(true);
			}
			else
				onTagFound(table[0]);
		}
		if (key == "Devices") {
			if (table.length == 0) {
				peer = null;
				if (self.peerListener)
					self.peerListener.ondetach();
				self.setPolling(true);
			}
			else
				onPeerFound(table[0]);
		}
	}

	if (!self.connected) {
		self.proxy.connectToSignal("org.neard.Adapter","PropertyChanged",
				onPropertyChanged);
		self.connected = true;
	}
};

nfc.NFCAdapter.prototype.setTagListener = function(detectCB, errorCB, tagFilter) {
	this.tagListener = detectCB;
	return this.setListener("Tags", errorCB);
};

nfc.NFCAdapter.prototype.setPeerListener = function(detectCB, errorCB) {
	this.peerListener = detectCB;
	return this.setListener("Devices", errorCB);
};

nfc.NFCAdapter.prototype.unsetListener = function() {
	if (this.tagListener || this.peerListener)
		return;
	this.proxy.disconnectSignal("org.neard.Adapter","PropertyChanged");
	this.connected = false;
};

nfc.NFCAdapter.prototype.unsetTagListener = function() {
	this.tagListener = null;
	return this.unsetListener();
};

nfc.NFCAdapter.prototype.unsetPeerListener = function() {
	this.peerListener = null;
	return this.unsetListener();
};

/*****************************************************************************/

nfc.NFCPeer = function(proxy) {
	this.proxy = proxy;
	if (proxy) {
		this.id = proxy.objectPath;
	}
	this.isConnected = false;
	return this;
};

nfc.NFCPeer.prototype.setReceiveNDEFListener = function(receiveCB, errorCB) {

	var self = this;

	if (!self.props)
		return errorCB("Peer properties unknown.");

	var records = [];

	function onRecPropsOk(props) {
		records.push(nfc.NDEFRecordForProps(props));
		if (records.length == self.props.Records.length && receiveCB)
			receiveCB(new NDEFMessage(records));
	}

	function NDEFMessageForRecords() {
		records = [];
		for (var i=0; i<self.props.Records.length; i++) {
			var recProxy = nfc.bus.getObject(nfc.busName, self.props.Records[i]);
			recProxy.callMethod("org.neard.Record", "GetProperties",
					[]).then(onRecPropsOk, errorCB);
		}
	}

	function onPropertyChanged(key, table) {
		if (key == "Records") {
			self.props.Records = table;
			NDEFMessageForRecords();
		}
	}

	if (!self.isConnected) {
		self.proxy.connectToSignal("org.neard.Device","PropertyChanged",
				onPropertyChanged);
		self.isConnected = true;
		NDEFMessageForRecords();
	}
};

nfc.NFCPeer.prototype.unsetReceiveNDEFListener = function() {
	this.proxy.disconnectSignal("org.neard.Device","PropertyChanged");
	this.isConnected = false;
};

nfc.NFCPeer.prototype.sendNDEF = function(ndefMessage, successCB, errorCB) {
	for (var i=0; i< ndefMessage.records.length; i++) {
		var ndefRecord = ndefMessage.records[i];
		var rec = ndefRecord.neardRecord();
		this.proxy.callMethod("org.neard.Device", "Push",
			[rec]).then(successCB, errorCB);
	}
};

nfc.NFCPeer.prototype.startHandover = function(type, successCB, errorCB) {
	this.proxy.callMethod("org.neard.Device", "Push",
		[{
			 Type: "Handover",
			 Carrier: type
		}]).then(
		successCB, errorCB);
};

/*****************************************************************************/

nfc.NFCTag = function(proxy) {
	this.proxy = proxy;
	this.type = "GENERIC_TARGET";
	if (proxy) {
		this.id = proxy.objectPath;
	}
	return this;
};

nfc.NFCTag.prototype.readNDEF = function(readCB, errorCB) {

	var self = this;

	if (!self.props)
		return errorCB("Tag properties unknown.");

	var records = [];

	function onRecPropsOk(props) {
		records.push(nfc.NDEFRecordForProps(props));
		if (records.length == self.props.Records.length && readCB)
			readCB(new NDEFMessage(records));
	}

	for (var i=0; i<self.props.Records.length; i++) {
		var recProxy = nfc.bus.getObject(nfc.busName, self.props.Records[i]);
		recProxy.callMethod("org.neard.Record", "GetProperties",
				[]).then(onRecPropsOk, errorCB);
	}
};

nfc.NFCTag.prototype.writeNDEF = function(ndefMessage, successCB, errorCB) {
	var ndefRecord = ndefMessage.records[0];
	var rec = ndefRecord.neardRecord();
	this.proxy.callMethod("org.neard.Tag", "Write",
			[rec]).then(successCB, errorCB);
};

/*****************************************************************************/

nfc.NDEFMessage = function() {
	return this;
};

NDEFMessage = function(records) {
	nfc.NDEFMessage.call(this);
	this.records = records;
	return this;
};

NDEFMessage.prototype = new nfc.NDEFMessage();
NDEFMessage.prototype.constructor = NDEFMessage;

/*****************************************************************************/

nfc.NDEFRecord = function(props) {
	return this;
};

nfc.NDEFRecord.prototype.neardRecord = function() {
	return {};
};

/*****************************************************************************/

nfc.NDEFRecordText = function(props) {
	nfc.NDEFRecord.call(this,props);
	if (props) {
		this.text = props.Representation;
		this.languageCode = props.Language;
		this.encoding = props.Encoding;
	}
	return this;
};

nfc.NDEFRecordText.prototype = new nfc.NDEFRecord();
nfc.NDEFRecordText.prototype.constructor = nfc.NDEFRecordText;

nfc.NDEFRecordText.prototype.neardRecord = function() {
	return {
		Type: "Text",
		Representation: this.text,
		Language: this.languageCode,
		Encoding: this.encoding
	};
};

NDEFRecordText = function(text, languageCode, encoding) {
	nfc.NDEFRecordText.call(this);
	this.text = text;
	this.languageCode = languageCode ? languageCode : "en-US";
	this.encoding = encoding ? encoding : "UTF-8";
	return this;
};

NDEFRecordText.prototype = new nfc.NDEFRecordText();
NDEFRecordText.prototype.constructor = NDEFRecordText;

/*****************************************************************************/

nfc.NDEFRecordURI = function(props) {
	nfc.NDEFRecord.call(this,props);
	if (props) {
		this.uri = props.URI;
	}
	return this;
};

nfc.NDEFRecordURI.prototype = new nfc.NDEFRecord();
nfc.NDEFRecordURI.prototype.constructor = nfc.NDEFRecordURI;

nfc.NDEFRecordURI.prototype.neardRecord = function() {
	return {
		Type: "URI",
		URI: this.uri
	};
};

NDEFRecordURI = function(uri) {
	nfc.NDEFRecordURI.call(this);
	this.uri = uri;
	return this;
};

NDEFRecordURI.prototype = new nfc.NDEFRecordURI();
NDEFRecordURI.prototype.constructor = NDEFRecordURI;

/*****************************************************************************/

nfc.NDEFRecordForProps = function(props) {
	if (props.Type == "Text")
		return new nfc.NDEFRecordText(props);
	if (props.Type == "URI")
		return new nfc.NDEFRecordURI(props);
	return new nfc.NDEFRecord(props);
};

<|endoftext|>


<commit_before>/**
 * @license Pass*Field | (c) 2013 Antelle | https://github.com/antelle/passfield/blob/master/MIT-LICENSE.txt
 */

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

/**
 * Entry point
 * Initializes PassField
 * If jQuery is present, sets jQuery plugin ($.passField)
 */
(function($, document, window) {
    "use strict";

    var PassField = window.PassField = window.PassField || {};
    PassField.Config = PassField.Config || {};

    PassField.Config.locales = {
        en: {
            lower: true,
                msg: {
                pass: "password",
                    and: "and",
                    showPass: "Show password",
                    hidePass: "Hide password",
                    genPass: "Random password",
                    passTooShort: "password is too short (min. length: {})",
                    noCharType: "password must contain {}",
                    digits: "digits",
                    letters: "letters",
                    lettersUp: "letters in UPPER case",
                    symbols: "symbols",
                    inBlackList: "password is in list of top used passwords",
                    passRequired: "password is required",
                    equalTo: "password is equal to login",
                    repeat: "password consists of repeating characters",
                    badChars: "password contains bad characters: “{}”",
                    weakWarn: "weak",
                    invalidPassWarn: "*",
                    weakTitle: "This password is weak",
                    generateMsg: "To generate a strong password, click {} button."
            }
        },
        de: {
            lower: false,
                msg: {
                pass: "Passwort",
                    and: "und",
                    showPass: "Passwort anzeigen",
                    hidePass: "Passwort verbergen",
                    genPass: "Zufallspasswort",
                    passTooShort: "Passwort ist zu kurz (Mindestlänge: {})",
                    noCharType: "Passwort muss {} enthalten",
                    digits: "Ziffern",
                    letters: "Buchstaben",
                    lettersUp: "Buchstaben in GROSSSCHRIFT",
                    symbols: "Symbole",
                    inBlackList: "Passwort steht auf der Liste der beliebtesten Passwörter",
                    passRequired: "Passwort wird benötigt",
                    equalTo: "Passwort ist wie Anmeldung",
                    repeat: "Passwort besteht aus sich wiederholenden Zeichen",
                    badChars: "Passwort enthält ungültige Zeichen: “{}”",
                    weakWarn: "Schwach",
                    invalidPassWarn: "*",
                    weakTitle: "Dieses Passwort ist schwach",
                    generateMsg: "Klicken Sie auf den {}-Button, um ein starkes Passwort zu generieren."
            }
        },
        fr: {
            lower: true,
                msg: {
                pass: "mot de passe",
                    and: "et",
                    showPass: "Montrer le mot de passe",
                    hidePass: "Cacher le mot de passe",
                    genPass: "Mot de passe aléatoire",
                    passTooShort: "le mot de passe est trop court (min. longueur: {})",
                    noCharType: "le mot de passe doit contenir des {}",
                    digits: "chiffres",
                    letters: "lettres",
                    lettersUp: "lettres en MAJUSCULES",
                    symbols: "symboles",
                    inBlackList: "le mot de passe est dans la liste des plus utilisés",
                    passRequired: "le mot de passe est requis",
                    equalTo: "le mot de passe est le même que l'identifiant",
                    repeat: "le mot de passe est une répétition de caractères",
                    badChars: "le mot de passe contient des caractères incorrects: “{}”",
                    weakWarn: "faible",
                    invalidPassWarn: "*",
                    weakTitle: "Ce mot de passe est faible",
                    generateMsg: "Pour créer un mot de passe fort cliquez sur le bouton {}."
            }
        },
        it: {
            lower: false,
                msg: {
                pass: "password",
                    and: "e",
                    showPass: "Mostra password",
                    hidePass: "Nascondi password",
                    genPass: "Password casuale",
                    passTooShort: "la password è troppo breve (lunghezza min.: {})",
                    noCharType: "la password deve contenere {}",
                    digits: "numeri",
                    letters: "lettere",
                    lettersUp: "lettere in MAIUSCOLO",
                    symbols: "simboli",
                    inBlackList: "la password è nella lista delle password più usate",
                    passRequired: "è necessaria una password",
                    equalTo: "la password è uguale al login",
                    repeat: "la password è composta da caratteri che si ripetono",
                    badChars: "la password contiene caratteri non accettati: “{}”",
                    weakWarn: "debole",
                    invalidPassWarn: "*",
                    weakTitle: "Questa password è debole",
                    generateMsg: "Per generare una password forte, clicca sul tasto {}."
            }
        },
        nl: {
            lower: true,
                msg: {
                pass: "wachtwoord",
                    and: "en",
                    showPass: "Toon wachtwoord",
                    hidePass: "Verberg wachtwoord",
                    genPass: "Willekeurig wachtwoord",
                    passTooShort: "Wachtwoord is te kort (min. lengte: {})",
                    noCharType: "wachtwoord moet {} bevatten",
                    digits: "cijfers",
                    letters: "letters",
                    letters_up: "hoofdletters",
                    symbols: "symbolen",
                    inBlackList: "wachtwoord is in de lijst meest gebruikte wachtwoorden",
                    passRequired: "wachtwoord is verplicht",
                    equalTo: "wachtwoord is hetzelfde als login",
                    repeat: "wachtwoord bestaat uit herhaalde karakters",
                    badChars: "wachtwoord bevat verboden karakters: “{}”",
                    weakWarn: "zwak",
                    invalidPassWarn: "*",
                    weakTitle: "Dit wachtwoord is zwak",
                    generateMsg: "Klik {} om een sterk wachtwoord te genereren."
            }
        },
        ru: {
            lower: true,
                msg: {
                pass: "пароль",
                    and: "и",
                    showPass: "Показать пароль",
                    hidePass: "Скрыть пароль",
                    genPass: "Случайный пароль",
                    passTooShort: "пароль слишком короткий (мин. длина: {})",
                    noCharType: "в пароле должны быть {}",
                    digits: "цифры",
                    letters: "буквы",
                    lettersUp: "буквы в ВЕРХНЕМ регистре",
                    symbols: "символы",
                    inBlackList: "этот пароль часто используется в Интернете",
                    passRequired: "пароль обязателен",
                    equalTo: "пароль совпадает с логином",
                    repeat: "пароль состоит из повторяющихся символов",
                    badChars: "в пароле есть недопустимые символы: «{}»",
                    weakWarn: "слабый",
                    invalidPassWarn: "*",
                    weakTitle: "Пароль слабый, его легко взломать",
                    generateMsg: "Чтобы сгенерировать пароль, нажмите кнопку {}."
            }
        },
        ua: {
            lower: true,
                msg: {
                pass: "пароль",
                    and: "i",
                    showPass: "Показати пароль",
                    hidePass: "Сховати пароль",
                    genPass: "Випадковий пароль",
                    passTooShort: "пароль є занадто коротким (мiн. довжина: {})",
                    noCharType: "пароль повинен містити {}",
                    digits: "цифри",
                    letters: "букви",
                    lettersUp: "букви у ВЕРХНЬОМУ регістрі",
                    symbols: "cимволи",
                    inBlackList: "пароль входить до списку паролей, що використовуються найчастіше",
                    passRequired: "пароль є обов'язковим",
                    equalTo: "пароль та логін однакові",
                    repeat: "пароль містить повторювані символи",
                    badChars: "пароль містить неприпустимі символи: «{}»",
                    weakWarn: "слабкий",
                    invalidPassWarn: "*",
                    weakTitle: "Цей пароль є слабким",
                    generateMsg: "Щоб створити надійний пароль, натисніть кнопку {}."
            }
        },
        es: {
            lower: true,
                msg: {
                pass: "contraseña",
                    and: "y",
                    showPass: "Mostrar contraseña",
                    hidePass: "Ocultar contraseña",
                    genPass: "Contraseña aleatoria",
                    passTooShort: "contraseña demasiado corta (longitud mín.: {})",
                    noCharType: "la contraseña debe contener {}",
                    digits: "dígitos",
                    letters: "letras",
                    lettersUp: "letras en MAYÚSCULAS",
                    symbols: "símbolos",
                    inBlackList: "la contraseña está en la lista de las contraseñas más usadas",
                    passRequired: "se requiere contraseña",
                    equalTo: "la contraseña es igual al inicio de sesión",
                    repeat: "la contraseña tiene caracteres repetidos",
                    badChars: "la contraseña contiene caracteres no permitidos: “{}”",
                    weakWarn: "débil",
                    invalidPassWarn: "*",
                    weakTitle: "Esta contraseña es débil",
                    generateMsg: "Para generar una contraseña segura, haga clic en el botón de {}."
            }
        },
        el: {
            lower: true,
                msg: {
                pass: "πρόσβασης",
                    and: "και",
                    showPass: "Προβολή κωδικού πρόσβασης",
                    hidePass: "Απόκρυψη κωδικού πρόσβασης",
                    genPass: "Τυχαίος κωδικός πρόσβασης",
                    passTooShort: "ο κωδικός πρόσβασης είναι πολύ μικρός (ελάχιστο μήκος: {})",
                    noCharType: "ο κωδικός πρόσβασης πρέπει να περιέχει {}",
                    digits: "ψηφία",
                    letters: "λατινικά γράμματα",
                    lettersUp: "λατινικά γράμματα με ΚΕΦΑΛΑΙΑ",
                    symbols: "σύμβολα",
                    inBlackList: "ο κωδικός πρόσβασης βρίσκεται σε κατάλογο δημοφιλέστερων κωδικών",
                    passRequired: "απαιτείται κωδικός πρόσβασης",
                    equalTo: "ο κωδικός είναι όμοιος με το όνομα χρήστη",
                    repeat: "ο κωδικός αποτελείται από επαναλαμβανόμενους χαρακτήρες",
                    badChars: "ο κωδικός περιέχει μη επιτρεπτούς χαρακτήρες: “{}”",
                    weakWarn: "αδύναμος",
                    invalidPassWarn: "*",
                    weakTitle: "Αυτός ο κωδικός πρόσβασης είναι αδύναμος",
                    generateMsg: "Για να δημιουργήσετε δυνατό κωδικό πρόσβασης, κάντε κλικ στο κουμπί {}."
            }
        },
        pt: {
            lower: true,
                msg: {
                pass: "senha",
                    and: "e",
                    showPass: "Mostrar senha",
                    hidePass: "Ocultar senha",
                    genPass: "Senha aleatória",
                    passTooShort: "senha muito curta (tamanho mínimo: {})",
                    noCharType: "Senha deve conter {}",
                    digits: "dígito",
                    letters: "letras",
                    lettersUp: "letras maiúsculas",
                    symbols: "símbolos",
                    inBlackList: "senha está na lista das senhas mais usadas",
                    passRequired: "senha é obrigatória",
                    equalTo: "senha igual ao login",
                    repeat: "senha consiste em uma repetição de caracteres",
                    badChars: "senha tem caracteres inválidos: “{}”",
                    weakWarn: "fraca",
                    invalidPassWarn: "*",
                    weakTitle: "Esta senha é fraca",
                    generateMsg: "Para gerar uma senha forte, clique no botão {}."
            }
        }
    };
})(window.jQuery, document, window);
<commit_msg>fix jshint warning<commit_after>/**
 * @license Pass*Field | (c) 2013 Antelle | https://github.com/antelle/passfield/blob/master/MIT-LICENSE.txt
 */

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

/**
 * Entry point
 * Initializes PassField
 * If jQuery is present, sets jQuery plugin ($.passField)
 */
(function($, document, window) {
    "use strict";

    var PassField = window.PassField = window.PassField || {};
    PassField.Config = PassField.Config || {};

    PassField.Config.locales = {
        en: {
            lower: true,
                msg: {
                pass: "password",
                    and: "and",
                    showPass: "Show password",
                    hidePass: "Hide password",
                    genPass: "Random password",
                    passTooShort: "password is too short (min. length: {})",
                    noCharType: "password must contain {}",
                    digits: "digits",
                    letters: "letters",
                    lettersUp: "letters in UPPER case",
                    symbols: "symbols",
                    inBlackList: "password is in list of top used passwords",
                    passRequired: "password is required",
                    equalTo: "password is equal to login",
                    repeat: "password consists of repeating characters",
                    badChars: "password contains bad characters: “{}”",
                    weakWarn: "weak",
                    invalidPassWarn: "*",
                    weakTitle: "This password is weak",
                    generateMsg: "To generate a strong password, click {} button."
            }
        },
        de: {
            lower: false,
                msg: {
                pass: "Passwort",
                    and: "und",
                    showPass: "Passwort anzeigen",
                    hidePass: "Passwort verbergen",
                    genPass: "Zufallspasswort",
                    passTooShort: "Passwort ist zu kurz (Mindestlänge: {})",
                    noCharType: "Passwort muss {} enthalten",
                    digits: "Ziffern",
                    letters: "Buchstaben",
                    lettersUp: "Buchstaben in GROSSSCHRIFT",
                    symbols: "Symbole",
                    inBlackList: "Passwort steht auf der Liste der beliebtesten Passwörter",
                    passRequired: "Passwort wird benötigt",
                    equalTo: "Passwort ist wie Anmeldung",
                    repeat: "Passwort besteht aus sich wiederholenden Zeichen",
                    badChars: "Passwort enthält ungültige Zeichen: “{}”",
                    weakWarn: "Schwach",
                    invalidPassWarn: "*",
                    weakTitle: "Dieses Passwort ist schwach",
                    generateMsg: "Klicken Sie auf den {}-Button, um ein starkes Passwort zu generieren."
            }
        },
        fr: {
            lower: true,
                msg: {
                pass: "mot de passe",
                    and: "et",
                    showPass: "Montrer le mot de passe",
                    hidePass: "Cacher le mot de passe",
                    genPass: "Mot de passe aléatoire",
                    passTooShort: "le mot de passe est trop court (min. longueur: {})",
                    noCharType: "le mot de passe doit contenir des {}",
                    digits: "chiffres",
                    letters: "lettres",
                    lettersUp: "lettres en MAJUSCULES",
                    symbols: "symboles",
                    inBlackList: "le mot de passe est dans la liste des plus utilisés",
                    passRequired: "le mot de passe est requis",
                    equalTo: "le mot de passe est le même que l'identifiant",
                    repeat: "le mot de passe est une répétition de caractères",
                    badChars: "le mot de passe contient des caractères incorrects: “{}”",
                    weakWarn: "faible",
                    invalidPassWarn: "*",
                    weakTitle: "Ce mot de passe est faible",
                    generateMsg: "Pour créer un mot de passe fort cliquez sur le bouton {}."
            }
        },
        it: {
            lower: false,
                msg: {
                pass: "password",
                    and: "e",
                    showPass: "Mostra password",
                    hidePass: "Nascondi password",
                    genPass: "Password casuale",
                    passTooShort: "la password è troppo breve (lunghezza min.: {})",
                    noCharType: "la password deve contenere {}",
                    digits: "numeri",
                    letters: "lettere",
                    lettersUp: "lettere in MAIUSCOLO",
                    symbols: "simboli",
                    inBlackList: "la password è nella lista delle password più usate",
                    passRequired: "è necessaria una password",
                    equalTo: "la password è uguale al login",
                    repeat: "la password è composta da caratteri che si ripetono",
                    badChars: "la password contiene caratteri non accettati: “{}”",
                    weakWarn: "debole",
                    invalidPassWarn: "*",
                    weakTitle: "Questa password è debole",
                    generateMsg: "Per generare una password forte, clicca sul tasto {}."
            }
        },
        nl: {
            lower: true,
                msg: {
                pass: "wachtwoord",
                    and: "en",
                    showPass: "Toon wachtwoord",
                    hidePass: "Verberg wachtwoord",
                    genPass: "Willekeurig wachtwoord",
                    passTooShort: "Wachtwoord is te kort (min. lengte: {})",
                    noCharType: "wachtwoord moet {} bevatten",
                    digits: "cijfers",
                    letters: "letters",
                    lettersUp: "hoofdletters",
                    symbols: "symbolen",
                    inBlackList: "wachtwoord is in de lijst meest gebruikte wachtwoorden",
                    passRequired: "wachtwoord is verplicht",
                    equalTo: "wachtwoord is hetzelfde als login",
                    repeat: "wachtwoord bestaat uit herhaalde karakters",
                    badChars: "wachtwoord bevat verboden karakters: “{}”",
                    weakWarn: "zwak",
                    invalidPassWarn: "*",
                    weakTitle: "Dit wachtwoord is zwak",
                    generateMsg: "Klik {} om een sterk wachtwoord te genereren."
            }
        },
        ru: {
            lower: true,
                msg: {
                pass: "пароль",
                    and: "и",
                    showPass: "Показать пароль",
                    hidePass: "Скрыть пароль",
                    genPass: "Случайный пароль",
                    passTooShort: "пароль слишком короткий (мин. длина: {})",
                    noCharType: "в пароле должны быть {}",
                    digits: "цифры",
                    letters: "буквы",
                    lettersUp: "буквы в ВЕРХНЕМ регистре",
                    symbols: "символы",
                    inBlackList: "этот пароль часто используется в Интернете",
                    passRequired: "пароль обязателен",
                    equalTo: "пароль совпадает с логином",
                    repeat: "пароль состоит из повторяющихся символов",
                    badChars: "в пароле есть недопустимые символы: «{}»",
                    weakWarn: "слабый",
                    invalidPassWarn: "*",
                    weakTitle: "Пароль слабый, его легко взломать",
                    generateMsg: "Чтобы сгенерировать пароль, нажмите кнопку {}."
            }
        },
        ua: {
            lower: true,
                msg: {
                pass: "пароль",
                    and: "i",
                    showPass: "Показати пароль",
                    hidePass: "Сховати пароль",
                    genPass: "Випадковий пароль",
                    passTooShort: "пароль є занадто коротким (мiн. довжина: {})",
                    noCharType: "пароль повинен містити {}",
                    digits: "цифри",
                    letters: "букви",
                    lettersUp: "букви у ВЕРХНЬОМУ регістрі",
                    symbols: "cимволи",
                    inBlackList: "пароль входить до списку паролей, що використовуються найчастіше",
                    passRequired: "пароль є обов'язковим",
                    equalTo: "пароль та логін однакові",
                    repeat: "пароль містить повторювані символи",
                    badChars: "пароль містить неприпустимі символи: «{}»",
                    weakWarn: "слабкий",
                    invalidPassWarn: "*",
                    weakTitle: "Цей пароль є слабким",
                    generateMsg: "Щоб створити надійний пароль, натисніть кнопку {}."
            }
        },
        es: {
            lower: true,
                msg: {
                pass: "contraseña",
                    and: "y",
                    showPass: "Mostrar contraseña",
                    hidePass: "Ocultar contraseña",
                    genPass: "Contraseña aleatoria",
                    passTooShort: "contraseña demasiado corta (longitud mín.: {})",
                    noCharType: "la contraseña debe contener {}",
                    digits: "dígitos",
                    letters: "letras",
                    lettersUp: "letras en MAYÚSCULAS",
                    symbols: "símbolos",
                    inBlackList: "la contraseña está en la lista de las contraseñas más usadas",
                    passRequired: "se requiere contraseña",
                    equalTo: "la contraseña es igual al inicio de sesión",
                    repeat: "la contraseña tiene caracteres repetidos",
                    badChars: "la contraseña contiene caracteres no permitidos: “{}”",
                    weakWarn: "débil",
                    invalidPassWarn: "*",
                    weakTitle: "Esta contraseña es débil",
                    generateMsg: "Para generar una contraseña segura, haga clic en el botón de {}."
            }
        },
        el: {
            lower: true,
                msg: {
                pass: "πρόσβασης",
                    and: "και",
                    showPass: "Προβολή κωδικού πρόσβασης",
                    hidePass: "Απόκρυψη κωδικού πρόσβασης",
                    genPass: "Τυχαίος κωδικός πρόσβασης",
                    passTooShort: "ο κωδικός πρόσβασης είναι πολύ μικρός (ελάχιστο μήκος: {})",
                    noCharType: "ο κωδικός πρόσβασης πρέπει να περιέχει {}",
                    digits: "ψηφία",
                    letters: "λατινικά γράμματα",
                    lettersUp: "λατινικά γράμματα με ΚΕΦΑΛΑΙΑ",
                    symbols: "σύμβολα",
                    inBlackList: "ο κωδικός πρόσβασης βρίσκεται σε κατάλογο δημοφιλέστερων κωδικών",
                    passRequired: "απαιτείται κωδικός πρόσβασης",
                    equalTo: "ο κωδικός είναι όμοιος με το όνομα χρήστη",
                    repeat: "ο κωδικός αποτελείται από επαναλαμβανόμενους χαρακτήρες",
                    badChars: "ο κωδικός περιέχει μη επιτρεπτούς χαρακτήρες: “{}”",
                    weakWarn: "αδύναμος",
                    invalidPassWarn: "*",
                    weakTitle: "Αυτός ο κωδικός πρόσβασης είναι αδύναμος",
                    generateMsg: "Για να δημιουργήσετε δυνατό κωδικό πρόσβασης, κάντε κλικ στο κουμπί {}."
            }
        },
        pt: {
            lower: true,
                msg: {
                pass: "senha",
                    and: "e",
                    showPass: "Mostrar senha",
                    hidePass: "Ocultar senha",
                    genPass: "Senha aleatória",
                    passTooShort: "senha muito curta (tamanho mínimo: {})",
                    noCharType: "Senha deve conter {}",
                    digits: "dígito",
                    letters: "letras",
                    lettersUp: "letras maiúsculas",
                    symbols: "símbolos",
                    inBlackList: "senha está na lista das senhas mais usadas",
                    passRequired: "senha é obrigatória",
                    equalTo: "senha igual ao login",
                    repeat: "senha consiste em uma repetição de caracteres",
                    badChars: "senha tem caracteres inválidos: “{}”",
                    weakWarn: "fraca",
                    invalidPassWarn: "*",
                    weakTitle: "Esta senha é fraca",
                    generateMsg: "Para gerar uma senha forte, clique no botão {}."
            }
        }
    };
})(window.jQuery, document, window);
<|endoftext|>


<commit_before>"use strict";

var fs = require('fs');

var Player = require('./player.js');
var Utils = require('./utils.js');

function World () {

	var _this = this;
	_this.state = resetState();

	function resetState () {
		var state = {
			session: "end",
			health: 50,
			players: {},
			controls: JSON.parse(fs.readFileSync('./controls.json', { encoding: 'utf8' })),
			outstanding: {},

			MINPLAYERS: 1,
			CONTROLS_PER_PLAYER: 4,
			COMMAND_TTL: 5000,

			HEALTH_TIMEOUT_PENALTY: -10,
			HEALTH_BONUS: 5,
		};

		state.controls = state.controls.unified;
		return state;
	}

	_this.setHealthIncr = function (incr) {
		var val = _this.state.health + incr;
		_this.state.health = Utils.clamp(val, 0, 100);
		_this.broadcast('health', _this.state.health);
	};

	_this.addPlayer = function (socket) {
		var player = new Player({
			socket: socket,
			world: _this,
		});

		var ip = socket.handshake.address.address;
		_this.state.players[ip] = player;

		socket.on('disconnect', function () {
			delete _this.state[ip];
			_this.endGame();
		});
	};

	_this.startGame = function () {
		var num_players = _this.numPlayersOnline();
		if (num_players < _this.state.MINPLAYERS) {
			_this.broadcast('alert', {
				text: Utils.plural(num_players, "You need " + (_this.state.MINPLAYERS - num_players) + " more player[[s]] to start."),
			});
			return;
		}

		_this.state.session = 'start';
		_this.broadcast('lifecycle', 'start');

		assignControls();

		_this.broadcast('health', _this.state.health);

		issueInitialCommands();

		setTimeout(function () {
			_this.endGame();
		}, 30000);
	};

	_this.endGame = function () {
		var players = _this.state.players;
		_this.state = resetState();
		_this.state.players = players;

		Utils.forEach(players, function (ip, player) {
			player.resetPlayerCommandState();
		});

		_this.broadcast('lifecycle', 'stop');
	};

	_this.broadcast = function (evt, payload) {
		Utils.forEach(_this.state.players, function (ip, player) {
			player.send(evt, payload);
		});
	};

	_this.numPlayersOnline = function () {
		return Object.keys(_this.state.players).length;
	};

	function issueInitialCommands () {
		Utils.forEach(_this.state.players, function (ip, player) {
			player.issueRandomCommand();
		});
	};

	function assignControls () {
		var controls = JSON.parse(JSON.stringify(_this.state.controls));
		var keys = Object.keys(controls);

		// Randomly assign controls
		Utils.forEach(_this.state.players, function (ip, player) {

			var player_controls = {};
			for (var ct = 0; ct < _this.state.CONTROLS_PER_PLAYER; ct++) {
				var control_id = Utils.random_choice_no_replacement(keys);
				var control = controls[control_id];
				player_controls[control_id] = control.label;
			}

			player.setControls(player_controls);
		});
	}
}

module.exports = World;<commit_msg>first test run success<commit_after>"use strict";

var fs = require('fs');

var Player = require('./player.js');
var Utils = require('./utils.js');

function World () {

	var _this = this;
	_this.state = resetState();

	function resetState () {
		var state = {
			session: "end",
			health: 50,
			players: {},
			controls: JSON.parse(fs.readFileSync('./controls.json', { encoding: 'utf8' })),
			outstanding: {},

			MINPLAYERS: 2,
			CONTROLS_PER_PLAYER: 4,
			COMMAND_TTL: 5000,

			HEALTH_TIMEOUT_PENALTY: -10,
			HEALTH_BONUS: 5,
		};

		state.controls = state.controls.unified;
		return state;
	}

	_this.setHealthIncr = function (incr) {
		var val = _this.state.health + incr;
		_this.state.health = Utils.clamp(val, 0, 100);
		_this.broadcast('health', _this.state.health);
	};

	_this.addPlayer = function (socket) {
		var player = new Player({
			socket: socket,
			world: _this,
		});

		var ip = socket.handshake.address.address;
		_this.state.players[ip] = player;

		socket.on('disconnect', function () {
			delete _this.state[ip];
			_this.endGame();
		});
	};

	_this.startGame = function () {
		var num_players = _this.numPlayersOnline();
		if (num_players < _this.state.MINPLAYERS) {
			_this.broadcast('alert', {
				text: Utils.plural(num_players, "You need " + (_this.state.MINPLAYERS - num_players) + " more player[[s]] to start."),
			});
			return;
		}

		_this.state.session = 'start';
		_this.broadcast('lifecycle', 'start');

		assignControls();

		_this.broadcast('health', _this.state.health);

		issueInitialCommands();

		setTimeout(function () {
			_this.endGame();
		}, 30000);
	};

	_this.endGame = function () {
		var players = _this.state.players;
		_this.state = resetState();
		_this.state.players = players;

		Utils.forEach(players, function (ip, player) {
			player.resetPlayerCommandState();
		});

		_this.broadcast('lifecycle', 'stop');
	};

	_this.broadcast = function (evt, payload) {
		Utils.forEach(_this.state.players, function (ip, player) {
			player.send(evt, payload);
		});
	};

	_this.numPlayersOnline = function () {
		return Object.keys(_this.state.players).length;
	};

	function issueInitialCommands () {
		Utils.forEach(_this.state.players, function (ip, player) {
			player.issueRandomCommand();
		});
	};

	function assignControls () {
		var controls = JSON.parse(JSON.stringify(_this.state.controls));
		var keys = Object.keys(controls);

		// Randomly assign controls
		Utils.forEach(_this.state.players, function (ip, player) {

			var player_controls = {};
			for (var ct = 0; ct < _this.state.CONTROLS_PER_PLAYER; ct++) {
				var control_id = Utils.random_choice_no_replacement(keys);
				var control = controls[control_id];
				player_controls[control_id] = control.label;
			}

			player.setControls(player_controls);
		});
	}
}

module.exports = World;<|endoftext|>


<commit_before>/*global GIGO, jQuery,  window,  alert,  Browser */
/*jslint browser: true */
/*properties mirrors, site, loc, provider, url, hide */

/* jslint complains about UTF8 characters in Cyrillic and Japanese.  I am ignoring that complaint.
I have seen no feedback that says the text shows up incorrectly. */

GIGO.mirrors = [{
    "site": "test-ipv6.com",
    "loc": "US CA",
    "provider": "Jason Fesler",
    "hide": 0
}, {
    "site": "test-ipv6.asnet.am",
    "loc": "AM",
    "provider": "Academic Scientific Research Computer Network of Armenia",
    "hide": 1
}, {
    "site": "test-ipv6.at",
    "loc": "AT",
    "provider": "Silver Server GmbH",
    "hide": 1
}, {
    "site": "test-ipv6.waia.asn.au",
    "loc": "AU",
    "provider": "Western Australian Internet Association",
    "hide": 1
}, {
    "site": "test-ipv6.cz",
    "loc": "CZ",
    "provider": "nic.cz",
    "hide": 1
}, {
    "site": "test-ipv6.monash.edu",
    "loc": "AU",
    "provider": "Monash University",
    "hide": 0
}, {
    "site": "test-ipv6.ceengine.eu",
    "loc": "RO",
    "provider": "Central and Eastern European Networking Engine",
    "hide": 0
}, {
    "site": "test-ipv6.internet.fo",
    "loc": "FO",
    "provider": "Faroese Telecom",
    "hide": 1
}, {
    "site": "test-ipv6.sth.sze.hu",
    "loc": "HU",
    "provider": "NetClub, Szechenyi Istvan University",
    "hide": 1
}, {
    "site": "test-ipv6.carnet.hr",
    "loc": "HR",
    "provider": "Croatian Academic and Research Network",
    "hide": 0
}, {
    "site": "test-ipv6.hu",
    "loc": "HU",
    "provider": "Polaris-N Systems",
    "hide": 0
}, {
    "site": "test-ipv6.jp",
    "loc": "JP",
    "provider": "CLARA ONLINE, Inc",
    "hide": 1
}, {
    "site": "test-ipv6.nl",
    "loc": "NL",
    "provider": "BIT BV",
    "hide": 0
}, {
    "site": "test-ipv6.roedu.net",
    "loc": "RO",
    "provider": "RoEduNet",
    "hide": 0
}, {
    "site": "test-ipv6.ro",
    "loc": "RO",
    "provider": "RCS & RDS",
    "skipsites": 1,
    "hide": 0
}, {
    "site": "test-ipv6.se",
    "loc": "SE",
    "provider": "Interlan Gefle AB",
    "hide": 1
}, {
    "site": "test-ipv6.ams.vr.org",
    "loc": "NL",
    "provider": "vr.org",
    "hide": 0
}, {
    "site": "test-ipv6.chi.vr.org",
    "loc": "US IL",
    "provider": "vr.org",
    "hide": 0
}, {
    "site": "test-ipv6.iad.vr.org",
    "loc": "US VA",
    "provider": "vr.org",
    "hide": 0
}, {
    "site": "test-ipv6.sjc.vr.org",
    "loc": "US CA",
    "provider": "vr.org",
    "hide": 0
}, {
    "site": "test-ipv6.ceengine.eu",
    "loc": "RO",
    "provider": "Central and Eastern European Networking Engine",
    "hide": 0
}, {
    "site": "test-ipv6.chiveworthy.com",
    "loc": "US WI",
    "provider": "Dale Hartung",
    "skipsites": 0,
    "hide": 0
}, {
    "site": "test-ipv6.gremlin.ca",
    "loc": "CA",
    "provider": "Scott Logan",
    "skipsites": 0,
    "hide": 0
}, {
    "site": "ipv6-test.co.uk",
    "loc": "UK",
    "provider": "BSC-Telecom",
    "skipsites": 1,
    "hide": 0
}, {
    "site": "test-ipv6.edu.cn",
    "loc": "CN",
    "provider": "CERNET",
    "skipsites": 0,
    "hide": 0
}, {
    "site": "test-ipv6.vyncke.org",
    "loc": "FR",
    "provider": "Éric Vyncke",
    "hide": 0
}, {
    "site": "test-ipv6.si",
    "loc": "SI",
    "provider": "Damjan Sirnik",
    "hide": 0
}, {
    "site": "test-ipv6.showmyip.ca",
    "loc": "UK",
    "provider": "Christopher Munz-Michielin",
    "hide": 0
}, {
    "site": "test-ipv6.no",
    "loc": "NO",
    "provider": "Availo AS",
    "hide": 0
}, {
    "site": "test-ipv6.co.za",
    "loc": "ZA",
    "provider": "Neology",
    "hide": 0
}, {
    "site": "test-ipv6.vtt.net",
    "loc": "RU",
    "provider": "JSC \"Volgatranstelecom\"",
    "hide": 1
}, {
    "site": "test-ipv6.vn.ua",
    "loc": "UA",
    "provider": "IP-Connect LLC",
    "hide": 0
}, {
    "site": "test-ipv6.go6.si",
    "loc": "SI",
    "provider": "Go6 Lab - Slovenian IPv6 Iniciative",
    "hide": 0
}, {
    "site": "v6research.net",
    "loc": "US",
    "provider": "Bill Cerveny",
    "hide": 0
}];
<commit_msg>test-ivp6.se restored<commit_after>/*global GIGO, jQuery,  window,  alert,  Browser */
/*jslint browser: true */
/*properties mirrors, site, loc, provider, url, hide */

/* jslint complains about UTF8 characters in Cyrillic and Japanese.  I am ignoring that complaint.
I have seen no feedback that says the text shows up incorrectly. */

GIGO.mirrors = [{
    "site": "test-ipv6.com",
    "loc": "US CA",
    "provider": "Jason Fesler",
    "hide": 0
}, {
    "site": "test-ipv6.asnet.am",
    "loc": "AM",
    "provider": "Academic Scientific Research Computer Network of Armenia",
    "hide": 1
}, {
    "site": "test-ipv6.at",
    "loc": "AT",
    "provider": "Silver Server GmbH",
    "hide": 1
}, {
    "site": "test-ipv6.waia.asn.au",
    "loc": "AU",
    "provider": "Western Australian Internet Association",
    "hide": 1
}, {
    "site": "test-ipv6.cz",
    "loc": "CZ",
    "provider": "nic.cz",
    "hide": 1
}, {
    "site": "test-ipv6.monash.edu",
    "loc": "AU",
    "provider": "Monash University",
    "hide": 0
}, {
    "site": "test-ipv6.ceengine.eu",
    "loc": "RO",
    "provider": "Central and Eastern European Networking Engine",
    "hide": 0
}, {
    "site": "test-ipv6.internet.fo",
    "loc": "FO",
    "provider": "Faroese Telecom",
    "hide": 1
}, {
    "site": "test-ipv6.sth.sze.hu",
    "loc": "HU",
    "provider": "NetClub, Szechenyi Istvan University",
    "hide": 1
}, {
    "site": "test-ipv6.carnet.hr",
    "loc": "HR",
    "provider": "Croatian Academic and Research Network",
    "hide": 0
}, {
    "site": "test-ipv6.hu",
    "loc": "HU",
    "provider": "Polaris-N Systems",
    "hide": 0
}, {
    "site": "test-ipv6.jp",
    "loc": "JP",
    "provider": "CLARA ONLINE, Inc",
    "hide": 1
}, {
    "site": "test-ipv6.nl",
    "loc": "NL",
    "provider": "BIT BV",
    "hide": 0
}, {
    "site": "test-ipv6.roedu.net",
    "loc": "RO",
    "provider": "RoEduNet",
    "hide": 0
}, {
    "site": "test-ipv6.ro",
    "loc": "RO",
    "provider": "RCS & RDS",
    "skipsites": 1,
    "hide": 0
}, {
    "site": "test-ipv6.se",
    "loc": "SE",
    "provider": "Interlan Gefle AB",
    "hide": 0
}, {
    "site": "test-ipv6.ams.vr.org",
    "loc": "NL",
    "provider": "vr.org",
    "hide": 0
}, {
    "site": "test-ipv6.chi.vr.org",
    "loc": "US IL",
    "provider": "vr.org",
    "hide": 0
}, {
    "site": "test-ipv6.iad.vr.org",
    "loc": "US VA",
    "provider": "vr.org",
    "hide": 0
}, {
    "site": "test-ipv6.sjc.vr.org",
    "loc": "US CA",
    "provider": "vr.org",
    "hide": 0
}, {
    "site": "test-ipv6.ceengine.eu",
    "loc": "RO",
    "provider": "Central and Eastern European Networking Engine",
    "hide": 0
}, {
    "site": "test-ipv6.chiveworthy.com",
    "loc": "US WI",
    "provider": "Dale Hartung",
    "skipsites": 0,
    "hide": 0
}, {
    "site": "test-ipv6.gremlin.ca",
    "loc": "CA",
    "provider": "Scott Logan",
    "skipsites": 0,
    "hide": 0
}, {
    "site": "ipv6-test.co.uk",
    "loc": "UK",
    "provider": "BSC-Telecom",
    "skipsites": 1,
    "hide": 0
}, {
    "site": "test-ipv6.edu.cn",
    "loc": "CN",
    "provider": "CERNET",
    "skipsites": 0,
    "hide": 0
}, {
    "site": "test-ipv6.vyncke.org",
    "loc": "FR",
    "provider": "Éric Vyncke",
    "hide": 0
}, {
    "site": "test-ipv6.si",
    "loc": "SI",
    "provider": "Damjan Sirnik",
    "hide": 0
}, {
    "site": "test-ipv6.showmyip.ca",
    "loc": "UK",
    "provider": "Christopher Munz-Michielin",
    "hide": 0
}, {
    "site": "test-ipv6.no",
    "loc": "NO",
    "provider": "Availo AS",
    "hide": 0
}, {
    "site": "test-ipv6.co.za",
    "loc": "ZA",
    "provider": "Neology",
    "hide": 0
}, {
    "site": "test-ipv6.vtt.net",
    "loc": "RU",
    "provider": "JSC \"Volgatranstelecom\"",
    "hide": 1
}, {
    "site": "test-ipv6.vn.ua",
    "loc": "UA",
    "provider": "IP-Connect LLC",
    "hide": 0
}, {
    "site": "test-ipv6.go6.si",
    "loc": "SI",
    "provider": "Go6 Lab - Slovenian IPv6 Iniciative",
    "hide": 0
}, {
    "site": "v6research.net",
    "loc": "US",
    "provider": "Bill Cerveny",
    "hide": 0
}];
<|endoftext|>


<commit_before>var GameState = function(game) {
        this.MAX_CATS = 3; // number of cats
};

GameState.prototype.preload = function() {
    this.game.load.image('tile', 'assets/env/tile.png');
    this.game.load.image('room', 'assets/env/room.png');
    this.game.load.spritesheet('heart', 'assets/env/heart-sprite.png');

    // Load items assets
    this.game.load.image('yarn', 'assets/items/yarn.png');

    // Load cat assets
    this.game.load.spritesheet('garfield', 'assets/cats/garfield-sprite.png', 100, 100);
    this.game.load.spritesheet('marie', 'assets/cats/marie-sprite.png', 100, 100);
    this.game.load.spritesheet('mimi', 'assets/cats/mimi-sprite.png', 100, 100);
    this.game.load.spritesheet('whitey', 'assets/cats/whitey-sprite.png', 100, 100);

    // Audio!
    this.game.load.audio('theme', 'assets/theme.ogg');
    this.game.load.audio('meow', 'assets/meow.ogg');

    // Background!
    this.game.load.image('background', 'assets/background.jpg');
};

GameState.prototype.create = function() {
    // Set stage background to something sky colored
    this.game.stage.backgroundColor = 0x4488cc;

    this.game.add.image(0, 0, 'background');

    // Create a group to hold the cat
    this.catGroup = this.game.add.group();
    this.obstacleGroup = this.game.add.group();

    this.target = game.add.sprite(10, 0, 'tile');
    this.target.scale.setTo(1, this.game.height/this.target.height);

    game.input.onDown.add(this.placeObstacle, this, 0, this);

    this.theme = this.game.add.audio('theme');
    meow = this.game.add.audio('meow');
    // Loop theme forever
    this.theme.loopFull(0.5);
};

GameState.prototype.update = function() {
    // If there are fewer than MAX_CATS, launch a new one
    if (this.catGroup.countLiving() < this.MAX_CATS) {
        // Set the launch point to a random location past the right edge
        // of the stage
        var newcat = this.launchCat(this.game.width - 100,
			this.game.rnd.integerInRange(50, this.game.height-50));

        // Move cat to the center of the left edge of the stage
        this.game.physics.arcade.moveToXY(newcat, 0, this.game.width / 2);
    }

    this.game.physics.arcade.collide(this.catGroup, this.obstacleGroup);
};

GameState.prototype.launchCat = function(x, y) {
    // Get the first dead cat from the catGroup :'(
    var cat = this.catGroup.getFirstDead();

    // If there aren't any available, create a new one
    if (cat === null) {
        cat = new Cat(this.game);
        this.catGroup.add(cat);
    }

    // Revive the cat (set it's alive property to true)
    // You can also define a onRevived event handler in your cat objects
    // to do stuff when they are revived.
    cat.revive();

    // Move the cat to the given coordinates
    cat.x = x;
    cat.y = y;

    return cat;
};

var Cat = function (game, x, y) {
    var type = ['garfield',
            'marie',
            'mimi',
            'whitey'][game.rnd.integerInRange(0,3)];

    Phaser.Sprite.call(this, game, x, y, type);
    this.animations.add('left', [0, 1], 2, true, true);

    game.physics.enable(this, Phaser.Physics.ARCADE);

    this.body.onCollide = new Phaser.Signal();
    this.body.onCollide.add(function(me, other) {
       console.log(me, other);
       meow.play();
       me.damage(100);
       other.damage(50);

      // TODO:
      // When a cat collides with an obstacle, that obstacle should lose one
      // hit point and the cat should lose one hit point.
      //
      // This can probably be done with sprite health things. If the health
      // gets to zero, either the obstacle or the cat is kill.
      //
      // If the cat is alive after a collision it should try to move again. If
      // the obstacle is alive again, it'll collide again. This repeats until
      // one or both dies.
      //
      // If the cat survives and obstacle dies, the cat should begin moving
      // back to the target.
    });
}

Cat.prototype = Object.create(Phaser.Sprite.prototype);
Cat.prototype.constructor = Cat;

Cat.prototype.update = function() {
    this.animations.play('left');
};

/*
    Obstacles code
*/

GameState.prototype.placeObstacle = function (t) {
    console.log(t.x, t.y);
    x = t.x;
    y = t.y;
    // Get the first dead cat from the catGroup :'(
    var obstacle = this.obstacleGroup.getFirstDead();

    // If there aren't any available, create a new one
    if (obstacle === null) {
        obstacle = new Obstacle(this.game);
        this.obstacleGroup.add(obstacle);
    }

    // Revive the cat (set it's alive property to true)
    // You can also define a onRevived event handler in your cat objects
    // to do stuff when they are revived.
    obstacle.revive();

    // Move the cat to the given coordinates
    obstacle.x = x;
    obstacle.y = y;

    return obstacle;
}

var Obstacle = function (game, x, y) {
    console.log(x,y);
    Phaser.Sprite.call(this, game, x, y, 'yarn');
    this.game.physics.enable(this, Phaser.Physics.ARCADE);
    this.body.immovable = true;
}

Obstacle.prototype = Object.create(Phaser.Sprite.prototype);
Obstacle.prototype.constructor = Obstacle;

var game = new Phaser.Game(1024, 768, Phaser.AUTO, 'game');
game.state.add('game', GameState, true);
<commit_msg>Remove target bar<commit_after>var GameState = function(game) {
        this.MAX_CATS = 3; // number of cats
};

GameState.prototype.preload = function() {
    this.game.load.image('tile', 'assets/env/tile.png');
    this.game.load.image('room', 'assets/env/room.png');
    this.game.load.spritesheet('heart', 'assets/env/heart-sprite.png');

    // Load items assets
    this.game.load.image('yarn', 'assets/items/yarn.png');

    // Load cat assets
    this.game.load.spritesheet('garfield', 'assets/cats/garfield-sprite.png', 100, 100);
    this.game.load.spritesheet('marie', 'assets/cats/marie-sprite.png', 100, 100);
    this.game.load.spritesheet('mimi', 'assets/cats/mimi-sprite.png', 100, 100);
    this.game.load.spritesheet('whitey', 'assets/cats/whitey-sprite.png', 100, 100);

    // Audio!
    this.game.load.audio('theme', 'assets/theme.ogg');
    this.game.load.audio('meow', 'assets/meow.ogg');

    // Background!
    this.game.load.image('background', 'assets/background.jpg');
};

GameState.prototype.create = function() {
    // Set stage background
    this.game.add.image(0, 0, 'background');

    // Create a group to hold the cat
    this.catGroup = this.game.add.group();
    this.obstacleGroup = this.game.add.group();

    game.input.onDown.add(this.placeObstacle, this, 0, this);

    this.theme = this.game.add.audio('theme');
    meow = this.game.add.audio('meow');
    // Loop theme forever
    this.theme.loopFull(0.5);
};

GameState.prototype.update = function() {
    // If there are fewer than MAX_CATS, launch a new one
    if (this.catGroup.countLiving() < this.MAX_CATS) {
        // Set the launch point to a random location past the right edge
        // of the stage
        var newcat = this.launchCat(this.game.width - 100,
			this.game.rnd.integerInRange(50, this.game.height-50));

        // Move cat to the center of the left edge of the stage
        this.game.physics.arcade.moveToXY(newcat, 0, this.game.width / 2);
    }

    this.game.physics.arcade.collide(this.catGroup, this.obstacleGroup);
};

GameState.prototype.launchCat = function(x, y) {
    // Get the first dead cat from the catGroup :'(
    var cat = this.catGroup.getFirstDead();

    // If there aren't any available, create a new one
    if (cat === null) {
        cat = new Cat(this.game);
        this.catGroup.add(cat);
    }

    // Revive the cat (set it's alive property to true)
    // You can also define a onRevived event handler in your cat objects
    // to do stuff when they are revived.
    cat.revive();

    // Move the cat to the given coordinates
    cat.x = x;
    cat.y = y;

    return cat;
};

var Cat = function (game, x, y) {
    var type = ['garfield',
            'marie',
            'mimi',
            'whitey'][game.rnd.integerInRange(0,3)];

    Phaser.Sprite.call(this, game, x, y, type);
    this.animations.add('left', [0, 1], 2, true, true);

    game.physics.enable(this, Phaser.Physics.ARCADE);

    this.body.onCollide = new Phaser.Signal();
    this.body.onCollide.add(function(me, other) {
       console.log(me, other);
       meow.play();
       me.damage(100);
       other.damage(50);

      // TODO:
      // When a cat collides with an obstacle, that obstacle should lose one
      // hit point and the cat should lose one hit point.
      //
      // This can probably be done with sprite health things. If the health
      // gets to zero, either the obstacle or the cat is kill.
      //
      // If the cat is alive after a collision it should try to move again. If
      // the obstacle is alive again, it'll collide again. This repeats until
      // one or both dies.
      //
      // If the cat survives and obstacle dies, the cat should begin moving
      // back to the target.
    });
}

Cat.prototype = Object.create(Phaser.Sprite.prototype);
Cat.prototype.constructor = Cat;

Cat.prototype.update = function() {
    this.animations.play('left');
};

/*
    Obstacles code
*/

GameState.prototype.placeObstacle = function (t) {
    console.log(t.x, t.y);
    x = t.x;
    y = t.y;
    // Get the first dead cat from the catGroup :'(
    var obstacle = this.obstacleGroup.getFirstDead();

    // If there aren't any available, create a new one
    if (obstacle === null) {
        obstacle = new Obstacle(this.game);
        this.obstacleGroup.add(obstacle);
    }

    // Revive the cat (set it's alive property to true)
    // You can also define a onRevived event handler in your cat objects
    // to do stuff when they are revived.
    obstacle.revive();

    // Move the cat to the given coordinates
    obstacle.x = x;
    obstacle.y = y;

    return obstacle;
}

var Obstacle = function (game, x, y) {
    console.log(x,y);
    Phaser.Sprite.call(this, game, x, y, 'yarn');
    this.game.physics.enable(this, Phaser.Physics.ARCADE);
    this.body.immovable = true;
}

Obstacle.prototype = Object.create(Phaser.Sprite.prototype);
Obstacle.prototype.constructor = Obstacle;

var game = new Phaser.Game(1024, 768, Phaser.AUTO, 'game');
game.state.add('game', GameState, true);
<|endoftext|>