Skip to content
Snippets Groups Projects
rulesAssistantOptions.js 103 KiB
Newer Older
brickode's avatar
brickode committed
/* eslint-disable camelcase */
brickode's avatar
brickode committed
/* eslint-disable no-unused-vars */
vas's avatar
vas committed
// rewrite of the rules assistant options page in javascript
// uses an object-oriented widget pattern
klorpa's avatar
klorpa committed
// wrapped in a closure so as not to pollute the global namespace
vas's avatar
vas committed
// 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
vas's avatar
vas committed

brickode's avatar
brickode committed
window.rulesAssistantOptions = (function() {
vas's avatar
vas committed
	"use strict";
vas's avatar
vas committed
	let V, current_rule;
vas's avatar
vas committed

vas's avatar
vas committed
	function rulesAssistantOptions(element) {
vas's avatar
vas committed
		V = State.variables;
		V.nextButton = "Back to Main";
		V.nextLink = "Main";
		V.returnTo = "Main";
		V.showEncyclopedia = 1;
		V.encyclopedia = "Personal Assistant";
		if (V.currentRule !== null) {
			const idx = V.defaultRules.findIndex(rule => rule.ID === V.currentRule);
DCoded's avatar
DCoded committed
			if (idx === -1) {
				current_rule = V.defaultRules[0];
			} else { current_rule = V.defaultRules[idx]; }
brickode's avatar
brickode committed
		const root = new Root(element);
vas's avatar
vas committed
	}

	function returnP(e) { return e.keyCode === 13; }
vas's avatar
vas committed

vas's avatar
vas committed
	function newRule(root) {
		const rule = emptyDefaultRule();
		V.defaultRules.push(rule);
vas's avatar
vas committed
		V.currentRule = rule.ID;
vas's avatar
vas committed
		reload(root);
vas's avatar
vas committed
	}

	function removeRule(root) {
vas's avatar
vas committed
		const idx = V.defaultRules.findIndex(rule => rule.ID === current_rule.ID);
vas's avatar
vas committed
		V.defaultRules.splice(idx, 1);
		if (V.defaultRules.length > 0) {
			const new_idx = idx < V.defaultRules.length ? idx : V.defaultRules.length - 1;
			V.currentRule = V.defaultRules[new_idx].ID;
DCoded's avatar
DCoded committed
		} else { V.currentRule = null; }
vas's avatar
vas committed
		reload(root);
vas's avatar
vas committed
	}

vas's avatar
vas committed
	function lowerPriority(root) {
DCoded's avatar
DCoded committed
		if (V.defaultRules.length === 1) { return; } // nothing to swap with
vas's avatar
vas committed
		const idx = V.defaultRules.findIndex(rule => rule.ID === current_rule.ID);
DCoded's avatar
DCoded committed
		if (idx === 0) { return; } // no lower rule
brickode's avatar
brickode committed
		arraySwap(V.defaultRules, idx, idx - 1);
vas's avatar
vas committed
		reload(root);
vas's avatar
vas committed
	}

	function higherPriority(root) {
DCoded's avatar
DCoded committed
		if (V.defaultRules.length === 1) { return; } // nothing to swap with
vas's avatar
vas committed
		const idx = V.defaultRules.findIndex(rule => rule.ID === current_rule.ID);
DCoded's avatar
DCoded committed
		if (idx === V.defaultRules.length - 1) { return; } // no higher rule
brickode's avatar
brickode committed
		arraySwap(V.defaultRules, idx, idx + 1);
vas's avatar
vas committed
		reload(root);
vas's avatar
vas committed
	}

	function changeName(name, root) {
DCoded's avatar
DCoded committed
		if (name === current_rule.name) { return; }
vas's avatar
vas committed
		current_rule.name = name;
vas's avatar
vas committed
		reload(root);
vas's avatar
vas committed
	}

	// reload the passage
vas's avatar
vas committed
	function reload(root) {
vas's avatar
vas committed
		const elem = root.element;
vas's avatar
vas committed
		elem.innerHTML = "";
vas's avatar
vas committed
		rulesAssistantOptions(elem);
vas's avatar
vas committed
	}

vas's avatar
vas committed
	const parse = {
		integer(string) {
			let n = parseInt(string, 10);
brickode's avatar
brickode committed
			return isNaN(n) ? 0 : n;
vas's avatar
vas committed
		},
		boobs(string) {
			return Math.clamp(parse.integer(string), 0, 48000);
		},
		butt(string) {
x's avatar
x committed
			return Math.clamp(parse.integer(string), 0, 20);
vas's avatar
vas committed
		},
		lips(string) {
			return Math.clamp(parse.integer(string), 0, 100);
		},
		dick(string) {
x's avatar
x committed
			return Math.clamp(parse.integer(string), 0, 30);
vas's avatar
vas committed
		},
		balls(string) {
x's avatar
x committed
			return Math.clamp(parse.integer(string), 0, 125);
vas's avatar
vas committed
		},
	};

vas's avatar
vas committed
	// 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) {
vas's avatar
vas committed
			this.parent = null;
			this.element = this.render(...args);
			this.children = [];
vas's avatar
vas committed
		}

		appendChild(child) {
vas's avatar
vas committed
			child.parent = this;
			this.children.push(child);
			this.element.appendChild(child.element);
vas's avatar
vas committed
		}

		// return the first argument to simplify creation of basic container items
		render(...args) {
vas's avatar
vas committed
			return args[0];
vas's avatar
vas committed
		}

		remove() {
			const idx = this.parent.children.findIndex(child => child === this);
			this.parent.children.slice(idx, 1);
			this.element.remove();
		}
vas's avatar
vas committed
	}
klorpa's avatar
klorpa committed

vas's avatar
vas committed
	class Section extends Element {
brickode's avatar
brickode committed
		constructor(header, hidden = false) {
vas's avatar
vas committed
			super(header);
			this.hidey = this.element.querySelector("div");
DCoded's avatar
DCoded committed
			if (hidden) { this.toggle_hidey(); }
vas's avatar
vas committed
		}
klorpa's avatar
klorpa committed

vas's avatar
vas committed
		render(header) {
			const section = document.createElement("section");
			section.classList.add("rajs-section");
			const h1 = document.createElement("h1");
brickode's avatar
brickode committed
			h1.onclick = () => { this.toggle_hidey(); };
vas's avatar
vas committed
			h1.innerHTML = header;
			const hidey = document.createElement("div");
			section.appendChild(h1);
			section.appendChild(hidey);
			return section;
		}

		appendChild(child) {
			child.parent = this;
			this.children.push(child);
			this.hidey.appendChild(child.element);
		}

		toggle_hidey() {
brickode's avatar
brickode committed
			switch (this.hidey.style.display) {
vas's avatar
vas committed
				case "none":
					this.hidey.style.display = "initial";
					break;
				default:
					this.hidey.style.display = "none";
					break;
			}
		}
	}
vas's avatar
vas committed

	// 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
ezsh's avatar
ezsh committed
	class EditorWithShortcuts extends Element {
brickode's avatar
brickode committed
		constructor(prefix, data = [], editor = false, ...args) {
Skriv's avatar
Skriv committed
			super(`${prefix}: `, editor, ...args);
vas's avatar
vas committed
			this.selectedItem = null;
vas's avatar
vas committed
			data.forEach(item => this.appendChild(new ListItem(...item)));
vas's avatar
vas committed
		}
vas's avatar
vas committed

		createEditor(...args) { return null; }
ezsh's avatar
ezsh committed

		render(prefix, editor, ...args) {
vas's avatar
vas committed
			const elem = document.createElement("div");
			const label = document.createElement("span");
			label.innerHTML = prefix;
ezsh's avatar
ezsh committed
			this.value = editor ? this.createEditor(...args) : document.createElement("strong");
vas's avatar
vas committed
			elem.appendChild(label);
ezsh's avatar
ezsh committed
			elem.appendChild(this.value);
vas's avatar
vas committed
			elem.classList.add("rajs-list");
			return elem;
vas's avatar
vas committed
		}
vas's avatar
vas committed

		inputEdited() {
DCoded's avatar
DCoded committed
			if (this.selectedItem) { this.selectedItem.deselect(); }
vas's avatar
vas committed
			this.propagateChange();
vas's avatar
vas committed
		}
vas's avatar
vas committed

vas's avatar
vas committed
		selectItem(item) {
DCoded's avatar
DCoded committed
			if (this.selectedItem) { this.selectedItem.deselect(); }
vas's avatar
vas committed
			this.selectedItem = item;
vas's avatar
vas committed
			this.setValue(item.data);
vas's avatar
vas committed
			this.propagateChange();
vas's avatar
vas committed
		}

		setValue(what) {
DCoded's avatar
DCoded committed
			if (this.value.tagName === "INPUT") {
Loading
Loading full blame...