diff --git a/index.html b/index.html index b8d0026..04b7f64 100644 --- a/index.html +++ b/index.html @@ -54,28 +54,17 @@
- - - + + + - - - + + + - - - - - - - - - - - - - - + + +
@@ -102,43 +91,41 @@ * @param max the upper bound of permissible values */ function generateRandom(min, max) { - return Math.floor((Math.random() * (max - min + 1)) + min); + return Math.floor(Math.random() * (max - min + 1) + min); } /** - * An element that can be validated. + * An input that can be validated. * * In particular, the century, year, and day inputs of the Doomsday test. */ - class ValidatableElement { + class ValidatableInput { /** - * Constructs a new validatable element and registers event listeners. + * Constructs a new validatable input and registers event listeners. * - * @param element the element that is validatable - * @param titleLabel the label with the `labelFor` property for the element given as the first parameter + * @param input the input that is validatable + * @param titleLabel the label with the `labelFor` property for the input given as the first parameter * @param errorLabel the label to display errors in */ - constructor(element, titleLabel, errorLabel) { - this.element = element; + constructor(input, titleLabel, errorLabel) { + this.input = input; this.titleLabel = titleLabel; this.errorLabel = errorLabel; - this.element.addEventListener("keydown", event => { + this.input.addEventListener("keydown", event => { if (event.key !== "Enter") return; - this.element.dataset["entered"] = "true"; + this.input.dataset["entered"] = "true"; - const errorMessage = this.validate(this.element.value); - this.element.setCustomValidity(errorMessage || ""); - this.errorLabel.innerText = errorMessage || ""; + const errorMessage = this.validate(this.input.value); if (errorMessage === null) { - this.errorLabel.classList.add("invisible"); + this.hideError(); this.onValidInput(); } else { - this.errorLabel.classList.remove("invisible"); - this.element.select(); + this.showError(errorMessage); + this.selectInput(); this.onInvalidInput(); } }); @@ -146,11 +133,11 @@ /** - * Returns `undefined` if the element is valid, or an error message explaining why it is invalid otherwise. + * Returns `null` if the input is valid, or an error message explaining why it is invalid otherwise. * * This method **must** be implemented by subclasses. * - * @param value the value of the element to validate + * @param value the value of the input to validate */ validate(value) { throw new Error("Implement this method."); @@ -176,31 +163,53 @@ /** - * Resets the element to its initial state, removing error messages and user input. + * Resets the input, title, and error message to their initial state, and removes the value from the input. */ reset() { - this.element.dataset["entered"] = "false"; - this.element.value = ""; - this.element.setCustomValidity(""); + this.input.value = ""; + this.input.dataset["entered"] = "false"; + + this.hideError(); + this.updateTitle(); + } + + /** + * Marks the input as invalid and displays an error label with the given message. + * + * @param message the error message to display + */ + showError(message) { + this.input.setCustomValidity(message || "Invalid"); + + this.errorLabel.classList.remove("invisible"); + this.errorLabel.innerText = message; + } + + /** + * Marks the input as valid and hides the error label. + */ + hideError() { + this.input.setCustomValidity(""); this.errorLabel.classList.add("invisible"); this.errorLabel.innerText = ""; } /** - * Focuses the input element. + * Updates the title label's contents. + * + * Does nothing by default. Implement this method to make it do something. */ - focus() { - this.element.select(); + updateTitle() { + // Do nothing } + /** - * Sets the title above the input. - * - * @param title the title to set + * Focuses the input element. */ - setTitle(title) { - this.titleLabel.innerText = title; + selectInput() { + this.input.select(); } } @@ -218,13 +227,18 @@ } + /** + * Returns the first year of this `DoomsdayDate`'s century. + */ + getCentury() { + return Math.floor(this.date.getFullYear() / 100) * 100; + } + /** * Returns the day of the week of the anchor of this `DoomsdayDate`'s century as a string. */ getCenturyAnchorString() { - const yearNumber = this.date.getFullYear(); - const centuryNumber = Math.floor(yearNumber / 100); - const centuryAnchorNumber = (5 * (centuryNumber % 4)) % 7 + 2; + const centuryAnchorNumber = (5 * (this.getCentury() % 4)) % 7 + 2; return DoomsdayDate.dayNumberToString(centuryAnchorNumber); }; @@ -286,34 +300,41 @@ doAfterLoad(() => { let quizDate; - const centuryClass = new class extends ValidatableElement { + const centuryInput = new class extends ValidatableInput { validate(value) { return value === quizDate.getCenturyAnchorString() ? null : ""; } onValidInput() { - console.log("valid"); - yearClass.focus(); + yearInput.selectInput(); } onInvalidInput() { // Do nothing } - }($("#century"), $("#century-label"), $("#century-error")); - const yearClass = new class extends ValidatableElement { + + updateTitle() { + this.titleLabel.innerText = `Anchor day of century starting in ${quizDate.getCentury()}?`; + } + }($("#century-input"), $("#century-title-label"), $("#century-error-label")); + const yearInput = new class extends ValidatableInput { validate(value) { return value === quizDate.getYearAnchorString() ? null : ""; } onValidInput() { - dayClass.focus(); + dayInput.selectInput(); } onInvalidInput() { // Do nothing } - }($("#year"), $("#year-label"), $("#year-error")); - const dayClass = new class extends ValidatableElement { + + updateTitle() { + this.titleLabel.innerText = `Doomsday of year ${quizDate.date.getFullYear()}?`; + } + }($("#year-input"), $("#year-title-label"), $("#year-error-label")); + const dayInput = new class extends ValidatableInput { validate(value) { return value === quizDate.getWeekdayString() ? null : ""; } @@ -325,26 +346,25 @@ onInvalidInput() { // Do nothing } - }($("#day"), $("#day-label"), $("#day-error")); + + updateTitle() { + this.titleLabel.innerText = `Weekday of ${quizDate.date.toISOString().substr(0, 10)}?`; + } + }($("#day-input"), $("#day-title-label"), $("#day-error-label")); + /** + * Generates a new date for the quiz and resets the inputs to reflect this. + */ function reloadQuiz() { quizDate = DoomsdayDate.random(); - centuryClass.reset(); - yearClass.reset(); - dayClass.reset(); - - centuryClass.setTitle("" + Math.floor(quizDate.date.getFullYear() / 100) * 100); - yearClass.setTitle("" + quizDate.date.getFullYear()); - dayClass.setTitle("" + quizDate.date.toISOString().substr(0, 10)); - - centuryClass.focus(); - - // console.log(centuryToAnchor(quizDate)); - // console.log(yearToAnchor(quizDate)); - // console.log(dateToWeekDay(quizDate)); + centuryInput.reset(); + yearInput.reset(); + dayInput.reset(); + centuryInput.selectInput(); } + // Let the fun begin reloadQuiz(); });