"README.md" did not exist on "1ed87267d500694a8cd483772b7691fab815114d"
Newer
Older
// rewrite of the rules assistant options page in javascript
// uses an object-oriented widget pattern
// wrapped in a closure so as not to polute the global namespace
// the widgets are generic enough to be reusable; if similar user interfaces are ported to JS, we could move the classes to the global scope
V = State.variables;
V.nextButton = "Back to Main";
V.nextLink = "Main";
V.returnTo = "Main";
V.showEncyclopedia = 1;
V.encyclopedia = "Personal Assistant";
const root = new Root(element);
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
ID: id,
name: `Rule ${id}`,
condition: {
function: false,
data: {},
excludeSpecialSlaves: false,
assignment: [],
excludeAssignment: [],
selectedSlaves: [],
excludedSlaves: [],
facility: [],
excludeFacility: [],
},
set: {
releaseRules: "no default setting",
clitSetting: "no default setting",
clitSettingXY: "no default setting",
clitSettingXX: "no default setting",
clitSettingEnergy: "no default setting",
speechRules: "no default setting",
clothes: "no default setting",
collar: "no default setting",
shoes: "no default setting",
virginAccessory: "no default setting",
aVirginAccessory: "no default setting",
vaginalAccessory: "no default setting",
aVirginDickAccessory: "no default setting",
dickAccessory: "no default setting",
bellyAccessory: "no default setting",
aVirginButtplug: "no default setting",
buttplug: "no default setting",
eyeColor: "no default setting",
makeup: "no default setting",
nails: "no default setting",
hColor: "no default setting",
hLength: "no default setting",
hStyle: "no default setting",
pubicHColor: "no default setting",
pubicHStyle: "no default setting",
nipplesPiercing: "no default setting",
areolaePiercing: "no default setting",
clitPiercing: "no default setting",
vaginaLube: "no default setting",
vaginaPiercing: "no default setting",
dickPiercing: "no default setting",
anusPiercing: "no default setting",
lipsPiercing: "no default setting",
tonguePiercing: "no default setting",
earPiercing: "no default setting",
nosePiercing: "no default setting",
eyebrowPiercing: "no default setting",
navelPiercing: "no default setting",
corsetPiercing: "no default setting",
boobsTat: "no default setting",
buttTat: "no default setting",
vaginaTat: "no default setting",
dickTat: "no default setting",
lipsTat: "no default setting",
anusTat: "no default setting",
shouldersTat: "no default setting",
armsTat: "no default setting",
legsTat: "no default setting",
backTat: "no default setting",
stampTat: "no default setting",
curatives: "no default setting",
livingRules: "no default setting",
relationshipRules: "no default setting",
standardPunishment: "no default setting",
standardReward: "no default setting",
diet: "no default setting",
dietCum: "no default setting",
dietMilk: "no default setting",
muscles: "no default setting",
XY: "no default setting",
XX: "no default setting",
gelding: "no default setting",
preg: "no default setting",
growth_boobs: "no default setting",
growth_butt: "no default setting",
growth_lips: "no default setting",
growth_dick: "no default setting",
growth_balls: "no default setting",
aphrodisiacs: "no default setting",
autoSurgery: 0,
autoBrand: 0,
pornFameSpending: "no default setting",
dietGrowthSupport: 0,
eyewear: "no default setting",
setAssignment: "no default setting",
facilityRemove: false,
removalAssignment: "rest",
surgery_eyes: "no default setting",
surgery_lactation: "no default setting",
surgery_prostate: "no default setting",
surgery_cosmetic: "no default setting",
surgery_accent: "no default setting",
surgery_shoulders: "no default setting",
surgery_shouldersImplant: "no default setting",
surgery_boobs: "no default setting",
surgery_hips: "no default setting",
surgery_hipsImplant: "no default setting",
surgery_butt: "no default setting",
surgery_faceShape: "no default setting",
surgery_lips: "no default setting",
surgery_holes: "no default setting",
surgery_hair: "no default setting",
surgery_bodyhair: "no default setting",
underArmHColor: "no default setting",
underArmHStyle: "no default setting",
drug: "no default setting",
eyes: "no default setting",
pregSpeed: "no default setting",
bellyImplantVol: -1,
}
});
V.currentRule = V.defaultRules[V.defaultRules.length-1];
reload(root);
const idx = V.defaultRules.findIndex(rule => rule.ID === V.currentRule.ID);
V.defaultRules.splice(idx, 1);
V.currentRule = idx < V.defaultRules.length ? idx : V.defaultRules.length - 1;
reload(root);
if (V.defaultRules.length === 1) return; // nothing to swap with
const idx = V.defaultRules.findIndex(rule => rule.ID === V.currentRule.ID);
if (idx === 0) return; // no lower rule
arraySwap(V.defaultRules, idx, idx-1);
reload(root);
if (V.defaultRules.length === 1) return; // nothing to swap with
const idx = V.defaultRules.findIndex(rule => rule.ID === V.currentRule.ID);
if (idx === V.defaultRules.length - 1) return; // no higher rule
arraySwap(V.defaultRules, idx, idx+1);
reload(root);
if (name === V.currentRule.name) return;
V.currentRule = name;
reload(root);
}
// the Element class wraps around a DOM element and adds extra functionality
// this is safer than extending DOM objects directly
// it also turns DOM manipulation into an implementation detail
class Element {
constructor(...args) {
this.parent = null;
this.element = this.render(...args);
this.children = [];
child.parent = this;
this.children.push(child);
this.element.appendChild(child.element);
}
// return the first argument to simplify creation of basic container items
render(...args) {
}
}
// 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 List extends Element {
const elem = document.createElement("div");
const label = document.createElement("span");
label.innerHTML = prefix;
let value;
value = document.createElement("input");
value.classList.add("rajs-value"); //
// call the variable binding when the input field is no longer being edited, and when the enter key is pressed
value.onfocusout = () => { this.inputEdited(); };
value.onkeypress = (e) => { onreturn(e, () => { this.inputEdited(); }); };
elem.appendChild(label);
elem.appendChild(value);
elem.classList.add("rajs-list");
return elem;
if (this.selectedItem) this.selectedItem.deselect();
this.propagateChange();
if (this.selectedItem) this.selectedItem.deselect();
this.selectedItem = item;
selectValue(what) {
this.children.some(child => {
if (child.data === what) {
return (this.value.tagName === "input" ? this.parse(this.value.value) : this.selectedItem.data);
propagateChange() {
if (this.onchange instanceof Function)
// a clickable item of a list
class ListItem extends Element {
super(displayvalue);
this.data = data !== undefined ? data: displayvalue;
this.selected = false;
const elem = document.createElement("span");
elem.classList.add("rajs-listitem");
elem.innerHTML = displayvalue;
elem.onclick = () => { return this.select(); };
return elem;
}
}
// a way to organise lists with too many elements in subsections
// children are bound to the master list
class ListSubSection extends Element {
const elem = document.createElement("em");
elem.innerText = label + ":";
return elem;
super.appendChild(child);
child.parent = this.parent;
this.parent.children.push(child);
const elem = document.createElement("div");
elem.classList.add("rajs-list");
return elem;
class OptionsItem extends Element {
constructor(label, onclick) {
const elem = document.createElement("span");
elem.classList.add("rajs-listitem");
elem.innerHTML = label;
elem.onclick = () => { return this.onclick(this); };
return elem;
const elem = document.createElement("div");
const labelel = document.createElement("span");
labelel.innerhTML = label += ":";
elem.appendChild(labelel);
return elem;
}
getSelection() {
return (this.children
.filter(child => child.selected)
.map(child => child.setvalue)
}
class ButtonItem extends Element {
constructor(label, setvalue, selected=false) {
super(label, selected);
this.selected = selected;
this.setvalue = setvalue ? setvalue : label;
const container = document.createElement("div");
container.classList.add("rajs-listitem");
const labelel = document.createElement("span");
labelel.innerHTML = label;
const button = document.createElement("input");
button.setAttribute("type", "checkbox");
button.checked = selected;
button.onchange = () => this.onchange(button.checked);
textarea.placeholder = "Paste your rule here";
container.appendChild(textarea);
this.textarea = textarea;
const button = document.createElement("button");
button.name = "Load";
button.onclick = () => { this.loadNewRule(); };
container.appendChild(button);
return container;
const rule = JSON.parse(text);
if (!rule.ID) rule.ID = generateNewID();
reload(this.root);
const paragraph = document.createElement("p");
paragraph.innerHTML = "<strong>No rules</strong>";
this.appendChild(new Element(paragraph));
this.appendChild(new NoRules(this));
return;
this.appendChild(new RuleSelector(this));
this.appendChild(new RuleOptions(this));
const greeting = document.createElement("p");
greeting.innerHTML = `<em>${properTitle()}, I will review your slaves and make changes that will have a beneficial effect. Apologies, ${properTitle()}, but this function is... not fully complete. It may have some serious limitations. Please use the 'no default setting' option to identify areas I should not address.</em>`;
element.appendChild(greeting);
return element;
super();
this.root = root;
const newrule = new OptionsItem("Add a new rule", () => { newRule(this.root); });
this.appendChild(newrule);
const importrule = new OptionsItem("Import a rule", () => { this.root.appendChild(new NewRuleField()); });
this.appendChild(importrule);
// buttons for selecting the current rule
class RuleSelector extends List {
constructor(root) {
}
}
// buttons for doing transformations on rules
class RuleOptions extends Options {
constructor(root) {
super();
this.appendChild(new OptionsItem("New Rule", () => newRule(root)));
this.appendChild(new OptionsItem("Remove Rule", () => removeRule(root)));
this.appendChild(new OptionsItem("Apply rules", () => this.appendChild(new ApplicationLog())));
this.appendChild(new OptionsItem("Lower Priotity", () => lowerPriority(root)));
this.appendChild(new OptionsItem("Higher Priority", () => higherPriority(root)));
this.appendChild(new OptionsItem("Rename", () => this.appendChild(new RenameField(root))));
this.appendChild(new OptionsItem("Export this rule", () => this.appendChild(new ExportField())));
this.appendChild(new OptionsItem("Export all rules", () => this.appendChild(new ExportField(true))));
this.appendChild(new OptionsItem("Import a rule", () => this.appendChild(new NewRuleField())));
const elem = document.createElement("div");
elem.innerHTML = DefaultRules();
return elem;
}
}
class RenameField extends Element {
constructor(root) {
super();
this.element.onfocusout = () => changeName(this.element.value, root);
this.element.onkeypress = (e) => onreturn(e, () => changeName(this.element.value, root));
const elem = document.createElement("div");
elem.setAttribute("type", "text");
elem.setAttribute("value", V.currentRule.name);
const element = document.createElement("textarea");
element.value = all ? JSON.stringify(V.currentRule) : map(i => JSON.stringify(i), V.defaultRules).join("\n\n");
return element;
// parent section for condition editing
class ConditionEditor extends Element {
constructor() {
super();
this.appendChild(new ConditionFunction());
this.appendChild(new AssignmentInclusion());
this.appendChild(new FacilityInclusion());
this.appendChild(new SpecialExclusion());
this.appendChild(new SpecificInclusionExclusion());
const items = ["Never", "Always", "Custom"];
["Devotion", "Trust", "Health", "Sex drive", "Weight", "Age", "Body Age", "Visible Age", "Muscles", "Lactation", "Pregnancy", "Pregnancy Multiples", "Belly Implant", "Belly Size"].forEach(i => items.push([i, this.getAttribute(i)]));
this.fnlist = new List("Activation function:", items);
this.fnlist.onchange = (value) => this.fnchanged(value);
switch(V.currentRule.condition.function) {
case "Never":
case "Always":
this.appendChild(new CustomEditor(V.currentRule.condition.data));
break;
this.appendChild(new RangeEditor(V.currentRule.condition.function, V.currentRule.condition.data));
break;
}
getAttribute(what) {
return {
"Devotion": "devotion",
"Trust": "trust",
"Health": "health",
"Sex drive": "energy",
"weight": "weight",
"Age": "actualAge",
"Body Age": "physicalAge",
"Visible Age": "visualAge",
"Muscles": "muscles",
"Lactation": "lactation",
"Pregnancy": "preg",
"Pregnancy Multiples": "pregType",
V.currentRule.condition.function = false;
V.currentRule.condition.data = {};
break;
V.currentRule.condition.function = true;
V.currentRule.condition.data = {};
break;
V.currentRule.condition.function = "custom";
V.currentRule.condition.data = {};
this.appendChild(new CustomEditor(V.currentRule.condition.data));
break;
V.currentRule.condition.function = "between";
V.currentRule.condition.data = { attribute: value, value: [null, null] };
this.appendChild(new RangeEditor(V.currentRule.condition.data));
break;
}
}
}
class CustomEditor extends Element {
constructor(data) {
if (data.length === 0) data = "function(slave) { return slave.slaveName === 'Fancy Name'; }";
super(data);
this.elem.onfocusout = () => V.currentRule.data = this.elem.value;
const elem = document.createElement("textarea");
elem.setAttribute(value, data);
return elem;
const min = document.createElement("input");
min.setAttribute("type", "text");
min.onkeypress = e => onreturn(e, () => this.setmin(min.value));
min.onfocusout = e => this.setmin(min.value);
elem.appendChild(min);
const max = document.createElement("input");
max.setAttribute("type", "text");
max.onkeypress = e => onreturn(e, () => this.setmax(max.value));
max.onfocusout = e => this.setmax(max.value);
elem.appendChild(max);
const infobar = document.createElement("div");
infobar.innerHTML = this.info(data.attribute);
elem.appendChild(infobar);
["Rest", "Fucktoy", "Subordinate Slave", "House Servant", "Confined", "Whore", "Public Servant", "Classes", "Milked", "Gloryhole"].forEach(
i => this.appendChild(new ButtonItem(i, this.getAttribute(i), V.currentRule.condition.assignment.includes(i))));
}
getAttribute(what) {
return {
"Rest": "rest",
"Fucktoy": "please you",
"Subordinate Slave": "be a subordinate slave",
"House Servant": "be a servant",
"Confined": "stay confined",
"Whore": "whore",
"Public Servant": "serve the public",
"Classes": "take classes",
"Milked": "get milked",
"Gloryhole": "work a glory hole",
super("Apply to assignments");
const facilities = [];
if (V.HGSuite > 0) facilities.push("Head Girl Suite");
if (V.brothel > 0) facilities.push("Brothel");
if (V.club > 0) facilities.push("Club");
if (V.arcade > 0) facilities.push("Arcade");
if (V.dairy > 0) facilities.push("Dairy");
if (V.servantQuarters > 0) facilities.push("Servant Quarters");
if (V.masterSuite > 0) facilities.push("Master Suite");
if (V.schoolroom > 0) facilities.push("Schoolroom");
if (V.spa > 0) facilities.push("Spa");
if (V.clinic > 0) facilities.push("Clinic");
if (V.cellblock > 0) facilities.push("Cellblock");
i => this.appendChild(new ButtonItem(i, this.getAttribute(i), V.currentRule.condition.facility.includes(i))));
}
getAttribute(what) {
return {
"Head Girl Suite": "live with your Head Girl",
"Brothel": "work in the brothel",
"Club": "serve in the club",
"Arcade": "be confined in the arcade",
"Dairy": "work in the dairy",
"Servant Quarters": "work as a servant",
"Master Suite": "serve in the master suite",
"Schoolroom": "learn in the schoolroom",
"Spa": "rest in the spa",
"Clinic": "get treatment in the clinic",
"Cellblock": "be confined in the cellblock",
}
}
class SpecialExclusion extends List {
constructor() {
this.setValue(V.currentRule.condition.excludeSpecialSlaves);
this.onchange = (value) => V.currentRule.condition.excludeSpecialSlaves = value;
}
}
class SpecificInclusionExclusion extends Options {
constructor() {
super();
this.appendChild(new OptionsItem("Limit to specific slaves", () => Engine.display("Rules Slave Select")));
this.appendChild(new OptionsItem("Exclude specific slaves", () => Engine.display("Rules Slave Exclude")));
// parent section for effect editing
class EffectEditor extends Element {
constructor() {
super();
this.appendChild(new AppearanceSection());
this.appendChild(new CosmeticSection());
this.appendChild(new BodyModSection());
this.appendChild(new AutosurgerySection());
this.appendChild(new RegimenSection());
this.appendChild(new BehaviourSection());
class AppearanceSection extends Element {
constructor() {
super();
this.appendChild(new ClothesList());
this.appendChild(new CollarList());
this.appendChild(new ShoeList());
this.appendChild(new CorsetList());
this.appendChild(new VagAccVirginsList());
this.appendChild(new VagAccAVirginsList());
this.appendChild(new VagAccOtherList());
this.appendChild(new DickAccVirginsList());
this.appendChild(new DickAccOtherList());
this.appendChild(new ButtplugsVirginsList());
this.appendChild(new ButtplugsOtherList());
this.appendChild(new ImplantVolumeList());
this.appendChild(new AutosurgerySwitch());
super();
this.appendChild(new GrowthList());
this.appendChild(new CurrativesList());
this.appendChild(new AphrodisiacList());
this.appendChild(new ContraceptiveList());
this.appendChild(new PregDrugsList());
this.appendChild(new FemaleHormonesList());
this.appendChild(new ShemaleHormonesList());
this.appendChild(new GeldingHormonesList());
this.appendChild(new OtherDrugsList());
this.appendChild(new DietList());
this.appendChild(new DietGrowthList());
this.appendChild(new DietBaseList());
this.appendChild(new MuscleList());
this.appendChild(new BraceList());
super();
this.appendChild(new LivingStandardList());
this.appendChild(new PunishmentList());
this.appendChild(new RewardList());
this.appendChild(new ReleaseList());
this.appendChild(new SmartFetishList());
this.appendChild(new SmartXYAttractionList());
this.appendChild(new SmartXXAttractionList());
this.appendChild(new SmartEnergyList());
this.appendChild(new SpeechList());
this.appendChild(new RelationshipList());
super();
this.appendChild(new EyewearList());
this.appendChild(new LensesList());
this.appendChild(new MakeupList());
this.appendChild(new NailsList());
this.appendChild(new HairLengthList());
this.appendChild(new HairColourList());
this.appendChild(new HairStyleList());
this.appendChild(new PubicHairColourList());
this.appendChild(new PubicHairStyleList());
this.appendChild(new ArmpitHairColourList());
this.appendChild(new ArmpitHairStyleList());
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
super();
this.appendChild(new EarPiercingList());
this.appendChild(new NosePiercingList());
this.appendChild(new EyebrowPiercingList());
this.appendChild(new NavelPiercingList());
this.appendChild(new NipplePiercingList());
this.appendChild(new AreolaPiercingList());
this.appendChild(new LipPiercingList());
this.appendChild(new TonguePiercingList());
this.appendChild(new ClitPiercingList());
this.appendChild(new LabiaPiercingList());
this.appendChild(new ShaftPiercingList());
this.appendChild(new PerineumPiercingList());
this.appendChild(new CorsetPiercingList());
this.appendChild(new AutoBrandingList());
this.appendChild(new BrandingLocationList());
this.appendChild(new BrandDesignList());
this.appendChild(new FaceTattooList());
this.appendChild(new ShoulderTattooList());
this.appendChild(new ChestTattooList());
this.appendChild(new ArmTattooList());
this.appendChild(new UpperBackTattooList());
this.appendChild(new LowerBackTattooList());
this.appendChild(new AbdomenTattooList());
this.appendChild(new DickTattooList());
this.appendChild(new ButtockTattooList());
this.appendChild(new AnalTattooList());
this.appendChild(new LegTattooList());
super();
this.appendChild(new VisionSurgeryList());
this.appendChild(new LactationSurgeryList());
this.appendChild(new SemenSurgeryList());
this.appendChild(new CosmeticSurgeryList());
this.appendChild(new LipSurgeryList());
this.appendChild(new ButtSurgeryList());
this.appendChild(new BreastSurgeryList());
this.appendChild(new TighteningSurgeryList());
this.appendChild(new BodyHairSurgeryList());
this.appendChild(new HairSurgeryList());
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
class ClothesList extends List {
constructor() {
const items = [
["Select her own outfit", "choosing her own clothes"]
];
super("Clothes", items);
const nclothes = [
["No default clothes setting", "no default setting"],
["Bangles", "slutty jewelry"],
["Bodysuit", "a comfortable bodysuit"],
["Cheerleader outfit", "a cheerleader outfit"],
["Clubslut netting", "clubslut netting"],
["Cutoffs and a t-shirt", "cutoffs and a t-shirt"],
["Fallen nun", "a fallen nuns habit"],
["Halter top", "a halter top dress"],
["Hijab and abaya", "a hijab and abaya"],
["Latex catsuit", "a latex catsuit"],
["Leotard", "a leotard"],
["Maid (nice)", "a nice maid outfit"],
["Maid (slutty)", "a slutty maid outfit"],
["Military uniform", "a military uniform"],
["Mini dress", "a mini dress"],
["Nice lingerie", "attractive lingerie"],
["Nurse (nice)", "a nice nurse outfit"],
["Schoolgirl", "a schoolgirl outfit"],
["Silken ballgown", "a ball gown"],
["Skimpy battledress", "battledress"],
["Slave gown", "a slave gown"],
["Slutty outfit", "a slutty outfit"],
["String bikini", "a stirng bikini"],