From a608394d3812429afc37a0039f362e72aac3438b Mon Sep 17 00:00:00 2001 From: "Felix W. Dekker" Date: Sun, 3 May 2020 23:50:55 +0200 Subject: [PATCH] Migrate to npm, use template package --- .gitignore | 117 ++++++++ Gruntfile.js | 127 +++++++++ README.md | 30 ++ index.html | 634 ------------------------------------------ package-lock.json | Bin 0 -> 231310 bytes package.json | 36 +++ src/main/css/main.css | 47 ++++ src/main/index.html | 87 ++++++ src/main/js/index.js | 500 +++++++++++++++++++++++++++++++++ 9 files changed, 944 insertions(+), 634 deletions(-) create mode 100644 .gitignore create mode 100644 Gruntfile.js delete mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/main/css/main.css create mode 100644 src/main/index.html create mode 100644 src/main/js/index.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e7ea1e8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,117 @@ +## Node +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 + +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.pnp.* diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..93e20f9 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,127 @@ +const path = require("path"); + +module.exports = grunt => { + grunt.initConfig({ + pkg: grunt.file.readJSON("package.json"), + clean: { + default: ["dist/"], + }, + copy: { + html: { + files: [{expand: true, cwd: "src/main/", src: "**/*.html", dest: "dist/"}] + }, + css: { + files: [{expand: true, cwd: "src/main/", src: "**/*.css", dest: "dist/"}] + }, + }, + focus: { + dev: { + include: ["css", "html", "js"], + }, + devLink: { + include: ["css", "html", "js", "link"], + } + }, + replace: { + dev: { + src: ["./dist/*.html", "./dist/*.js"], + replacements: [ + { + from: "%%VERSION_NUMBER%%", + to: "<%= pkg.version %>+" + new Date().toISOString().slice(0, 19).replace(/[-:T]/g, "") + } + ], + overwrite: true + }, + deploy: { + src: ["./dist/*.html", "./dist/*.js"], + replacements: [ + { + from: "%%VERSION_NUMBER%%", + to: "<%= pkg.version %>" + } + ], + overwrite: true + }, + }, + webpack: { + options: { + entry: "./src/main/js/index.js", + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + }, + ], + }, + resolve: { + extensions: [".js"], + }, + output: { + filename: "bundle.js", + path: path.resolve(__dirname, "dist/"), + }, + }, + dev: { + mode: "development", + devtool: "inline-source-map", + }, + deploy: { + mode: "production", + }, + }, + watch: { + css: { + files: ["src/main/**/*.css"], + tasks: ["copy:css"], + }, + html: { + files: ["src/main/**/*.html"], + tasks: ["copy:html"], + }, + js: { + files: ["src/main/**/*.js"], + tasks: ["webpack:dev", "replace:dev"], + }, + link: { + files: ["node_modules/@fwdekker/*/dist/**"], + tasks: ["webpack:dev", "replace:dev"], + }, + }, + }); + + grunt.loadNpmTasks("grunt-contrib-clean"); + grunt.loadNpmTasks("grunt-contrib-copy"); + grunt.loadNpmTasks("grunt-contrib-watch"); + grunt.loadNpmTasks("grunt-focus"); + grunt.loadNpmTasks("grunt-text-replace"); + grunt.loadNpmTasks("grunt-webpack"); + + grunt.registerTask("dev", [ + // Pre + "clean", + // Copy files + "copy:html", + "copy:css", + // Compile + "webpack:dev", + // Post + "replace:dev" + ]); + grunt.registerTask("dev:server", ["dev", "focus:dev"]); + grunt.registerTask("dev:server:link", ["dev", "focus:devLink"]); + grunt.registerTask("deploy", [ + // Pre + "clean", + // Copy files + "copy:html", + "copy:css", + // Compile JS + "webpack:deploy", + // Post + "replace:deploy" + ]); + + grunt.registerTask("default", ["dev"]); +}; diff --git a/README.md b/README.md index b4faec1..a60b3a6 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,33 @@ Given a random date, can you determine the weekday of that date? weekday of any date in the Gregorian calendar. This tool will help you practice the algorithm by giving you random dates to test on. Set this as your homepage to get yourself to practice this every day. + +## Development +### Requirements +* [npm](https://www.npmjs.com/) + +### Setting up +```shell script +# Install dependencies (only needed once) +$> npm ci +``` + +### Building +```shell script +# Build the template in `dist/` for development +$> npm run dev +# Same as above, but automatically rerun it whenever files are changed +$> npm run dev:server +# Same as above, but also rerun when linked `@fwdekker` dependencies change +$> npm run dev:server:link +# Build the template in `dist/` for deployment +$> npm run deploy +``` + +### Publishing +```shell script +# Log in to npm +$> npm login +# Push to npm +$> npm publish --access public +``` diff --git a/index.html b/index.html deleted file mode 100644 index 664cb04..0000000 --- a/index.html +++ /dev/null @@ -1,634 +0,0 @@ - - - - - - - - - - - Doomsday | FWDekker - - - - - - -
- -
-
-

