From 72d8b4e6bee0ffac79e96dd649a9da2adb86b507 Mon Sep 17 00:00:00 2001 From: name Date: Wed, 27 Sep 2023 11:34:23 +0000 Subject: 2023-09-26 --- typescript/.dockerignore | 24 ++++ typescript/.gitignore | 24 ++++ typescript/package.json | 7 ++ typescript/src/AjaxResponse.ts | 11 ++ typescript/src/AjaxSettings.ts | 30 +++++ typescript/src/AppID.ts | 5 + typescript/src/EntrypointParameter.ts | 11 ++ typescript/src/Pod.ts | 12 ++ typescript/src/State.ts | 10 ++ typescript/src/SubPod.ts | 11 ++ typescript/src/entrypoint.ts | 33 ++++++ typescript/src/getAjaxResponse.ts | 16 +++ typescript/src/getAjaxSettings.ts | 23 ++++ typescript/src/getAppID.ts | 67 +++++++++++ typescript/src/getNewHTML.ts | 199 +++++++++++++++++++++++++++++++++ typescript/src/listenToChangeEvent.ts | 52 +++++++++ typescript/src/onload.ts | 94 ++++++++++++++++ typescript/src/placeholder.ts | 13 +++ typescript/src/podsClassName.ts | 3 + typescript/src/reRenderComponent.ts | 70 ++++++++++++ typescript/src/setContentEditable.ts | 34 ++++++ typescript/src/typescriptExhaustive.ts | 46 ++++++++ typescript/src/typescriptNever.ts | 10 ++ typescript/tsconfig.json | 34 ++++++ 24 files changed, 839 insertions(+) create mode 100644 typescript/.dockerignore create mode 100644 typescript/.gitignore create mode 100644 typescript/package.json create mode 100644 typescript/src/AjaxResponse.ts create mode 100644 typescript/src/AjaxSettings.ts create mode 100644 typescript/src/AppID.ts create mode 100644 typescript/src/EntrypointParameter.ts create mode 100644 typescript/src/Pod.ts create mode 100644 typescript/src/State.ts create mode 100644 typescript/src/SubPod.ts create mode 100644 typescript/src/entrypoint.ts create mode 100644 typescript/src/getAjaxResponse.ts create mode 100644 typescript/src/getAjaxSettings.ts create mode 100644 typescript/src/getAppID.ts create mode 100644 typescript/src/getNewHTML.ts create mode 100644 typescript/src/listenToChangeEvent.ts create mode 100644 typescript/src/onload.ts create mode 100644 typescript/src/placeholder.ts create mode 100644 typescript/src/podsClassName.ts create mode 100644 typescript/src/reRenderComponent.ts create mode 100644 typescript/src/setContentEditable.ts create mode 100644 typescript/src/typescriptExhaustive.ts create mode 100644 typescript/src/typescriptNever.ts create mode 100644 typescript/tsconfig.json (limited to 'typescript') diff --git a/typescript/.dockerignore b/typescript/.dockerignore new file mode 100644 index 0000000..ecec047 --- /dev/null +++ b/typescript/.dockerignore @@ -0,0 +1,24 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.docusaurus +.cache-loader + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +package-lock.json + +# SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/typescript/.gitignore b/typescript/.gitignore new file mode 100644 index 0000000..ecec047 --- /dev/null +++ b/typescript/.gitignore @@ -0,0 +1,24 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.docusaurus +.cache-loader + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +package-lock.json + +# SPDX-License-Identifier: AGPL-3.0-or-later diff --git a/typescript/package.json b/typescript/package.json new file mode 100644 index 0000000..36e9180 --- /dev/null +++ b/typescript/package.json @@ -0,0 +1,7 @@ +{ + "devDependencies": { + "@types/dompurify": "^3.0.2", + "@types/jquery": "^3.5.19", + "typescript": "^5.2.2" + } +} diff --git a/typescript/src/AjaxResponse.ts b/typescript/src/AjaxResponse.ts new file mode 100644 index 0000000..8b578a8 --- /dev/null +++ b/typescript/src/AjaxResponse.ts @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +import type Pod from "./Pod.js"; + +export default AjaxResponse; + +type AjaxResponse = Readonly<{ + queryresult?: { + pods?: Pod[]; + }; +}>; diff --git a/typescript/src/AjaxSettings.ts b/typescript/src/AjaxSettings.ts new file mode 100644 index 0000000..b5b4cf4 --- /dev/null +++ b/typescript/src/AjaxSettings.ts @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +import AppID from "./AppID.js"; +import EntrypointParameter from "./EntrypointParameter.js"; + +export default AjaxSettings; + +type AjaxSettings = Readonly<{ + url: "https://api.wolframalpha.com/v2/query"; + dataType: "jsonp"; + traditional: true; + data: Readonly< + EntrypointParameter & { + appid: AppID; + output: "json"; + reinterpret: true; + podtimeout: 30; + scantimeout: 30; + parsetimeout: 30; + totaltimeout: 30; + formattimeout: 30; + } + >; +}>; + +// Wolfram|Alpha Full Results API Reference +// https://products.wolframalpha.com/api/documentation + +// jQuery.ajax() | jQuery API Documentation +// https://api.jquery.com/jQuery.ajax/ diff --git a/typescript/src/AppID.ts b/typescript/src/AppID.ts new file mode 100644 index 0000000..af4edb2 --- /dev/null +++ b/typescript/src/AppID.ts @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +export default AppID; + +type AppID = "H9V325-HTALUWHKGK" | "AKJTJT-LR5LL8WTG6" | "LKY83U-XW6ATU9URU"; diff --git a/typescript/src/EntrypointParameter.ts b/typescript/src/EntrypointParameter.ts new file mode 100644 index 0000000..f78f1fc --- /dev/null +++ b/typescript/src/EntrypointParameter.ts @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +export default EntrypointParameter; + +type EntrypointParameter = Readonly<{ + input: string; + i2d?: true; + podstate: Readonly< + ["Step-by-step solution", "Step-by-step", "Show all steps", string?] + >; +}>; diff --git a/typescript/src/Pod.ts b/typescript/src/Pod.ts new file mode 100644 index 0000000..169ff5a --- /dev/null +++ b/typescript/src/Pod.ts @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +import type State from "./State.js"; +import type SubPod from "./SubPod.js"; + +export default Pod; + +type Pod = Readonly<{ + title?: string; + states?: State[]; + subpods?: SubPod[]; +}>; diff --git a/typescript/src/State.ts b/typescript/src/State.ts new file mode 100644 index 0000000..e3bb35e --- /dev/null +++ b/typescript/src/State.ts @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +export default State; + +type State = Readonly<{ + value?: string; + states?: { + name?: string; + }[]; +}>; diff --git a/typescript/src/SubPod.ts b/typescript/src/SubPod.ts new file mode 100644 index 0000000..1e26a87 --- /dev/null +++ b/typescript/src/SubPod.ts @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +export default SubPod; + +type SubPod = Readonly<{ + img?: { + src?: string; + alt?: string; + }; + plaintext?: string; +}>; diff --git a/typescript/src/entrypoint.ts b/typescript/src/entrypoint.ts new file mode 100644 index 0000000..e939d92 --- /dev/null +++ b/typescript/src/entrypoint.ts @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +import type EntrypointParameter from "./EntrypointParameter.js"; +import getAjaxResponse from "./getAjaxResponse.js"; +import getAjaxSettings from "./getAjaxSettings.js"; +import getNewHTML from "./getNewHTML.js"; +import listenToChangeEvent from "./listenToChangeEvent.js"; +import placeholder from "./placeholder.js"; +import reRenderComponent from "./reRenderComponent.js"; +import setContentEditable from "./setContentEditable.js"; +import typescriptExhaustive from "./typescriptExhaustive.js"; + +export default async ( + EntrypointParameter: EntrypointParameter +): Promise => { + try { + Object.freeze(EntrypointParameter); + typescriptExhaustive(EntrypointParameter); + + window.scroll(0, 0); + reRenderComponent(placeholder); + + const AjaxSettings = getAjaxSettings(EntrypointParameter); + const AjaxResponse = await getAjaxResponse(AjaxSettings); + const newHTML = getNewHTML(AjaxSettings, AjaxResponse); + reRenderComponent(newHTML); + + listenToChangeEvent(EntrypointParameter); + setContentEditable(); + } catch (error) { + console.warn({ error }); + } +}; diff --git a/typescript/src/getAjaxResponse.ts b/typescript/src/getAjaxResponse.ts new file mode 100644 index 0000000..1459114 --- /dev/null +++ b/typescript/src/getAjaxResponse.ts @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +import AjaxResponse from "./AjaxResponse.js"; +import type AjaxSettings from "./AjaxSettings.js"; + +export default async (AjaxSettings: AjaxSettings): Promise => { + try { + Object.freeze(AjaxSettings); + + // npm i @types/jquery + return Object.freeze(await jQuery.ajax(AjaxSettings)); + } catch (error) { + console.warn({ error }); + return {}; + } +}; diff --git a/typescript/src/getAjaxSettings.ts b/typescript/src/getAjaxSettings.ts new file mode 100644 index 0000000..5899637 --- /dev/null +++ b/typescript/src/getAjaxSettings.ts @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +import AjaxSettings from "./AjaxSettings.js"; +import EntrypointParameter from "./EntrypointParameter.js"; +import getAppID from "./getAppID.js"; + +export default (EntrypointParameter: EntrypointParameter): AjaxSettings => + Object.freeze({ + url: "https://api.wolframalpha.com/v2/query", + dataType: "jsonp", + traditional: true, + data: Object.freeze({ + ...EntrypointParameter, + appid: getAppID(), + output: "json", + reinterpret: true, + podtimeout: 30, + scantimeout: 30, + parsetimeout: 30, + totaltimeout: 30, + formattimeout: 30, + }), + }); diff --git a/typescript/src/getAppID.ts b/typescript/src/getAppID.ts new file mode 100644 index 0000000..eed7e1b --- /dev/null +++ b/typescript/src/getAppID.ts @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +import AppID from "./AppID.js"; +import typescriptNever from "./typescriptNever.js"; + +/** + * To generate a new AppID, please follow these steps: + * + * 1. Open Tor Browser and go to: + * https://products.wolframalpha.com/api/ + * 2. Click the orange "Get API Access" button. + * Tor Browser will redirect you to: + * https://account.wolfram.com/login/oauth2/sign-in + * 3. Click the red "Create one" hyperlink to create a new Wolfram ID. + * Tor Browser will redirect you to: + * https://account.wolfram.com/login/create + * 4. Fill out the form with random alphanumeric characters. + * 5. Click the red "Create Wolfram ID" button. + * Tor Browser will redirect you to: + * https://developer.wolframalpha.com/portal/myapps/index.html + * 6. Click the orange "Sign up to get your first AppID" button. + * 7. Fill out the form with random alphanumeric characters. + * 8. Click the orange "Sign up" button. + * 9. Click the orange "Get an AppID" button. + * 10. Fill out the form with random alphanumeric characters. + * 11. Click the orange "Get AppID" button. + */ +const appIDArray: Readonly = Object.freeze([ + "H9V325-HTALUWHKGK", + "AKJTJT-LR5LL8WTG6", + "LKY83U-XW6ATU9URU", +]); + +console.assert(appIDArray.length > 0); + +appIDArray.forEach((appID) => { + console.assert(appID.length === 17); + console.assert(/[0-9A-Z]{6}-[0-9A-Z]{10}/.test(appID)); +}); + +export default (): AppID => { + const random = appIDArray[getRandomInt() % appIDArray.length]; + + if (typeof random === "string") { + return random; + } else if (random === undefined) { + console.warn({ random }); + } else { + typescriptNever(random); + } + + return "H9V325-HTALUWHKGK"; +}; + +const getRandomInt = (): number => { + const random = crypto.getRandomValues(new Uint32Array(1))[0]; + + if (typeof random === "number") { + return random; + } else if (random === undefined) { + console.warn({ random }); + } else { + typescriptNever(random); + } + + return 0; +}; diff --git a/typescript/src/getNewHTML.ts b/typescript/src/getNewHTML.ts new file mode 100644 index 0000000..f5d2070 --- /dev/null +++ b/typescript/src/getNewHTML.ts @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +import type AjaxResponse from "./AjaxResponse.js"; +import type AjaxSettings from "./AjaxSettings.js"; +import type Pod from "./Pod.js"; +import PodsClassName from "./podsClassName.js"; +import type State from "./State.js"; +import type SubPod from "./SubPod.js"; + +export default ( + AjaxSettings: AjaxSettings, + AjaxResponse: AjaxResponse +): string => + ` +
+
+
+
+ ${ + // Wolfram Alpha does have some limitations when it comes to processing certain types of input. + // When "input" is an empty string, + // the "pods" property will not be present in the "queryresult" object. + // To see an example of an empty input string, + // visit the following URL: + // https://www.wolframalpha.com/input?i= + Object.freeze(AjaxResponse) === undefined + ? (console.warn({ AjaxResponse }), "") + : AjaxResponse.queryresult === undefined + ? (console.warn({ AjaxResponse }), "") + : AjaxResponse.queryresult.pods === undefined + ? "" // console.warn + : AjaxResponse.queryresult.pods.map(buildPod).join("") + } +
+
+

+ Technical information +

+
+
+
+
+
+
+
+ If you have programming knowledge, feel free to explore the technical information provided below: +
+ +
+
+
+
+
+
+
+
+
+
+ `; + +const buildPod = (pod: Pod): string => + ` +
+
+ ${ + pod.title === undefined + ? (console.warn({ pod }), "") + : `

${escapeHTML(pod.title)}

` + } + ${ + // In most cases, pods are stateless and do not have specific states. + // As a result, the "states" property of "pod" is typically undefined. + pod.states === undefined + ? "" // console.warn + : pod.states.map(buildSelectElement).join("") + } +
+
+ ${ + pod.subpods === undefined + ? (console.warn({ pod }), "") + : pod.subpods.map(buildSubpod).join("") + } +
+ `; + +const buildSelectElement = (state: State): string => + // Although it is possible to handle the scenario where the "states" property is undefined, + // implementing the necessary logic may result in a cluttered user interface. + // This challenge is primarily due to my limited expertise in front-end design. + // Consequently, the current implementation of the parsing logic is still insufficient in addressing the situation where the "states" property is undefined. + state.states === undefined + ? "" // console.warn + : ` + + `; + +const buildOption = (state: { name?: string }): string => + state.name === undefined + ? (console.warn({ state }), "") + : ` + + `; + +const buildSubpod = (subpod: SubPod): string => + ` + ${ + subpod.img === undefined + ? (console.warn({ subpod }), "") + : ` +
+
+ ${escapeHTML(
+                    subpod.img.alt === undefined
+                      ? (console.warn({ subpod }), +
+
+ ` + } + ${ + subpod.plaintext === undefined + ? (console.warn({ subpod }), "") + : ` +
+
+
+
+ +
+
${escapeHTML(subpod.plaintext)}
+
+
+
+
+
+
+ ` + } + `; + +const tryStringify = ( + AjaxSettings: AjaxSettings, + AjaxResponse: AjaxResponse +): string => { + try { + return JSON.stringify( + { + document, + AjaxSettings, + AjaxResponse, + }, + null, + 4 + ); + } catch (error) { + console.warn({ error }); + return error instanceof TypeError ? error.message + "\n" + error.stack : ""; + // JSON.stringify() - JavaScript | MDN + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#exceptions + } +}; + +const escapeHTML = (unsafeHTML: string): string => + unsafeHTML + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + +// Can I escape HTML special chars in JavaScript? - Stack Overflow +// https://stackoverflow.com/questions/6234773/can-i-escape-html-special-chars-in-javascript + +// test case: +// https://www.wolframalpha.com/input?i=solve+%7By%27%28x%29+%3D+-2+y%2C+y%280%29%3D1%7D+from+0+to+10+using+r+k+f+algorithm diff --git a/typescript/src/listenToChangeEvent.ts b/typescript/src/listenToChangeEvent.ts new file mode 100644 index 0000000..7f091a9 --- /dev/null +++ b/typescript/src/listenToChangeEvent.ts @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +import type EntrypointParameter from "./EntrypointParameter.js"; +import entrypoint from "./entrypoint.js"; +import podsClassName from "./podsClassName.js"; +import typescriptNever from "./typescriptNever.js"; + +export default (EntrypointParameter: EntrypointParameter): void => { + try { + Object.freeze(EntrypointParameter); + + Array.from( + document.querySelectorAll( + `html > body > div#__next > div > main > main > div.${podsClassName} > div > div > section > section > div:is(:first-child) > select` + ) + ) + .filter((element: Element): boolean => { + if (element instanceof HTMLSelectElement) { + return true; + } else if (element instanceof Element) { + console.warn({ element }); + } else { + typescriptNever(element); + } + + return false; + }) + .map((element: Element): void => { + element.addEventListener("change", async (event): Promise => { + if (event.target instanceof HTMLSelectElement) { + await entrypoint({ + ...EntrypointParameter, + podstate: [ + "Step-by-step solution", + "Step-by-step", + "Show all steps", + event.target.value, + ], + }); + } else if (event.target instanceof EventTarget) { + console.warn({ event }); + } else if (event.target === null) { + console.warn({ event }); + } else { + typescriptNever(event.target); + } + }); + }); + } catch (error) { + console.warn({ error }); + } +}; diff --git a/typescript/src/onload.ts b/typescript/src/onload.ts new file mode 100644 index 0000000..6c864ad --- /dev/null +++ b/typescript/src/onload.ts @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +import typescriptNever from "./typescriptNever.js"; + +export default addEventListener( + "load", + (): void => ( + setTimeout( + (): void => + console.assert( + Array.from( + document.querySelectorAll( + // To select the form element, we use different selectors depending on the input mode. + // If the input mode is "natural language", the form is a direct child of the section element, so we use the selector "section > form". + // However, if the input mode is "math input", the form is a direct child of the div element, so we use the selector "div > form". + ` + html > body > #__next > div > main > main > div > div > section > form > div > div > input, + html > body > #__next > div > main > main > div > div > div form > div > div > input + ` + ) + ) + .filter((element: Element): boolean => { + if (element instanceof HTMLInputElement) { + return true; + } else if (element instanceof Element) { + console.warn({ element }); + } else { + typescriptNever(element); + } + + return false; + }) + .map((element: Element): void => { + if (element instanceof HTMLElement) { + element.focus(); + } else if (element instanceof Element) { + console.warn({ element }); + } else { + typescriptNever(element); + } + }).length === 1 + ), + 1000 + ), + [ + (): void => + Array.from( + document.querySelectorAll( + // The positioning of the ul element is dynamically adjusted to ensure it adapts well to different viewport widths. + // To specifically target the ul element when the viewport width is larger, we use the selector "div:is(:first-child) > ul". + // Conversely, to target the ul element when the viewport width is smaller, we use the selector "div:is(:first-child) + ul". + ` + html > body > #__next > div > main > main > div > div > div > section > section > div:is(:first-child) > ul > li, + html > body > #__next > div > main > main > div > div > div > section > section > div:is(:first-child) + ul > li + ` + ) + ) + .filter((element: Element): boolean => { + if (element instanceof HTMLLIElement) { + return true; + } else if (element instanceof Element) { + console.warn({ element }); + } else { + typescriptNever(element); + } + + return false; + }) + .forEach((element: Element): void => { + if (element instanceof HTMLElement) { + if (element.innerHTML.includes("Step-by-step")) { + element.style.display = "none"; + } else { + } + } else if (element instanceof Element) { + console.warn({ element }); + } else { + typescriptNever(element); + } + }), + (): void => { + document.title = document.title.replace( + "- Wolfram|Alpha", + "- Free Wolfram|Alpha Step-by-step Solution - Wolfree" + ); + }, + ].map( + (callback: () => void): void => ( + setInterval(callback, 2000), addEventListener("click", callback) + ) + ), + scroll(0, 0) + ) +); diff --git a/typescript/src/placeholder.ts b/typescript/src/placeholder.ts new file mode 100644 index 0000000..083226c --- /dev/null +++ b/typescript/src/placeholder.ts @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +export default ` +
+
+
+
+
+
+
+
+
+`; diff --git a/typescript/src/podsClassName.ts b/typescript/src/podsClassName.ts new file mode 100644 index 0000000..fea9f4c --- /dev/null +++ b/typescript/src/podsClassName.ts @@ -0,0 +1,3 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +export default "wolfree-pods"; diff --git a/typescript/src/reRenderComponent.ts b/typescript/src/reRenderComponent.ts new file mode 100644 index 0000000..f73fd1a --- /dev/null +++ b/typescript/src/reRenderComponent.ts @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +import podsClassName from "./podsClassName.js"; +import typescriptNever from "./typescriptNever.js"; + +export default (newHTML: string): void => { + try { + console.assert( + Array.from( + document.querySelectorAll( + `html > body > #__next > div > main > main > div.${podsClassName}` + ) + ) + .filter((element: Element): boolean => { + if (element instanceof HTMLDivElement) { + return true; + } else if (element instanceof Element) { + console.warn({ element }); + } else { + typescriptNever(element); + } + + return false; + }) + .map((element: Element): void => { + if (element instanceof Element) { + element.remove(); + } else { + typescriptNever(element); + } + }).length <= 1 + ); + + console.assert( + Array.from( + document.querySelectorAll( + "html > body > #__next > div > main > main > div:nth-of-type(1)" + ) + ) + .filter((element: Element): boolean => { + if (element instanceof HTMLDivElement) { + return true; + } else if (element instanceof Element) { + console.warn({ element }); + } else if (element === null) { + console.warn({ element }); + } else { + typescriptNever(element); + } + + return false; + }) + .map((element: Element): void => { + if (element instanceof Element) { + element.insertAdjacentHTML( + "afterend", + // npm i @types/dompurify + globalThis.DOMPurify.sanitize(newHTML) + ); + } else if (element === null) { + console.warn({ element }); + } else { + typescriptNever(element); + } + }).length === 1 + ); + } catch (error) { + console.warn({ error }); + } +}; diff --git a/typescript/src/setContentEditable.ts b/typescript/src/setContentEditable.ts new file mode 100644 index 0000000..ebfe423 --- /dev/null +++ b/typescript/src/setContentEditable.ts @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +import podsClassName from "./podsClassName.js"; +import typescriptNever from "./typescriptNever.js"; + +export default (): void => { + try { + Array.from( + document.querySelectorAll( + `html > body > div#__next > div > main > main > div.${podsClassName} > div > div > section > section > div > div > div > details > div` + ) + ) + .filter((element: Element): boolean => { + if (element instanceof HTMLDivElement) { + return true; + } else if (element instanceof Element) { + console.warn({ element }); + } else { + typescriptNever(element); + } + + return false; + }) + .map((element: Element): void => { + if (element instanceof Element) { + element.setAttribute("contenteditable", ""); + } else { + typescriptNever(element); + } + }); + } catch (error) { + console.warn({ error }); + } +}; diff --git a/typescript/src/typescriptExhaustive.ts b/typescript/src/typescriptExhaustive.ts new file mode 100644 index 0000000..656a419 --- /dev/null +++ b/typescript/src/typescriptExhaustive.ts @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +import type EntrypointParameter from "./EntrypointParameter.js"; +import typescriptNever from "./typescriptNever.js"; + +export default (EntrypointParameter: EntrypointParameter): void => { + Object.freeze(EntrypointParameter); + + if (typeof EntrypointParameter.input !== "string") { + typescriptNever(EntrypointParameter.input); + } + + if (EntrypointParameter.i2d !== true) { + if (EntrypointParameter.i2d !== undefined) { + typescriptNever(EntrypointParameter.i2d); + } + } + + if (!(EntrypointParameter.podstate instanceof Array)) { + typescriptNever(EntrypointParameter.podstate); + } + + if (EntrypointParameter.podstate.length !== 3) { + if (EntrypointParameter.podstate.length !== 4) { + typescriptNever(EntrypointParameter.podstate.length); + } + } + + if (EntrypointParameter.podstate[0] !== "Step-by-step solution") { + typescriptNever(EntrypointParameter.podstate[0]); + } + + if (EntrypointParameter.podstate[1] !== "Step-by-step") { + typescriptNever(EntrypointParameter.podstate[1]); + } + + if (EntrypointParameter.podstate[2] !== "Show all steps") { + typescriptNever(EntrypointParameter.podstate[2]); + } + + if (typeof EntrypointParameter.podstate[3] !== "string") { + if (typeof EntrypointParameter.podstate[3] !== "undefined") { + typescriptNever(EntrypointParameter.podstate[3]); + } + } +}; diff --git a/typescript/src/typescriptNever.ts b/typescript/src/typescriptNever.ts new file mode 100644 index 0000000..42becd5 --- /dev/null +++ b/typescript/src/typescriptNever.ts @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: AGPL-3.0-or-later */ + +export default (typescriptNeverValue: never): never => { + console.warn({ typescriptNeverValue }); + + return typescriptNeverValue; +}; + +// How do I check that a switch block is exhaustive in TypeScript? - Stack Overflow +// https://stackoverflow.com/questions/39419170/how-do-i-check-that-a-switch-block-is-exhaustive-in-typescript diff --git a/typescript/tsconfig.json b/typescript/tsconfig.json new file mode 100644 index 0000000..71ac60a --- /dev/null +++ b/typescript/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "strict": true, + "allowUnusedLabels": false, + "allowUnreachableCode": false, + "exactOptionalPropertyTypes": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noPropertyAccessFromIndexSignature": true, + "noUncheckedIndexedAccess": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + + "isolatedModules": true, + + "checkJs": true, + + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + + "target": "ES6", + "outDir": "../docusaurus/static/ajax/libs/wolfree/2023.8.31/js/" + }, + "$schema": "https://json.schemastore.org/tsconfig", + "display": "Strictest", + "_version": "2.0.0" +} + +// GitHub - tsconfig/bases: Hosts TSConfigs to extend in a TypeScript app, tuned to a particular runtime environment +// https://github.com/tsconfig/bases/blob/main/bases/strictest.json + +/* SPDX-License-Identifier: AGPL-3.0-or-later */ -- cgit v1.2.3