Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ thead {
border-bottom: 3px solid black;
}

thead th {
position: relative;
}

tbody tr:nth-child(even) {
background-color: #ededed;
}
Expand All @@ -15,18 +19,18 @@ tbody tr:nth-child(odd) {
background-color: #f6f6f6;
}

thead tr th:has(+th[class="formula"]),
tbody tr th:last-of-type,
thead tr th[class="formula"]:has(+th),
tbody tr td:first-of-type:has(+td) {
thead th:has(+th[class="formula"]),
tbody th:last-of-type,
thead th[class="formula"]:has(+th),
tbody td:first-of-type:has(+td) {
border-right: 3px solid black;
}

thead .formula-input-wrapper input {
width: 8rem !important;
}

tbody .formula-input-wrapper input {
tbody input {
width: 3.5rem !important;
text-align: center;
}
Expand All @@ -36,6 +40,20 @@ td, th {
padding: 0.5rem;
height: 3.5rem;
text-align: center;
min-width: 5rem;
}

thead th button {
position: absolute;
top: 0;
right: 0;
border-top: none;
border-right: none;
border-left: 1px solid black;
border-bottom: 1px solid black;
background: transparent;
cursor: pointer;
font-size: 12px;
}

.bit, .variable {
Expand Down
5 changes: 1 addition & 4 deletions templates/formulation.xhtml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,7 @@

<!-- Input field for the results of the formula. -->
<td>
<div class="formula-input-wrapper">
<input type="text" name="{{ get_result_input_name(row) }}" maxlength="1" pattern="0|1" class="formula-input" style="display: none" qpy:correct-response="{{ get_correct_response(row) }}" />
<span class="formula-display" style="display: none;" />
</div>
<input type="text" name="{{ get_result_input_name(row) }}" maxlength="1" pattern="0|1" class="formula-input" qpy:correct-response="{{ get_correct_response(row) }}" />
</td>
</tr>
{% endfor %}
Expand Down
2 changes: 1 addition & 1 deletion ts/.prettierignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ts/parser/generated/**/*
src/parser/generated/**/*
2 changes: 0 additions & 2 deletions ts/src/formula_input_element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,6 @@ export class FormulaInput {
this.#inputElement.addEventListener("input", () => {
this.setInputValidity("valid");
});

this.#displayElement.addEventListener("click", () => this.viewInputField());
}
}

Expand Down
129 changes: 67 additions & 62 deletions ts/src/intermediate_formulas.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,47 @@
import { OutputFormat } from "./parser/parser";
import { createFormulaInput, type FormulaInput } from "./formula_input_element";
import { createFormulaInput } from "./formula_input_element";

const tableElement = document.querySelector("table")!;

function createIntermediateFormulaHeader(
formula: string,
formulaId: number,
format: OutputFormat,
data: Record<string, unknown>,
enabled: boolean,
): HTMLTableCellElement {
const cellElement = document.createElement("th");

const formulaInput = createFormulaInput(formula, format, enabled);
cellElement.appendChild(formulaInput.element);

if (enabled) {
formulaInput.inputElement.addEventListener("change", () => {
const formulas = (data["intermediate-formulas"] ?? {}) as Record<string, string>;
formulas[formulaId] = formulaInput.getInput();
data["intermediate-formulas"] = formulas;
});

const editButton = document.createElement("button");
editButton.tabIndex = -1;
editButton.innerHTML = "✏️";

editButton.addEventListener("mousedown", (e) => {
// This prevents the edit button to trigger the blur event of the input element.
e.preventDefault();
});

editButton.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
formulaInput.viewInputField();
});
cellElement.appendChild(editButton);
}

return cellElement;
}

