:: Edit Genetics [nobr]
<html>
<script type="text/x-template" id="birthFullNameTmpl">
	<% if(["Chinese", "Japanese", "Korean", "Cambodian", "Vietnamese", "Hungarian"].includes(s.nationality)) {
		%><% if(s.birthSurname || s.surname) {	%><%- s.birthSurname || s.surname%> <% } %><b><u><%- s.birthName || s.name %></u></b><%
	} else {
		%><b><u><%- s.birthName || s.name %></u></b><% if(s.birthSurname || s.surname) { %> <%- s.birthSurname || s.surname %><% } %><%
	} %>
</script>
<script type="text/x-template" id="currentFullNameTmpl">
	<% if(["Chinese", "Japanese", "Korean", "Cambodian", "Vietnamese", "Hungarian"].includes(s.nationality)) {
		%><% if(s.slaveSurname) {	%><%- s.slaveSurname %> <% } %><b><u><%- s.slaveName %></u></b><%
	} else {
		%><b><u><%- s.slaveName %></u></b><% if(s.slaveSurname) { %> <%- s.slaveSurname %><% } %><%
	} %>
</script>
<script type="text/x-template" id="nameButtonTmpl">
	<button class="slavepicker" data-slave="<%- s.ID %>" style="width: 100%"><%= tmpl.birthFullName(s) %></button>
