diff --git a/devTools/FC.d.ts b/devTools/FC.d.ts index 3c0e94debd2d951332bef19dc578c63a179060d2..358ed6425eefc4b44f4877e552a715d6ccd3afad 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 ca589dea8d465bf170ccb75c97be67a452ff401f..5571282eb1ee793ea5abb3341c80bdcce28184d6 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 21c4722a39db5759634a2693b7999f737feabaed..9a9e659eda3a62d837df803d8703e23b9be6234d 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 b76a70dbcef7b427754f0ca6a3c8fd0f60436b85..8afd6c3c3a2bef7ef89913a123bbbdc0320ba6c3 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 {