From e423deffd814db9ba19118aea91a6bef0f75df8a Mon Sep 17 00:00:00 2001
From: ezsh <ezsh.junk@gmail.com>
Date: Wed, 12 Jun 2019 23:38:53 +0200
Subject: [PATCH] RA UI: grid layout for setters

---
 devTools/FC.d.ts                 |   2 +-
 src/003-assets/CSS/RAoptions.css |  18 +-
 src/js/DefaultRules.js           |   2 +-
 src/js/rulesAssistantOptions.js  | 404 ++++++++++++++++++-------------
 4 files changed, 255 insertions(+), 171 deletions(-)

diff --git a/devTools/FC.d.ts b/devTools/FC.d.ts
index 3c0e94debd2..358ed6425ee 100644
--- a/devTools/FC.d.ts
+++ b/devTools/FC.d.ts
@@ -110,7 +110,7 @@ declare namespace App {
 			vaginalAttachment: string;
 			buttplugAttachment: string;
 			eyeColor: string;
-			makeup: string;
+			makeup: number;
 			nails: string;
 			hColor: string;
 			hLength: number;
diff --git a/src/003-assets/CSS/RAoptions.css b/src/003-assets/CSS/RAoptions.css
index ca589dea8d4..5571282eb1e 100644
--- a/src/003-assets/CSS/RAoptions.css
+++ b/src/003-assets/CSS/RAoptions.css
@@ -50,8 +50,14 @@
 	margin: 0.5em;
 }
 
