11 Commits

Author SHA1 Message Date
yoannchb-pro ea57a315b7 Merge pull request #36 from yoannchb-pro/dev
Dev
2024-05-05 14:05:38 -04:00
yoannchb-pro 8303dffe99 Merge branch 'main' into dev 2024-05-05 14:01:19 -04:00
yoannchb-pro ac2ca56a54 Fixed bug and added atto handler 2024-05-05 14:00:47 -04:00
yoannchb-pro 3dcc6dc6eb build 2024-05-05 14:00:32 -04:00
yoannchb-pro f5679dd825 doc update 2024-05-05 14:00:17 -04:00
yoannchb-pro dcfe8f3320 removed get visible text because not working 2024-05-04 00:55:26 -04:00
yoannchb-pro ea5cd3763d Update TODO.md 2024-05-04 00:38:18 -04:00
yoannchb-pro 71f43590db code from 3 to 2 chars 2024-05-04 00:33:46 -04:00
yoannchb-pro eb58ac44ca fixed backspace for text input, number input and contenteditable. Also moved to v1.1.1 2024-04-19 15:17:00 -04:00
yoannchb-pro b60b3106a0 Update TODO.md 2024-03-25 20:23:03 -04:00
yoannchb-pro 292d00e664 fixing issues templates 2024-03-21 20:10:28 -04:00
21 changed files with 184 additions and 30 deletions
+8
View File
@@ -1,3 +1,11 @@
---
name: Bug Report
about: Report a bug or issue with the software.
title: '[BUG]'
labels: 'bug'
assignees: ''
---
## Bug Report
**Description of the Bug**
@@ -1,3 +1,11 @@
---
name: Feature Request
about: Suggest a new feature or enhancement for the project.
title: '[FEATURE]'
labels: 'enhancement'
assignees: ''
---
## Feature Request
**Description of the Feature**
+8
View File
@@ -1,3 +1,11 @@
---
name: Help Request
about: Use this template to request help or support.
title: '[HELP]'
labels: help
assignees: ''
---
## Help Request
**Issue Summary**
+5
View File
@@ -1,5 +1,10 @@
# CHANGELOG
## v1.1.1
- Bugs correction
- Support for Atto editor
## v1.1.0
- Bugs correction
+8 -3
View File
@@ -2,7 +2,7 @@
href="https://www.flaticon.com/free-icons/mortarboard" target="_blank" rel="noopener noreferrer"
title="Mortarboard icons created by itim2101 - Flaticon" ><img src="./extension/icon.png" alt="Mortarboard icons created by itim2101 - Flaticon" width="150" style="display:block; margin:auto;"></a></p>
# MoodleGPT 1.1.0
# MoodleGPT 1.1.1
This extension allows you to hide CHAT-GPT in a Moodle quiz. You just need to click on the question you want to solve, and CHAT-GPT will automatically provide the answer. However, one needs to be careful because as we know, CHAT-GPT can make errors especially in calculations.
@@ -12,7 +12,7 @@ Find the extension on the Chrome Webstore right [here](https://chrome.google.com
## Summary
- [MoodleGPT 1.1.0](#moodlegpt-110)
- [MoodleGPT 1.1.1](#moodlegpt-111)
- [Chrome Webstore](#chrome-webstore)
- [Summary](#summary)
- [Disclaimer !](#disclaimer-)
@@ -32,6 +32,7 @@ Find the extension on the Chrome Webstore right [here](https://chrome.google.com
- [True or false](#true-or-false)
- [Number](#number)
- [Text](#text)
- [Atto](#atto)
- [What about if the question can't be autocompleted ?](#what-about-if-the-question-cant-be-autocompleted-)
- [Test](#test)
- [Beta version with advanced features](#beta-version-with-advanced-features)
@@ -145,6 +146,10 @@ Person 2 | Yann  | 19/01/2000 | no
![Text](./assets/text.gif)
### Atto
![Atto](./assets/atto.gif)
## What about if the question can't be autocompleted ?
To know if the answer has been copied to the clipboard, you can look at the title of the page which will become <b>"Copied to clipboard"</b> for 3 seconds if `Title indication` is on.
@@ -153,7 +158,7 @@ To know if the answer has been copied to the clipboard, you can look at the titl
## Test
- <b>Solution 1</b>: Go on [this moodle test page](https://school.moodledemo.net/login/index.php) (username: `student`, password: `moodle`) and choose any quiz.
- <b>Solution 1</b>: Go on this [moodle demo page](https://moodle.org/demo).
- <b>Solution 2</b>: Run the `index.html` file located in the `test/fake-moodle` folder.
## Beta version with advanced features
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "MoodleGPT",
"version": "1.1.0",
"version": "1.1.1",
"description": "Hidden chat-gpt for your moodle quiz",
"permissions": ["storage"],
"action": {
+2 -2
View File
@@ -32,9 +32,9 @@ saveBtn.addEventListener('click', function () {
return;
}
if (code.length > 0 && code.length < 3) {
if (code.length > 0 && code.length < 2) {
showMessage({
msg: 'The code should at least contain 3 characters',
msg: 'The code should at least contain 2 characters',
error: true
});
return;
+1 -1
View File
@@ -1,6 +1,6 @@
'use strict';
const CURRENT_VERSION = '1.1.0';
const CURRENT_VERSION = '1.1.1';
const versionDisplay = document.querySelector('#version');
/**
+3 -2
View File
@@ -1,9 +1,10 @@
{
"name": "moodlegpt",
"version": "1.1.0",
"version": "1.1.1",
"description": "This extension allows you to hide CHAT-GPT in a Moodle quiz.",
"scripts": {
"build": "npm run prettier && npm run lint && rollup -c",
"build": "npm run prettier && npm run lint && npm run fastBuild",
"fastBuild": "rollup -c",
"lint": "eslint . --ext .ts",
"prettier": "prettier --write ."
},
+1 -1
View File
@@ -58,7 +58,7 @@ function setUpMoodleGpt(config: Config) {
const inputTypeQuery = ['checkbox', 'radio', 'text', 'number']
.map(e => `input[type="${e}"]`)
.join(',');
const inputQuery = inputTypeQuery + ', textarea, select, [contenteditable]';
const inputQuery = inputTypeQuery + ', textarea, select, [contenteditable], .qtype_essay_editor';
const forms = document.querySelectorAll('.formulation');
// For each form we inject a function on the queqtion
+4
View File
@@ -16,6 +16,10 @@ function createAndNormalizeQuestion(questionContainer: HTMLElement) {
for (const useless of accesshideElements) {
question = question.replace(useless.innerText, '');
}
const attoText = questionContainer.querySelector('.qtype_essay_editor');
if (attoText) {
question = question.replace((attoText as HTMLElement).innerText, '');
}
// Make tables more readable for chat-gpt
const tables: NodeListOf<HTMLTableElement> = questionContainer.querySelectorAll('.qtext table');
+2
View File
@@ -7,6 +7,7 @@ import handleRadio from '@core/questions/radio';
import handleCheckbox from '@core/questions/checkbox';
import handleSelect from '@core/questions/select';
import handleTextbox from '@core/questions/textbox';
import handleAtto from '@core/questions/atto';
type Props = {
config: Config;
@@ -26,6 +27,7 @@ function autoCompleteMode(props: Props) {
if (!props.config.infinite) props.removeListener();
const handlers = [
handleAtto,
handleContentEditable,
handleTextbox,
handleNumber,
+67
View File
@@ -0,0 +1,67 @@
import type Config from '@typing/config';
import type GPTAnswer from '@typing/gpt-answer';
/**
* Hanlde atto editor
* See: https://docs.moodle.org/404/en/Atto_editor#Atto_accessibility
* @param config
* @param inputList
* @param gptAnswer
* @returns
*/
function handleAtto(
config: Config,
inputList: NodeListOf<HTMLElement>,
gptAnswer: GPTAnswer
): boolean {
const input = inputList[0];
if (!input.classList.contains('qtype_essay_editor')) {
return false;
}
const iframe = input.querySelector('iframe');
if (!iframe || !iframe.contentDocument || !iframe.contentDocument.body || !iframe.contentWindow) {
return false;
}
const iframeBody = iframe.contentDocument.body;
const textContainer = iframeBody.querySelector('p');
if (!textContainer) return false;
if (config.typing) {
let index = 0;
const eventHandler = function (event: KeyboardEvent) {
event.preventDefault();
if (event.key === 'Backspace' || index >= gptAnswer.response.length) {
iframe.contentWindow!.removeEventListener('keydown', eventHandler);
return;
}
// Append text one character at a time
const textNode = document.createTextNode(gptAnswer.response.charAt(index++));
textContainer.appendChild(textNode);
// Move the cursor after the last character
const range = iframe.contentDocument!.createRange();
range.selectNodeContents(textContainer);
range.collapse(false); // Collapse the range to the end point
const selection = iframe.contentWindow!.getSelection();
if (selection) {
selection.removeAllRanges();
selection.addRange(range);
}
iframe.contentWindow!.focus(); // Focus the iframe window to see cursor
};
iframe.contentWindow.addEventListener('keydown', eventHandler);
} else {
textContainer.textContent += gptAnswer.response;
}
return true;
}
export default handleAtto;
+16 -4
View File
@@ -26,11 +26,13 @@ function handleCheckbox(
const possibleAnswers = Array.from(inputList)
.map(inp => ({
element: inp,
element: inp as HTMLInputElement,
value: normalizeText(inp?.parentElement?.textContent ?? '')
}))
.filter(obj => obj.value !== '');
// Find the best answers elements
const correctElements: Set<HTMLInputElement> = new Set();
for (const correct of corrects) {
const bestAnswer = pickBestReponse(correct, possibleAnswers);
@@ -38,13 +40,23 @@ function handleCheckbox(
Logs.bestAnswer(bestAnswer.value, bestAnswer.similarity);
}
const correctInput = bestAnswer.element as HTMLInputElement;
correctElements.add(bestAnswer.element as HTMLInputElement);
}
// Check if it should be checked or not
for (const element of possibleAnswers.map(e => e.element)) {
const needAction =
(element.checked && !correctElements.has(element)) ||
(!element.checked && correctElements.has(element));
const action = () => needAction && element.click();
if (config.mouseover) {
correctInput.addEventListener('mouseover', () => correctInput.click(), {
element.addEventListener('mouseover', action, {
once: true
});
} else {
correctInput.click();
action();
}
}
+17 -5
View File
@@ -1,6 +1,11 @@
import type Config from '@typing/config';
import type GPTAnswer from '@typing/gpt-answer';
function isContentEditable(element: HTMLElement) {
const contenteditable = element.getAttribute('contenteditable');
return typeof contenteditable === 'string' && contenteditable !== 'false';
}
/**
* Hanlde contenteditable elements
* @param config
@@ -17,17 +22,22 @@ function handleContentEditable(
if (
inputList.length !== 1 || // for now we don't handle many input for editable textcontent
input.getAttribute('contenteditable') !== 'true'
!isContentEditable(input)
) {
return false;
}
if (config.typing) {
let index = 0;
input.addEventListener('keydown', function (event: KeyboardEvent) {
if (event.key === 'Backspace') index = gptAnswer.response.length + 1;
if (index > gptAnswer.response.length) return;
const eventHandler = function (event: KeyboardEvent) {
event.preventDefault();
if (event.key === 'Backspace' || index >= gptAnswer.response.length) {
input.removeEventListener('keydown', eventHandler);
return;
}
input.textContent = gptAnswer.response.slice(0, ++index);
// Put the cursor at the end of the typed text
@@ -40,7 +50,9 @@ function handleContentEditable(
selection.removeAllRanges();
selection.addRange(range);
}
});
};
input.addEventListener('keydown', eventHandler);
} else {
input.textContent = gptAnswer.response;
}
+11 -4
View File
@@ -28,13 +28,20 @@ function handleNumber(
if (config.typing) {
let index = 0;
input.addEventListener('keydown', function (event: Event) {
const eventHanlder = function (event: Event) {
event.preventDefault();
if ((<KeyboardEvent>event).key === 'Backspace') index = number.length + 1;
if (index > number.length) return;
if ((<KeyboardEvent>event).key === 'Backspace' || index >= number.length) {
input.removeEventListener('keydown', eventHanlder);
return;
}
if (number.slice(index, index + 1) === '.') ++index;
input.value = number.slice(0, ++index);
});
};
input.addEventListener('keydown', eventHanlder);
} else {
input.value = number;
}
+10 -5
View File
@@ -24,14 +24,19 @@ function handleTextbox(
if (config.typing) {
let index = 0;
input.addEventListener('keydown', function (event: Event) {
const eventHandler = function (event: Event) {
event.preventDefault();
if ((<KeyboardEvent>event).key === 'Backspace') {
index = gptAnswer.response.length + 1;
if ((<KeyboardEvent>event).key === 'Backspace' || index >= gptAnswer.response.length) {
input.removeEventListener('keydown', eventHandler);
return;
}
if (index > gptAnswer.response.length) return;
input.value = gptAnswer.response.slice(0, ++index);
});
};
input.addEventListener('keydown', eventHandler);
} else {
input.value = gptAnswer.response;
}
+10
View File
@@ -104,6 +104,16 @@
</div>
</section>
<!-- Textbox -->
<section class="formulation">
<div class="qtext">
<p>What is the name of the USA president ?</p>
</div>
<div>
<input type="text" />
</div>
</section>
<!-- Select -->
<section class="formulation">
<div class="qtext">