From d443fb7c48c6a9599f93b138643bebeb69375583 Mon Sep 17 00:00:00 2001 From: "Florine W. Dekker" Date: Tue, 8 Mar 2022 20:38:16 +0100 Subject: [PATCH] Naively rewrite to TypeScript --- Gruntfile.js | 11 +- package-lock.json | Bin 209780 -> 213774 bytes package.json | 6 +- src/main/js/{main.js => Main.ts} | 172 ++++++++++++++++++------------- tsconfig.json | 11 ++ 5 files changed, 122 insertions(+), 78 deletions(-) rename src/main/js/{main.js => Main.ts} (75%) create mode 100644 tsconfig.json diff --git a/Gruntfile.js b/Gruntfile.js index 4a5adcf..166e196 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -16,7 +16,7 @@ module.exports = grunt => { }, focus: { dev: { - include: ["css", "html", "js"], + include: ["css", "html", "ts"], }, }, replace: { @@ -51,23 +51,24 @@ module.exports = grunt => { tasks: ["copy:html"], }, js: { - files: ["src/main/**/*.js"], + files: ["src/main/**/*.ts"], tasks: ["webpack:dev", "replace:dev"], }, }, webpack: { options: { - entry: "./src/main/js/main.js", + entry: "./src/main/js/Main.ts", module: { rules: [ { - test: /\.js$/, + test: /\.ts$/, + use: "ts-loader", exclude: /node_modules/, }, ], }, resolve: { - extensions: [".js"], + extensions: [".ts"], }, output: { filename: "bundle.js", diff --git a/package-lock.json b/package-lock.json index 9aded6875e626c5dc39173cd2ed967d59f297b42..404a828b08a732da4f87fd7f9dd357c9ff7e14a2 100644 GIT binary patch delta 2702 zcmd6nYit`;7RPn&wC*dxj zvE6;BXdg(Rv`sJS)k-a8!$N@%r6Gtxlt+pHEiZ|D0VGf*776XHke~=YK%u)6C(Z6^ zS6XSM1wPHaXJ(}H|DW^!eerSK$=}wW?#G=s>K2(_%Ma!9+HatxBn}GGu_Qm+bEvE5 zr>sV=VX!5uY_1l0F)^DdmTGmQ#zd|*Q%ZdF-Fj+B!;m!-s7T`y>?DOQO0jY;y5f9# z5Bw2UY{OSNfyK=+mRx8>y}**I0`!Mm!+Ar>pDKIY=18=n7xQym(K?a~+vutHiupm?>^q!EXP_c0_E{7N2^O4gk z#NpL<7;@?yFe@KrKkAaM)xmqP^74HPDXFam?uHfb=a<{?m4!b1*AEWk&XfBkt`Xj% zq~}`UZIFJoAHG&l!t5N09fX7AvI4e|%U>e1N-hm1Vu>`b9n@}}2Tf!#2?HVbv?=M< z8EyGMcJ6qg><$DB#SlN4Pii?`DisuR;qX*Bno;vJbjX&U@t19BHSZB4zKE?HVOct( zcgE7jG;MW^+A7AGVA>TIGqND>j%rL0GGd8XDConwwW#!qZpdwup4bOpf%NfhaLkSJ z^JJ|9I)NeHv+$EZgZi^7UBjgJuzF%&3V(zLsu-OyV3LGvxWsvMe2p^O#w85WX$L8+U zv~1s4z3WP*V{0X4MO){=jrYsD`a;f1+Gxy=2pQJTAD@aRZQ-25Jy|L6NxzPdB^i4- z$f<=?*fGW@=hP9_Q5tpYV}90UuxIHk<2TWIhnY3dv*ohcYmcU5#$YCn>6>1B=kvSC zb_%tVXD)yWw=Jm1&mTrD32ckyn{tqn7tqIJ?UY}cmZ34JUP8cl`Ng3HdD z!)96>8B-UXF*c>MNAtF_m30;37Cdu)V0~iy>&3*aX_dspdi+(Q$j{0<^^fw#9d|$L zkbBl+IBbw!zaM&b0)xA693t9&Y6sDrhkE&b{hT@!9i5!4nr-}CFqpMf;(W|Mtfwmh zu`KdV{j}FtPB1!uV8qLh&F3o)COl~{WtjP*buB=Od)tR9&tIli;dmMqGV9^&Cw;0OVutgZ2 z98J&V^||rHyw*qu3VJ^8*Cne7|Dj0BNcw5dty0c_I>O|Ii%7Np$B`EJlpMuKMgF`CnpTc| z-n%KPcnG>{q8b-aV+*-3jvDZ53k}lhL+A!2-MpQ81EfbTq23<|Xlri#Gilpv&A<5+ zdYmHdkIDAX4TO6P?LE`lN1BxIYHf692Vgt-Rtc8c$E9b`t|Q+!irxQ);pK3j^s_Hf z=>X|#fF|jQHu&-8jILZnbtG^JDe*B|>;DMf;Qs@@xOjfBX7GOlaO1xM*hpsTsoSJa w_fp+IAgq$>S3xK-z6N$-3`c1POn1!e@3IAlzF)4`{yCa8K_-<2mBC{`~Uy| delta 1199 zcmcgqO-z$n6wSRKP^kh1TjCGWLPbZaK!yHkD3H)8w1pN0T8Ii_{Ap{k{8)>LlbOkc z;5Z61FL=>toMenKMwv0+;*A=$n&841ttLiiF)@*Z#JJFf(Zx*EuLY$G*S^)c_q})S zIVbnnaMqidMPsEI`m)M0z4_PjBXfx9GZ$Ze^dr#g6OfHY3j@ecV3`=l+uw>?oLAIf zX1{N>Fn4ejn2*0iU8}vW#~x`g1x&ud^}RbPY^HjP#kF1U_S*)`uHGSkD7b0&0b4ZO z*0gy?v(c@qj9La8y#pqXtEIEOIa<|GudApS2sZdUKEHmbr6F7w>SRASnn}Gz$P?T4 zjueS}^vEvp&bv!X#IeS^KwS#R7ymu_6u5-C(!`p#KP8(=ep?Iw&A}T=hyz_zLLUEl z9HxQ)k`Mo9@D4Qu)A-F2*yiAderVV7%9BtA+;R)r!15)RNq9Tu|iSv~Fh17-ZoIk*8}rtS-n&j-)L z#sdC)3gorCONJYkeU;Sd)(AS1vGd2pcyBphS&K`3tlF2uqbKmNlAcXrI=xEAJbE2K zC9B<;#^l2a3Ks~Or0B%e>{a78v(;ad(*0V;ZW=QQ9`?XdOdm|ba_TY)i=`@yE?O}= zNkZKdm_~Q4m`YK^ECtVean>Eu{WEdJ|K%23EqH)?`+Z z7#33ePt2t87#1aEb18#s(vC_=B>bpnqx_zw)75dvnHNv$6@vM*Dizfnhh51u6JC;3 zRz5A_sthv6QNjIcCxRtTyx` element that persists the state in local storage. */ class ToggleableSection { + /** + * The name to identify this component with in persistent storage. + * + * @private + */ + private readonly name: string; + /** + * The element that can be toggled. + * + * @private + */ + private readonly details: HTMLDetailsElement; + + /** * 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 + * @param name the name to identify this component with in persistent storage + * @param details the element that can be toggled */ - constructor(name, details) { - this._name = name; - this._details = details; - this._details.addEventListener("toggle", () => this.onToggle(this.isOpened())); + constructor(name: string, details: HTMLDetailsElement) { + this.name = name; + this.details = details; + this.details.addEventListener("toggle", () => this.onToggle(this.isOpened())); - this._loadToggle(); + 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. + * @return `true` if and only if the component is currently open. */ - isOpened() { - return !!this._details.open; + isOpened(): boolean { + return this.details.open; } /** * Opens or closes the component. * - * @param isOpened {boolean} whether to open the component + * @param isOpened whether to open the component */ - setOpened(isOpened) { - this._details.open = isOpened; + setOpened(isOpened: boolean): void { + this.details.open = isOpened; } /** * This method is invoked whenever the component is toggled. * - * @param isOpened {boolean} the new state of the component + * @param isOpened the new state of the component */ - onToggle(isOpened) { - this._storeToggle(); + onToggle(isOpened: boolean): void { + this.storeToggle(); } @@ -203,8 +227,8 @@ class ToggleableSection { * * @private */ - _storeToggle() { - localStorage.setItem(`/tools/doomsday//toggle-${this._name}`, "" + this.isOpened()); + private storeToggle(): void { + localStorage.setItem(`/tools/doomsday//toggle-${this.name}`, "" + this.isOpened()); } /** @@ -212,11 +236,11 @@ class ToggleableSection { * * @private */ - _loadToggle() { - const target = localStorage.getItem(`/tools/doomsday//toggle-${this._name}`); + private loadToggle(): void { + const target = localStorage.getItem(`/tools/doomsday//toggle-${this.name}`); if (target === null) { this.setOpened(true); - this._storeToggle(); + this.storeToggle(); return; } @@ -226,16 +250,20 @@ class ToggleableSection { /** * A wrapper around the good ol' `Date` class that provides a bunch of useful Doomsday-specific methods. - * - * @property {Date} date the underlying date */ class DoomsdayDate { + /** + * The underlying date. + */ + readonly date: Date; + + /** * Wraps a `DoomsdayDate` around the given date. * - * @param date {Date} the date to be wrapped + * @param date the date to be wrapped */ - constructor(date) { + constructor(date: Date) { this.date = date; } @@ -243,18 +271,18 @@ class DoomsdayDate { /** * Returns the number of this `DoomsdayDate`'s century. * - * @return {number} the number of this `DoomsdayDate`'s century + * @return the number of this `DoomsdayDate`'s century */ - getCentury() { + getCentury(): number { 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 + * @return the day of the week of the anchor of this `DoomsdayDate`'s century */ - getCenturyAnchorString() { + getCenturyAnchorString(): string { const centuryAnchorNumber = (5 * (this.getCentury() % 4)) % 7 + 2; return DoomsdayDate.getWeekDayOf(centuryAnchorNumber); }; @@ -262,9 +290,9 @@ class DoomsdayDate { /** * 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 + * @return the day of the week of the anchor day of this `DoomsdayDate`'s year */ - getYearAnchorString() { + getYearAnchorString(): string { const anchorDate = new Date(this.date); anchorDate.setDate(4); // 4th anchorDate.setMonth(3); // April @@ -275,9 +303,9 @@ class DoomsdayDate { /** * Returns the day of the week of this `DoomsdayDate`. * - * @return {string} the day of the week of this `DoomsdayDate` + * @return the day of the week of this `DoomsdayDate` */ - getWeekdayString() { + getWeekdayString(): string { return DoomsdayDate.getWeekDayOf(this.date); }; @@ -285,10 +313,10 @@ class DoomsdayDate { /** * Returns the week day of [date]. * - * @param date {Date|number} the date to get the week day of; if it is a `number`, then 0 corresponds to Sunday - * @return {string} the name of the week day corresponding to [date] + * @param date the date to get the week day of; if it is a `number`, then 0 corresponds to Sunday + * @return the name of the week day corresponding to [date] */ - static getWeekDayOf(date) { + static getWeekDayOf(date: Date|number): string { if (date instanceof Date) { return date.toLocaleString("en-US", {weekday: "long"}); } else { @@ -307,19 +335,21 @@ class DoomsdayDate { return "Friday"; case 6: return "Saturday"; + default: + throw new Error(`Unexpected weekday number '${date}'.`); } } }; /** - * Returns the day of the week corresponding to the given string. + * Returns the day of the week corresponding to `dayString`, or an empty string if no day was recognized. * * 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 + * @param dayString the day of the week to expand + * @return the day of the week corresponding to `dayString`, or an empty string if no day was recognized */ - static expandDayString(dayString) { + static expandDayString(dayString: string): string { dayString = dayString.toLowerCase(); if (dayString.startsWith("m")) return "Monday"; @@ -336,7 +366,7 @@ class DoomsdayDate { else if (dayString.startsWith("su")) return "Sunday"; else - return undefined; + return ""; } /** @@ -372,17 +402,17 @@ doAfterLoad(() => { // Initialize quiz - let quizDate; + let quizDate: DoomsdayDate; const centuryDetails = new class extends ToggleableSection { - onToggle(isOpened) { + onToggle(isOpened: boolean): void { super.onToggle(isOpened); if (isOpened) centuryInput.selectInput(); centuryInput.updateTitle(); } }("century", $("#century-details")); const yearDetails = new class extends ToggleableSection { - onToggle(isOpened) { + onToggle(isOpened: boolean): void { super.onToggle(isOpened); if (isOpened) yearInput.selectInput(); yearInput.updateTitle(); @@ -390,7 +420,7 @@ doAfterLoad(() => { }("year", $("#year-details")); const centuryInput = new class extends ValidatableInput { - isValid(value) { + isValid(value: string): boolean { console.log("# Validate century"); console.log(`Input: ${value}`); console.log(`Expanded: ${DoomsdayDate.expandDayString(value)}`); @@ -398,7 +428,7 @@ doAfterLoad(() => { return DoomsdayDate.expandDayString(value) === quizDate.getCenturyAnchorString(); } - onValidInput() { + onValidInput(): void { this.input.value = DoomsdayDate.expandDayString(this.input.value); if (yearDetails.isOpened()) yearInput.selectInput(); @@ -418,7 +448,7 @@ doAfterLoad(() => { } }($("#century-input"), $("#century-title-label"), $("#century-submit")); const yearInput = new class extends ValidatableInput { - isValid(value) { + isValid(value: string): boolean { console.log("# Validate year"); console.log(`Input: ${value}`); console.log(`Expanded: ${DoomsdayDate.expandDayString(value)}`); @@ -443,7 +473,7 @@ doAfterLoad(() => { } }($("#year-input"), $("#year-title-label"), $("#year-submit")); const dayInput = new class extends ValidatableInput { - isValid(value) { + isValid(value: string): boolean { console.log("# Validate day"); console.log(`Input: ${value}`); console.log(`Expanded: ${DoomsdayDate.expandDayString(value)}`); @@ -461,7 +491,7 @@ doAfterLoad(() => { } updateTitle() { - this.titleLabel.innerText = `Weekday of ${quizDate.date.toISOString().substr(0, 10)}?`; + this.titleLabel.innerText = `Weekday of ${quizDate.date.toISOString().substring(0, 10)}?`; } }($("#day-input"), $("#day-title-label"), $("#day-submit")); @@ -479,7 +509,7 @@ doAfterLoad(() => { function reloadQuiz() { quizDate = DoomsdayDate.random(); console.log("# Reset"); - console.log(`New date: ${quizDate.date.toISOString().substr(0, 10)}`); + console.log(`New date: ${quizDate.date.toISOString().substring(0, 10)}`); console.log(` ${quizDate.date}`); console.log(`Century#: ${quizDate.getCentury()}`); console.log(`Century: ${quizDate.getCenturyAnchorString()}`); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..69c22e6 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es6", + "strict": true, + "rootDir": "./src/main/js/", + "outDir": "./dist/js/" + }, + "include": [ + "src/main/js/**/*.ts" + ] +}