-.ra-label {
+.ra-container {
+	display: grid;
+	grid-template-columns: minmax(100px, max-content) auto;
+	grid-row-gap: 0.5ex;
+}
 
+.ra-label {
+	grid-column: 1;
 }
 
 .ra-label::after {
@@ -59,6 +65,16 @@
 	margin-right: 0.5em;
 }
 
+.ra-sub-label {
+	font-style: italic;
+	margin-left: 2.5em;
+}
+
+.ra-sub-label::after {
+	content: ":";
+	margin-right: 0.5em;
+}
+
 .ra-setters {
 	margin-top: 3ex;
 }
diff --git a/src/js/DefaultRules.js b/src/js/DefaultRules.js
index 21c4722a39d..9a9e659eda3 100644
--- a/src/js/DefaultRules.js
+++ b/src/js/DefaultRules.js
@@ -2248,7 +2248,7 @@ window.DefaultRules = (function() {
 
 	/**
 	 * @param {App.Entity.SlaveState} slave
-	 * @param {object} rule
+	 * @param {App.RA.RuleSetters} rule
 	 */
 	function ProcessStyle(slave, rule) {
 		if (rule.eyeColor !== undefined && (rule.eyeColor !== null)) {
diff --git a/src/js/rulesAssistantOptions.js b/src/js/rulesAssistantOptions.js
index b76a70dbcef..8afd6c3c3a2 100644
--- a/src/js/rulesAssistantOptions.js
+++ b/src/js/rulesAssistantOptions.js
@@ -112,9 +112,13 @@ window.rulesAssistantOptions = (function() {
 			this.children = [];
 		}
 
+		/**
+		 * @param {Element} child
+		 */
 		appendChild(child) {
 			child.parent = this;
 			this.children.push(child);
+			child._appendContentTo(this.element);
 			this.element.appendChild(child.element);
 		}
 
@@ -131,6 +135,14 @@ window.rulesAssistantOptions = (function() {
 			this.parent.children.slice(idx, 1);
 			this.element.remove();
 		}
+
+		/**
+		 * @protected
+		 * @param {HTMLElement} container
+		 */
+		_appendContentTo(container) {
+			container.appendChild(this.element);
+		}
 	}
 
 	class Section extends Element {
@@ -155,7 +167,7 @@ window.rulesAssistantOptions = (function() {
 		appendChild(child) {
 			child.parent = this;
 			this.children.push(child);
-			this.hidey.appendChild(child.element);
+			child._appendContentTo(this.hidey);
 		}
 
 		toggle_hidey() {
@@ -187,7 +199,8 @@ window.rulesAssistantOptions = (function() {
 			tab.className = "tabcontent";
 
 			this.tabContent_ = document.createElement("div");
-			this.tabContent_.className = "content";
+			this.tabContent_.classList.add("content");
+			this.tabContent_.classList.add("ra-container");
 			tab.appendChild(this.tabContent_);
 
 			return tab;
@@ -196,7 +209,7 @@ window.rulesAssistantOptions = (function() {
 		appendChild(child) {
 			child.parent = this;
 			this.children.push(child);
-			this.tabContent_.appendChild(child.element);
+			child._appendContentTo(this.tabContent_);
 		}
 
 		static makeTabButton(name, text) {
@@ -209,12 +222,34 @@ window.rulesAssistantOptions = (function() {
 		}
 	}
 
+	class ElementWithLabel extends Element {
+		/**
+		 * @param {string} label
+		 * @param {*} args
+		 */
+		constructor(label, ...args) {
+			super(...args);
+			this.labelElement_ = document.createElement("span");
+			this.labelElement_.className = "ra-label";
+			this.labelElement_.innerHTML = label;
+		}
+
+		/**
+		 * @protected
+		 * @param {HTMLElement} container
+		 */
+		_appendContentTo(container) {
+			container.appendChild(this.labelElement_);
+			super._appendContentTo(container);
+		}
+	}
+
 
 	// list of clickable elements
 	// has a short explanation (the prefix) and a value display
 	// value display can optionally be an editable text input field
 	// it can be "bound" to a variable by setting its "onchange" method
-	class EditorWithShortcuts extends Element {
+	class EditorWithShortcuts extends ElementWithLabel {
 		/**
 		 *
 		 * @param {string} prefix
@@ -225,7 +260,7 @@ window.rulesAssistantOptions = (function() {
 		 * @param {...any} args
 		 */
 		constructor(prefix, data = [], allowNullValue = true, editor = false, capitalizeShortcuts = false, ...args) {
-			super(`${prefix}: `, editor, ...args);
+			super(prefix, editor, ...args);
 			this.selectedItem = null;
 			/** @protected */
 			this._allowNullValue = allowNullValue;
@@ -234,22 +269,24 @@ window.rulesAssistantOptions = (function() {
 			if (allowNullValue) {
 				this.appendChild(new ListItem(capFirstChar(noDefaultSetting.text), null));
 			}
-			data.forEach(item => this.appendChild(this._createListItem(...item)));
+			data.forEach(item => this.appendChild(this._createListItem(item)));
 		}
 
 		createEditor(...args) { return null; }
 
-		render(prefix, editor, ...args) {
+		createValueElement() { return document.createElement("strong"); }
+
+		render(editor, ...args) {
 			const elem = document.createElement("div");
-			const label = document.createElement("span");
-			label.innerHTML = prefix;
-			this.value = editor ? this.createEditor(...args) : document.createElement("strong");
-			elem.appendChild(label);
-			elem.appendChild(this.value);
+			this.value = editor ? this.createEditor(...args) : this.createValueElement();
+			if (this.value !== null) {
+				elem.appendChild(this.value);
+			}
 			elem.classList.add("rajs-list");
 			return elem;
 		}
 
+
 		inputEdited() {
 			if (this.selectedItem) { this.selectedItem.deselect(); }
 			this.propagateChange();
@@ -264,10 +301,12 @@ window.rulesAssistantOptions = (function() {
 
 		setValue(what) {
 			const str = what === null ? "no default setting" : `${what}`;
-			if (this.value.tagName === "INPUT") {
-				this.value.value = str;
-			} else {
-				this.value.innerHTML = str;
+			if (this.value) {
+				if (this.value.tagName === "INPUT") {
+					this.value.value = str;
+				} else {
+					this.value.innerHTML = str;
+				}
 			}
 		}
 
@@ -286,16 +325,23 @@ window.rulesAssistantOptions = (function() {
 
 		/**
 		 * @private
-		 * @param {string} display
-		 * @param {any} data
+		 * @param {string|string[]} item
 		 * @returns {ListItem}
 		 */
-		_createListItem(display, data) {
-			if (this._capitalizeShortcuts) {
-				return new ListItem(capFirstChar(display), data);
+		_createListItem(item) {
+			let display = '';
+			let data = null;
+			if (Array.isArray(item)) {
+				display = item[0];
+				data = item.length > 1 ? item[1] : display;
 			} else {
-				return new ListItem(display, data);
+				display = item;
+				data = item;
 			}
+			if (this._capitalizeShortcuts) {
+				display = capFirstChar(display);
+			}
+			return new ListItem(display, data);
 		}
 	}
 
@@ -329,17 +375,14 @@ window.rulesAssistantOptions = (function() {
 		}
 	}
 
-	class ListSelector extends Element {
+	class ListSelector extends ElementWithLabel {
 		constructor(prefix, data = [], allowNullValue = true) {
-			super(`${prefix}: `, data, allowNullValue);
+			super(prefix, data, allowNullValue);
 		}
 
-		render(prefix, data, allowNullValue) {
+		render(data, allowNullValue) {
 			const elem = document.createElement("div");
-			const label = document.createElement("span");
-			label.innerHTML = prefix;
 			this.value = document.createElement("select");
-			elem.appendChild(label);
 			elem.appendChild(this.value);
 			elem.classList.add("rajs-list");
 			this.values_ = new Map();
@@ -384,7 +427,7 @@ window.rulesAssistantOptions = (function() {
 		}
 	}
 
-	class RadioSelector extends Element {
+	class RadioSelector extends ElementWithLabel {
 		/**
 		 *
 		 * @param {string} prefix
@@ -392,17 +435,12 @@ window.rulesAssistantOptions = (function() {
 		 * @param {boolean} [allowNullValue=true]
 		 */
 		constructor(prefix, data = [], allowNullValue = true) {
-			super(prefix, data, allowNullValue);
+			super(prefix, prefix, data, allowNullValue);
 		}
 
 		render(prefix, data, allowNullValue) {
 			this.name_ = prefix.replace(' ', '_');
 			const elem = document.createElement("div");
-			const label = document.createElement("span");
-			label.className = "ra-label";
-			label.innerHTML = prefix;
-			elem.appendChild(label);
-
 			this.values_ = new Map();
 			this.radios_ = new Map();
 
@@ -529,13 +567,13 @@ window.rulesAssistantOptions = (function() {
 		}
 	}
 
-	class BooleanSwitch extends Element {
+	class BooleanSwitch extends ElementWithLabel {
 		/**
 		 * @param {string} prefix
 		 * @param {Array} values values for "false" and "true"
 		 */
 		constructor(prefix, values = [false, true]) {
-			super(prefix);
+			super(prefix, prefix);
 
 			/** @private */
 			this.values_ = {
@@ -546,8 +584,6 @@ window.rulesAssistantOptions = (function() {
 
 		render(prefix) {
 			const elem = document.createElement("div");
-			const label = document.createElement("span");
-			label.innerHTML = `${prefix}: `;
 			let switchContainer = document.createElement("div");
 			switchContainer.className = "ra-onoffswitch";
 			this.checkBox_ = document.createElement("input");
@@ -563,13 +599,12 @@ window.rulesAssistantOptions = (function() {
 			switchSpan.className = "ra-onoffswitch-switch";
 			switchLabel.appendChild(innerSpan);
 			switchLabel.appendChild(switchSpan);
-			elem.appendChild(label);
 			switchContainer.appendChild(this.checkBox_);
 			switchContainer.appendChild(switchLabel);
 			elem.appendChild(switchContainer);
 			elem.classList.add("rajs-list");
 
-			this.checkBox_.onchange = () => { this.inputEdited();}
+			this.checkBox_.onchange = () => { this.inputEdited(); };
 			return elem;
 		}
 
@@ -622,9 +657,9 @@ window.rulesAssistantOptions = (function() {
 			};
 
 			this.numEditor = document.createElement("input");
-			this.numEditor.setAttribute("type", "number");
-			this.numEditor.setAttribute("min", min);
-			this.numEditor.setAttribute("max", max);
+			this.numEditor.type = "number";
+			this.numEditor.min = min;
+			this.numEditor.max= max;
 			this.numEditor.classList.add("rajs-value"); //
 			this.numEditor.onblur = () => {
 				this.inputEdited();
@@ -663,18 +698,16 @@ window.rulesAssistantOptions = (function() {
 
 	// a way to organize lists with too many elements in subsections
 	// children are bound to the master list
-	class ListSubSection extends Element {
+	class ListSubSection extends ElementWithLabel {
 		constructor(parent, label, pairs) {
 			super(label);
 			this.parent = parent;
-			pairs.forEach(item => this.appendChild(new ListItem(...item)));
+			this.labelElement_.className = "ra-sub-label";
+			pairs.forEach(item => this.appendChild(Array.isArray(item) ? new ListItem( ...item) : new ListItem(item)));
 		}
 
-		render(label) {
+		render() {
 			const elem = document.createElement("div");
-			const lelem = document.createElement("em");
-			lelem.innerText = `${label}: `;
-			elem.appendChild(lelem);
 			return elem;
 		}
 
@@ -699,6 +732,24 @@ window.rulesAssistantOptions = (function() {
 		}
 	}
 
+	class OptionsWithLabel extends Options {
+		constructor(prefix, elements = []) {
+			super(elements);
+			this.labelElement_ = document.createElement("span");
+			this.labelElement_.className = "ra-label";
+			this.labelElement_.innerHTML = prefix;
+		}
+
+		/**
+		 * @protected
+		 * @param {HTMLElement} container
+		 */
+		_appendContentTo(container) {
+			container.appendChild(this.labelElement_);
+			super._appendContentTo(container);
+		}
+	}
+
 	// options equivalent of ListItem
 	class OptionsItem extends Element {
 		constructor(label, onclick) {
@@ -1643,8 +1694,7 @@ window.rulesAssistantOptions = (function() {
 			];
 			spclothes.forEach(pair => { if (isItemAccessible(pair[1])) { nclothes.push(pair); } });
 			fsnclothes.forEach(pair => { if (isItemAccessible(pair[1])) { nclothes.push(pair); } });
-			const nice = new ListSubSection(this, "Nice", nclothes);
-			this.appendChild(nice);
+			this._nice = new ListSubSection(this, "Nice", nclothes);
 
 			const hclothes = [
 				["Nude", "no clothing"],
@@ -1658,12 +1708,17 @@ window.rulesAssistantOptions = (function() {
 			];
 			fshclothes.forEach(pair => { if (isItemAccessible(pair[1])) { hclothes.push(pair); } });
 
-			const harsh = new ListSubSection(this, "Harsh", hclothes);
-			this.appendChild(harsh);
+			this._harsh = new ListSubSection(this, "Harsh", hclothes);
 
 			this.setValue(current_rule.set.clothes);
 			this.onchange = (value) => current_rule.set.clothes = value;
 		}
+
+		_appendContentTo(container) {
+			super._appendContentTo(container);
+			this._nice._appendContentTo(container);
+			this._harsh._appendContentTo(container);
+		}
 	}
 
 	class CollarList extends List {
@@ -1688,8 +1743,7 @@ window.rulesAssistantOptions = (function() {
 				["Ancient Egyptian", "ancient Egyptian"],
 			];
 			fsncollars.forEach(pair => { if (isItemAccessible(pair[1])) { ncollars.push(pair); } });
-			const nice = new ListSubSection(this, "Nice", ncollars);
-			this.appendChild(nice);
+			this._nice = new ListSubSection(this, "Nice", ncollars);
 
 			const hcollars = [];
 			setup.harshCollars.forEach(item => {
@@ -1703,12 +1757,17 @@ window.rulesAssistantOptions = (function() {
 					hcollars.push([item.name, item.value]);
 				}
 			});
-			const harsh = new ListSubSection(this, "Harsh", hcollars);
-			this.appendChild(harsh);
+			this._harsh = new ListSubSection(this, "Harsh", hcollars);
 
 			this.setValue(current_rule.set.collar);
 			this.onchange = (value) => current_rule.set.collar = value;
 		}
+
+		_appendContentTo(container) {
+			super._appendContentTo(container);
+			this._nice._appendContentTo(container);
+			this._harsh._appendContentTo(container);
+		}
 	}
 
 	class ShoeList extends ListSelector {
@@ -1973,9 +2032,9 @@ window.rulesAssistantOptions = (function() {
 		}
 	}
 
-	class GrowthList extends Options {
+	class GrowthList extends OptionsWithLabel {
 		constructor() {
-			super();
+			super("Growth hormone regimes for healthy slaves");
 			this.sublists = [];
 			const pairs = [
 				[capFirstChar(noDefaultSetting.text), () => this.nds()],
@@ -1997,16 +2056,11 @@ window.rulesAssistantOptions = (function() {
 				this.balls = new BallGrowthList();
 				this.sublists.push(this.dicks, this.balls);
 			}
-
-			this.sublists.forEach(i => this.appendChild(i));
 		}
 
-		render() {
-			const elem = document.createElement("div");
-			const span = document.createElement("span");
-			span.innerHTML = "Growth hormone regimes for healthy slaves: ";
-			elem.appendChild(span);
-			return elem;
+		_appendContentTo(container) {
+			super._appendContentTo(container);
+			this.sublists.forEach(i => i._appendContentTo(container));
 		}
 
 		nds() {
@@ -2640,92 +2694,103 @@ window.rulesAssistantOptions = (function() {
 		}
 	}
 
-	class LensesList extends Element {
+	class LensesList extends List {
 		constructor() {
-			super(current_rule.set.eyeColor);
-			this.appendChild(new OptionsItem(capFirstChar(noDefaultSetting.text), () => this.setValue(null)));
-			this.colorlist = new LensesColorList();
-			this.shapelist = new LensesShapeList();
-			this.appendChild(this.colorlist);
-			this.appendChild(this.shapelist);
+			super("Eye coloring");
+			this.colorlist = new LensesColorList(this);
+			this.shapelist = new LensesShapeList(this);
+			this.colorlist.onchange = () => this.setValue(undefined);
+			this.shapelist.onchange = () => this.setValue(undefined);
+			this.setValue(current_rule.set.eyeColor);
+			this.onchange = (value) => current_rule.set.eyeColor = value;
 		}
 
-		render(color) {
-			const elem = document.createElement("div");
-			elem.innerHTML = "Eye coloring: ";
-			this.label = document.createElement("strong");
-			this.label.innerText = color;
-			elem.appendChild(this.label);
-			return elem;
+		_appendContentTo(container) {
+			super._appendContentTo(container);
+			this.colorlist._appendContentTo(container);
+			this.shapelist._appendContentTo(container);
 		}
 
 		combine() {
 			const lst = [];
-			if (this.colorlist.value !== null)
-				lst.push(this.colorlist.value);
-			if (this.shapelist.value !== null)
-				lst.push(this.shapelist.value);
+			if (this.colorlist.getData() !== null)
+				lst.push(this.colorlist.getData());
+			if (this.shapelist.getData() !== null)
+				lst.push(this.shapelist.getData());
 			if (lst.length === 0) return null;
 			else return lst.join(" ");
 		}
 
 		setValue(val) {
-			if (val === undefined) { val = this.combine(); }
-			this.label.innerText = `${val} `;
+			if (val === undefined) {
+				val = this.combine();
+			} else {
+				if (val === noDefaultSetting.value) {
+					this.colorlist.setValue(val);
+					this.shapelist.setValue(val);
+				} else {
+					//
+				}
+			}
+			super.setValue(val);
 			current_rule.set.eyeColor = val;
 		}
 	}
 
-	class LensesColorList extends Options {
-		constructor() {
-			const items = [];
-			[
-				null,
-				"blue",
-				"black",
-				"brown",
-				"green",
-				"turquoise",
-				"sky-blue",
-				"hazel",
-				"pale-grey",
-				"white",
-				"pink",
-				"yellow",
-				"orange",
-				"amber",
-				"red"
-			].forEach(i => items.push(new OptionsItem(i, item => {
-				this.value = item.label;
-				this.parent.setValue();
-			})));
-			super(items);
-		}
-	}
-
-	class LensesShapeList extends Options {
-		constructor() {
-			const items = [];
-			[
-				null,
-				"catlike",
-				"serpent-like",
-				"goat-like",
-				"devilish",
-				"demonic",
-				"hypnotic",
-				"heart-shaped",
-				"star-shaped",
-				"wide-eyed",
-				"almond-shaped",
-				"bright",
-				"teary",
-				"vacant"
-			].forEach(i => items.push(new OptionsItem(i, item => {
-				this.value = item.label;
-				this.parent.setValue();
-			})));
-			super(items);
+	class LensesColorList extends List {
+		constructor(parent) {
+			const items =
+				[
+					"blue",
+					"black",
+					"brown",
+					"green",
+					"turquoise",
+					"sky-blue",
+					"hazel",
+					"pale-grey",
+					"white",
+					"pink",
+					"yellow",
+					"orange",
+					"amber",
+					"red"
+				];
+			super("Color", items);
+			this.labelElement_.className = "ra-sub-label";
+			this.parent = parent;
+		}
+
+		createValueElement() {
+			return null;
+		}
+	}
+
+	class LensesShapeList extends List {
+		constructor(parent) {
+			const items =
+				[
+					"catlike",
+					"serpent-like",
+					"goat-like",
+					"devilish",
+					"demonic",
+					"hypnotic",
+					"heart-shaped",
+					"star-shaped",
+					"wide-eyed",
+					"almond-shaped",
+					"bright",
+					"teary",
+					"vacant"
+				];
+			super("Shape", items);
+			this.labelElement_.className = "ra-sub-label";
+			this.parent = parent;
+		}
+
+		createValueElement() {
+			return null;
 		}
 	}
 
@@ -2737,7 +2802,7 @@ window.rulesAssistantOptions = (function() {
 				["muffle with ear plugs"],
 				["deafen with ear plugs"]
 			];
-			super("Earwear", pairs, true, false, true);
+			super("Earwear", pairs, true);
 			this.setValue(current_rule.set.earwear);
 			this.onchange = (value) => current_rule.set.earwear = value;
 		}
@@ -3226,106 +3291,93 @@ window.rulesAssistantOptions = (function() {
 			// I sorted this next section from top of body down, to make it easier to read for users. Hopefully when making similar lists elsewhere in the game, folks will use the same order. Makes it much easier to compare and make sure nothing is missing. And alphabetical is a poor choice for user facing lists.
 
 			// Head
-			const cheeks = new ListSubSection(this, "Cheeks", [
+			this._cheeks = new ListSubSection(this, "Cheeks", [
 				["Left", "left cheek"],
 				["Right", "right cheek"],
 				["Both", "cheeks"]
 			]);
-			this.appendChild(cheeks);
 
-			const ears = new ListSubSection(this, "Ears", [
+			this._ears = new ListSubSection(this, "Ears", [
 				["Left", "left ear"],
 				["Right", "right ear"],
 				["Both", "ears"]
 
 			]);
-			this.appendChild(ears);
 
 			// Torso
-			const breasts = new ListSubSection(this, "Breasts", [
+			this._breasts = new ListSubSection(this, "Breasts", [
 				["Left", "left breast"],
 				["Right", "right breast"],
 				["Both", "breasts"]
 
 			]);
-			this.appendChild(breasts);
 
 			// Arms
-			const shoulders = new ListSubSection(this, "Shoulders", [
+			this._shoulders = new ListSubSection(this, "Shoulders", [
 				["Left", "left shoulder"],
 				["Right", "right shoulder"],
 				["Both", "shoulders"]
 
 			]);
-			this.appendChild(shoulders);
 
-			const upper_arms = new ListSubSection(this, "Arms, upper", [
+			this._upperArms = new ListSubSection(this, "Arms, upper", [
 				["Left", "left upper arm"],
 				["Right", "right upper arm"],
 				["Both", "upper arms"]
 
 			]);
-			this.appendChild(upper_arms);
 
-			const lower_arms = new ListSubSection(this, "Arms, lower", [
+			this._lowerArms = new ListSubSection(this, "Arms, lower", [
 				["Left", "left lower arm"],
 				["Right", "right lower arm"],
 				["Both", "lower arms"]
 			]);
-			this.appendChild(lower_arms);
 
-			const wrist = new ListSubSection(this, "Wrist", [
+			this._wrist = new ListSubSection(this, "Wrist", [
 				["Left", "left wrist"],
 				["Right", "right wrist"],
 				["Both", "wrists"]
 			]);
-			this.appendChild(wrist);
 
-			const hand = new ListSubSection(this, "Hand", [
+			this._hand = new ListSubSection(this, "Hand", [
 				["Left", "left hand"],
 				["Right", "right hand"],
 				["Both", "hands"]
 			]);
-			this.appendChild(hand);
 
 			// Legs
-			const buttocks = new ListSubSection(this, "Buttocks", [
+			this._buttocks = new ListSubSection(this, "Buttocks", [
 				["Left", "left buttock"],
 				["Right", "right buttock"],
 				["Both", "buttocks"]
 			]);
-			this.appendChild(buttocks);
 
-			const thigh = new ListSubSection(this, "Thigh", [
+			this._thigh = new ListSubSection(this, "Thigh", [
 				["Left", "left thigh"],
 				["Right", "right thigh"],
 				["Both", "thighs"]
 			]);
-			this.appendChild(thigh);
 
-			const calf = new ListSubSection(this, "Calf", [
+			this._calf = new ListSubSection(this, "Calf", [
 				["Left", "left calf"],
 				["Right", "right calf"],
 				["Both", "calves"]
 			]);
-			this.appendChild(calf);
 
-			const ankle = new ListSubSection(this, "Ankle", [
+			this._ankle = new ListSubSection(this, "Ankle", [
 				["Left", "left ankle"],
 				["Right", "right ankle"],
 				["Both", "ankles"]
 			]);
-			this.appendChild(ankle);
 
-			const feet = new ListSubSection(this, "Feet", [
+			this._feet = new ListSubSection(this, "Feet", [
 				["Left", "left foot"],
 				["Right", "right foot"],
 				["Both", "feet"]
 			]);
-			this.appendChild(feet);
 
 			// Other
-			const other = new ListSubSection(this, "Other", [
+			this._other = new ListSubSection(this, "Other", [
 				["Neck", "neck"],
 				["Chest", "chest"],
 				["Back", "back"],
@@ -3336,11 +3388,27 @@ window.rulesAssistantOptions = (function() {
 
 			// Ignoring testicles and penis for now, as not all slaves have them.
 
-			this.appendChild(other);
-
 			this.setValue(current_rule.set.brandTarget);
 			this.onchange = (value) => current_rule.set.brandTarget = value;
 		}
+
+		_appendContentTo(container) {
+			super._appendContentTo(container);
+			this._cheeks._appendContentTo(container);
+			this._ears._appendContentTo(container);
+			this._breasts._appendContentTo(container);
+			this._shoulders._appendContentTo(container);
+			this._upperArms._appendContentTo(container);
+			this._lowerArms._appendContentTo(container);
+			this._wrist._appendContentTo(container);
+			this._hand._appendContentTo(container);
+			this._buttocks._appendContentTo(container);
+			this._thigh._appendContentTo(container);
+			this._calf._appendContentTo(container);
+			this._ankle._appendContentTo(container);
+			this._feet._appendContentTo(container);
+			this._other._appendContentTo(container);
+		}
 	}
 
 	class BrandDesignList extends StringEditor {
-- 
GitLab