</script>
<script type="text/x-template" id="geneDetailsTmpl">
	<table class="slave-genetic-details">
		<tr>
			<th>Given name(s)</th><td colspan="2"><%- s.birthName %></td>
			<th>Family name</th><td colspan="2"><%- s.birthSurname || '' %></td>
			<th>Karyotype</th><td class="editor choice-editor" data-param="genes" data-choices="XX, XY"><%- s.genes %> (<%= tmpl.toSex(s.genes) %>)</td>
		</tr>
		<tr>
			<th>Nationality</th><td colspan="2" class="editor string-editor" data-param="nationality"><%- s.nationality %></td>
			<th>Ethnicity</th><td colspan="2" class="editor choice-editor" data-param="race" data-choices="amerindian, asian, black, indo-aryan, latina, middle eastern, pacific islander, semitic, southern european, white, mixed race"><%- s.race %></td>
			<th>Career</th><td><i><%- s.career ? s.career : '' %></i></td>
		</tr>
		<tr>
			<th>Father</th><td colspan="2"><%= tmpl.parentFullName(s.father) %></td>
			<th>Mother</th><td colspan="2"><%= tmpl.parentFullName(s.mother) %></td>
			<th>Age</th><td><%- tmpl.ageDesc(s) %></td>
		</tr>
		<% if(s.origin) { %><tr><td colspan="8"><i><%- s.origin %></td></i></tr><% } %>
		<% if(s.prestigeDesc) { %><tr><td colspan="8"><i><%- s.prestigeDesc %></td></i></tr><% } %>
		<% if(s.customDesc) { %><tr><td colspan="8"><i><%- s.customDesc %></td></i></tr><% } %>
		<% if(s.birthName !== s.slaveName || s.birthSurname !== s.slaveSurname) { %><tr><td colspan="8"><i>Currently called <%= tmpl.currentFullName(s) %>.</td></i></tr><% } %>
		<tr><td></td><td colspan="6"><hr></td><td></td></tr>
		<tr>
			<th>Skin</th><td class="editor string-editor" data-param="origSkin"><%- s.origSkin %></td>
			<th>Skin markings</th><td class="editor choice-editor" data-param="markings" data-choices="none, beauty mark, birthmark, freckles, heavily freckled"><%- s.markings %></td>
			<th>Eyes</th><td class="editor string-editor" data-param="origEye"><%- s.origEye %></td>
			<th>Hair</th><td class="editor string-editor" data-param="origHColor"><%- s.origHColor %></td>
		</tr>
		<tr>
			<th>Height</th><td class="editor number-editor" data-param="height" data-min="50" data-max="250"><%- s.height %> cm (<%= tmpl.toFeet(s.height) %>)</td>
			<th>Weight</th><td class="editor number-editor" data-param="weight"><%= tmpl.percent(s.weight) %></td>
			<th>Muscles</th><td class="editor number-editor" data-param="muscles"><%= tmpl.percent(s.muscles) %></td>
		<td colspan="2"></td></tr>
		<tr>
			<th>Shoulders</th><td class="editor number-editor" data-param="shoulders" data-min="-2" data-max="2"><%= tmpl.shouldersDesc(s.shoulders) %></td>
			<th>Waist</th><td class="editor number-editor" data-param="waist"><%= tmpl.percent(s.waist) %></td>
			<th>Hips</th><td class="editor number-editor" data-param="hips" data-min="-2" data-max="3"><%= tmpl.hipsDesc(s.hips) %></td>
			<th>Rear</th><td class="editor number-editor" data-param="butt" data-min="0" data-max="20"><%= tmpl.buttDesc(s.butt) %></td>
		</tr>
		<tr>
			<th>Breasts</th><td class="editor number-editor" data-param="boobs" data-min="0" data-max="25000"><%- s.boobs %> cc (<%- tmpl.cupCat.cat(s.boobs) %>)</td>
			<th>Breast shape</th><td class="editor choice-editor" data-param="boobShape" data-choices="normal, perky, saggy, torpedo-shaped, downward-facing, wide-set"><%- s.boobShape %></td>
			<th>Nipples</th><td class="editor choice-editor" data-param="nipples" data-choices="huge, puffy, inverted, tiny, cute, partially inverted"><%- s.nipples %></td>
			<th>Areolae</th><td class="editor number-editor" data-param="areolae" data-min="0" data-max="5"><%- tmpl.areolaeDesc(s.areolae) %></td>
		</tr>
		<tr>
			<th>Face</th><td class="editor number-editor" data-param="face"><%- tmpl.faceCat.cat(s.face) %> (<%= tmpl.percent(s.face) %>)</td>
			<th>Lips</th><td class="editor number-editor" data-param="lips" data-min="0" data-max="100"><%- tmpl.lipsCat.cat(s.lips) %> (<%= s.lips %>)</td>
			<th>Teeth</th><td class="editor choice-editor" data-param="teeth" data-choices="normal, crooked, straightening braces, cosmetic braces, removable, pointy"><%- s.teeth %></td>
			<th>Voice</th><td class="editor number-editor" data-param="voice" data-min="0" data-max="3"><%- tmpl.voiceDesc(s.voice) %></td>
		</tr>
		<tr><td></td><td colspan="6"><hr></td><td></td></tr>
		<% if(s.vagina >= 0) { %>
		<tr>
			<th>Labia</th><td class="editor number-editor" data-param="labia" data-min="0" data-max="3"><%- tmpl.labiaDesc(s.labia) %></td>
			<th>Clitoris</th><td class="editor number-editor" data-param="clit" data-min="0" data-max="5"><%- tmpl.clitDesc(s.clit) %></td>
			<th>Ovaries</th><td><%= s.ovaries ? 'working <span class="green">&#x2714;</span>' : 'broken <span class="red">&#x2718;</span>' %></td>
			<th>Ovum type</th><td><%= s.eggType %></td>
		</tr>
		<% } %>
		<% if(s.dick > 0 || s.balls > 0) { %>
		<tr>
			<th>Penis</th><td class="editor number-editor" data-param="dick" data-min="0" data-max="11"><%- tmpl.penisDesc(s.dick) %></td>
			<th>Testes</th><td class="editor number-editor" data-param="balls" data-min="0" data-max="10"><%- tmpl.testesDesc(s.balls) %></td>
			<th>Prostate</th><td><%= s.prostate ? 'working <span class="green">&#x2714;</span>' : 'broken <span class="red">&#x2718;</span>' %></td>
			<th>Spermium type</th><td><%= s.ballType %></td>
		</tr>
		<% } %>
		<tr>
			<th>Hormonal</th><td class="editor number-editor" data-param="hormoneBalance"><%- tmpl.hormonalCat.cat(s.hormoneBalance) %> (<%- s.hormoneBalance %>)</td>
			<th>Anal area</th><td class="editor number-editor" data-param="analArea" data-min="0" data-max="3"><%- s.analArea %></td><td colspan="4"></td>
		</tr>
		<tr><td></td><td colspan="6"><hr></td><td></td></tr>
		<tr>
			<th>Intelligence</th><td class="editor number-editor" data-param="intelligence" data-min="-3" data-max="3"><%= tmpl.intelligenceDesc(s.intelligence) %></td>
			<th>Behavioral</th><td><%= s.behavioralFlaw !== 'none' ? s.behavioralFlaw : s.behavioralQuirk %></td>
			<th>Sexual</th><td><%= s.sexualFlaw !== 'none' ? s.sexualFlaw : s.sexualQuirk %></td>
			<td colspan="2"></td>
		</tr>
		<% if(s.chem > 0 || s.addict > 0) { %>
			<tr>
				<td colspan="8"><%= tmpl.chemicalsDesc(s) %></td>
			</tr>
		<% } %>
	</table>
