diff --git a/src/cloze-matching.ts b/src/cloze-matching.ts new file mode 100644 index 00000000..464e7fd1 --- /dev/null +++ b/src/cloze-matching.ts @@ -0,0 +1,27 @@ +import { SRSettings } from "./settings"; + +export function matchClozesWithinCardText( + cardText: string, + settings: SRSettings +): RegExpMatchArray[] { + const siblings: RegExpMatchArray[] = []; + if (settings.convertHighlightsToClozes) { + siblings.push(...cardText.matchAll(/==(.*?)==/gm)); + } + if (settings.convertBoldTextToClozes) { + siblings.push(...cardText.matchAll(/\*\*(.*?)\*\*/gm)); + } + if (settings.convertCurlyBracketsToClozes) { + siblings.push(...cardText.matchAll(/{{(.*?)}}/gm)); + } + + return siblings.sort((a, b) => { + if (a.index < b.index) { + return -1; + } + if (a.index > b.index) { + return 1; + } + return 0; + }); +} diff --git a/src/flashcard-modal.tsx b/src/flashcard-modal.tsx index 7b4cba98..fda459dc 100644 --- a/src/flashcard-modal.tsx +++ b/src/flashcard-modal.tsx @@ -24,6 +24,7 @@ import { } from "src/constants"; import { escapeRegexString, cyrb53 } from "src/utils"; import { t } from "src/lang/helpers"; +import { matchClozesWithinCardText } from "./cloze-matching"; export enum FlashcardModalMode { DecksList, @@ -193,6 +194,7 @@ export class FlashcardModal extends Modal { // Checks if the input textbox is in focus before processing keyboard shortcuts. if ( document.activeElement.nodeName != "TEXTAREA" && + document.activeElement.nodeName !== "INPUT" && this.mode !== FlashcardModalMode.DecksList ) { const consume = () => { @@ -413,7 +415,26 @@ export class FlashcardModal extends Modal { this.currentDeck.nextCard(this); } + private getClozeBackView(clozeInputs: string[]): string { + const { cardText } = this.currentCard; + + const clozeMatches = matchClozesWithinCardText(cardText, this.plugin.data.settings); + const correctAnswers = clozeMatches.map((match) => match[1]); + + return correctAnswers.reduce((acc, answer, index) => { + return acc.replace( + clozeMatches[index][0], + answer === clozeInputs[index] + ? `${clozeInputs[index]}` + : `[${clozeInputs[index]}${answer}]` + ); + }, this.currentCard.cardText); + } + private showAnswer(): void { + const clozeInputFields = Array.from(document.getElementsByClassName("cloze-input")); + const clozeInputs = clozeInputFields.map((clozeInput) => clozeInput.value); + this.mode = FlashcardModalMode.Back; this.answerBtn.style.display = "none"; @@ -428,6 +449,7 @@ export class FlashcardModal extends Modal { hr.setAttribute("id", "sr-hr-card-divide"); this.flashcardView.appendChild(hr); } else { + this.currentCard.back = this.getClozeBackView(clozeInputs); this.flashcardView.empty(); } diff --git a/src/main.ts b/src/main.ts index 5704b1d7..35353491 100644 --- a/src/main.ts +++ b/src/main.ts @@ -25,6 +25,7 @@ import { ReviewDeck, ReviewDeckSelectionModal } from "src/review-deck"; import { t } from "src/lang/helpers"; import { parse } from "src/parser"; import { appIcon } from "src/icons/appicon"; +import { matchClozesWithinCardText } from "./cloze-matching"; interface PluginData { settings: SRSettings; @@ -690,52 +691,19 @@ export default class SRPlugin extends Plugin { const siblingMatches: [string, string][] = []; if (cardType === CardType.Cloze) { - const siblings: RegExpMatchArray[] = []; - if (settings.convertHighlightsToClozes) { - siblings.push(...cardText.matchAll(/==(.*?)==/gm)); - } - if (settings.convertBoldTextToClozes) { - siblings.push(...cardText.matchAll(/\*\*(.*?)\*\*/gm)); - } - if (settings.convertCurlyBracketsToClozes) { - siblings.push(...cardText.matchAll(/{{(.*?)}}/gm)); - } - siblings.sort((a, b) => { - if (a.index < b.index) { - return -1; - } - if (a.index > b.index) { - return 1; - } - return 0; - }); - - let front: string, back: string; - for (const m of siblings) { - const deletionStart: number = m.index, - deletionEnd: number = deletionStart + m[0].length; - front = - cardText.substring(0, deletionStart) + - "[...]" + - cardText.substring(deletionEnd); - front = front - .replace(/==/gm, "") - .replace(/\*\*/gm, "") - .replace(/{{/gm, "") - .replace(/}}/gm, ""); - back = - cardText.substring(0, deletionStart) + - "" + - cardText.substring(deletionStart, deletionEnd) + - "" + - cardText.substring(deletionEnd); - back = back - .replace(/==/gm, "") - .replace(/\*\*/gm, "") - .replace(/{{/gm, "") - .replace(/}}/gm, ""); - siblingMatches.push([front, back]); - } + const front = matchClozesWithinCardText(cardText, this.data.settings).reduce( + (acc, sibling) => { + const inputHTML = ``; + + return acc + ? acc.replace(sibling[0], inputHTML) + : acc + sibling.input.replace(sibling[0], inputHTML); + }, + "" + ); + + // back is being created in flashcard-modal.tsx with getClozeBackView() + siblingMatches.push([front, ""]); } else { let idx: number; if (cardType === CardType.SingleLineBasic) {