2023-08-10

This commit is contained in:
name
2023-08-11 05:09:24 +00:00
commit b6ebc5f88f
60 changed files with 3029 additions and 0 deletions

View File

@ -0,0 +1,90 @@
/**
* @license
* SPDX-License-Identifier: AGPL-3.0-or-later
* This file is part of Wolfree.
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*/
.wolfree-placeholder {
display: flex;
width: 100%;
justify-content: center;
/* padding: 26px 40px 20px; */
padding: 26px 0 0;
}
.wolfree-placeholder>div {
position: relative;
}
.wolfree-placeholder>div {
max-width: 780px;
width: 100%;
}
.wolfree-placeholder>div>div {
box-shadow: 0 0 10px 0 hsla(0, 0%, 80%, 0.5), 0 0 1px 0 hsla(0, 0%, 80%, 0.5);
border-radius: 5px;
}
.wolfree-placeholder>div>div>div {
min-height: 16vh;
background: #fdfdfd;
opacity: 1;
border-bottom: 1px solid transparent;
}
.wolfree-placeholder>div>div>div:nth-of-type(1)>div {
background-image: linear-gradient(90deg,
#f5f5f5 50%,
#ebebeb 60%,
#f5f5f5 70%);
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.wolfree-placeholder>div>div>div:nth-of-type(2)>div {
background-image: linear-gradient(90deg,
#f5f5f5 30%,
#ebebeb 40%,
#f5f5f5 50%);
}
.wolfree-placeholder>div>div>div:nth-of-type(3)>div {
background-image: linear-gradient(90deg,
#f5f5f5 10%,
#ebebeb 20%,
#f5f5f5 30%);
}
.wolfree-placeholder>div>div>div>div {
height: 30px;
width: 100%;
display: flex;
flex-direction: row;
align-items: center;
}
.wolfree-placeholder>div>div>div>div {
background-size: 1200%;
-webkit-animation-duration: 3.5s;
animation-duration: 3.5s;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
-webkit-animation-iteration-count: infinite;
animation-iteration-count: infinite;
-webkit-animation-name: _QC3H;
animation-name: _QC3H;
-webkit-animation-timing-function: cubic-bezier(0.55, 0.45, 1, 1);
animation-timing-function: cubic-bezier(0.55, 0.45, 1, 1);
}
@keyframes _QC3H {
0% {
background-position: 100% 0;
}
to {
background-position: -100% 0;
}
}

View File

@ -0,0 +1,125 @@
/**
* @license
* SPDX-License-Identifier: AGPL-3.0-or-later
* This file is part of Wolfree.
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*/
.wolfree-pods {
display: flex;
width: 100%;
justify-content: center;
/* padding: 26px 0 20px; */
padding: 26px 0 0;
}
.wolfree-pods>div {
position: relative;
}
.wolfree-pods>div {
max-width: 780px;
width: 100%;
}
.wolfree-pods>div>div {
position: relative;
}
.wolfree-pods>div>div>section {
box-shadow: 0 0 10px 0 hsla(0, 0%, 80%, 0.5), 0 0 1px 0 hsla(0, 0%, 80%, 0.5);
border-radius: 5px;
/* margin-bottom: 20px; */
margin-bottom: 0;
}
.wolfree-pods>div>div>section>section {
border-top-color: transparent;
}
.wolfree-pods>div>div>section>section {
display: flex;
flex-direction: column;
position: relative;
background: #f5f5f5;
outline: none;
border-color: #fff transparent;
border-style: solid;
border-width: 1px;
}
.wolfree-pods>div>div>section>section:first-child {
border-top-left-radius: inherit;
border-top-right-radius: inherit;
}
.wolfree-pods>div>div>section>section>div:is(:first-child) {
min-height: 30px;
font-size: 13px;
font-family: WebRoboto, Hiragino Kaku Gothic ProN, ProN, Meiryo,
, Arial, Helvetica, sans-serif;
background: #f5f5f5;
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 20px;
}
.wolfree-pods>div>div>section>section>div:is(:first-child)>h2 {
color: #678e9c;
margin-right: 20px;
}
.wolfree-pods>div>div>section>section>div:is(:first-child)>select {
/* We style this element manually. */
border-radius: 4px;
border-style: solid;
border-width: thin;
min-height: 20px;
cursor: pointer;
color: #ff6c00;
background-color: #fff;
border-color: #ec561a;
touch-action: manipulation;
}
.wolfree-pods>div>div>section>section>div:not(:first-child) {
display: flex;
align-items: flex-end;
width: 100%;
height: 100%;
background: #fff;
border-radius: inherit;
padding: 6px 20px;
}
.wolfree-pods>div>div>section>section>div:not(:first-child)>div {
position: relative;
flex: 1 1 auto;
}
.wolfree-pods>div>div>section>section>div:not(:first-child)>div>img {
max-width: 100%;
/* width: 100%; */
width: auto;
}
.wolfree-pods>div>div>section>section>div:not(:first-child)>div>div>details>summary {
/* We style this element manually. */
cursor: pointer;
touch-action: manipulation;
}
.wolfree-pods>div>div>section>section>div:not(:first-child)>div>div>details>div {
/* We style this element manually. */
padding: 1rem;
line-height: 2;
touch-action: manipulation;
}
.wolfree-pods>div>div>section>section>div:not(:first-child)>div>div>details>textarea {
/* We style this element manually. */
width: 99%;
height: 50vh;
touch-action: manipulation;
}

View File

@ -0,0 +1,56 @@
/**
* @license
* SPDX-License-Identifier: AGPL-3.0-or-later
* This file is part of Wolfree.
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*/
// @ts-check
import SearchParams from "./SearchParams.js";
export default class AJAX {
ajax = async ({
input = String(),
i2d = Boolean(),
extraPodstates = Array(),
} = {}) => {
try {
const data = new SearchParams().getSearchParams({
input,
extraPodstates,
i2d,
});
const options = {
url: "https://api.wolframalpha.com/v2/query",
dataType: "jsonp",
traditional: true,
data,
};
try {
/**
* https://www.npmjs.com/package/@types/jquery
* @type {import('jQuery')}
*/
const response = await jQuery.ajax(options);
return (
console.assert(response instanceof Object),
console.assert(response.hasOwnProperty("queryresult")),
{ response }
);
} catch (error) {
return (
console.error(
{ error },
"We encountered an issue while attempting to retrieve a response from the Wolfram Alpha API using the jQuery library."
),
{ error }
);
}
} catch (error) {
return console.error({ error }), { error };
}
};
}

View File

@ -0,0 +1,57 @@
/**
* @license
* SPDX-License-Identifier: AGPL-3.0-or-later
* This file is part of Wolfree.
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*/
// @ts-check
// To generate a new AppID:
// 1. Open Tor Browser and go to: https://products.wolframalpha.com/api/
// 2. Click the orangish "Get API Access" button. You will go to: https://account.wolfram.com/login/oauth2/sign-in
// 3. Click the reddish "Create one" hyperlink to create a new Wolfram ID. You will go to: https://account.wolfram.com/login/create
// 4. Fill out the form using random alphanumeric characters.
// 5. Click the reddish "Create Wolfram ID" button. You will go to: https://developer.wolframalpha.com/portal/myapps/index.html
// 6. Click the orangish "Sign up to get your first AppID" button.
// 7. Fill out the form using random alphanumeric characters.
// 8. Click the orangish "Sign up" button.
// 9. Click the orangish "Get an AppID" button.
// 10. Fill out the form using random alphanumeric characters.
// 11. Click the orangish "Get AppID" button.
export default class AppID {
appIDArray = Array();
constructor(
appIDArray = ["H9V325-HTALUWHKGK", "AKJTJT-LR5LL8WTG6", "LKY83U-XW6ATU9URU"]
) {
(this.appIDArray = appIDArray),
Array.from(this.appIDArray).forEach(
(appID) => (
console.assert(17 === appID.length),
console.assert(/[0-9A-Z]{6}-[0-9A-Z]{10}/.test(appID))
)
);
}
getAppID = ({
appID = this.appIDArray[this.getRandomInt() % this.appIDArray.length],
} = {}) => {
try {
return appID;
} catch (error) {
return console.error({ error }), String();
}
};
getRandomInt = ({
randomInt = crypto.getRandomValues(new Uint32Array(1))[0],
} = {}) => {
try {
return randomInt;
} catch (error) {
return console.error({ error }), Number();
}
};
}

View File

@ -0,0 +1,26 @@
/**
* @license
* SPDX-License-Identifier: AGPL-3.0-or-later
* This file is part of Wolfree.
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*/
// @ts-check
import Processor from "./Processor.js";
import Preprocessor from "./Preprocessor.js";
import PostProcessor from "./PostProcessor.js";
export default async ({
input = String(),
i2d = Boolean(),
extraPodstates = Array(),
} = {}) => {
try {
new Preprocessor().process();
await new Processor().process({ input, i2d, extraPodstates });
new PostProcessor().process({ input, i2d });
} catch (error) {
console.error({ error });
}
};

View File

@ -0,0 +1,69 @@
/**
* @license
* SPDX-License-Identifier: AGPL-3.0-or-later
* This file is part of Wolfree.
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*/
// @ts-check
export default class Placeholder {
className = "wolfree-placeholder";
placeholderHTML = `
<div class="${this.className}">
<div>
<div>
<div><div></div></div>
<div><div></div></div>
<div><div></div></div>
</div>
</div>
</div>
`;
insert = ({
container = document.querySelector(
"html > body > #__next > div > main > main > div:nth-of-type(1)"
),
} = {}) => {
try {
return (
container instanceof HTMLDivElement
? container.insertAdjacentHTML("afterend", this.placeholderHTML)
: console.error({ container }),
{ container }
);
} catch (error) {
return (
console.error(
{ error },
"Fail to insert the skeleton placeholder components."
),
{ error }
);
}
};
remove = ({
allPlaceholders = document.querySelectorAll(
`html > body > #__next > div > main > main > div.${this.className}`
),
} = {}) => {
try {
return (
console.assert(allPlaceholders.length === 1),
allPlaceholders.forEach((placeholder) => placeholder.remove()),
{ allPlaceholders }
);
} catch (error) {
return (
console.error(
{ error },
"Fail to remove the skeleton placeholder components."
),
{ error }
);
}
};
}

View File

@ -0,0 +1,56 @@
/**
* @license
* SPDX-License-Identifier: AGPL-3.0-or-later
* This file is part of Wolfree.
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*/
// @ts-check
export default class Pods {
insert = ({
container = document.querySelector("main > div:nth-of-type(1)"),
html = String(),
} = {}) => {
try {
/**
* https://www.npmjs.com/package/@types/dompurify
* @type {import('DOMPurify')}
*/
return (
container instanceof HTMLDivElement
? container.insertAdjacentHTML(
"afterend",
globalThis.DOMPurify.sanitize(html)
)
: console.error({ container }),
{ container, html }
);
} catch (error) {
return (
console.error(
{ error },
"Fail to insert the results of the calculation."
),
{ error }
);
}
};
remove = ({
pods = document.querySelectorAll("main > div.wolfree-pods"),
} = {}) => {
try {
pods.forEach((element) => {
element.remove();
});
return { pods };
} catch (error) {
console.error(
"Fail to remove the results of the previous calculation.",
error
);
return { error };
}
};
}

View File

@ -0,0 +1,201 @@
/**
* @license
* SPDX-License-Identifier: AGPL-3.0-or-later
* This file is part of Wolfree.
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*/
// @ts-check
export default class PodsParser {
parse = ({
input = String(),
i2d = Boolean(),
extraPodstates = Array(),
response = { queryresult: { pods: Array() } },
} = {}) => {
try {
return { html: this.buildHTML({ input, i2d, extraPodstates, response }) };
} catch (error) {
return console.error({ error }), { error };
}
};
buildHTML = ({
input = String(),
i2d = Boolean(),
extraPodstates = Array(),
response = { queryresult: { pods: Array() } },
} = {}) => {
return `
<div class="wolfree-pods">
<div>
<div>
<section>
${this.parseQueryResult({ response })}
${this.buildTechnicalInfo({
input,
i2d,
extraPodstates,
response,
})}
</section>
</div>
</div>
</div>
`;
};
parseQueryResult = ({ response = { queryresult: { pods: Array() } } }) => {
const { queryresult } = response;
const { pods } = queryresult;
if (pods) {
return pods.map(this.parsePod).join("");
}
return "";
};
parsePod = (
pod = {
title: String(),
states: Array(),
subpods: Array(),
}
) => {
return `
<section>
${this.buildPodHeader(pod)}
<div></div>
${pod.subpods.map(this.buildSubpod).join("")}
</section>
`;
};
buildPodHeader = (pod = { title: String(), states: Array() }) => {
return `
<div>
<h2>${this.escapeHTML(pod.title)}</h2>
${pod.states ? pod.states.map(this.buildSelectElement).join("") : ""}
</div>
`;
};
buildSubpod = (
subpod = { img: { src: String(), alt: String() }, plaintext: String() }
) => {
return `
<div><div>
<img
src="${this.escapeHTML(subpod.img.src)}"
alt="${this.escapeHTML(subpod.img.alt)}"
>
</div></div>
<div style="font-family: monospace; overflow: auto;">
<div><div>${this.buildSubpodDetails(subpod)}</div></div>
</div>
`;
};
buildSubpodDetails = (subpod = { plaintext: String() }) => {
return `
<details>
<summary style="direction: rtl;"></summary>
<div><pre>${this.escapeHTML(subpod.plaintext)}</pre></div>
<br>
</details>
`;
};
buildSelectElement = (state = { value: String(), states: Array() }) => {
if (state.states) {
return `
<select name="pod-states">
<option>${this.escapeHTML(state.value)}</option>
${state.states.map(this.buildOption).join("")}
</select>
`;
}
return "";
};
buildOption = (state = { name: String() }) => {
return `
<option>${this.escapeHTML(state.name)}</option>
`;
};
buildTechnicalInfo = ({
input = String(),
i2d = Boolean(),
extraPodstates = Array(),
response = { queryresult: { pods: Array() } },
}) => {
return `
<section>
<div><h2>Technical information</h2></div>
<div></div>
<div><div><div>${this.buildTechnicalInfoDetails({
input,
i2d,
extraPodstates,
response,
})}</div></div></div>
<div></div>
</section>
`;
};
buildTechnicalInfoDetails = ({
input = String(),
i2d = Boolean(),
extraPodstates = Array(),
response = { queryresult: { pods: Array() } },
}) => {
return `
<details>
<div>
If you have programming knowledge, feel free to explore the technical information provided below:
</div>
${this.buildTextarea({ input, i2d, extraPodstates, response })}
</details>
`;
};
buildTextarea = ({
input = String(),
i2d = Boolean(),
extraPodstates = Array(),
response = { queryresult: { pods: Array() } },
}) => {
return `
<textarea name="technical-information">${this.escapeHTML(
JSON.stringify(
{
document,
input,
i2d,
extraPodstates,
response,
},
null,
4
)
)}</textarea>
`;
};
escapeHTML = (unsafe = String()) => {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
};
}
// 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

View File

@ -0,0 +1,63 @@
/**
* @license
* SPDX-License-Identifier: AGPL-3.0-or-later
* This file is part of Wolfree.
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*/
// @ts-check
import Placeholder from "./Placeholder.js";
import Entrypoint from "./Entrypoint.js";
export default class PostProcessor {
process = ({ input = String(), i2d = Boolean() } = {}) => {
try {
new Placeholder().remove();
try {
document
.querySelectorAll(
"html > body > div#__next > div > main > main > div.wolfree-pods > div > div > section > section > div > div > div > details > div"
)
.forEach((element = new HTMLElement()) =>
element.setAttribute("contenteditable", "")
);
} catch (error) {
console.error(
"Fail to make the plaintext in details tags easy to copy.",
error
);
}
try {
document
.querySelectorAll(
"html > body > div#__next > div > main > main > div.wolfree-pods > div > div > section > section > div:is(:first-child) > select"
)
.forEach((element = new HTMLSelectElement()) =>
element.addEventListener(
"change",
async (event = new Event(String())) =>
Entrypoint({
input,
i2d,
extraPodstates: [
event.target instanceof HTMLSelectElement
? event.target.value
: (console.error({ event }), String()),
],
})
)
);
} catch (error) {
console.error(
"Fail to set up the drop-down menu for problem-solving strategies.",
error
);
}
} catch (error) {
console.error({ error });
}
};
}

View File

@ -0,0 +1,29 @@
/**
* @license
* SPDX-License-Identifier: AGPL-3.0-or-later
* This file is part of Wolfree.
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*/
// @ts-check
import Placeholder from "./Placeholder.js";
import Pods from "./Pods.js";
export default class Preprocessor {
process = () => {
try {
try {
window.scroll(0, 0);
} catch (error) {
console.error({ error }, "Fail to scroll to the top of the webpage.");
}
new Placeholder().insert();
new Pods().remove();
} catch (error) {
console.error({ error });
}
};
}

View File

@ -0,0 +1,54 @@
/**
* @license
* SPDX-License-Identifier: AGPL-3.0-or-later
* This file is part of Wolfree.
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*/
// @ts-check
import AJAX from "./AJAX.js";
import PodsParser from "./PodsParser.js";
import Pods from "./Pods.js";
export default class Processor {
process = async ({
input = String(),
i2d = Boolean(),
extraPodstates = Array(),
} = {}) => {
try {
const { response } = await new AJAX().ajax({
input,
i2d,
extraPodstates,
});
try {
const { html } = new PodsParser().parse({
input,
i2d,
extraPodstates,
/** @type {any} */
response,
});
try {
new Pods().remove();
} catch (error) {
console.error(error);
}
try {
new Pods().insert({ html });
} catch (error) {
console.error(error);
}
} catch (error) {
console.error(error);
}
} catch (error) {
console.error(error);
}
};
}

View File

@ -0,0 +1,44 @@
/**
* @license
* SPDX-License-Identifier: AGPL-3.0-or-later
* This file is part of Wolfree.
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*/
// @ts-check
import AppID from "./AppID.js";
export default class SearchParams {
constructor({ getAppID = new AppID().getAppID } = {}) {
this.getAppID = getAppID;
}
getSearchParams = ({
input = String(),
i2d = Boolean(),
extraPodstates = Array(),
} = {}) => {
const appid = this.getAppID();
return {
...(appid && { appid }),
...(input && { input }),
...(i2d && { i2d }),
podstate: [
"Step-by-step solution",
"Step-by-step",
"Show all steps",
...extraPodstates,
// Note: extraPodstates must be the last item in the array.
// Otherwise, the Wolfram Alpha API may ignore the pod states in extraPodstates.
],
output: "json",
reinterpret: true,
podtimeout: 30,
scantimeout: 30,
parsetimeout: 30,
totaltimeout: 30,
formattimeout: 30,
};
};
}

View File

@ -0,0 +1,51 @@
/**
* @license
* SPDX-License-Identifier: AGPL-3.0-or-later
* This file is part of Wolfree.
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*/
// @ts-check
export default addEventListener(
"load",
() => (
setTimeout(
() =>
document
.querySelectorAll(
"html > body > #__next > div > main > main > div > div > section > form > div > div > input"
)
.forEach((input) =>
input instanceof HTMLInputElement
? input.focus()
: console.error({ input })
),
1000
),
[
() =>
document
.querySelectorAll(
'html > body > #__next > div > main > main > div > div > div > section > section > div:is(:first-child) > ul > li'
)
.forEach(
(li) =>
li.innerHTML.includes("Step-by-step") &&
(li instanceof HTMLElement
? (li.style.display = "none")
: console.error({ li }))
),
() =>
(document.title = document.title.replace(
"- Wolfram|Alpha",
"- Free Wolfram|Alpha Step-by-step Solution - Wolfree"
)),
].forEach(
(callback) => (
setInterval(callback, 2000), addEventListener("click", callback)
)
),
scroll(0, 0)
)
);