</script>
<script type="text/javascript">
	var tmpl = tmpl || {};
	tmpl.percent = function(n) {
		if(n > 0) { return '+' + Number(n).toFixed(0) + '%' };
		if(n == 0) { return '&plusmn;0%'; }
		return Number(n).toFixed(0) + '%';
	};
	tmpl.toFeet = function(n) {
		var realFeet = Number(n) / 30.48;
		var feet = Math.floor(realFeet);
		var inches = Math.round((realFeet - feet) * 12);
		return feet + "&prime;" + inches + '&Prime;';
	};
	tmpl.toSex = function(karyotype) {
		return {
			XX: 'female', XY: 'male',
			X: 'Turner syndrome female', X0: 'Turner syndrome female',
			XYY: 'XYY syndrome male', XXY: 'Klinefelter syndrome male', XXX: 'triple X syndrome female'
		}[String(karyotype).toUpperCase()] || 'unknown/not viable';
	};
	tmpl.ageDesc = function(slave) {
		var age = slave.actualAge + (slave.birthWeek + SugarCube.State.variables.week - slave.weekAcquired) / 52.0;
		var years = Math.floor(age);
		var months = Math.floor((age - years) * 12);
		return (years > 0 ? years + 'y ' + months + 'm' : months + 'm');
	};
	tmpl.shouldersDesc = function(s) {
		return ({'-2': 'very narrow', '-1': 'narrow', '0': 'feminine', '1': 'broad', '2': 'very broad'}[s] || 'unknown') + ' (' + Number(s) + ')';
	};
	tmpl.hipsDesc = function(s) {
		return ({'-2': 'very narrow', '-1': 'narrow', '0': 'normal', '1': 'wide', '2': 'very wide', '3': 'inhumanly wide'}[s] || 'unknown') + ' (' + Number(s) + ')';
	};
	tmpl.buttDesc = function(s) {
		return ({
			'0': 'flat', '1': 'small', '2': 'plump', '3': 'big', '4': 'huge', '5': 'enormous', '6': 'gigantic', '7': 'ridiculous',
			'8': 'immense', '9': 'immense', '10': 'immense', '11': 'inhuman', '12': 'inhuman', '13': 'inhuman', '14': 'inhuman',
			'15': 'inhuman', '16': 'inhuman', '17': 'inhuman', '18': 'inhuman', '19': 'inhuman', '20': 'inhuman'}[s] || 'unknown') + ' (' + Number(s) + ')';
	};
	tmpl.cupCat = new Categorizer([0, 'flat'], [300, 'A cup'], [400, 'B cup'], [500, 'C cup'], [650, 'D cup'], [800, 'DD cup'],
		[1000, 'F cup'], [1200, 'G cup'], [1400, 'H cup'], [1600, 'I cup'], [1800, 'J cup'], [2050, 'K cup'], [2300, 'L cup'],
		[2600, 'M cup'], [2900, 'N cup'], [3250, 'O cup'], [3600, 'P cup'], [3950, 'Q cup'], [4300, 'R cup'], [4700, 'S cup'],
		[5100, 'massive']);
	tmpl.areolaeDesc = function(s) {
		return ({'0': 'normal', '1': 'large', '2': 'unusually wide', '3': 'huge', '4': 'heart shaped', '5': 'star shaped'}[s] || 'unknown') + ' (' + Number(s) + ')';
	};
	tmpl.faceCat = new Categorizer(
		[-Infinity, 'very ugly'], [-95, 'ugly'], [-40, 'unattractive'],
		[-10, 'attractive'], [11, 'very pretty'], [41, 'gorgeous'], [96, 'mind blowing']);
	tmpl.lipsCat = new Categorizer([0, 'thin'], [11, 'normal'], [21, 'pretty'], [41, 'plush'], [71, 'huge'], [96, 'facepussy']);
	tmpl.voiceDesc = function(s) {
		return ({'0': 'mute', '1': 'deep', '2': 'feminine', '3': 'high'}[s] || 'unknown') + ' (' + Number(s) + ')';
	};
	tmpl.labiaDesc = function(s) {
		return ({'0': 'minimal', '1': 'big', '2': 'huge', '3': 'huge dangling'}[s] || 'unknown') + ' (' + Number(s) + ')';
	};
	tmpl.clitDesc = function(s) {
		return ({'0': 'normal', '1': 'large', '2': 'huge', '3': 'enormous', '4': 'penis-like', '5': 'like a massive penis'}[s] || 'unknown') + ' (' + Number(s) + ')';
	};
	tmpl.penisDesc = function(s) {
		return ({'0': 'none', '1': 'tiny', '2': 'little', '3': 'normal', '4': 'big', '5': 'huge', '6': 'gigantic',
			'7': 'gigantic', '8': 'titanic', '9': 'absurd', '10': 'inhuman', '11': 'hypertrophied'}[s] || 'unknown') + ' (' + Number(s) + ')';
	};
	tmpl.testesDesc = function(s) {
		return ({'0': 'none', '1': 'vestigal', '2': 'small', '3': 'average', '4': 'big', '5': 'huge', '6': 'enormous',
			'7': 'hypertrophied', '8': 'hypertrophied', '9': 'hypertrophied', '10': 'hypertrophied'}[s] || 'unknown') + ' (' + Number(s) + ')';
	};
	tmpl.hormonalCat = new Categorizer([-Infinity, 'heavy male'], [-49, 'male'], [-24, 'natural'], [25, 'female'], [50, 'heavy female']);
	tmpl.chemicalsDesc = function(slave) {
		var res = [];
		if(slave.chem > 0) {
			res.push('Immune system damaged (' + slave.chem.toFixed(0) + ').');
		}
		if(slave.addict > 0) {
			res.push('Addicted to aphrodisiaca (' + slave.addict.toFixed(0) + ').');
		}
		return res.join(' ');
	};
	tmpl.intelligenceDesc = function(s) {
		return ({
			'-3': 'borderline retarded', '-2': 'very slow', '-1': 'slow', '0': 'average',
			'1': 'smart', '2': 'very smart', '3': 'brilliant'}[s] || 'unknown') + ' (' + Number(s) + ')';
	};
	tmpl.birthFullName = _.template(jQuery('#birthFullNameTmpl').html(), {variable: 's'});
	tmpl.currentFullName = _.template(jQuery('#currentFullNameTmpl').html(), {variable: 's'});
	tmpl.parentFullName = function(id) {
		if(id === 0) { return '<i>unknown</i>'; }
		if(id === -1) {
			/* The PC */
			return tmpl.birthFullName(SugarCube.State.variables.PC) + ' (PC)';
		} else {
			var parent = SugarCube.State.variables.genePool.find(function(s) { return s.ID === id; });
			return (parent ? tmpl.birthFullName(parent) : '<i>missing</i>') + ' (' + id + ')';
		}
	};
	tmpl.nameButton = _.template(jQuery('#nameButtonTmpl').html(), {variable: 's'});
	tmpl.geneDetails = _.template(jQuery('#geneDetailsTmpl').html(), {variable: 's'});
