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 a8f3728ab77415ba9689a30ceab601e3a4b629c7..5571282eb1ee793ea5abb3341c80bdcce28184d6 100644 --- a/src/003-assets/CSS/RAoptions.css +++ b/src/003-assets/CSS/RAoptions.css @@ -46,3 +46,35 @@ right: 0px; background-color: #2D80E0; } +.ra-radio-label { + margin: 0.5em; +} + +.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 { + content: ":"; + 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 443f6e425dddbe818cf748760f8d5ae642dc9d65..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() { @@ -170,12 +182,74 @@ window.rulesAssistantOptions = (function() { } } + class Tab extends Element { + /** + * + * @param {string} name + * @param {string} label + * @param {HTMLDivElement} tabButtonsContainer + */ + constructor(name, label, tabButtonsContainer) { + super(name); + tabButtonsContainer.appendChild(Tab.makeTabButton(name, label)); + } + render(name) { + const tab = document.createElement("div"); + tab.id = name; + tab.className = "tabcontent"; + + this.tabContent_ = document.createElement("div"); + this.tabContent_.classList.add("content"); + this.tabContent_.classList.add("ra-container"); + tab.appendChild(this.tabContent_); + + return tab; + } + + appendChild(child) { + child.parent = this; + this.children.push(child); + child._appendContentTo(this.tabContent_); + } + + static makeTabButton(name, text) { + const btn = document.createElement("button"); + btn.className = "tablinks"; + btn.id = `tab ${name}`; + btn.innerHTML = text; + btn.onclick = (event) => App.UI.tabbar.openTab(event, name); + return btn; + } + } + + 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 @@ -186,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; @@ -195,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(); @@ -225,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; + } } } @@ -247,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); } } @@ -290,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(); @@ -345,6 +427,73 @@ window.rulesAssistantOptions = (function() { } } + class RadioSelector extends ElementWithLabel { + /** + * + * @param {string} prefix + * @param {Array} [data=[]] + * @param {boolean} [allowNullValue=true] + */ + constructor(prefix, data = [], allowNullValue = true) { + super(prefix, prefix, data, allowNullValue); + } + + render(prefix, data, allowNullValue) { + this.name_ = prefix.replace(' ', '_'); + const elem = document.createElement("div"); + this.values_ = new Map(); + this.radios_ = new Map(); + + let values = []; + if (allowNullValue) { + values.push([noDefaultSetting.value, noDefaultSetting.text]); + this.values_.set(noDefaultSetting.value, null); + } + for (const dr of data) { + const dv = Array.isArray(dr) ? (dr.length > 1 ? [dr[1], dr[0]] : [dr[0], dr[0]]) : [dr, dr]; + values.push(dv); + this.values_.set(`${dv[0]}`, dv[0]); + } + + for (const v of values) { + let inp = document.createElement("input"); + inp.type = "radio"; + inp.name = this.name_; + inp.id = prefix + '_' + v[0]; + inp.value = v[0]; + + let lbl = document.createElement("label"); + lbl.htmlFor = inp.id; + lbl.className = "ra-radio-label"; + lbl.innerHTML = capFirstChar(v[1]); + inp.onclick = () => { this.inputEdited(); }; + this.radios_.set(v[0], inp); + + elem.appendChild(inp); + elem.appendChild(lbl); + } + return elem; + } + + getData() { + return this.values_.get($(`input[name='${this.name_}']:checked`).val()); + } + + setValue(what) { + this.radios_.get(what === null ? noDefaultSetting.value : what).checked = true; + } + + inputEdited() { + this.propagateChange(); + } + + propagateChange() { + if (this.onchange instanceof Function) { + this.onchange(this.getData()); + } + } + } + class List extends EditorWithShortcuts { constructor(prefix, data = [], allowNullValue = true, textinput = false, capitalizeShortcuts = true) { super(prefix, data, allowNullValue, textinput, capitalizeShortcuts); @@ -418,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_ = { @@ -435,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"); @@ -452,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; } @@ -511,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(); @@ -552,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; } @@ -588,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) { @@ -714,6 +876,7 @@ window.rulesAssistantOptions = (function() { this.appendChild(new RuleOptions(this)); this.appendChild(new ConditionEditor(this)); this.appendChild(new EffectEditor(this)); + App.UI.tabbar.handlePreSelectedTab("appearance", true); } render(element) { @@ -1218,24 +1381,28 @@ window.rulesAssistantOptions = (function() { class EffectEditor extends Element { constructor() { super(); - this.appendChild(new AppearanceSection(true)); - this.appendChild(new CosmeticSection(true)); - this.appendChild(new BodyModSection(true)); - this.appendChild(new AutosurgerySection(true)); - this.appendChild(new RegimenSection(true)); - this.appendChild(new BehaviourSection(true)); - this.appendChild(new OtherSection(true)); + this.appendChild(new AppearanceTab(this.tabButtons_)); + this.appendChild(new CosmeticTab(this.tabButtons_)); + this.appendChild(new BodyModTab(this.tabButtons_)); + this.appendChild(new AutosurgeryTab(this.tabButtons_)); + this.appendChild(new RegimenTab(this.tabButtons_)); + this.appendChild(new BehaviourTab(this.tabButtons_)); + this.appendChild(new OtherTab(this.tabButtons_)); } render() { const element = document.createElement("div"); + element.className = "ra-setters"; + this.tabButtons_ = document.createElement("div"); + this.tabButtons_.className = "tab"; + element.appendChild(this.tabButtons_); return element; } } - class AppearanceSection extends Section { - constructor(isCollapsed) { - super("Appearance Settings", isCollapsed); + class AppearanceTab extends Tab { + constructor(tabButtons) { + super("appearance", "Appearance", tabButtons); this.appendChild(new ClothesList()); this.appendChild(new CollarList()); this.appendChild(new ShoeList()); @@ -1260,9 +1427,9 @@ window.rulesAssistantOptions = (function() { } } - class RegimenSection extends Section { - constructor(isCollapsed) { - super("Physical Regimen Settings", isCollapsed); + class RegimenTab extends Tab { + constructor(tabButtons) { + super("regiment", "Physical Regiment", tabButtons); if (V.arcologies[0].FSAssetExpansionistResearch === 1) this.appendChild(new HyperGrowthSwitch()); this.appendChild(new IntensiveGrowthSwitch()); @@ -1292,9 +1459,9 @@ window.rulesAssistantOptions = (function() { } } - class BehaviourSection extends Section { - constructor(isCollapsed) { - super("Behavior Settings", isCollapsed); + class BehaviourTab extends Tab { + constructor(tabButtons) { + super("behavior", "Behavior", tabButtons); this.appendChild(new AutomaticAssignmentList()); this.appendChild(new LivingStandardList()); this.appendChild(new PunishmentList()); @@ -1314,17 +1481,17 @@ window.rulesAssistantOptions = (function() { } } - class OtherSection extends Section { - constructor(isCollapsed) { - super("Other Settings", isCollapsed); + class OtherTab extends Tab { + constructor(tabButtons) { + super("other", "Other", tabButtons); this.appendChild(new LabelList()); this.appendChild(new LabelRemoveList()); } } - class CosmeticSection extends Section { - constructor(isCollapsed) { - super("Cosmetic Settings", isCollapsed); + class CosmeticTab extends Tab { + constructor(tabButtons) { + super("cosmetic", "Cosmetic", tabButtons); this.appendChild(new EyewearList()); this.appendChild(new LensesList()); this.appendChild(new EarwearList()); @@ -1346,9 +1513,9 @@ window.rulesAssistantOptions = (function() { } } - class BodyModSection extends Section { - constructor(isCollapsed) { - super("Body Mod Settings", isCollapsed); + class BodyModTab extends Tab { + constructor(tabButtons) { + super("bodyMod", "Body Mod", tabButtons); this.appendChild(new EarPiercingList()); this.appendChild(new NosePiercingList()); this.appendChild(new EyebrowPiercingList()); @@ -1383,9 +1550,9 @@ window.rulesAssistantOptions = (function() { } } - class AutosurgerySection extends Section { - constructor() { - super("Autosurgery Settings", true); + class AutosurgeryTab extends Tab { + constructor(tabButtons) { + super("autosurgery", "Autosurgery", tabButtons); this.appendChild(new AutosurgerySwitch()); this.appendChild(new VisionSurgeryList()); this.appendChild(new HearingSurgeryList()); @@ -1527,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"], @@ -1542,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 { @@ -1572,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 => { @@ -1587,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 { @@ -1647,13 +1822,13 @@ window.rulesAssistantOptions = (function() { } } - class VagChastityList extends List { + class VagChastityList extends RadioSelector { constructor() { const chaste = [ ["none", 0], ["chastity", 1], ]; - super("Vaginal chastity", chaste, true, false, true); + super("Vaginal chastity", chaste, true); this.setValue(current_rule.set.chastityVagina); this.onchange = (value) => current_rule.set.chastityVagina = value; } @@ -1723,13 +1898,13 @@ window.rulesAssistantOptions = (function() { } } - class DickChastityList extends List { + class DickChastityList extends RadioSelector { constructor() { const items = [ ["none", 0], ["chastity cage", 1], ]; - super("Penile chastity", items, true, false, true); + super("Penile chastity", items, true); this.setValue(current_rule.set.chastityPenis); this.onchange = (value) => current_rule.set.chastityPenis = value; } @@ -1751,13 +1926,13 @@ window.rulesAssistantOptions = (function() { } } - class AnalChastityList extends List { + class AnalChastityList extends RadioSelector { constructor() { const items = [ ["none", 0], ["chastity", 1], ]; - super("Anal chastity", items, true, false, true); + super("Anal chastity", items, true); this.setValue(current_rule.set.chastityAnus); this.onchange = (value) => current_rule.set.chastityAnus = value; } @@ -1857,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()], @@ -1881,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() { @@ -2043,13 +2213,13 @@ window.rulesAssistantOptions = (function() { } } - class ContraceptiveList extends List { + class ContraceptiveList extends RadioSelector { constructor() { const drugs = [ ["contraceptives", true], ["fertile", false], ]; - super("Contraceptives for fertile slaves", drugs, true, false, true); + super("Contraceptives for fertile slaves", drugs, true); this.setValue(current_rule.set.preg); this.onchange = (value) => current_rule.set.preg = value; } @@ -2060,7 +2230,7 @@ window.rulesAssistantOptions = (function() { const pairs = [ ["abort all", "all"], ]; - if (V.pregnancyMonitoringUpgrade === 1 && V.geneticMappingUpgrade === 1) { + if (V.pregnancyMonitoringUpgrade === 1 && V.geneticMappingUpgrade >= 1) { pairs.push(["abort boys", "male"]); pairs.push(["abort girls", "female"]); } @@ -2277,13 +2447,13 @@ window.rulesAssistantOptions = (function() { } } - class DietSolidFoodList extends List { + class DietSolidFoodList extends RadioSelector { constructor() { const pairs = [ ["permitted", 0], ["forbidden", 1], ]; - super("Solid food access", pairs, true, false, true); + super("Solid food access", pairs, true); this.setValue(current_rule.set.onDiet); this.onchange = (value) => current_rule.set.onDiet = value; } @@ -2453,39 +2623,39 @@ window.rulesAssistantOptions = (function() { } } - class SpeechList extends List { + class SpeechList extends RadioSelector { constructor() { const pairs = [ ["permissive"], ["suppress accents", "accent elimination"], ["restrictive"] ]; - super("Speech rules", pairs, true, false, true); + super("Speech rules", pairs, true); this.setValue(current_rule.set.speechRules); this.onchange = (value) => current_rule.set.speechRules = value; } } - class RelationshipList extends List { + class RelationshipList extends RadioSelector { constructor() { const pairs = [ ["permissive"], ["just friends"], ["restrictive"] ]; - super("Relationship rules", pairs, true, false, true); + super("Relationship rules", pairs, true); this.setValue(current_rule.set.relationshipRules); this.onchange = (value) => current_rule.set.relationshipRules = value; } } - class PornBroadcastStatus extends List { + class PornBroadcastStatus extends RadioSelector { constructor() { const pairs = [ ["disabled", 0], ["enabled", 1] ]; - super("Porn Broadcasting Status", pairs, true, false, true); + super("Porn Broadcasting Status", pairs, true); this.setValue(current_rule.set.pornFeed); this.onchange = (value) => current_rule.set.pornFeed = value; } @@ -2524,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; } } @@ -2621,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; } @@ -3110,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"], @@ -3220,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 { @@ -3458,85 +3642,85 @@ window.rulesAssistantOptions = (function() { } } - class VisionSurgeryList extends List { + class VisionSurgeryList extends RadioSelector { constructor() { const items = [ ["fixed", 1], ["blurred", -1], ]; - super("Vision correction", items, true, false, true); + super("Vision correction", items, true); this.setValue(current_rule.set.surgery.eyes); this.onchange = (value) => current_rule.set.surgery.eyes = value; } } - class HearingSurgeryList extends List { + class HearingSurgeryList extends RadioSelector { constructor() { const items = [ ["fixed", 0], ["muffled", -1], ]; - super("Hearing correction", items, true, false, true); + super("Hearing correction", items, true); this.setValue(current_rule.set.surgery.hears); this.onchange = (value) => current_rule.set.surgery.hears = value; } } - class SmellSurgeryList extends List { + class SmellSurgeryList extends RadioSelector { constructor() { const items = [ ["fixed", 0], ["disabled", -1], ]; - super("Olfactory correction", items, true, false, true); + super("Olfactory correction", items, true); this.setValue(current_rule.set.surgery.smells); this.onchange = (value) => current_rule.set.surgery.smells = value; } } - class TasteSurgeryList extends List { + class TasteSurgeryList extends RadioSelector { constructor() { const items = [ ["fixed", 0], ["disabled", -1], ]; - super("Gustatory correction", items, true, false, true); + super("Gustatory correction", items, true); this.setValue(current_rule.set.surgery.tastes); this.onchange = (value) => current_rule.set.surgery.tastes = value; } } - class LactationSurgeryList extends List { + class LactationSurgeryList extends RadioSelector { constructor() { const items = [ ["implanted", 1], ["removed", 0], ]; - super("Lactation drug implants", items, true, false, true); + super("Lactation drug implants", items, true); this.setValue(current_rule.set.surgery.lactation); this.onchange = (value) => current_rule.set.surgery.lactation = value; } } - class SemenSurgeryList extends List { + class SemenSurgeryList extends RadioSelector { constructor() { const items = [ ["implanted", 1], ["removed", 0], ]; - super("Prostate production enhancing drug implants", items, true, false, true); + super("Prostate production enhancing drug implants", items, true); this.setValue(current_rule.set.surgery.prostate); this.onchange = (value) => current_rule.set.surgery.prostate = value; } } - class VasectomyList extends List { + class VasectomyList extends RadioSelector { constructor() { const items = [ ["apply vasectomy", true], ["undo vasectomy", false], ]; - super("Apply or undo vasectomy for slaves with testicles", items, true, false, true); + super("Apply or undo vasectomy for slaves with testicles", items, true); this.setValue(current_rule.set.surgery.vasectomy); this.onchange = (value) => current_rule.set.surgery.vasectomy = value; } @@ -3549,7 +3733,7 @@ window.rulesAssistantOptions = (function() { ["subtle", 1], ["invasive", 2], ]; - super("Cosmetic Surgery", items, true, false, true); + super("Cosmetic Surgery", items, true); this.setValue(current_rule.set.surgery.cosmetic); this.onchange = (value) => current_rule.set.surgery.cosmetic = value; } @@ -3601,49 +3785,49 @@ window.rulesAssistantOptions = (function() { } } - class TighteningSurgeryList extends List { + class TighteningSurgeryList extends RadioSelector { constructor() { const items = [ ["tightening", 1], ["virginity restoration", 2], ]; - super("Orifice Tightening", items, true, false, true); + super("Orifice Tightening", items, true); this.setValue(current_rule.set.surgery.holes); this.onchange = (value) => current_rule.set.surgery.holes = value; } } - class TummyTuckSurgeryList extends List { + class TummyTuckSurgeryList extends RadioSelector { constructor() { const items = [ ["tuck", 1], ]; - super("Tummy Tuck", items, true, false, true); + super("Tummy Tuck", items, true); this.setValue(current_rule.set.surgery.tummy); this.onchange = (value) => current_rule.set.surgery.tummy = value; } } - class BodyHairSurgeryList extends List { + class BodyHairSurgeryList extends RadioSelector { constructor() { const items = [ ["keep", 1], ["removal", 2], ]; - super("Body Hair", items, true, false, true); + super("Body Hair", items, true); this.setValue(current_rule.set.surgery.bodyhair); this.onchange = (value) => current_rule.set.surgery.bodyhair = value; } } - class HairSurgeryList extends List { + class HairSurgeryList extends RadioSelector { constructor() { const items = [ ["keep", 1], ["removal", 2], ]; - super("Hair", items, true, false, true); + super("Hair", items, true); this.setValue(current_rule.set.surgery.hair); this.onchange = (value) => current_rule.set.surgery.hair = value; } @@ -3708,13 +3892,13 @@ window.rulesAssistantOptions = (function() { } } - class BellyImplantList extends List { + class BellyImplantList extends RadioSelector { constructor() { const items = [ ["install", "install"], ["remove", "remove"], ]; - super("Belly implant", items, true, false, true); + super("Belly implant", items, true); this.setValue(current_rule.set.surgery.bellyImplant); this.onchange = (value) => current_rule.set.surgery.bellyImplant = value; } diff --git a/src/js/utilJS.js b/src/js/utilJS.js index c22d7a04396c625d1418d26fedd34696cf379660..4c109dfbe18d2b915ffee4bbd799664d1abb7483 100644 --- a/src/js/utilJS.js +++ b/src/js/utilJS.js @@ -2179,7 +2179,8 @@ App.UI.tabbar = function() { openTab: openTab, tabButton: tabButton, makeTab: makeTab, - handlePreSelectedTab: handlePreSelectedTab + handlePreSelectedTab: handlePreSelectedTab, + tabChoiceVarName: tabChoiceVarName }; function openTab(evt, tabName) { @@ -2193,7 +2194,7 @@ App.UI.tabbar = function() { for (let i = 0; i < tablinks.length; i++) { tablinks[i].className = tablinks[i].className.replace(" active", ""); } - V.tabChoice[_tabChoiceVarName()] = tabName; /* The regex strips spaces and " ' " from passage names, making "Servants' Quarters" into "ServantsQuarters" and allowing it to be used as a label in this object. */ + V.tabChoice[tabChoiceVarName()] = tabName; /* The regex strips spaces and " ' " from passage names, making "Servants' Quarters" into "ServantsQuarters" and allowing it to be used as a label in this object. */ document.getElementById(tabName).style.display = "block"; evt.currentTarget.className += " active"; } @@ -2216,19 +2217,24 @@ App.UI.tabbar = function() { return `<div id="${name}" class="tabcontent"><div class="content">${content}</div></div>`; } - function handlePreSelectedTab() { - let selectedTab = State.variables.tabChoice[_tabChoiceVarName()]; - if (!selectedTab) { selectedTab = "assign"; } - $(document).one(':passagedisplay', function() { + function handlePreSelectedTab(defaultTab = "assign", immidiate = false) { + let selectedTab = State.variables.tabChoice[tabChoiceVarName()]; + if (!selectedTab) { selectedTab = defaultTab; } + function selectTab() { let tabBtn = document.getElementById(`tab ${selectedTab}`); if (!tabBtn) { tabBtn = document.getElementsByClassName('tablinks').item(0); } if (tabBtn) { tabBtn.click(); } - }); + } + if (immidiate) { + selectTab(); + } else { + $(document).one(':passagedisplay', selectTab); + } } - function _tabChoiceVarName() { + function tabChoiceVarName() { return passage().trim().replace(/ |'/g, ''); } }();