Doomsday

- -
-

- Test your mastery of - Conway's Doomsday rule. -

-
-
-
- - - -
-
-
- Century -
-
- -
-
- -
-
-
- -
- Year -
-
- -
-
- -
-
-
- -
- -
-
- -
-
- -
-
-
- -
- -
-
- -
-
-
-
- - - - -
- - - - - - - - diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..1da16352c0ede7817ab82f94c8f2238e3b2f24b5 GIT binary patch literal 231310 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 L&;Rs4|I_~hWif5t literal 0 HcmV?d00001 diff --git a/package.json b/package.json new file mode 100644 index 0000000..8d87856 --- /dev/null +++ b/package.json @@ -0,0 +1,36 @@ +{ + "name": "doomsday", + "version": "1.3.3", + "description": "Test your mastery of Conway's Doomsday rule.", + "author": "Felix W. Dekker", + "browser": "dist/bundle.js", + "repository": { + "type": "git", + "url": "git@git.fwdekker.com:FWDekker/interlanguage-checker.git" + }, + "private": true, + "scripts": { + "clean": "grunt clean", + "dev": "grunt dev", + "dev:server": "grunt dev:server", + "dev:server:link": "grunt dev:server:link", + "deploy": "grunt deploy" + }, + "dependencies": { + "@fwdekker/template": "^0.0.10", + "js-cookie": "^2.2.1" + }, + "devDependencies": { + "@types/js-cookie": "^2.2.6", + "grunt": "^1.1.0", + "grunt-cli": "^1.3.2", + "grunt-contrib-clean": "^2.0.0", + "grunt-contrib-copy": "^1.0.0", + "grunt-contrib-watch": "^1.1.0", + "grunt-focus": "^1.0.0", + "grunt-text-replace": "^0.4.0", + "grunt-webpack": "^3.1.3", + "webpack": "^4.42.1", + "webpack-cli": "^3.3.11" + } +} diff --git a/src/main/css/main.css b/src/main/css/main.css new file mode 100644 index 0000000..6c9aeb5 --- /dev/null +++ b/src/main/css/main.css @@ -0,0 +1,47 @@ +:root { + --error-color: red; + --success-color: green; +} + + +.row .column.quiz-button-column { + display: flex; + align-items: flex-end; +} + +.row .column.quiz-button-column .quiz-button { + margin-bottom: 15px; +} + +label, summary b { + cursor: pointer; +} + + +.success-message { + color: var(--success-color); +} + +.error-message { + color: var(--error-color); +} + +.success-box { + background-color: var(--success-color); + border-color: var(--success-color); +} + +.error-box { + background-color: var(--error-color); + border-color: var(--error-color); +} + +input[data-entered=true]:valid { + border-color: var(--success-color); + color: var(--success-color); +} + +input[data-entered=true]:invalid { + border-color: var(--error-color); + color: var(--error-color); +} diff --git a/src/main/index.html b/src/main/index.html new file mode 100644 index 0000000..4cfd53c --- /dev/null +++ b/src/main/index.html @@ -0,0 +1,87 @@ + + + + + + + + + + + Doomsday | FWDekker + + + + + + +
+ +
+ + + + +
+
+
+ Century +
+
+ +
+
+ +
+
+
+ +
+ Year +
+
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+
+
+ +
+ + + + + + diff --git a/src/main/js/index.js b/src/main/js/index.js new file mode 100644 index 0000000..b92c044 --- /dev/null +++ b/src/main/js/index.js @@ -0,0 +1,500 @@ +import {$, doAfterLoad, footer, header, nav} from "@fwdekker/template"; +import Cookies from "js-cookie"; + + +/** + * Returns a number between `min` (inclusive) and `max` (inclusive). + * + * @param min the lower bound of permissible values + * @param max the upper bound of permissible values + */ +function generateRandom(min, max) { + return Math.floor(Math.random() * (max - min + 1) + min); +} + + +/** + * An input that can be validated. + * + * In particular, the century, year, and day inputs of the Doomsday test. + * + * @property input {HTMLInputElement} the input that is validatable + * @property titleLabel {HTMLElement} the label of which to update the text + * @property button {HTMLButtonElement} the submission button that activates validation + */ +class ValidatableInput { + /** + * Constructs a new validatable input and registers event listeners. + * + * @param input {HTMLInputElement} the input that is validatable + * @param titleLabel {HTMLElement} the label of which to update the text + * @param button {HTMLButtonElement} the submission button that activates validation + */ + constructor(input, titleLabel, button) { + this.input = input; + this.titleLabel = titleLabel; + this.button = button; + + this.button.addEventListener("click", () => this.onSubmit()); + this.input.addEventListener("keydown", event => { + if (event.key !== "Enter") + return; + + this.onSubmit(); + event.preventDefault(); + }); + } + + + /** + * Handles the user submitting the input. + */ + onSubmit() { + this.input.dataset["entered"] = "true"; + + if (this.isValid(this.input.value)) { + this.showSuccess(); + this.onValidInput(); + } else { + this.showError(); + this.selectInput(); + this.onInvalidInput(); + } + } + + /** + * Returns `true` if and only if the input is valid. + * + * This method **must** be implemented by subclasses. + * + * @param value {string} the value of the input to validate + * @return {boolean} `true` if and only if the input is valid + */ + isValid(value) { + throw new Error("Implement this method."); + } + + /** + * Runs when the user submits a valid input. + * + * This method **must** be implemented by subclasses. + */ + onValidInput() { + throw new Error("Implement this method."); + } + + /** + * Runs when a user submits an invalid input. + * + * This method **must** be implemented by subclasses. + */ + onInvalidInput() { + throw new Error("Implement this method."); + } + + + /** + * Resets the input, title, and error message to their initial state, and removes the value from the input. + */ + reset() { + this.input.value = ""; + this.input.dataset["entered"] = "false"; + + this.showSuccess(); + this.updateTitle(); + + this.titleLabel.classList.remove("success-message"); + this.button.classList.remove("success-box"); + } + + /** + * Marks the input as invalid. + */ + showError() { + this.input.setCustomValidity("Incorrect"); + + this.titleLabel.classList.remove("success-message"); + this.titleLabel.classList.add("error-message"); + + this.button.classList.remove("success-box"); + this.button.classList.add("error-box"); + } + + /** + * Marks the input as valid. + */ + showSuccess() { + this.input.setCustomValidity(""); + + this.titleLabel.classList.remove("error-message"); + this.titleLabel.classList.add("success-message"); + + this.button.classList.remove("error-box"); + this.button.classList.add("success-box"); + } + + /** + * Updates the title label's contents. + * + * Does nothing by default. Implement this method to make it do something. + */ + updateTitle() { + // Do nothing + } + + + /** + * Focuses the input element. + */ + selectInput() { + this.input.select(); + } +} + +/** + * A wrapper around a `
` element that persists the state in a cookie. + */ +class ToggleableSection { + /** + * Constructs a new `ToggleableSection`. + * + * @param name {string} the name to identify this component with in persistent storage + * @param details {HTMLDetailsElement} the element that can be toggled + */ + constructor(name, details) { + this._name = name; + this._details = details; + this._details.addEventListener("toggle", () => this.onToggle(this.isOpened())); + + this._loadToggle(); + } + + + /** + * Returns `true` if and only if the component is currently open. + * + * @return {boolean} `true` if and only if the component is currently open. + */ + isOpened() { + return !!this._details.open; + } + + /** + * Opens or closes the component. + * + * @param isOpened {boolean} whether to open the component + */ + setOpened(isOpened) { + this._details.open = isOpened; + } + + /** + * This method is invoked whenever the component is toggled. + * + * @param isOpened {boolean} the new state of the component + */ + onToggle(isOpened) { + this._storeToggle(); + } + + + /** + * Persists the state of this component. + * + * @private + */ + _storeToggle() { + Cookies.set(`toggle-${this._name}`, this.isOpened(), {expires: 365 * 10}); + } + + /** + * Reads the state of this component from persistent storage and applies it. + * + * @private + */ + _loadToggle() { + const target = Cookies.get(`toggle-${this._name}`); + if (target === undefined) { + this._storeToggle(); + return; + } + + this.setOpened(target === "true"); + } +} + +/** + * A wrapper around the good ol' `Date` class that provides a bunch of useful Doomsday-specific methods. + * + * @property {Date} the underlying date + */ +class DoomsdayDate { + /** + * Wraps a `DoomsdayDate` around the given date. + * + * @param date {Date} the date to be wrapped + */ + constructor(date) { + this.date = date; + } + + + /** + * Returns the number of this `DoomsdayDate`'s century. + * + * @return {number} the number of this `DoomsdayDate`'s century + */ + getCentury() { + return Math.floor(this.date.getFullYear() / 100); + } + + /** + * Returns the day of the week of the anchor of this `DoomsdayDate`'s century. + * + * @return {string} the day of the week of the anchor of this `DoomsdayDate`'s century + */ + getCenturyAnchorString() { + const centuryAnchorNumber = (5 * (this.getCentury() % 4)) % 7 + 2; + return DoomsdayDate.dayNumberToString(centuryAnchorNumber); + }; + + /** + * Returns the day of the week of the anchor day of this `DoomsdayDate`'s year. + * + * @return {string} the day of the week of the anchor day of this `DoomsdayDate`'s year + */ + getYearAnchorString() { + const anchorDate = new Date(this.date); + anchorDate.setDate(4); // 4th + anchorDate.setMonth(3); // April + return DoomsdayDate.dayNumberToString(anchorDate.getDay()); + }; + + /** + * Returns the day of the week of this `DoomsdayDate`. + * + * @return {string} the day of the week of this `DoomsdayDate` + */ + getWeekdayString() { + return DoomsdayDate.dayNumberToString(this.date.getDay()); + }; + + + /** + * Returns the name of the day given its 0-based index, where 0 is `Sunday`. + * + * @param dayNumber {number} the number of the day, as returned by `Date`'s `#getDay` function. + * @return {string} the name of the day given its 0-based index, where 0 is `Sunday` + */ + static dayNumberToString(dayNumber) { + switch (dayNumber % 7) { + case 0: + return "Sunday"; + case 1: + return "Monday"; + case 2: + return "Tuesday"; + case 3: + return "Wednesday"; + case 4: + return "Thursday"; + case 5: + return "Friday"; + case 6: + return "Saturday"; + } + }; + + /** + * Returns the day of the week corresponding to the given string. + * + * This is a convenience method for interpreting (incomplete) user inputs. + * + * @param dayString {string} the day of the week to expand + * @return {string} the day of the week corresponding to the given string + */ + static expandDayString(dayString) { + dayString = dayString.toLowerCase(); + if (dayString.startsWith("m")) + return "Monday"; + else if (dayString.startsWith("tu")) + return "Tuesday"; + else if (dayString.startsWith("w")) + return "Wednesday"; + else if (dayString.startsWith("th")) + return "Thursday"; + else if (dayString.startsWith("f")) + return "Friday"; + else if (dayString.startsWith("sa")) + return "Saturday"; + else if (dayString.startsWith("su")) + return "Sunday"; + else + return undefined; + } + + /** + * Returns a random date in the range `0001-01-01` (inclusive) to `9999-12-31` (inclusive), wrapped inside a + * `DoomsdayDate` object. + * + * @return {DoomsdayDate} a random date + */ + static random() { + // TODO Give custom dates to this method + const minDate = new Date("0001-01-01").getTime() / 86400000; + const maxDate = new Date("9999-12-31").getTime() / 86400000; + return new DoomsdayDate(new Date(generateRandom(minDate, maxDate) * 86400000)); + } +} + + +doAfterLoad(() => { + // Initialize template + $("#nav").appendChild(nav()); + $("#header").appendChild(header({ + title: "Doomsday", + description: ` + Test your mastery of \ + Conway's Doomsday rule. + ` + })); + $("#footer").appendChild(footer({ + author: "Felix W. Dekker", + authorURL: "https://fwdekker.com/", + license: "MIT License", + licenseURL: "https://git.fwdekker.com/FWDekker/doomsday/src/branch/master/LICENSE", + vcs: "git", + vcsURL: "https://git.fwdekker.com/FWDekker/doomsday/", + version: "v%%VERSION_NUMBER%%" + })); + $("main").style.display = null; + + + // Initialize quiz + let quizDate; + + const centuryDetails = new class extends ToggleableSection { + onToggle(isOpened) { + super.onToggle(isOpened); + if (isOpened) centuryInput.selectInput(); + centuryInput.updateTitle(); + } + }("century", $("#century-details")); + const yearDetails = new class extends ToggleableSection { + onToggle(isOpened) { + super.onToggle(isOpened); + if (isOpened) yearInput.selectInput(); + yearInput.updateTitle(); + } + }("year", $("#year-details")); + + const centuryInput = new class extends ValidatableInput { + isValid(value) { + console.log("# Validate century"); + console.log(`Input: ${value}`); + console.log(`Expanded: ${DoomsdayDate.expandDayString(value)}`); + console.log(`Expected: ${quizDate.getCenturyAnchorString()}`); + return DoomsdayDate.expandDayString(value) === quizDate.getCenturyAnchorString(); + } + + onValidInput() { + this.input.value = DoomsdayDate.expandDayString(this.input.value); + if (yearDetails.isOpened()) + yearInput.selectInput(); + else + dayInput.selectInput(); + } + + onInvalidInput() { + // Do nothing + } + + updateTitle() { + if (centuryDetails.isOpened()) + this.titleLabel.innerText = `Anchor day of century starting in ${quizDate.getCentury() * 100}?`; + else + this.titleLabel.innerText = `Century`; + } + }($("#century-input"), $("#century-title-label"), $("#century-submit")); + const yearInput = new class extends ValidatableInput { + isValid(value) { + console.log("# Validate year"); + console.log(`Input: ${value}`); + console.log(`Expanded: ${DoomsdayDate.expandDayString(value)}`); + console.log(`Expected: ${quizDate.getYearAnchorString()}`); + return DoomsdayDate.expandDayString(value) === quizDate.getYearAnchorString(); + } + + onValidInput() { + this.input.value = DoomsdayDate.expandDayString(this.input.value); + dayInput.selectInput(); + } + + onInvalidInput() { + // Do nothing + } + + updateTitle() { + if (yearDetails.isOpened()) + this.titleLabel.innerText = `Doomsday of year ${quizDate.date.getFullYear()}?`; + else + this.titleLabel.innerText = `Year`; + } + }($("#year-input"), $("#year-title-label"), $("#year-submit")); + const dayInput = new class extends ValidatableInput { + isValid(value) { + console.log("# Validate day"); + console.log(`Input: ${value}`); + console.log(`Expanded: ${DoomsdayDate.expandDayString(value)}`); + console.log(`Expected: ${quizDate.getWeekdayString()}`); + return DoomsdayDate.expandDayString(value) === quizDate.getWeekdayString(); + } + + onValidInput() { + this.input.value = DoomsdayDate.expandDayString(this.input.value); + resetButton.focus(); + } + + onInvalidInput() { + // Do nothing + } + + updateTitle() { + this.titleLabel.innerText = `Weekday of ${quizDate.date.toISOString().substr(0, 10)}?`; + } + }($("#day-input"), $("#day-title-label"), $("#day-submit")); + + const resetButton = $("#reset-button"); + resetButton.addEventListener("click", () => { + console.log(" "); + console.log(" "); + reloadQuiz(); + }); + + + /** + * Generates a new date for the quiz and resets the inputs to reflect this. + */ + function reloadQuiz() { + quizDate = DoomsdayDate.random(); + console.log("# Reset"); + console.log(`New date: ${quizDate.date.toISOString().substr(0, 10)}`); + console.log(`Century#: ${quizDate.getCentury()}`); + console.log(`Century: ${quizDate.getCenturyAnchorString()}`); + console.log(`Year: ${quizDate.getYearAnchorString()}`); + console.log(`Day: ${quizDate.getWeekdayString()}`); + + centuryInput.reset(); + yearInput.reset(); + dayInput.reset(); + if (centuryDetails.isOpened()) + centuryInput.selectInput(); + else if (yearDetails.isOpened()) + yearInput.selectInput(); + else + dayInput.selectInput(); + } + + // Let the fun begin + reloadQuiz(); +});