diff --git a/package-lock.json b/package-lock.json
index 502b751..14da8e1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,9 +8,6 @@
"name": "h5p-advanced-blanks",
"version": "1.1.0",
"license": "MIT",
- "dependencies": {
- "ractive": "^1.4.2"
- },
"devDependencies": {
"@babel/cli": "^7.18.10",
"@babel/core": "^7.18.13",
@@ -19,7 +16,6 @@
"@babel/preset-env": "^7.18.10",
"@types/diff": "^5.0.3",
"@types/jquery": "^3.5.16",
- "@types/ractive": "^0.7.27",
"autoprefixer": "^10.4.14",
"ava": "^4.3.3",
"babel-loader": "^8.2.5",
@@ -1995,13 +1991,6 @@
"integrity": "sha512-DZxSZWXxFfOlx7k7Rv4LAyiMroaxa3Ly/7OOzZO8cBNho0YzAi4qlbrx8W27JGqG57IgR/6J7r+nOJWw6kcvZA==",
"dev": true
},
- "node_modules/@types/ractive": {
- "version": "0.7.27",
- "resolved": "https://registry.npmjs.org/@types/ractive/-/ractive-0.7.27.tgz",
- "integrity": "sha1-3CtGLftuz0gFIVOfpDR9wkBSxx0=",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@types/sizzle": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz",
@@ -5116,18 +5105,6 @@
}
]
},
- "node_modules/ractive": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/ractive/-/ractive-1.4.2.tgz",
- "integrity": "sha512-fv11k0fvSlFaVhUHdYLBnEZ4FdYuBB4BV4nURycrzTXNy5lWSf9B2bVHN1B6uIIBvoWEUaEVTQUlRn61W+NFeg==",
- "bin": {
- "ractive": "bin/ractive.js"
- },
- "engines": {
- "node": ">=4.0.0",
- "npm": ">=2.14.2"
- }
- },
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -7745,12 +7722,6 @@
"integrity": "sha512-DZxSZWXxFfOlx7k7Rv4LAyiMroaxa3Ly/7OOzZO8cBNho0YzAi4qlbrx8W27JGqG57IgR/6J7r+nOJWw6kcvZA==",
"dev": true
},
- "@types/ractive": {
- "version": "0.7.27",
- "resolved": "https://registry.npmjs.org/@types/ractive/-/ractive-0.7.27.tgz",
- "integrity": "sha1-3CtGLftuz0gFIVOfpDR9wkBSxx0=",
- "dev": true
- },
"@types/sizzle": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz",
@@ -10001,11 +9972,6 @@
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true
},
- "ractive": {
- "version": "1.4.2",
- "resolved": "https://registry.npmjs.org/ractive/-/ractive-1.4.2.tgz",
- "integrity": "sha512-fv11k0fvSlFaVhUHdYLBnEZ4FdYuBB4BV4nURycrzTXNy5lWSf9B2bVHN1B6uIIBvoWEUaEVTQUlRn61W+NFeg=="
- },
"randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
diff --git a/package.json b/package.json
index 0ef9b7c..46f200a 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
"private": true,
"scripts": {
"test": "npx ava",
- "build": "webpack --mode=development && cp node_modules/ractive/ractive.min.js ./dist",
+ "build": "webpack --mode=development",
"watch": "webpack --watch --mode=development"
},
"repository": {
@@ -26,7 +26,6 @@
"@babel/preset-env": "^7.18.10",
"@types/diff": "^5.0.3",
"@types/jquery": "^3.5.16",
- "@types/ractive": "^0.7.27",
"autoprefixer": "^10.4.14",
"ava": "^4.3.3",
"babel-loader": "^8.2.5",
@@ -41,7 +40,5 @@
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0"
},
- "dependencies": {
- "ractive": "^1.4.2"
- }
-}
+ "dependencies": {}
+}
\ No newline at end of file
diff --git a/src/scripts/controllers/cloze-controller.ts b/src/scripts/controllers/cloze-controller.ts
index 650a642..c5822ac 100644
--- a/src/scripts/controllers/cloze-controller.ts
+++ b/src/scripts/controllers/cloze-controller.ts
@@ -9,11 +9,7 @@ import { ClozeType, SelectAlternatives } from "../models/enums";
import { Highlight } from "../models/highlight";
import { Blank } from "../models/blank";
import { Correctness } from '../models/answer';
-
-import highlightTemplate from '../views/highlight.ractive.html';
-import blankTemplate from '../views/blank.ractive.html';
-
-import * as RactiveEventsKeys from '../../lib/ractive-events-keys';
+import { highlightTemplate, blankTemplate } from '../views/templates';
interface ScoreChanged {
(score: number, maxScore: number): void;
@@ -32,7 +28,7 @@ interface Typed {
}
interface TextChanged {
- () : void;
+ (): void;
}
export class ClozeController {
@@ -47,10 +43,6 @@ export class ClozeController {
public onTyped: Typed;
public onTextChanged: TextChanged;
- // Storage of the ractive objects that link models and views
- private highlightRactives: { [id: string]: Ractive.Ractive } = {};
- private blankRactives: { [id: string]: Ractive.Ractive } = {};
-
public get maxScore(): number {
return this.cloze.blanks.length;
}
@@ -106,18 +98,15 @@ export class ClozeController {
}
/**
- * Sets up all blanks, the cloze itself and the ractive bindings.
+ * Sets up all blanks, the cloze itself, and binds event listeners using jQuery.
* @param {HTMLElement} root
*/
initialize(root: HTMLElement, jquery: JQuery) {
this.jquery = jquery;
- this.isSelectCloze = this.settings.clozeType === ClozeType.Select ? true : false;
+ this.isSelectCloze = this.settings.clozeType === ClozeType.Select;
var blanks = this.repository.getBlanks();
- // Stop ractive debug mode
- Ractive.DEBUG = false;
-
if (this.isSelectCloze && this.settings.selectAlternatives === SelectAlternatives.All) {
for (var blank of blanks) {
let otherBlanks = blanks.filter(v => v !== blank);
@@ -131,8 +120,8 @@ export class ClozeController {
this.cloze = ClozeLoader.createCloze(this.repository.getClozeText(), blanks);
var containers = this.createAndAddContainers(root);
- containers.cloze.innerHTML = this.cloze.html;
- this.createRactiveBindings();
+ this.jquery.find(containers.cloze).html(this.cloze.html);
+ this.createBindings();
}
checkAll = () => {
@@ -145,37 +134,46 @@ export class ClozeController {
this.checkAndNotifyCompleteness();
}
- textTyped = (event, blank: Blank) => {
- blank.onTyped();
- if (this.onTyped)
- this.onTyped();
- this.refreshCloze();
- }
+ textTyped = (blank: Blank) => {
+ // Persist the current value from the input field to the Blank model
+ const newValue = this.jquery.find(`#${blank.id}`).val() as string;
+ blank.enteredText = newValue; // Store the new value in the Blank model
+
+ blank.onTyped(); // Trigger any typed event
+ if (this.onTyped) this.onTyped();
- focus = (event, blank: Blank) => {
+ // Refresh only this specific blank field
+ this.refreshCloze(blank);
+ };
+
+ focus = (blank: Blank) => {
blank.onFocused();
- this.refreshCloze();
- }
+ this.refreshCloze(blank);
+ };
- displayFeedback = (event, blank: Blank) => {
+ displayFeedback = (blank: Blank) => {
blank.onDisplayFeedback();
- this.refreshCloze();
- }
+ this.refreshCloze(blank);
+ };
- showHint = (event, blank: Blank) => {
+ showHint = (blank: Blank) => {
this.cloze.hideAllHighlights();
blank.showHint();
- this.refreshCloze();
- }
+ this.refreshCloze(blank);
+ };
- requestCloseTooltip = (event, blank: Blank) => {
+ requestCloseTooltip = (blank: Blank) => {
blank.removeTooltip();
- this.refreshCloze();
- this.jquery.find("#" + blank.id).focus();
- }
+ this.refreshCloze(blank);
+ this.jquery.find(`#${blank.id}`).focus();
+ };
+
+ checkBlank = (blank: Blank, cause: string) => {
+ // Persist the current value before checking
+ const newValue = this.jquery.find(`#${blank.id}`).val() as string;
+ blank.enteredText = newValue; // Store the new value in the Blank model
- checkBlank = (event, blank: Blank, cause: string) => {
- if ((cause === 'blur' || cause === 'change')) {
+ if (cause === 'blur' || cause === 'change') {
blank.lostFocus();
}
@@ -184,19 +182,17 @@ export class ClozeController {
}
if (this.settings.autoCheck) {
- if (!blank.enteredText || blank.enteredText === "")
- return;
+ if (!blank.enteredText || blank.enteredText === "") return;
this.cloze.hideAllHighlights();
blank.evaluateAttempt(false);
this.checkAndNotifyCompleteness();
- this.refreshCloze();
+ this.refreshCloze(blank); // Refresh only this blank field
this.onAutoChecked();
}
if ((cause === 'enter')
- && ((this.settings.autoCheck && blank.isCorrect && !this.isSolved)
- || !this.settings.autoCheck)) {
- // move to next blank
+ && ((this.settings.autoCheck && blank.isCorrect && !this.cloze.isSolved) || !this.settings.autoCheck)
+ ) {
var index = this.cloze.blanks.indexOf(blank);
var nextId;
while (index < this.cloze.blanks.length - 1 && !nextId) {
@@ -204,11 +200,11 @@ export class ClozeController {
if (!this.cloze.blanks[index].isCorrect)
nextId = this.cloze.blanks[index].id;
}
-
- if (nextId)
+ if (nextId) {
this.jquery.find("#" + nextId).focus();
+ }
}
- }
+ };
reset = () => {
this.cloze.reset();
@@ -235,64 +231,60 @@ export class ClozeController {
};
}
- private createHighlightBinding(highlight: Highlight) {
- this.highlightRactives[highlight.id] = new Ractive({
- el: '#container_' + highlight.id,
- template: highlightTemplate,
- data: {
- object: highlight
- }
+ private createBindings() {
+ this.cloze.highlights.forEach((highlight) => {
+ this.bindHighlight(highlight);
});
- }
- private createBlankBinding(blank: Blank) {
- var ractive = new Ractive({
- el: '#container_' + blank.id,
- template: blankTemplate,
- data: {
- isSelectCloze: this.isSelectCloze,
- blank: blank
- },
- events: {
- enter: RactiveEventsKeys.enter,
- escape: RactiveEventsKeys.escape,
- anykey: RactiveEventsKeys.anykey
- }
+ this.cloze.blanks.forEach((blank) => {
+ this.bindBlank(blank);
});
- ractive.on("checkBlank", this.checkBlank);
- ractive.on("showHint", this.showHint);
- ractive.on("textTyped", this.textTyped);
- ractive.on("textChanged", this.onTextChanged);
- ractive.on("closeMessage", this.requestCloseTooltip);
- ractive.on("focus", this.focus);
- ractive.on("displayFeedback", this.displayFeedback);
-
- this.blankRactives[blank.id] = ractive;
}
- private createRactiveBindings() {
- for (var highlight of this.cloze.highlights) {
- this.createHighlightBinding(highlight);
- }
+ private bindHighlight(highlight: Highlight) {
+ const highlightContainer = this.jquery.find(`#container_${highlight.id}`);
+ highlightContainer.html(highlightTemplate(highlight.id, highlight.isHighlighted, highlight.text));
+ }
- for (var blank of this.cloze.blanks) {
- this.createBlankBinding(blank);
+ private bindBlank(blank: Blank) {
+ const blankContainer = this.jquery.find(`#container_${blank.id}`);
+ blankContainer.html(blankTemplate(blank, this.isSelectCloze));
+
+ const blankInput = blankContainer.find(`#${blank.id}`);
+ if (this.isSelectCloze) {
+ blankInput.on('change', () => this.checkBlank(blank, 'change'));
+ } else {
+ blankInput.on('keyup', () => this.textTyped(blank));
+ blankInput.on('blur', () => this.checkBlank(blank, 'blur'));
+ blankInput.on('focus', () => this.focus(blank));
}
}
- /**
- * Updates all views of highlights and blanks. Can be called when a model
- * was changed
- */
- private refreshCloze() {
- for (var highlight of this.cloze.highlights) {
- var highlightRactive = this.highlightRactives[highlight.id];
- highlightRactive.set("object", highlight);
- }
+ private createSelectOptions(blank: Blank) {
+ let optionsHTML = '';
+ blank.choices.forEach((choice) => {
+ optionsHTML += ``;
+ });
+ return ``;
+ }
- for (var blank of this.cloze.blanks) {
- var blankRactive = this.blankRactives[blank.id];
- blankRactive.set("blank", blank);
+ // Modify refreshCloze to update only specific blanks if needed
+ private refreshCloze(blank?: Blank) {
+ if (blank) {
+ // Update only the specific blank input field
+ const blankContainer = this.jquery.find(`#container_${blank.id}`);
+ blankContainer.html(blankTemplate(blank, this.isSelectCloze));
+ } else {
+ // Update all blanks and highlights (if needed)
+ this.cloze.highlights.forEach((highlight) => {
+ const highlightContainer = this.jquery.find(`#container_${highlight.id}`);
+ highlightContainer.html(highlightTemplate(highlight.id, highlight.isHighlighted, highlight.text));
+ });
+
+ this.cloze.blanks.forEach((blank) => {
+ const blankContainer = this.jquery.find(`#container_${blank.id}`);
+ blankContainer.html(blankTemplate(blank, this.isSelectCloze));
+ });
}
}
@@ -325,10 +317,9 @@ export class ClozeController {
if (!this.cloze || this.cloze.blanks.length === 0)
return [[]];
let result = [];
- for (var blank of this.cloze.blanks) {
+ this.cloze.blanks.forEach((blank) => {
result.push(blank.getCorrectAnswers());
- }
-
+ });
return result;
}
}
diff --git a/src/scripts/views/templates.ts b/src/scripts/views/templates.ts
new file mode 100644
index 0000000..4bb21d6
--- /dev/null
+++ b/src/scripts/views/templates.ts
@@ -0,0 +1,46 @@
+export const highlightTemplate = (id, isHighlighted, text) => `
+ ${text}
+`;
+
+export const blankTemplate = (blank, isSelectCloze) => `
+
+ ${isSelectCloze ? `
+
+
+
+
+ ` : `
+
+
+ ${blank.hasHint ? `
+
+
+ ` : ''}
+
+ `}
+
+`;
+
diff --git a/webpack.config.js b/webpack.config.js
index 7539175..d392aea 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -26,20 +26,11 @@ module.exports = (env, argv) => {
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
- },
- {
- test: /\.ractive.html$/,
- use: {
- loader: 'html-loader',
- options: {
- minimize: false
- }
- }
}
]
},
resolve: {
- extensions: [".tsx", ".ts", ".js", ".ractive.html"]
+ extensions: [".tsx", ".ts", ".js"]
}
};