mirror of
https://git.disroot.org/wolfree/wolfree-dockerfile
synced 2025-06-06 00:49:20 +02:00
2023-09-26
This commit is contained in:
24
typescript/.dockerignore
Normal file
24
typescript/.dockerignore
Normal file
@ -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
|
24
typescript/.gitignore
vendored
Normal file
24
typescript/.gitignore
vendored
Normal file
@ -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
|
7
typescript/package.json
Normal file
7
typescript/package.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@types/dompurify": "^3.0.2",
|
||||
"@types/jquery": "^3.5.19",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
}
|
11
typescript/src/AjaxResponse.ts
Normal file
11
typescript/src/AjaxResponse.ts
Normal file
@ -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[];
|
||||
};
|
||||
}>;
|
30
typescript/src/AjaxSettings.ts
Normal file
30
typescript/src/AjaxSettings.ts
Normal file
@ -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/
|
5
typescript/src/AppID.ts
Normal file
5
typescript/src/AppID.ts
Normal file
@ -0,0 +1,5 @@
|
||||
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
||||
|
||||
export default AppID;
|
||||
|
||||
type AppID = "H9V325-HTALUWHKGK" | "AKJTJT-LR5LL8WTG6" | "LKY83U-XW6ATU9URU";
|
11
typescript/src/EntrypointParameter.ts
Normal file
11
typescript/src/EntrypointParameter.ts
Normal file
@ -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?]
|
||||
>;
|
||||
}>;
|
12
typescript/src/Pod.ts
Normal file
12
typescript/src/Pod.ts
Normal file
@ -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[];
|
||||
}>;
|
10
typescript/src/State.ts
Normal file
10
typescript/src/State.ts
Normal file
@ -0,0 +1,10 @@
|
||||
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
||||
|
||||
export default State;
|
||||
|
||||
type State = Readonly<{
|
||||
value?: string;
|
||||
states?: {
|
||||
name?: string;
|
||||
}[];
|
||||
}>;
|
11
typescript/src/SubPod.ts
Normal file
11
typescript/src/SubPod.ts
Normal file
@ -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;
|
||||
}>;
|
33
typescript/src/entrypoint.ts
Normal file
33
typescript/src/entrypoint.ts
Normal file
@ -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<void> => {
|
||||
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 });
|
||||
}
|
||||
};
|
16
typescript/src/getAjaxResponse.ts
Normal file
16
typescript/src/getAjaxResponse.ts
Normal file
@ -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<AjaxResponse> => {
|
||||
try {
|
||||
Object.freeze(AjaxSettings);
|
||||
|
||||
// npm i @types/jquery
|
||||
return Object.freeze(await jQuery.ajax(AjaxSettings));
|
||||
} catch (error) {
|
||||
console.warn({ error });
|
||||
return {};
|
||||
}
|
||||
};
|
23
typescript/src/getAjaxSettings.ts
Normal file
23
typescript/src/getAjaxSettings.ts
Normal file
@ -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,
|
||||
}),
|
||||
});
|
67
typescript/src/getAppID.ts
Normal file
67
typescript/src/getAppID.ts
Normal file
@ -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<AppID[]> = 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;
|
||||
};
|
199
typescript/src/getNewHTML.ts
Normal file
199
typescript/src/getNewHTML.ts
Normal file
@ -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 =>
|
||||
`
|
||||
<div class="${PodsClassName}">
|
||||
<div>
|
||||
<div>
|
||||
<section>
|
||||
${
|
||||
// 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("")
|
||||
}
|
||||
<section>
|
||||
<div>
|
||||
<h2>
|
||||
Technical information
|
||||
</h2>
|
||||
</div>
|
||||
<div> </div>
|
||||
<div>
|
||||
<div>
|
||||
<div>
|
||||
<details>
|
||||
<div>
|
||||
If you have programming knowledge, feel free to explore the technical information provided below:
|
||||
</div>
|
||||
<textarea name="technical-information">${
|
||||
Object.freeze(AjaxSettings) === undefined
|
||||
? (console.warn({ AjaxSettings }), "")
|
||||
: Object.freeze(AjaxResponse) === undefined
|
||||
? (console.warn({ AjaxResponse }), "")
|
||||
: escapeHTML(tryStringify(AjaxSettings, AjaxResponse))
|
||||
}</textarea>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div> </div>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const buildPod = (pod: Pod): string =>
|
||||
`
|
||||
<section>
|
||||
<div>
|
||||
${
|
||||
pod.title === undefined
|
||||
? (console.warn({ pod }), "")
|
||||
: `<h2>${escapeHTML(pod.title)}</h2>`
|
||||
}
|
||||
${
|
||||
// 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("")
|
||||
}
|
||||
</div>
|
||||
<div> </div>
|
||||
${
|
||||
pod.subpods === undefined
|
||||
? (console.warn({ pod }), "")
|
||||
: pod.subpods.map(buildSubpod).join("")
|
||||
}
|
||||
</section>
|
||||
`;
|
||||
|
||||
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
|
||||
: `
|
||||
<select name="pod-states">
|
||||
${
|
||||
state.value === undefined
|
||||
? (console.warn({ state }), "")
|
||||
: `
|
||||
<option>${escapeHTML(state.value)}</option>
|
||||
`
|
||||
}
|
||||
${state.states.map(buildOption).join("")}
|
||||
</select>
|
||||
`;
|
||||
|
||||
const buildOption = (state: { name?: string }): string =>
|
||||
state.name === undefined
|
||||
? (console.warn({ state }), "")
|
||||
: `
|
||||
<option>${escapeHTML(state.name)}</option>
|
||||
`;
|
||||
|
||||
const buildSubpod = (subpod: SubPod): string =>
|
||||
`
|
||||
${
|
||||
subpod.img === undefined
|
||||
? (console.warn({ subpod }), "")
|
||||
: `
|
||||
<div>
|
||||
<div>
|
||||
<img
|
||||
src="${escapeHTML(
|
||||
subpod.img.src === undefined
|
||||
? (console.warn({ subpod }), "")
|
||||
: subpod.img.src
|
||||
)}"
|
||||
alt="${escapeHTML(
|
||||
subpod.img.alt === undefined
|
||||
? (console.warn({ subpod }), "")
|
||||
: subpod.img.alt
|
||||
)}"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
${
|
||||
subpod.plaintext === undefined
|
||||
? (console.warn({ subpod }), "")
|
||||
: `
|
||||
<div style="font-family: monospace; overflow: auto;">
|
||||
<div>
|
||||
<div>
|
||||
<details>
|
||||
<summary style="direction: rtl;"></summary>
|
||||
<div>
|
||||
<pre>${escapeHTML(subpod.plaintext)}</pre>
|
||||
</div>
|
||||
<br />
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
`;
|
||||
|
||||
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, """)
|
||||
.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
|
52
typescript/src/listenToChangeEvent.ts
Normal file
52
typescript/src/listenToChangeEvent.ts
Normal file
@ -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<void> => {
|
||||
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 });
|
||||
}
|
||||
};
|
94
typescript/src/onload.ts
Normal file
94
typescript/src/onload.ts
Normal file
@ -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)
|
||||
)
|
||||
);
|
13
typescript/src/placeholder.ts
Normal file
13
typescript/src/placeholder.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
||||
|
||||
export default `
|
||||
<div class="wolfree-pods wolfree-placeholder">
|
||||
<div>
|
||||
<div>
|
||||
<div><div></div></div>
|
||||
<div><div></div></div>
|
||||
<div><div></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
3
typescript/src/podsClassName.ts
Normal file
3
typescript/src/podsClassName.ts
Normal file
@ -0,0 +1,3 @@
|
||||
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
||||
|
||||
export default "wolfree-pods";
|
70
typescript/src/reRenderComponent.ts
Normal file
70
typescript/src/reRenderComponent.ts
Normal file
@ -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 });
|
||||
}
|
||||
};
|
34
typescript/src/setContentEditable.ts
Normal file
34
typescript/src/setContentEditable.ts
Normal file
@ -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 });
|
||||
}
|
||||
};
|
46
typescript/src/typescriptExhaustive.ts
Normal file
46
typescript/src/typescriptExhaustive.ts
Normal file
@ -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]);
|
||||
}
|
||||
}
|
||||
};
|
10
typescript/src/typescriptNever.ts
Normal file
10
typescript/src/typescriptNever.ts
Normal file
@ -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
|
34
typescript/tsconfig.json
Normal file
34
typescript/tsconfig.json
Normal file
@ -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 */
|
Reference in New Issue
Block a user