export function setupButtonToAddIntermediateFormula(attempt: object, total_rows: number, format: OutputFormat) {
// @ts-expect-error Attempt object definition is not here.
const data = attempt.data as Record<string, unknown>;
Expand All @@ -15,31 +54,22 @@ export function setupButtonToAddIntermediateFormula(attempt: object, total_rows:
const rows = tableElement.querySelectorAll("tr");

for (const [index, row] of rows.entries()) {
let cellElement, intermediateFormulaInput;
let cellElement;
if (index === 0) {
// The first row of the table contains the formulas.
cellElement = document.createElement("th");
intermediateFormulaInput = createIntermediateFormulaInput(
cellElement = createIntermediateFormulaHeader(
"",
intermediateFormulaId,
format,
true,
data,
// @ts-expect-error Attempt object definition is not here.
!attempt.readOnly,
);
initValues(data, intermediateFormulaId, total_rows);
} else {
cellElement = document.createElement("td");
intermediateFormulaInput = createIntermediateResultInput(
"",
intermediateFormulaId,
index - 1,
format,
true,
data,
);
cellElement = createIntermediateResultInput("", intermediateFormulaId, index - 1, true, data);
}

cellElement.appendChild(intermediateFormulaInput.element);
row.appendChild(cellElement);
}

Expand All @@ -58,7 +88,7 @@ function initValues(data: Record<string, unknown>, intermediateFormulaId: number
data["intermediate-results"] = intermediateResults;
}

export function viewExistingIntermediateFormulas(format: OutputFormat, attempt: object) {
export function initIntermediateFormulas(format: OutputFormat, attempt: object) {
// @ts-expect-error Attempt object definition is not here.
const data = attempt.data as Record<string, unknown>;
// @ts-expect-error Attempt object definition is not here.
Expand All @@ -69,83 +99,58 @@ export function viewExistingIntermediateFormulas(format: OutputFormat, attempt:
const rows = tableElement.querySelectorAll("tr");

for (const [intermediateFormulaId, intermediateFormula] of Object.entries(intermediateFormulas)) {
const cellElement = document.createElement("th");
const intermediateFormulaInput = createIntermediateFormulaInput(
const cellElement = createIntermediateFormulaHeader(
intermediateFormula,
parseInt(intermediateFormulaId),
format,
isActive,
data,
isActive,
);
cellElement.appendChild(intermediateFormulaInput.element);
rows[0].appendChild(cellElement);

for (const [index, intermediateResult] of Object.entries(intermediateResults[intermediateFormulaId])) {
const cellElement = document.createElement("td");
const intermediateResultInput = createIntermediateResultInput(
const cellElement = createIntermediateResultInput(
intermediateResult,
parseInt(intermediateFormulaId),
parseInt(index),
format,
isActive,
data,
);
cellElement.appendChild(intermediateResultInput.element);
rows[parseInt(index) + 1].appendChild(cellElement);
}
}
}

function createIntermediateFormulaInput(
formula: string,
intermediateFormulaId: number,
format: OutputFormat,
enabled: boolean,
data: Record<string, unknown>,
): FormulaInput {
const formulaInputElement = createFormulaInput(formula, format, enabled);
formulaInputElement.inputElement.setAttribute("data-intermediate-formula", `${intermediateFormulaId}`);

const intermediateFormulas = (data["intermediate-formulas"] ?? {}) as Record<string, string>;

if (enabled) {
formulaInputElement.inputElement.addEventListener("change", () => {
const formulaId = formulaInputElement.inputElement.getAttribute("data-intermediate-formula")!;
intermediateFormulas[formulaId] = formulaInputElement.getInput();
data["intermediate-formulas"] = intermediateFormulas;
});
}

return formulaInputElement;
}

function createIntermediateResultInput(
formula: string,
intermediateFormulaId: number,
row: number,
format: OutputFormat,
enabled: boolean,
data: Record<string, unknown>,
): FormulaInput {
const resultInputElement = createFormulaInput(formula, format, enabled);
resultInputElement.inputElement.maxLength = 1;
resultInputElement.inputElement.pattern = "0|1";
resultInputElement.inputElement.setAttribute("data-intermediate-result", `${row}`);
resultInputElement.inputElement.setAttribute("data-intermediate-result-belongs-to", `${intermediateFormulaId}`);
): HTMLTableCellElement {
const cellElement = document.createElement("td");
// Create the input element for the result.
const resultInputElement = document.createElement("input");
resultInputElement.value = formula;
resultInputElement.maxLength = 1;
resultInputElement.pattern = "0|1";
resultInputElement.disabled = !enabled;

const intermediateResults = (data["intermediate-results"] ?? {}) as Record<string, string[]>;

if (enabled) {
resultInputElement.inputElement.addEventListener("change", () => {
const resultId = parseInt(resultInputElement.inputElement.getAttribute("data-intermediate-result")!);
const belongsToFormulaId = resultInputElement.inputElement.getAttribute(
"data-intermediate-result-belongs-to",
)!;

intermediateResults[belongsToFormulaId][resultId] = resultInputElement.getInput();
resultInputElement.addEventListener("change", () => {
intermediateResults[intermediateFormulaId][row] = resultInputElement.value;
data["intermediate-results"] = intermediateResults;
});

resultInputElement.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
event.preventDefault();
}
});
}

return resultInputElement;
cellElement.appendChild(resultInputElement);
return cellElement;
}
21 changes: 9 additions & 12 deletions ts/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { type OutputFormat } from "./parser/parser.js";
import { renderBora } from "./utils";
import { FormulaInput } from "./formula_input_element";
import { setupButtonToAddIntermediateFormula, viewExistingIntermediateFormulas } from "./intermediate_formulas";
import { initIntermediateFormulas, setupButtonToAddIntermediateFormula } from "./intermediate_formulas";

export function init(attempt: object, [format, total_rows]: [OutputFormat, number]) {
initFormulaElements(format);
initFormulaInputElements(format, attempt);
viewExistingIntermediateFormulas(format, attempt);
initIntermediateFormulas(format, attempt);
// @ts-expect-error Attempt object definition is not here.
if (!attempt.readOnly) {
setupButtonToAddIntermediateFormula(attempt, total_rows, format);
Expand All @@ -18,14 +16,13 @@ function initFormulaElements(format: OutputFormat) {
for (const formulaElement of formulaElements) {
renderBora(formulaElement.textContent!, formulaElement, format);
}
}

function initFormulaInputElements(format: OutputFormat, attempt: object) {
const wrapperElements: NodeListOf<HTMLDivElement> = document.querySelectorAll(".formula-input-wrapper");
for (const wrapperElement of wrapperElements) {
const inputElement: HTMLInputElement = wrapperElement.querySelector(".formula-input")!;
const displayElement: HTMLSpanElement = wrapperElement.querySelector(".formula-display")!;
// @ts-expect-error Attempt object definition is not here.
new FormulaInput(wrapperElement, inputElement, displayElement, format, !attempt.readOnly);
const formulaInputElements = document.querySelectorAll(".formula-input") as NodeListOf<HTMLInputElement>;
for (const formulaInputElement of formulaInputElements) {
formulaInputElement.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
event.preventDefault();
}
});
}
}
4 changes: 2 additions & 2 deletions ts/src/mathjax/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export abstract class BaseMathJaxHelper {
return this.loadPromise;
}

return this.loadPromise = new Promise((resolve, reject) => {
return (this.loadPromise = new Promise((resolve, reject) => {
const script = document.createElement("script");
script.type = "text/javascript";
script.src = this.cdnUrl;
Expand All @@ -40,7 +40,7 @@ export abstract class BaseMathJaxHelper {
script.onerror = () => reject(new Error(`Failed to load ${this.cdnUrl}.`));

document.head.appendChild(script);
});
}));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion ts/src/mathjax/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function renderLaTeX(element: Element, inline: boolean = true): Promise<v
}
} else {
// We should in theory switch to MathJax 4 as the default, but we might need to change our UI.
mathJaxHelper = new MathJax3Helper();
mathJaxHelper = new MathJax4Helper();
}
}

Expand Down
4 changes: 2 additions & 2 deletions ts/src/mathjax/v3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class MathJax3Helper extends BaseMathJaxHelper {
protected _render(element: Element, inline: boolean): Promise<void> {
// We need to manually chain every render call.
// https://docs.mathjax.org/en/v3.2/web/typeset.html#handling-asynchronous-typesetting
return this.queue = this.queue.then(() => {
return (this.queue = this.queue.then(() => {
// Get the delimiters.
const inlineDelimiters = this.mathjax.config.tex?.inlineMath?.[0] ?? ["\\(", "\\)"];
const displayDelimiters = this.mathjax.config.tex?.displayMath?.[0] ?? ["\\[", "\\]"];
Expand All @@ -48,6 +48,6 @@ export class MathJax3Helper extends BaseMathJaxHelper {

// Perform the rendering.
return this.mathjax.typesetPromise([element]);
});
}));
}
}
2 changes: 1 addition & 1 deletion ts/src/mathjax/v4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ export class MathJax4Helper extends BaseMathJaxHelper {
// Add the delimiters.
element.textContent = `${openingDelimiter} ${element.textContent} ${closingDelimiter}`;

return this.mathjax.typesetPromise([element])
return this.mathjax.typesetPromise([element]);
}
}