diff --git a/frontend/src/ksp-task-grabber.ts b/frontend/src/ksp-task-grabber.ts index d436bd3..e0824e3 100644 --- a/frontend/src/ksp-task-grabber.ts +++ b/frontend/src/ksp-task-grabber.ts @@ -72,12 +72,17 @@ function getLocation(id: string, solution: boolean): TaskLocation { } } +function htmlEncode(text: string): string { + const p = document.createElement("p") + p.textContent = text + return p.innerHTML +} + function parseTask(startElementId: string, doc: HTMLDocument): TaskAssignmentData { const titleElement = doc.getElementById(startElementId) if (!titleElement) throw new Error(`Document does not contain ${startElementId}`) fixAllLinks(titleElement) - const elements = [] let e = titleElement @@ -90,37 +95,52 @@ function parseTask(startElementId: string, doc: HTMLDocument): TaskAssignmentDat e = e.nextElementSibling as HTMLElement + // skip first
while (e.nextElementSibling && e.tagName.toLowerCase() == "hr") e = e.nextElementSibling as HTMLElement - while (!e.classList.contains("story") && - // !e.classList.contains("clearfloat") && - e.tagName.toLowerCase() != "h3" && - e.textContent!.trim() != "Řešení" - ) - { - elements.push(e) - if (!e.nextElementSibling) break; - e = e.nextElementSibling as HTMLElement - } // hack: remove img tag that shows this task is a practical one. Some tasks have it, some don't, so we remove it for consistency - const intoImgTag = elements[0]?.firstElementChild + const intoImgTag = e.firstElementChild if (intoImgTag && intoImgTag.tagName.toLowerCase() == "img" && intoImgTag.classList.contains("leftfloat")) { intoImgTag.remove() } let r = "" - for (const e of elements) { - // hack: remove the paragraph with the matching text. Occurs in KSP-H, but is useless in this context. - if (e.textContent!.trim().replace(/\s+/g, " ") == "Toto je praktická open-data úloha. V odevzdávacím systému si necháte vygenerovat vstupy a odevzdáte příslušné výstupy. Záleží jen na vás, jak výstupy vyrobíte.") { - continue; + + copyElements: while (!e.classList.contains("story") && + // !e.classList.contains("clearfloat") && + e.tagName.toLowerCase() != "h3" && + e.textContent!.trim() != "Řešení" + ) { + + processElement: { + // hack: remove the paragraph with the matching text. Occurs in KSP-H, but is useless in this context. + if (e.textContent!.trim().replace(/\s+/g, " ") == "Toto je praktická open-data úloha. V odevzdávacím systému si necháte vygenerovat vstupy a odevzdáte příslušné výstupy. Záleží jen na vás, jak výstupy vyrobíte.") { + break processElement + } + + fixAllLinks(e) + + r += e.outerHTML + "\n" } - fixAllLinks(e) - r += e.outerHTML + "\n" + let n = e.nextSibling + copyNodes: while(true) { + if (!n) { + break copyElements + } + if (n.nodeType == Node.ELEMENT_NODE) { + e = n as HTMLElement + break copyNodes + } else if (n.nodeType == Node.TEXT_NODE && n.textContent!.trim() != "") { + r += htmlEncode(n.textContent!) + } + n = n.nextSibling + } } + return { description: r, id: id.trim(), diff --git a/frontend/src/tests/example_assignments/31-3-3.html b/frontend/src/tests/example_assignments/31-3-3.html index bad1e26..3f676aa 100644 --- a/frontend/src/tests/example_assignments/31-3-3.html +++ b/frontend/src/tests/example_assignments/31-3-3.html @@ -48,4 +48,6 @@ ANO
Poznámka: -BUG + Všimněte si, že stejnou černou figurkou (střelcem) můžeme vzít +dva bílé pěšce. Naopak bílého pěšce vpravo nahoře nemůžeme vzít ani spodní věží +(v cestě stojí střelec), ani levou věží (v cestě stojí jiné bílé figurky). diff --git a/frontend/src/tests/grabber.test.ts b/frontend/src/tests/grabber.test.ts index 663b0c0..8d128fc 100644 --- a/frontend/src/tests/grabber.test.ts +++ b/frontend/src/tests/grabber.test.ts @@ -81,7 +81,7 @@ describe('task assignment (exact match)', () => { const assignmentFiles = readdirSync(assignmentDir) for (const a of assignmentFiles) { const [_, id] = /(.*?)\.html/.exec(a)! - test(id, async () => { + test.concurrent(id, async () => { const assignment = await g.grabAssignment(id) const expected = readFileSync(resolve(assignmentDir, a)).toString() try { @@ -98,7 +98,7 @@ describe('task solution (exact match)', () => { const assignmentFiles = readdirSync(assignmentDir) for (const a of assignmentFiles) { const [_, id] = /(.*?)\.html/.exec(a)! - test(id, async () => { + test.concurrent(id, async () => { const solution = await g.grabSolution(id) const expected = readFileSync(resolve(assignmentDir, a)).toString() try {