From bc01737b2a83f324748b101ed8f2ac789f560e41 Mon Sep 17 00:00:00 2001 From: "Felix W. Dekker" Date: Fri, 26 Mar 2021 01:48:55 +0100 Subject: [PATCH] Migrate to npm, migrate to KaTeX --- .gitattributes | 1 + .gitignore | 117 +++++++++++++++++++++++ Gruntfile.js | 126 ++++++++++++++++++++++++ README.md | 20 ++++ index.html | 226 -------------------------------------------- package-lock.json | Bin 0 -> 151935 bytes package.json | 37 ++++++++ src/main/index.html | 52 ++++++++++ src/main/js/Main.js | 174 ++++++++++++++++++++++++++++++++++ 9 files changed, 527 insertions(+), 226 deletions(-) create mode 100644 .gitattributes 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/index.html create mode 100644 src/main/js/Main.js diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1a6bd45 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +package-lock.json binary 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..e275c1c --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,126 @@ +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/"}], + }, + }, + focus: { + dev: { + include: ["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, + }, + }, + watch: { + 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"], + }, + }, + webpack: { + options: { + entry: "./src/main/js/Main.js", + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + }, + { + test: /\.css$/i, + use: ["style-loader", "css-loader"], + }, + { + test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/, + use: [{ + loader: "file-loader", + options: { + name: "[name].[ext]", + outputPath: "./", + }, + }], + }, + ], + }, + resolve: { + extensions: [".css", ".js"], + }, + output: { + filename: "bundle.js", + path: path.resolve(__dirname, "dist/"), + }, + }, + dev: { + mode: "development", + devtool: "inline-source-map", + }, + deploy: { + mode: "production", + }, + }, + }); + + 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", + // Compile JS + "webpack:dev", + "replace:dev", + ]); + grunt.registerTask("dev:server", ["dev", "focus:dev"]); + grunt.registerTask("deploy", [ + // Pre + "clean", + // Copy files + "copy:html", + // Compile JS + "webpack:deploy", + "replace:deploy", + ]); + + grunt.registerTask("default", ["dev"]); +}; diff --git a/README.md b/README.md index 3b76aad..ecf5d1c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,22 @@ # Simplify fractions Simplifies a fraction of integers by dividing both operands by their greatest common divisor. + +## Development +### Requirements +* [npm](https://www.npmjs.com/) + +### Setting up +```shell script +# Install dependencies (only needed once) +$> npm ci +``` + +### Building +```shell script +# Build the tool in `dist/` for development +$> npm run dev +# Same as above, but automatically rerun it whenever files are changed +$> npm run dev:server +# Build the tool in `dist/` for deployment +$> npm run deploy +``` diff --git a/index.html b/index.html deleted file mode 100644 index 60d49ab..0000000 --- a/index.html +++ /dev/null @@ -1,226 +0,0 @@ - - - - - - - - - - - Simplify fractions | FWDekker - - - - - -
- -
-
-

Simplify fractions

- -
-

Simplify a fraction to eliminate common factors.

-
-
-
- - - -
-
-
- - - - - -
-
-
- - - -
- -
- - - - -
- - - - - - - - diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..44cfd9946fb9ff29bcdcd2341ae1807558d47e92 GIT binary patch literal 151935 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 literal 0 HcmV?d00001 diff --git a/package.json b/package.json new file mode 100644 index 0000000..c2bcaf1 --- /dev/null +++ b/package.json @@ -0,0 +1,37 @@ +{ + "name": "simplify-fractions", + "version": "v1.2.0", + "description": "Simple web tool for simplifying fractions", + "author": "Felix W. Dekker", + "browser": "dist/bundle.js", + "repository": { + "type": "git", + "url": "git@git.fwdekker.com:FWDekker/simplify-fractions.git" + }, + "private": true, + "scripts": { + "clean": "grunt clean", + "dev": "grunt dev", + "dev:server": "grunt dev:server", + "deploy": "grunt deploy" + }, + "dependencies": { + "@fwdekker/template": "^0.0.22", + "katex": "^0.13.0" + }, + "devDependencies": { + "css-loader": "^5.2.0", + "file-loader": "^6.2.0", + "grunt": "^1.3.0", + "grunt-cli": "^1.4.1", + "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": "^4.0.2", + "style-loader": "^2.0.0", + "webpack": "^5.28.0", + "webpack-cli": "^4.5.0" + } +} diff --git a/src/main/index.html b/src/main/index.html new file mode 100644 index 0000000..dceca9c --- /dev/null +++ b/src/main/index.html @@ -0,0 +1,52 @@ + + + + + + + + + + + Simplify fractions | FWDekker + + + + + +
+ +
+ + + +
+
+
+ + + + + +
+
+
+ + +
+ +
+
+ +
+ + + + diff --git a/src/main/js/Main.js b/src/main/js/Main.js new file mode 100644 index 0000000..39e0180 --- /dev/null +++ b/src/main/js/Main.js @@ -0,0 +1,174 @@ +import {$, doAfterLoad, footer, header, nav, showPage} from "@fwdekker/template"; +import katex from "katex"; +import "katex/dist/katex.min.css"; + + +/** + * A fraction that can be simplified. + */ +class Fraction { + constructor(numerator, denominator) { + if (!isInt(numerator) || !isInt(denominator)) + throw new Error("Numerator and denominator must be integer-like."); + + this.sign = numerator < 0 !== denominator < 0 ? -1 : 1; + this.numerator = Math.abs(+numerator); + this.denominator = Math.abs(+denominator); + } + + + /** + * Returns a new fraction such that the gcd of the numerator and denominator is 1. + * + * @returns {Fraction} a new fraction such that the gcd of the numerator and denominator is 1 + */ + simplify() { + const common = gcd(this.numerator, this.denominator); + return new Fraction(this.sign * this.numerator / common, this.denominator / common); + } + + /** + * Returns the LaTeX string representation of this fraction. + * + * @returns {string} the LaTeX string representation of this fraction + */ + toString() { + let frac = `\\frac{${this.numerator}}{${this.denominator}}`; + if (this.sign === -1) + frac = `-${frac}`; + + return frac; + } + + /** + * Returns the LaTeX string representation of this fraction, or of the numerator if the denominator is 1. + * + * @returns {string} the LaTeX string representation of this fraction, or of the numerator if the denominator + * is 1. + */ + toReducedString() { + if (this.numerator === 0) + return "0"; + + let frac; + if (this.denominator === 1) + frac = `${this.numerator}`; + else + frac = `\\frac{${this.numerator}}{${this.denominator}}`; + + if (this.sign === -1) + frac = `-${frac}`; + + return frac; + } +} + + +// noinspection EqualityComparisonWithCoercionJS +/** + * Returns `true` if and only if `n` is an integer. + * + * @param n {*} the value to check for integerness + * @returns {boolean} `true` if and only if `n` is an integer + */ +const isInt = n => n == parseInt(n); + +/** + * Returns the greatest common divisor of `a` and `b`. + * + * @param a {number} the first operand + * @param b {number} the second operand + * @returns {number} the greatest common divisor of `a` and `b` + */ +const gcd = (a, b) => { + if (b > a) { + const temp = a; + a = b; + b = temp; + } + + while (true) { + if (b === 0) return a; + a %= b; + + if (a === 0) return b; + b %= a; + } +}; + + +doAfterLoad(() => { + $("#nav").appendChild(nav("/Tools/Simplify Fractions/")); + $("#header").appendChild(header({ + title: "Simplify Fractions", + description: "Simple web tool for simplifying fractions" + })); + $("#footer").appendChild(footer({ + author: "Felix W. Dekker", + authorURL: "https://fwdekker.com/", + license: "MIT License", + licenseURL: "https://git.fwdekker.com/FWDekker/simplify-fractions/src/branch/master/LICENSE", + vcs: "git", + vcsURL: "https://git.fwdekker.com/FWDekker/simplify-fractions/", + version: "v%%VERSION_NUMBER%%" + })); + showPage(); +}); + +doAfterLoad(() => { + const numeratorInput = $("#numerator"); + const denominatorInput = $("#denominator"); + const outputField = $("#out"); + + + /** + * Returns `undefined` if the inputs are valid, or a tuple consisting of the invalid element and an explanation + * of its invalidity otherwise. + * + * @param numerator {string} the numerator value + * @param denominator {string} the denominator value + * @returns {(HTMLElement|string)[]|undefined} `undefined` if the inputs are valid, or a tuple consisting of the + * invalid element and an explanation of its invalidity otherwise + */ + const validateInputs = (numerator, denominator) => { + if (numerator === "") + return [numeratorInput, ""]; + if (denominator === "") + return [denominatorInput, ""]; + if (!isInt(numerator)) + return [numeratorInput, "Numerator must be an integer."]; + if (!isInt(denominator)) + return [denominatorInput, "Denominator must be an integer."]; + if (+denominator === 0) + return [denominatorInput, "Denominator must not be 0."]; + + return undefined; + }; + + /** + * Reads the inputs and tries to output the simplified fraction. + */ + const outputSimplifiedFraction = () => { + let numerator = numeratorInput.value; + let denominator = denominatorInput.value; + + const validationInfo = validateInputs(numerator, denominator); + if (validationInfo !== undefined) { + outputField.innerText = validationInfo[1]; + return; + } + + const fraction = new Fraction(numeratorInput.value, denominatorInput.value); + outputField.innerHTML = katex.renderToString( + fraction.toString() + " = " + fraction.simplify().toReducedString(), + { + displayMode: true, + throwOnError: false + } + ); + }; + + + numeratorInput.addEventListener("input", () => outputSimplifiedFraction()); + denominatorInput.addEventListener("input", () => outputSimplifiedFraction()); +});