</script>
</html>
<style>
button.selectedslave { background-color: #b84; }
table.slave-genetic-details { width: 100%; margin: 0 10px; border: 1px solid white; table-layout: fixed; font-size: 85%;     border-collapse: separate; padding: 2px; }
table.slave-genetic-details tr { padding: 0 3px; }
table.slave-genetic-details td { vertical-align: top; border: 1px solid black; padding: 0 3px; }
table.slave-genetic-details th { vertical-align: top; background-color: rgba(127, 127, 127, 0.2); padding: 0 3px; }
table.slave-genetic-details td.editor { border: 1px solid #777; }
table.slave-genetic-details td.editor:hover { border: 1px solid #8cf; transition-duration: 0.3s; }
table.slave-genetic-details td.editor input { width: calc(100% + 8px); height: 100%; min-width: 0; margin: 0 -4px;  padding: 4px 5px; box-sizing: border-box; }
table.slave-genetic-details td.editor select { width: calc(100% + 8px); height: 100%; min-width: 0; margin: 0 -4px;  padding: 3px 5px; box-sizing: border-box; border: 1px solid #777; }
table.slave-genetic-details td.editor select:hover { border: 1px solid #8cf; transition-duration: 0.3s; }
</style>
<div style="position: relative; width: 100%; height: 100%;">
	<div id="slaveList" style="width: 20em; float: left;"></div>
	<div id="geneDetails" style="position: fixed; left: 0; right: 0; top: 0; bottom: 0; margin-left: 40em; margin-right: 2.5em; margin-top: 2.5em;"></div>
</div>
<<script>>
	jQuery(function() {
		var slaveList = jQuery('#slaveList');
		_(State.variables.genePool).sortBy(['birthName', 'birthSurname']).forEach(function(s) {
			slaveList.append(tmpl.nameButton(s));
		});
		jQuery(document).off('.edit-genetics');
		jQuery(document).on('keyup.edit-genetics', function(e) {
			if(slaveList.is(':visible')) {
				var currentSelection = jQuery('button.selectedslave');
				switch(e.keyCode) {
					case 38: /* ArrowUp */
						if(currentSelection.length === 0) {
							slaveList.find('button').last().trigger('click');
						} else {
							currentSelection.prev().trigger('click');
						}
						break;
					case 40: /* ArrowDown */
						if(currentSelection.length === 0) {
							slaveList.find('button').first().trigger('click');
						} else {
							currentSelection.next().trigger('click');
						}
						break;
					default:
						/* do nothing */
						break;
				}
			}
		});
		var geneDetails = jQuery('#geneDetails');
		jQuery('.slavepicker').each(function() {
			var el = jQuery(this);
			var id = Number(el.attr('data-slave'));
			el.on('click', function() {
				jQuery('button.selectedslave').removeClass('selectedslave');
				el.addClass('selectedslave');
				var slave = State.variables.genePool.find(function(s) { return s.ID === id; });
				geneDetails.html(tmpl.geneDetails(slave));
				
				var numberEditorOpen = function() {
					var td = jQuery(this);
					td.off('click');
					var attr = String(td.attr('data-param'));
					var min = Number(td.attr('data-min')) || -100;
					var max = Number(td.attr('data-max')) || 100;
					var editableText = jQuery('<input type="number" min="' + min + '" max="' + max + '" />');
					editableText.val(Number(slave[attr]));
					editableText.keyup(function(e) {
						if(e.keyCode === 13) {
							slave[attr] = Number(jQuery(this).val());
							el.trigger('click');
						}
					});
					editableText.blur(function() {
						slave[attr] = Number(jQuery(this).val());
						el.trigger('click');
					});
					td.empty().append(editableText);
					editableText.focus();
				};
				geneDetails.find('td.number-editor').click(numberEditorOpen);
				var stringEditorOpen = function() {
					var td = jQuery(this);
					td.off('click');
					var attr = String(td.attr('data-param'));
					var editableText = jQuery('<input type="text" />');
					editableText.val(String(slave[attr]));
					editableText.keyup(function(e) {
						if(e.keyCode === 13) {
							slave[attr] = String(jQuery(this).val());
							el.trigger('click');
						}
					});
					editableText.blur(function() {
						slave[attr] = String(jQuery(this).val());
						el.trigger('click');
					});
					td.empty().append(editableText);
					editableText.focus();
				};
				geneDetails.find('td.string-editor').click(stringEditorOpen);
				var choiceEditorOpen = function() {
					var td = jQuery(this);
					td.off('click');
					var attr = String(td.attr('data-param'));
					var choices = String(td.attr('data-choices')).split(/,\s*/);
					var editableText = jQuery('<select>' + choices.map(ch => '<option>' + ch + '</option>').join('') + '</select>');
					editableText.val(String(slave[attr]));
					editableText.keyup(function(e) {
						if(e.keyCode === 13) {
							slave[attr] = String(jQuery(this).val());
						}
					});
					editableText.blur(function() {
						slave[attr] = String(jQuery(this).val());
					});
					td.empty().append(editableText);
					editableText.focus();
				};
				geneDetails.find('td.choice-editor').each(choiceEditorOpen);
			});
		});
	});
<</script>>