prettier formatage

This commit is contained in:
yoannchb-pro
2024-02-28 19:07:19 -05:00
parent 8c364e286b
commit 6a5e1eb8c3
40 changed files with 1666 additions and 1295 deletions
+11 -11
View File
@@ -1,18 +1,18 @@
module.exports = { module.exports = {
root: true, root: true,
parser: "@typescript-eslint/parser", parser: '@typescript-eslint/parser',
plugins: ["@typescript-eslint"], plugins: ['@typescript-eslint'],
extends: [ extends: [
"eslint:recommended", 'eslint:recommended',
"plugin:@typescript-eslint/eslint-recommended", 'plugin:@typescript-eslint/eslint-recommended',
"plugin:@typescript-eslint/recommended", 'plugin:@typescript-eslint/recommended',
"prettier", 'prettier'
], ],
ignorePatterns: ["node_modules/"], ignorePatterns: ['node_modules/'],
overrides: [ overrides: [
{ {
files: ["extension/popup/*.js", "src/**/*.ts"], files: ['extension/popup/*.js', 'src/**/*.ts'],
rules: {}, rules: {}
}, }
], ]
}; };
+10
View File
@@ -0,0 +1,10 @@
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"useTabs": false,
"printWidth": 100,
"trailingComma": "none",
"arrowParens": "avoid",
"endOfLine": "auto"
}
+1 -1
View File
@@ -1,7 +1,7 @@
# TODO # TODO
- Historic for questions (implemented but need testing) - Historic for questions (implemented but need testing)
- Contributing.md - Contributing.md / Fixe readme.md 'MoodleGPT don't complete my quiz ?'
- Better prompt (Fixe put in order question, Fixe calculation question) - Better prompt (Fixe put in order question, Fixe calculation question)
- Support math equation from image stocked in the `data-mathml` attribute - Support math equation from image stocked in the `data-mathml` attribute
- Better assets - Better assets
+418 -1
View File
File diff suppressed because one or more lines are too long
+4 -10
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
@@ -41,15 +41,11 @@
</div> </div>
</div> </div>
<div class="line center"> <div class="line center">
<label for="apiKey" class="textLabel" <label for="apiKey" class="textLabel">Api Key<span class="required">*</span>:</label>
>Api Key<span class="required">*</span>:</label
>
<input id="apiKey" type="text" /> <input id="apiKey" type="text" />
</div> </div>
<div class="line center"> <div class="line center">
<label for="model" class="textLabel" <label for="model" class="textLabel">GPT Model<span class="required">*</span>:</label>
>GPT Model<span class="required">*</span>:</label
>
<input id="model" type="text" /> <input id="model" type="text" />
<i <i
id="reloadModel" id="reloadModel"
@@ -73,9 +69,7 @@
<button value="clipboard" class="not-selected">clipboard</button> <button value="clipboard" class="not-selected">clipboard</button>
</li> </li>
<li> <li>
<button value="question-to-answer" class="not-selected"> <button value="question-to-answer" class="not-selected">question to answer</button>
question to answer
</button>
</li> </li>
</ul> </ul>
</div> </div>
+15 -15
View File
@@ -1,49 +1,49 @@
"use strict"; 'use strict';
/** /**
* Get the last ChatGPT version * Get the last ChatGPT version
*/ */
function getLastChatGPTVersion() { function getLastChatGPTVersion() {
const apiKeySelector = document.querySelector("#apiKey"); const apiKeySelector = document.querySelector('#apiKey');
const reloadModel = document.querySelector("#reloadModel"); const reloadModel = document.querySelector('#reloadModel');
let apiKey = apiKeySelector.value; let apiKey = apiKeySelector.value;
// If the api key is set we enable the button to get the last chatgpt version // If the api key is set we enable the button to get the last chatgpt version
function checkFiledApiKey() { function checkFiledApiKey() {
if (apiKey) { if (apiKey) {
reloadModel.removeAttribute("disabled"); reloadModel.removeAttribute('disabled');
reloadModel.setAttribute("title", "Get last ChatGPT version"); reloadModel.setAttribute('title', 'Get last ChatGPT version');
return; return;
} }
reloadModel.setAttribute("disabled", true); reloadModel.setAttribute('disabled', true);
reloadModel.setAttribute("title", "Provide an api key first"); reloadModel.setAttribute('title', 'Provide an api key first');
} }
checkFiledApiKey(); checkFiledApiKey();
// Check if the api key is set // Check if the api key is set
apiKeySelector.addEventListener("input", function () { apiKeySelector.addEventListener('input', function () {
apiKey = apiKeySelector.value.trim(); apiKey = apiKeySelector.value.trim();
checkFiledApiKey(); checkFiledApiKey();
}); });
// Event listener to handle a click on the relaod icon button // Event listener to handle a click on the relaod icon button
reloadModel.addEventListener("click", async function () { reloadModel.addEventListener('click', async function () {
if (!apiKey) return; if (!apiKey) return;
try { try {
const req = await fetch("https://api.openai.com/v1/models", { const req = await fetch('https://api.openai.com/v1/models', {
headers: { headers: {
Authorization: `Bearer ${apiKey}`, Authorization: `Bearer ${apiKey}`
}, }
}); });
const rep = await req.json(); const rep = await req.json();
const model = rep.data.find((model) => model.id.startsWith("gpt")); const model = rep.data.find(model => model.id.startsWith('gpt'));
document.querySelector("#model").value = model.id; document.querySelector('#model').value = model.id;
} catch (err) { } catch (err) {
console.error(err); console.error(err);
showMessage({ msg: "Failed to fetch last ChatGPT version", error: true }); showMessage({ msg: 'Failed to fetch last ChatGPT version', error: true });
} }
}); });
} }
+31 -34
View File
@@ -1,38 +1,39 @@
const saveBtn = document.querySelector(".save"); const saveBtn = document.querySelector('.save');
/* inputs id */ /* inputs id */
const inputsText = ["apiKey", "code", "model"]; const inputsText = ['apiKey', 'code', 'model'];
const inputsCheckbox = [ const inputsCheckbox = [
"logs", 'logs',
"title", 'title',
"cursor", 'cursor',
"typing", 'typing',
"mouseover", 'mouseover',
"infinite", 'infinite',
"timeout", 'timeout',
"history", 'history'
]; ];
/* Save the configuration */ /* Save the configuration */
saveBtn.addEventListener("click", function () { saveBtn.addEventListener('click', function () {
const [apiKey, code, model] = inputsText.map((selector) => const [apiKey, code, model] = inputsText.map(selector =>
document.querySelector("#" + selector).value.trim() document.querySelector('#' + selector).value.trim()
);
const [logs, title, cursor, typing, mouseover, infinite, timeout, history] = inputsCheckbox.map(
selector => {
const element = document.querySelector('#' + selector);
return element.checked && element.parentElement.style.display !== 'none';
}
); );
const [logs, title, cursor, typing, mouseover, infinite, timeout, history] =
inputsCheckbox.map((selector) => {
const element = document.querySelector("#" + selector);
return element.checked && element.parentElement.style.display !== "none";
});
if (!apiKey || !model) { if (!apiKey || !model) {
showMessage({ msg: "Please complete all the form", error: true }); showMessage({ msg: 'Please complete all the form', error: true });
return; return;
} }
if (code.length > 0 && code.length < 3) { if (code.length > 0 && code.length < 3) {
showMessage({ showMessage({
msg: "The code should at least contain 3 characters", msg: 'The code should at least contain 3 characters',
error: true, error: true
}); });
return; return;
} }
@@ -50,15 +51,15 @@ saveBtn.addEventListener("click", function () {
infinite, infinite,
timeout, timeout,
history, history,
mode: actualMode, mode: actualMode
}, }
}); });
showMessage({ msg: "Configuration saved" }); showMessage({ msg: 'Configuration saved' });
}); });
/* we load back the configuration */ /* we load back the configuration */
chrome.storage.sync.get(["moodleGPT"]).then(function (storage) { chrome.storage.sync.get(['moodleGPT']).then(function (storage) {
const config = storage.moodleGPT; const config = storage.moodleGPT;
if (config) { if (config) {
@@ -66,21 +67,17 @@ chrome.storage.sync.get(["moodleGPT"]).then(function (storage) {
actualMode = config.mode; actualMode = config.mode;
for (const mode of modes) { for (const mode of modes) {
if (mode.value === config.mode) { if (mode.value === config.mode) {
mode.classList.remove("not-selected"); mode.classList.remove('not-selected');
} else { } else {
mode.classList.add("not-selected"); mode.classList.add('not-selected');
} }
} }
} }
inputsText.forEach((key) => inputsText.forEach(key =>
config[key] config[key] ? (document.querySelector('#' + key).value = config[key]) : null
? (document.querySelector("#" + key).value = config[key])
: null
);
inputsCheckbox.forEach(
(key) => (document.querySelector("#" + key).checked = config[key] || "")
); );
inputsCheckbox.forEach(key => (document.querySelector('#' + key).checked = config[key] || ''));
} }
handleModeChange(); handleModeChange();
+12 -14
View File
@@ -1,15 +1,15 @@
"use strict"; 'use strict';
const mode = document.querySelector("#mode"); const mode = document.querySelector('#mode');
const modes = mode.querySelectorAll("button"); const modes = mode.querySelectorAll('button');
let actualMode = "autocomplete"; let actualMode = 'autocomplete';
/* inputs id that need to be disabled for a specific mode */ /* inputs id that need to be disabled for a specific mode */
const disabledForThisMode = { const disabledForThisMode = {
autocomplete: [], autocomplete: [],
clipboard: ["typing", "mouseover"], clipboard: ['typing', 'mouseover'],
"question-to-answer": ["typing", "infinite", "mouseover"], 'question-to-answer': ['typing', 'infinite', 'mouseover']
}; };
/** /**
@@ -17,27 +17,25 @@ const disabledForThisMode = {
*/ */
function handleModeChange() { function handleModeChange() {
const needDisable = disabledForThisMode[actualMode]; const needDisable = disabledForThisMode[actualMode];
const dontNeedDisable = inputsCheckbox.filter( const dontNeedDisable = inputsCheckbox.filter(input => !needDisable.includes(input));
(input) => !needDisable.includes(input)
);
for (const id of needDisable) { for (const id of needDisable) {
document.querySelector("#" + id).parentElement.style.display = "none"; document.querySelector('#' + id).parentElement.style.display = 'none';
} }
for (const id of dontNeedDisable) { for (const id of dontNeedDisable) {
document.querySelector("#" + id).parentElement.style.display = null; document.querySelector('#' + id).parentElement.style.display = null;
} }
} }
/* Mode handler */ /* Mode handler */
for (const button of modes) { for (const button of modes) {
button.addEventListener("click", function () { button.addEventListener('click', function () {
const value = button.value; const value = button.value;
actualMode = value; actualMode = value;
for (const mode of modes) { for (const mode of modes) {
if (mode.value !== value) { if (mode.value !== value) {
mode.classList.add("not-selected"); mode.classList.add('not-selected');
} else { } else {
mode.classList.remove("not-selected"); mode.classList.remove('not-selected');
} }
} }
handleModeChange(); handleModeChange();
+5 -5
View File
@@ -1,12 +1,12 @@
"use strict"; 'use strict';
/** /**
* Show message into the popup * Show message into the popup
*/ */
function showMessage({ msg, error, infinite }) { function showMessage({ msg, error, infinite }) {
const message = document.querySelector("#message"); const message = document.querySelector('#message');
message.style.color = error ? "red" : "limegreen"; message.style.color = error ? 'red' : 'limegreen';
message.textContent = msg; message.textContent = msg;
message.style.display = "block"; message.style.display = 'block';
if (!infinite) setTimeout(() => (message.style.display = "none"), 5000); if (!infinite) setTimeout(() => (message.style.display = 'none'), 5000);
} }
+15 -18
View File
@@ -1,7 +1,7 @@
"use strict"; 'use strict';
const CURRENT_VERSION = "1.0.4"; const CURRENT_VERSION = '1.0.4';
const versionDisplay = document.querySelector("#version"); const versionDisplay = document.querySelector('#version');
/** /**
* Get the last version from the github * Get the last version from the github
@@ -9,7 +9,7 @@ const versionDisplay = document.querySelector("#version");
*/ */
async function getLastVersion() { async function getLastVersion() {
const req = await fetch( const req = await fetch(
"https://raw.githubusercontent.com/yoannchb-pro/MoodleGPT/main/package.json" 'https://raw.githubusercontent.com/yoannchb-pro/MoodleGPT/main/package.json'
); );
const rep = await req.json(); const rep = await req.json();
return rep.version; return rep.version;
@@ -23,34 +23,31 @@ async function getLastVersion() {
*/ */
function setVersion(version, isCurrent = true) { function setVersion(version, isCurrent = true) {
if (isCurrent) { if (isCurrent) {
versionDisplay.textContent = "v" + version; versionDisplay.textContent = 'v' + version;
return; return;
} }
const link = document.createElement("a"); const link = document.createElement('a');
link.href = "https://github.com/yoannchb-pro/MoodleGPT"; link.href = 'https://github.com/yoannchb-pro/MoodleGPT';
link.rel = "noopener noreferrer"; link.rel = 'noopener noreferrer';
link.target = "_blank"; link.target = '_blank';
link.textContent = "v" + version; link.textContent = 'v' + version;
versionDisplay.appendChild(link); versionDisplay.appendChild(link);
versionDisplay.appendChild(document.createTextNode(" is now available !")); versionDisplay.appendChild(document.createTextNode(' is now available !'));
} }
/** /**
* Check if the extension neeed an update or not * Check if the extension neeed an update or not
*/ */
async function notifyUpdate() { async function notifyUpdate() {
const lastVersion = await getLastVersion().catch((err) => { const lastVersion = await getLastVersion().catch(err => {
console.error(err); console.error(err);
return CURRENT_VERSION; return CURRENT_VERSION;
}); });
const lastVertionSplitted = lastVersion.split("."); const lastVertionSplitted = lastVersion.split('.');
const currentVersionSplitted = CURRENT_VERSION.split("."); const currentVersionSplitted = CURRENT_VERSION.split('.');
const minVersionLength = Math.min( const minVersionLength = Math.min(lastVertionSplitted.length, currentVersionSplitted.length);
lastVertionSplitted.length,
currentVersionSplitted.length
);
for (let i = 0; i < minVersionLength; ++i) { for (let i = 0; i < minVersionLength; ++i) {
if (parseInt(lastVertionSplitted[i]) > parseInt(currentVersionSplitted[i])) if (parseInt(lastVertionSplitted[i]) > parseInt(currentVersionSplitted[i]))
+4 -4
View File
@@ -13,7 +13,7 @@
box-sizing: border-box; box-sizing: border-box;
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: "Segeo UI", sans-serif; font-family: 'Segeo UI', sans-serif;
color: var(--color); color: var(--color);
} }
@@ -67,8 +67,8 @@ a {
font-weight: bold; font-weight: bold;
} }
.line input[type="text"], .line input[type='text'],
.line input[type="password"] { .line input[type='password'] {
flex: 1 1; flex: 1 1;
border: thin solid var(--color); border: thin solid var(--color);
padding: 0.3rem 0.5rem; padding: 0.3rem 0.5rem;
@@ -77,7 +77,7 @@ a {
background-color: transparent; background-color: transparent;
} }
.line input[type="checkbox"] { .line input[type='checkbox'] {
accent-color: var(--btn-color); accent-color: var(--btn-color);
} }
+9 -9
View File
@@ -1,16 +1,16 @@
const ts = require("rollup-plugin-ts"); const ts = require('rollup-plugin-ts');
const terser = require("@rollup/plugin-terser"); const terser = require('@rollup/plugin-terser');
const config = require("./tsconfig.json"); const config = require('./tsconfig.json');
module.exports = { module.exports = {
input: "./src/index.ts", input: './src/index.ts',
output: [ output: [
{ {
file: "./extension/MoodleGPT.js", file: './extension/MoodleGPT.js',
format: "umd", format: 'umd',
sourcemap: true, sourcemap: true
}, }
], ],
plugins: [ts(config), terser()], plugins: [ts(config), terser()]
}; };
+20 -20
View File
@@ -1,6 +1,6 @@
import type Config from "@typing/config"; import type Config from '@typing/config';
import titleIndications from "@utils/title-indications"; import titleIndications from '@utils/title-indications';
import reply from "./reply"; import reply from './reply';
type Listener = { type Listener = {
element: HTMLElement; element: HTMLElement;
@@ -15,10 +15,10 @@ const listeners: Listener[] = [];
* @param config * @param config
*/ */
function codeListener(config: Config) { function codeListener(config: Config) {
document.body.addEventListener("keydown", function (event) { document.body.addEventListener('keydown', function (event) {
pressedKeys.push(event.key); pressedKeys.push(event.key);
if (pressedKeys.length > config.code!.length) pressedKeys.shift(); if (pressedKeys.length > config.code!.length) pressedKeys.shift();
if (pressedKeys.join("") === config.code) { if (pressedKeys.join('') === config.code) {
pressedKeys.length = 0; pressedKeys.length = 0;
setUpMoodleGpt(config); setUpMoodleGpt(config);
} }
@@ -30,10 +30,10 @@ function codeListener(config: Config) {
* @param element * @param element
*/ */
function removeListener(element: HTMLElement) { function removeListener(element: HTMLElement) {
const index = listeners.findIndex((listener) => listener.element === element); const index = listeners.findIndex(listener => listener.element === element);
if (index !== -1) { if (index !== -1) {
const listener = listeners.splice(index, 1)[0]; const listener = listeners.splice(index, 1)[0];
listener.element.removeEventListener("click", listener.fn); listener.element.removeEventListener('click', listener.fn);
} }
} }
@@ -46,42 +46,42 @@ function setUpMoodleGpt(config: Config) {
// Removing events if there are already declared // Removing events if there are already declared
if (listeners.length > 0) { if (listeners.length > 0) {
for (const listener of listeners) { for (const listener of listeners) {
if (config.cursor) listener.element.style.cursor = "initial"; if (config.cursor) listener.element.style.cursor = 'initial';
listener.element.removeEventListener("click", listener.fn); listener.element.removeEventListener('click', listener.fn);
} }
if (config.title) titleIndications("Removed"); if (config.title) titleIndications('Removed');
listeners.length = 0; listeners.length = 0;
return; return;
} }
// Query to find inputs and forms // Query to find inputs and forms
const inputTypeQuery = ["checkbox", "radio", "text", "number"] const inputTypeQuery = ['checkbox', 'radio', 'text', 'number']
.map((e) => `input[type="${e}"]`) .map(e => `input[type="${e}"]`)
.join(","); .join(',');
const inputQuery = inputTypeQuery + ", textarea, select, [contenteditable]"; const inputQuery = inputTypeQuery + ', textarea, select, [contenteditable]';
const forms = document.querySelectorAll(".formulation"); const forms = document.querySelectorAll('.formulation');
// For each form we inject a function on the queqtion // For each form we inject a function on the queqtion
for (const form of forms) { for (const form of forms) {
const questionElement: HTMLElement | null = form.querySelector(".qtext"); const questionElement: HTMLElement | null = form.querySelector('.qtext');
if (questionElement === null) continue; if (questionElement === null) continue;
if (config.cursor) questionElement.style.cursor = "pointer"; if (config.cursor) questionElement.style.cursor = 'pointer';
const injectionFunction = reply.bind(null, { const injectionFunction = reply.bind(null, {
config, config,
questionElement, questionElement,
form: form as HTMLElement, form: form as HTMLElement,
inputQuery, inputQuery,
removeListener: () => removeListener(questionElement), removeListener: () => removeListener(questionElement)
}); });
listeners.push({ element: questionElement, fn: injectionFunction }); listeners.push({ element: questionElement, fn: injectionFunction });
questionElement.addEventListener("click", injectionFunction); questionElement.addEventListener('click', injectionFunction);
} }
if (config.title) titleIndications("Injected"); if (config.title) titleIndications('Injected');
} }
export { codeListener, removeListener, setUpMoodleGpt }; export { codeListener, removeListener, setUpMoodleGpt };
+6 -10
View File
@@ -1,5 +1,5 @@
import normalizeText from "@utils/normalize-text"; import normalizeText from '@utils/normalize-text';
import htmlTableToString from "@utils/html-table-to-string"; import htmlTableToString from '@utils/html-table-to-string';
/** /**
* Normalize the question as text and add sub informations * Normalize the question as text and add sub informations
@@ -12,19 +12,15 @@ function createAndNormalizeQuestion(questionContainer: HTMLElement) {
// We remove unnecessary information // We remove unnecessary information
const accesshideElements: NodeListOf<HTMLElement> = const accesshideElements: NodeListOf<HTMLElement> =
questionContainer.querySelectorAll(".accesshide"); questionContainer.querySelectorAll('.accesshide');
for (const useless of accesshideElements) { for (const useless of accesshideElements) {
question = question.replace(useless.innerText, ""); question = question.replace(useless.innerText, '');
} }
// Make tables more readable for chat-gpt // Make tables more readable for chat-gpt
const tables: NodeListOf<HTMLTableElement> = const tables: NodeListOf<HTMLTableElement> = questionContainer.querySelectorAll('.qtext table');
questionContainer.querySelectorAll(".qtext table");
for (const table of tables) { for (const table of tables) {
question = question.replace( question = question.replace(table.innerText, '\n' + htmlTableToString(table) + '\n');
table.innerText,
"\n" + htmlTableToString(table) + "\n"
);
} }
return normalizeText(question, false); return normalizeText(question, false);
+16 -19
View File
@@ -1,6 +1,6 @@
import type Config from "@typing/config"; import type Config from '@typing/config';
import type GPTAnswer from "@typing/gptAnswer"; import type GPTAnswer from '@typing/gptAnswer';
import normalizeText from "@utils/normalize-text"; import normalizeText from '@utils/normalize-text';
type History = { type History = {
url: string | null; url: string | null;
@@ -9,9 +9,9 @@ type History = {
}; };
enum ROLE { enum ROLE {
SYSTEM = "system", SYSTEM = 'system',
USER = "user", USER = 'user',
ASSISTANT = "assistant", ASSISTANT = 'assistant'
} }
const INSTRUCTION: string = ` const INSTRUCTION: string = `
@@ -32,9 +32,9 @@ const history: History = {
url: null, url: null,
system: { system: {
role: ROLE.SYSTEM, role: ROLE.SYSTEM,
content: INSTRUCTION, content: INSTRUCTION
}, },
history: [], history: []
}; };
/** /**
@@ -43,10 +43,7 @@ const history: History = {
* @param question * @param question
* @returns * @returns
*/ */
async function getChatGPTResponse( async function getChatGPTResponse(config: Config, question: string): Promise<GPTAnswer> {
config: Config,
question: string
): Promise<GPTAnswer> {
const URL = location.hostname + location.pathname; const URL = location.hostname + location.pathname;
// We reset the history when we enter a new moodle quiz or when it's desactivate // We reset the history when we enter a new moodle quiz or when it's desactivate
@@ -60,11 +57,11 @@ async function getChatGPTResponse(
const message = { role: ROLE.USER, content: question }; const message = { role: ROLE.USER, content: question };
const req = await fetch("https://api.openai.com/v1/chat/completions", { const req = await fetch('https://api.openai.com/v1/chat/completions', {
method: "POST", method: 'POST',
headers: { headers: {
"Content-Type": "application/json", 'Content-Type': 'application/json',
Authorization: `Bearer ${config.apiKey}`, Authorization: `Bearer ${config.apiKey}`
}, },
signal: config.timeout ? controller.signal : null, signal: config.timeout ? controller.signal : null,
body: JSON.stringify({ body: JSON.stringify({
@@ -73,8 +70,8 @@ async function getChatGPTResponse(
temperature: 0.8, temperature: 0.8,
top_p: 1.0, top_p: 1.0,
presence_penalty: 1.0, presence_penalty: 1.0,
stop: null, stop: null
}), })
}); });
clearTimeout(timeoutControler); clearTimeout(timeoutControler);
@@ -91,7 +88,7 @@ async function getChatGPTResponse(
return { return {
question, question,
response, response,
normalizedResponse: normalizeText(response), normalizedResponse: normalizeText(response)
}; };
} }
+10 -10
View File
@@ -1,12 +1,12 @@
import type GPTAnswer from "@typing/gptAnswer"; import type GPTAnswer from '@typing/gptAnswer';
import type Config from "@typing/config"; import type Config from '@typing/config';
import handleClipboard from "@core/questions/clipboard"; import handleClipboard from '@core/questions/clipboard';
import handleContentEditable from "@core/questions/contenteditable"; import handleContentEditable from '@core/questions/contenteditable';
import handleNumber from "@core/questions/number"; import handleNumber from '@core/questions/number';
import handleRadio from "@core/questions/radio"; import handleRadio from '@core/questions/radio';
import handleCheckbox from "@core/questions/checkbox"; import handleCheckbox from '@core/questions/checkbox';
import handleSelect from "@core/questions/select"; import handleSelect from '@core/questions/select';
import handleTextbox from "@core/questions/textbox"; import handleTextbox from '@core/questions/textbox';
type Props = { type Props = {
config: Config; config: Config;
@@ -31,7 +31,7 @@ function autoCompleteMode(props: Props) {
handleNumber, handleNumber,
handleSelect, handleSelect,
handleRadio, handleRadio,
handleCheckbox, handleCheckbox
]; ];
for (const handler of handlers) { for (const handler of handlers) {
+3 -3
View File
@@ -1,6 +1,6 @@
import type Config from "@typing/config"; import type Config from '@typing/config';
import type GPTAnswer from "@typing/gptAnswer"; import type GPTAnswer from '@typing/gptAnswer';
import handleClipboard from "@core/questions/clipboard"; import handleClipboard from '@core/questions/clipboard';
type Props = { type Props = {
config: Config; config: Config;
+5 -7
View File
@@ -1,4 +1,4 @@
import type GPTAnswer from "@typing/gptAnswer"; import type GPTAnswer from '@typing/gptAnswer';
type Props = { type Props = {
questionElement: HTMLElement; questionElement: HTMLElement;
@@ -21,15 +21,13 @@ function questionToAnswerMode(props: Props) {
questionElement.textContent = props.gptAnswer.response; questionElement.textContent = props.gptAnswer.response;
// Format the content // Format the content
questionElement.style.whiteSpace = "pre-wrap"; questionElement.style.whiteSpace = 'pre-wrap';
// To go back to the question / answer // To go back to the question / answer
let contentIsResponse = true; let contentIsResponse = true;
questionElement.addEventListener("click", function () { questionElement.addEventListener('click', function () {
questionElement.style.whiteSpace = contentIsResponse ? "" : "pre-warp"; questionElement.style.whiteSpace = contentIsResponse ? '' : 'pre-warp';
questionElement.textContent = contentIsResponse questionElement.textContent = contentIsResponse ? questionBackup : props.gptAnswer.response;
? questionBackup
: props.gptAnswer.response;
contentIsResponse = !contentIsResponse; contentIsResponse = !contentIsResponse;
}); });
+13 -17
View File
@@ -1,8 +1,8 @@
import type Config from "@typing/config"; import type Config from '@typing/config';
import type GPTAnswer from "@typing/gptAnswer"; import type GPTAnswer from '@typing/gptAnswer';
import Logs from "@utils/logs"; import Logs from '@utils/logs';
import normalizeText from "@utils/normalize-text"; import normalizeText from '@utils/normalize-text';
import { pickBestReponse } from "@utils/pick-best-response"; import { pickBestReponse } from '@utils/pick-best-response';
/** /**
* Handle input checkbox elements * Handle input checkbox elements
@@ -18,18 +18,18 @@ function handleCheckbox(
const firstInput = inputList?.[0] as HTMLInputElement; const firstInput = inputList?.[0] as HTMLInputElement;
// Handle the case the input is not a checkbox // Handle the case the input is not a checkbox
if (!firstInput || firstInput.type !== "checkbox") { if (!firstInput || firstInput.type !== 'checkbox') {
return false; return false;
} }
const corrects = gptAnswer.normalizedResponse.split("\n"); const corrects = gptAnswer.normalizedResponse.split('\n');
const possibleAnswers = Array.from(inputList) const possibleAnswers = Array.from(inputList)
.map((inp) => ({ .map(inp => ({
element: inp, element: inp,
value: normalizeText(inp?.parentElement?.textContent ?? ""), value: normalizeText(inp?.parentElement?.textContent ?? '')
})) }))
.filter((obj) => obj.value !== ""); .filter(obj => obj.value !== '');
for (const correct of corrects) { for (const correct of corrects) {
const bestAnswer = pickBestReponse(correct, possibleAnswers); const bestAnswer = pickBestReponse(correct, possibleAnswers);
@@ -40,13 +40,9 @@ function handleCheckbox(
const correctInput = bestAnswer.element as HTMLInputElement; const correctInput = bestAnswer.element as HTMLInputElement;
if (config.mouseover) { if (config.mouseover) {
correctInput.addEventListener( correctInput.addEventListener('mouseover', () => (correctInput.checked = true), {
"mouseover", once: true
() => (correctInput.checked = true), });
{
once: true,
}
);
} else { } else {
correctInput.checked = true; correctInput.checked = true;
} }
+4 -4
View File
@@ -1,6 +1,6 @@
import type Config from "@typing/config"; import type Config from '@typing/config';
import type GPTAnswer from "@typing/gptAnswer"; import type GPTAnswer from '@typing/gptAnswer';
import titleIndications from "@utils/title-indications"; import titleIndications from '@utils/title-indications';
/** /**
* Copy the response in the clipboard if we can automaticaly fill the question * Copy the response in the clipboard if we can automaticaly fill the question
@@ -8,7 +8,7 @@ import titleIndications from "@utils/title-indications";
* @param gptAnswer * @param gptAnswer
*/ */
function handleClipboard(config: Config, gptAnswer: GPTAnswer) { function handleClipboard(config: Config, gptAnswer: GPTAnswer) {
if (config.title) titleIndications("Copied to clipboard"); if (config.title) titleIndications('Copied to clipboard');
navigator.clipboard.writeText(gptAnswer.response); navigator.clipboard.writeText(gptAnswer.response);
} }
+5 -5
View File
@@ -1,5 +1,5 @@
import type Config from "@typing/config"; import type Config from '@typing/config';
import type GPTAnswer from "@typing/gptAnswer"; import type GPTAnswer from '@typing/gptAnswer';
/** /**
* Hanlde contenteditable elements * Hanlde contenteditable elements
@@ -17,15 +17,15 @@ function handleContentEditable(
if ( if (
inputList.length !== 1 || // for now we don't handle many input for editable textcontent inputList.length !== 1 || // for now we don't handle many input for editable textcontent
input.getAttribute("contenteditable") !== "true" input.getAttribute('contenteditable') !== 'true'
) { ) {
return false; return false;
} }
if (config.typing) { if (config.typing) {
let index = 0; let index = 0;
input.addEventListener("keydown", function (event: KeyboardEvent) { input.addEventListener('keydown', function (event: KeyboardEvent) {
if (event.key === "Backspace") index = gptAnswer.response.length + 1; if (event.key === 'Backspace') index = gptAnswer.response.length + 1;
if (index > gptAnswer.response.length) return; if (index > gptAnswer.response.length) return;
event.preventDefault(); event.preventDefault();
input.textContent = gptAnswer.response.slice(0, ++index); input.textContent = gptAnswer.response.slice(0, ++index);
+7 -9
View File
@@ -1,5 +1,5 @@
import type Config from "@typing/config"; import type Config from '@typing/config';
import type GPTAnswer from "@typing/gptAnswer"; import type GPTAnswer from '@typing/gptAnswer';
/** /**
* Handle number input * Handle number input
@@ -17,24 +17,22 @@ function handleNumber(
if ( if (
inputList.length !== 1 || // for now we don't handle many input number inputList.length !== 1 || // for now we don't handle many input number
input.type !== "number" input.type !== 'number'
) { ) {
return false; return false;
} }
const number = gptAnswer.normalizedResponse const number = gptAnswer.normalizedResponse.match(/\d+([,.]\d+)?/gi)?.[0]?.replace(',', '.');
.match(/\d+([,.]\d+)?/gi)?.[0]
?.replace(",", ".");
if (number === undefined) return false; if (number === undefined) return false;
if (config.typing) { if (config.typing) {
let index = 0; let index = 0;
input.addEventListener("keydown", function (event: Event) { input.addEventListener('keydown', function (event: Event) {
event.preventDefault(); event.preventDefault();
if ((<KeyboardEvent>event).key === "Backspace") index = number.length + 1; if ((<KeyboardEvent>event).key === 'Backspace') index = number.length + 1;
if (index > number.length) return; if (index > number.length) return;
if (number.slice(index, index + 1) === ".") ++index; if (number.slice(index, index + 1) === '.') ++index;
input.value = number.slice(0, ++index); input.value = number.slice(0, ++index);
}); });
} else { } else {
+13 -20
View File
@@ -1,8 +1,8 @@
import type Config from "@typing/config"; import type Config from '@typing/config';
import type GPTAnswer from "@typing/gptAnswer"; import type GPTAnswer from '@typing/gptAnswer';
import Logs from "@utils/logs"; import Logs from '@utils/logs';
import normalizeText from "@utils/normalize-text"; import normalizeText from '@utils/normalize-text';
import { pickBestReponse } from "@utils/pick-best-response"; import { pickBestReponse } from '@utils/pick-best-response';
/** /**
* Handle input radio elements * Handle input radio elements
@@ -18,21 +18,18 @@ function handleRadio(
const firstInput = inputList?.[0] as HTMLInputElement; const firstInput = inputList?.[0] as HTMLInputElement;
// Handle the case the input is not a radio // Handle the case the input is not a radio
if (!firstInput || firstInput.type !== "radio") { if (!firstInput || firstInput.type !== 'radio') {
return false; return false;
} }
const possibleAnswers = Array.from(inputList) const possibleAnswers = Array.from(inputList)
.map((inp) => ({ .map(inp => ({
element: inp, element: inp,
value: normalizeText(inp?.parentElement?.textContent ?? ""), value: normalizeText(inp?.parentElement?.textContent ?? '')
})) }))
.filter((obj) => obj.value !== ""); .filter(obj => obj.value !== '');
const bestAnswer = pickBestReponse( const bestAnswer = pickBestReponse(gptAnswer.normalizedResponse, possibleAnswers);
gptAnswer.normalizedResponse,
possibleAnswers
);
if (config.logs && bestAnswer.value) { if (config.logs && bestAnswer.value) {
Logs.bestAnswer(bestAnswer.value, bestAnswer.similarity); Logs.bestAnswer(bestAnswer.value, bestAnswer.similarity);
@@ -40,13 +37,9 @@ function handleRadio(
const correctInput = bestAnswer.element as HTMLInputElement; const correctInput = bestAnswer.element as HTMLInputElement;
if (config.mouseover) { if (config.mouseover) {
correctInput.addEventListener( correctInput.addEventListener('mouseover', () => (correctInput.checked = true), {
"mouseover", once: true
() => (correctInput.checked = true), });
{
once: true,
}
);
} else { } else {
correctInput.checked = true; correctInput.checked = true;
} }
+15 -19
View File
@@ -1,8 +1,8 @@
import type Config from "@typing/config"; import type Config from '@typing/config';
import type GPTAnswer from "@typing/gptAnswer"; import type GPTAnswer from '@typing/gptAnswer';
import Logs from "@utils/logs"; import Logs from '@utils/logs';
import normalizeText from "@utils/normalize-text"; import normalizeText from '@utils/normalize-text';
import { pickBestReponse } from "@utils/pick-best-response"; import { pickBestReponse } from '@utils/pick-best-response';
/** /**
* Handle select elements (and put in order select) * Handle select elements (and put in order select)
@@ -16,23 +16,23 @@ function handleSelect(
inputList: NodeListOf<HTMLElement>, inputList: NodeListOf<HTMLElement>,
gptAnswer: GPTAnswer gptAnswer: GPTAnswer
): boolean { ): boolean {
if (inputList.length === 0 || inputList[0].tagName !== "SELECT") return false; if (inputList.length === 0 || inputList[0].tagName !== 'SELECT') return false;
const corrects = gptAnswer.normalizedResponse.split("\n"); const corrects = gptAnswer.normalizedResponse.split('\n');
if (config.logs) Logs.array(corrects); if (config.logs) Logs.array(corrects);
for (let i = 0; i < inputList.length; ++i) { for (let i = 0; i < inputList.length; ++i) {
if (!corrects[i]) break; if (!corrects[i]) break;
const options = inputList[i].querySelectorAll("option"); const options = inputList[i].querySelectorAll('option');
const possibleAnswers = Array.from(options) const possibleAnswers = Array.from(options)
.map((opt) => ({ .map(opt => ({
element: opt, element: opt,
value: normalizeText(opt.textContent ?? ""), value: normalizeText(opt.textContent ?? '')
})) }))
.filter((obj) => obj.value !== ""); .filter(obj => obj.value !== '');
const bestAnswer = pickBestReponse(corrects[i], possibleAnswers); const bestAnswer = pickBestReponse(corrects[i], possibleAnswers);
@@ -41,18 +41,14 @@ function handleSelect(
} }
const correctOption = bestAnswer.element as HTMLOptionElement; const correctOption = bestAnswer.element as HTMLOptionElement;
const currentSelect = correctOption.closest("select"); const currentSelect = correctOption.closest('select');
if (currentSelect === null) continue; if (currentSelect === null) continue;
if (config.mouseover) { if (config.mouseover) {
currentSelect.addEventListener( currentSelect.addEventListener('click', () => (correctOption.selected = true), {
"click", once: true
() => (correctOption.selected = true), });
{
once: true,
}
);
} else { } else {
correctOption.selected = true; correctOption.selected = true;
} }
+5 -5
View File
@@ -1,5 +1,5 @@
import type Config from "@typing/config"; import type Config from '@typing/config';
import type GPTAnswer from "@typing/gptAnswer"; import type GPTAnswer from '@typing/gptAnswer';
/** /**
* Handle textbox * Handle textbox
@@ -17,16 +17,16 @@ function handleTextbox(
if ( if (
inputList.length !== 1 || // for now we don't handle many input text inputList.length !== 1 || // for now we don't handle many input text
(input.tagName !== "TEXTAREA" && input.type !== "text") (input.tagName !== 'TEXTAREA' && input.type !== 'text')
) { ) {
return false; return false;
} }
if (config.typing) { if (config.typing) {
let index = 0; let index = 0;
input.addEventListener("keydown", function (event: Event) { input.addEventListener('keydown', function (event: Event) {
event.preventDefault(); event.preventDefault();
if ((<KeyboardEvent>event).key === "Backspace") { if ((<KeyboardEvent>event).key === 'Backspace') {
index = gptAnswer.response.length + 1; index = gptAnswer.response.length + 1;
} }
if (index > gptAnswer.response.length) return; if (index > gptAnswer.response.length) return;
+20 -25
View File
@@ -1,10 +1,10 @@
import type Config from "@typing/config"; import type Config from '@typing/config';
import Logs from "@utils/logs"; import Logs from '@utils/logs';
import getChatGPTResponse from "./get-response"; import getChatGPTResponse from './get-response';
import createAndNormalizeQuestion from "./create-question"; import createAndNormalizeQuestion from './create-question';
import clipboardMode from "./modes/clipboard"; import clipboardMode from './modes/clipboard';
import questionToAnswerMode from "./modes/question-to-answer"; import questionToAnswerMode from './modes/question-to-answer';
import autoCompleteMode from "./modes/autocomplete"; import autoCompleteMode from './modes/autocomplete';
type Props = { type Props = {
config: Config; config: Config;
@@ -20,24 +20,19 @@ type Props = {
* @returns * @returns
*/ */
async function reply(props: Props): Promise<void> { async function reply(props: Props): Promise<void> {
if (props.config.cursor) props.questionElement.style.cursor = "wait"; if (props.config.cursor) props.questionElement.style.cursor = 'wait';
const question = createAndNormalizeQuestion(props.form); const question = createAndNormalizeQuestion(props.form);
const inputList: NodeListOf<HTMLElement> = props.form.querySelectorAll( const inputList: NodeListOf<HTMLElement> = props.form.querySelectorAll(props.inputQuery);
props.inputQuery
);
const gptAnswer = await getChatGPTResponse(props.config, question).catch( const gptAnswer = await getChatGPTResponse(props.config, question).catch(error => ({
(error) => ({ error
error, }));
})
);
const haveError = typeof gptAnswer === "object" && "error" in gptAnswer; const haveError = typeof gptAnswer === 'object' && 'error' in gptAnswer;
if (props.config.cursor) { if (props.config.cursor) {
props.questionElement.style.cursor = props.questionElement.style.cursor = props.config.infinite || haveError ? 'pointer' : 'initial';
props.config.infinite || haveError ? "pointer" : "initial";
} }
if (haveError) { if (haveError) {
@@ -51,28 +46,28 @@ async function reply(props: Props): Promise<void> {
} }
switch (props.config.mode) { switch (props.config.mode) {
case "clipboard": case 'clipboard':
clipboardMode({ clipboardMode({
config: props.config, config: props.config,
questionElement: props.questionElement, questionElement: props.questionElement,
gptAnswer, gptAnswer,
removeListener: props.removeListener, removeListener: props.removeListener
}); });
break; break;
case "question-to-answer": case 'question-to-answer':
questionToAnswerMode({ questionToAnswerMode({
gptAnswer, gptAnswer,
questionElement: props.questionElement, questionElement: props.questionElement,
removeListener: props.removeListener, removeListener: props.removeListener
}); });
break; break;
case "autocomplete": case 'autocomplete':
autoCompleteMode({ autoCompleteMode({
config: props.config, config: props.config,
gptAnswer, gptAnswer,
inputList, inputList,
questionElement: props.questionElement, questionElement: props.questionElement,
removeListener: props.removeListener, removeListener: props.removeListener
}); });
break; break;
} }
+4 -4
View File
@@ -1,10 +1,10 @@
import type Config from "@typing/config"; import type Config from '@typing/config';
import { codeListener, setUpMoodleGpt } from "./core/code-listener"; import { codeListener, setUpMoodleGpt } from './core/code-listener';
chrome.storage.sync.get(["moodleGPT"]).then(function (storage) { chrome.storage.sync.get(['moodleGPT']).then(function (storage) {
const config: Config = storage.moodleGPT; const config: Config = storage.moodleGPT;
if (!config) throw new Error("Please configure MoodleGPT into the extension"); if (!config) throw new Error('Please configure MoodleGPT into the extension');
if (config.code) { if (config.code) {
codeListener(config); codeListener(config);
+1 -1
View File
@@ -10,7 +10,7 @@ type Config = {
title?: boolean; title?: boolean;
timeout?: boolean; timeout?: boolean;
history?: boolean; history?: boolean;
mode?: "autocomplete" | "question-to-answer" | "clipboard"; mode?: 'autocomplete' | 'question-to-answer' | 'clipboard';
}; };
export default Config; export default Config;
+11 -16
View File
@@ -5,37 +5,32 @@
*/ */
function htmlTableToString(table: HTMLTableElement) { function htmlTableToString(table: HTMLTableElement) {
const tab: string[][] = []; const tab: string[][] = [];
const lines = Array.from(table.querySelectorAll("tr")); const lines = Array.from(table.querySelectorAll('tr'));
const maxColumnsLength: number[] = []; const maxColumnsLength: number[] = [];
lines.map((line) => { lines.map(line => {
const cells = Array.from(line.querySelectorAll("td, th")); const cells = Array.from(line.querySelectorAll('td, th'));
const cellsContent = cells.map((cell, index) => { const cellsContent = cells.map((cell, index) => {
const content = cell.textContent?.trim(); const content = cell.textContent?.trim();
maxColumnsLength[index] = Math.max( maxColumnsLength[index] = Math.max(maxColumnsLength[index] || 0, content?.length || 0);
maxColumnsLength[index] || 0, return content ?? '';
content?.length || 0
);
return content ?? "";
}); });
tab.push(cellsContent); tab.push(cellsContent);
}); });
const lineSeparationSize = const lineSeparationSize = maxColumnsLength.reduce((a, b) => a + b) + tab[0].length * 3 + 1;
maxColumnsLength.reduce((a, b) => a + b) + tab[0].length * 3 + 1; const lineSeparation = '\n' + Array(lineSeparationSize).fill('-').join('') + '\n';
const lineSeparation =
"\n" + Array(lineSeparationSize).fill("-").join("") + "\n";
const mappedTab = tab.map((line) => { const mappedTab = tab.map(line => {
const mappedLine = line.map((content, index) => const mappedLine = line.map((content, index) =>
content.padEnd( content.padEnd(
maxColumnsLength[index], maxColumnsLength[index],
"\u00A0" // For no matching with \s '\u00A0' // For no matching with \s
) )
); );
return "| " + mappedLine.join(" | ") + " |"; return '| ' + mappedLine.join(' | ') + ' |';
}); });
const head = mappedTab.shift(); const head = mappedTab.shift();
return head + lineSeparation + mappedTab.join("\n"); return head + lineSeparation + mappedTab.join('\n');
} }
export default htmlTableToString; export default htmlTableToString;
+9 -9
View File
@@ -1,28 +1,28 @@
import GPTAnswer from "@typing/gptAnswer"; import GPTAnswer from '@typing/gptAnswer';
import { toPourcentage } from "./pick-best-response"; import { toPourcentage } from './pick-best-response';
class Logs { class Logs {
static question(text: string) { static question(text: string) {
const css = "color: cyan"; const css = 'color: cyan';
console.log("%c[QUESTION]: %s", css, text); console.log('%c[QUESTION]: %s', css, text);
} }
static bestAnswer(answer: string, similarity: number) { static bestAnswer(answer: string, similarity: number) {
const css = "color: green"; const css = 'color: green';
console.log( console.log(
"%c[BEST ANSWER]: %s", '%c[BEST ANSWER]: %s',
css, css,
`"${answer}" with a similarity of ${toPourcentage(similarity)}` `"${answer}" with a similarity of ${toPourcentage(similarity)}`
); );
} }
static array(arr: unknown[]) { static array(arr: unknown[]) {
console.log("[CORRECTS] ", arr); console.log('[CORRECTS] ', arr);
} }
static response(gptAnswer: GPTAnswer) { static response(gptAnswer: GPTAnswer) {
console.log("Original:\n" + gptAnswer.response); console.log('Original:\n' + gptAnswer.response);
console.log("Normalized:\n" + gptAnswer.normalizedResponse); console.log('Normalized:\n' + gptAnswer.normalizedResponse);
} }
} }
+5 -5
View File
@@ -6,13 +6,13 @@ function normalizeText(text: string, toLowerCase: boolean = true) {
if (toLowerCase) text = text.toLowerCase(); if (toLowerCase) text = text.toLowerCase();
const normalizedText = text const normalizedText = text
.replace(/\n+/gi, "\n") //remove duplicate new lines .replace(/\n+/gi, '\n') //remove duplicate new lines
.replace(/(\n\s*\n)+/g, "\n") //remove useless white space from textcontent .replace(/(\n\s*\n)+/g, '\n') //remove useless white space from textcontent
.replace(/[ \t]+/gi, " ") //replace multiples space or tabs by a space .replace(/[ \t]+/gi, ' ') //replace multiples space or tabs by a space
.trim() .trim()
// We remove the following content because sometimes ChatGPT will reply: "answer d" // We remove the following content because sometimes ChatGPT will reply: "answer d"
.replace(/^[a-z\d]\.\s/gi, "") //a. text, b. text, c. text, 1. text, 2. text, 3.text .replace(/^[a-z\d]\.\s/gi, '') //a. text, b. text, c. text, 1. text, 2. text, 3.text
.replace(/\n[a-z\d]\.\s/gi, "\n"); //same but with new line .replace(/\n[a-z\d]\.\s/gi, '\n'); //same but with new line
return normalizedText; return normalizedText;
} }
+6 -7
View File
@@ -21,8 +21,8 @@ function levenshteinDistance(str1: string, str2: string) {
if (str2.length === 0) return str1.length; if (str2.length === 0) return str1.length;
const matrix: number[][] = []; const matrix: number[][] = [];
const str1WithoutSpaces = str1.replace(/\s+/, ""); const str1WithoutSpaces = str1.replace(/\s+/, '');
const str2WithoutSpaces = str2.replace(/\s+/, ""); const str2WithoutSpaces = str2.replace(/\s+/, '');
for (let i = 0; i <= str1WithoutSpaces.length; ++i) { for (let i = 0; i <= str1WithoutSpaces.length; ++i) {
matrix.push([i]); matrix.push([i]);
@@ -33,8 +33,7 @@ function levenshteinDistance(str1: string, str2: string) {
: Math.min( : Math.min(
matrix[i - 1][j] + 1, matrix[i - 1][j] + 1,
matrix[i][j - 1] + 1, matrix[i][j - 1] + 1,
matrix[i - 1][j - 1] + matrix[i - 1][j - 1] + (str1WithoutSpaces[i - 1] === str2WithoutSpaces[j - 1] ? 0 : 1)
(str1WithoutSpaces[i - 1] === str2WithoutSpaces[j - 1] ? 0 : 1)
); );
} }
} }
@@ -67,7 +66,7 @@ export function pickBestReponse(
let bestResponse: BestResponse = { let bestResponse: BestResponse = {
element: null, element: null,
similarity: 0, similarity: 0,
value: null, value: null
}; };
for (const obj of arr) { for (const obj of arr) {
const similarity = sentenceSimilarity(obj.value, answer); const similarity = sentenceSimilarity(obj.value, answer);
@@ -100,7 +99,7 @@ export function pickResponsesWithSimilarityGreaterThan(
responses.push({ responses.push({
similarity, similarity,
value: obj.value, value: obj.value,
element: obj.element, element: obj.element
}); });
} }
return responses.sort((a, b) => a.similarity - b.similarity); return responses.sort((a, b) => a.similarity - b.similarity);
@@ -111,5 +110,5 @@ export function pickResponsesWithSimilarityGreaterThan(
* @param similarity * @param similarity
*/ */
export function toPourcentage(similarity: number): string { export function toPourcentage(similarity: number): string {
return Math.round(similarity * 100 * 100) / 100 + "%"; return Math.round(similarity * 100 * 100) / 100 + '%';
} }
+1 -1
View File
@@ -7,7 +7,7 @@
box-sizing: border-box; box-sizing: border-box;
padding: 0; padding: 0;
margin: 0; margin: 0;
font-family: "Segeo UI"; font-family: 'Segeo UI';
} }
body { body {
+8 -11
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
@@ -17,23 +17,20 @@
<div class="inp"> <div class="inp">
<input type="checkbox" /> <input type="checkbox" />
<label <label
>a. Systems Administrator: Managing and maintaining computer systems >a. Systems Administrator: Managing and maintaining computer systems and
and networks.</label networks.</label
> >
</div> </div>
<div class="inp"> <div class="inp">
<input type="checkbox" /> <input type="checkbox" />
<label <label
>b. Software Developer: Designing, coding, testing, and maintaining >b. Software Developer: Designing, coding, testing, and maintaining software
software applications.</label applications.</label
> >
</div> </div>
<div class="inp"> <div class="inp">
<input type="checkbox" /> <input type="checkbox" />
<label> <label> c. Professional Chef: Creating delicious meals in a restaurant kitchen. </label>
c. Professional Chef: Creating delicious meals in a restaurant
kitchen.
</label>
</div> </div>
</div> </div>
</section> </section>
@@ -276,8 +273,8 @@
<section class="formulation"> <section class="formulation">
<div class="qtext"> <div class="qtext">
<p> <p>
Gives a "reverseWorld" function in javascript which takes as a Gives a "reverseWorld" function in javascript which takes as a parameter a word and flips
parameter a word and flips it in the opposite direction it in the opposite direction
</p> </p>
</div> </div>
<div> <div>
+5 -7
View File
@@ -1,21 +1,19 @@
/* Reset real moodle inputs to try in real env */ /* Reset real moodle inputs to try in real env */
for (const option of document.querySelectorAll("option")) { for (const option of document.querySelectorAll('option')) {
option.selected = false; option.selected = false;
option.disabled = false; option.disabled = false;
option.closest("select").disabled = false; option.closest('select').disabled = false;
} }
for (const input of document.querySelectorAll( for (const input of document.querySelectorAll('input[type="radio"], input[type="checkbox"]')) {
'input[type="radio"], input[type="checkbox"]'
)) {
input.checked = false; input.checked = false;
input.disabled = false; input.disabled = false;
} }
for (const icon of document.querySelectorAll(".text-danger, .text-success")) { for (const icon of document.querySelectorAll('.text-danger, .text-success')) {
icon.remove(); icon.remove();
} }
for (const feedback of document.querySelectorAll(".specificfeedback")) { for (const feedback of document.querySelectorAll('.specificfeedback')) {
feedback.remove(); feedback.remove();
} }