From c7b906fb63a148ccc70e9e8fb2d73074467d7abc Mon Sep 17 00:00:00 2001 From: Anders Weinstein Date: Mon, 27 Jul 2026 20:52:24 -0400 Subject: [PATCH 1/2] replace blank foreign tags with space --- src/resources/workbook.ts | 7 ++++++ .../foreign-whitespace.xml | 1 + test/foreign-whitespace-test.ts | 24 +++++++++++++++++-- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/resources/workbook.ts b/src/resources/workbook.ts index 761594f..1d24a8a 100644 --- a/src/resources/workbook.ts +++ b/src/resources/workbook.ts @@ -66,6 +66,13 @@ export function removeDoubleGroupHeaders($: any) { export function performRestructure($: any) { failIfPresent($, ['multipanel', 'dependency']); + + $('foreign').each((_i: any, elem: any) => { + if ($(elem).children().length === 0 && $(elem).text().trim() === '') { + DOM.stripElement($, elem); + } + }); + standardContentManipulations($); liftTitle($); diff --git a/test/content/x-oli-workbook_page/foreign-whitespace.xml b/test/content/x-oli-workbook_page/foreign-whitespace.xml index 5bc4fb1..a2dc162 100644 --- a/test/content/x-oli-workbook_page/foreign-whitespace.xml +++ b/test/content/x-oli-workbook_page/foreign-whitespace.xml @@ -6,5 +6,6 @@

Il est grand. Il est grand.

+

ellui as iels elleux

diff --git a/test/foreign-whitespace-test.ts b/test/foreign-whitespace-test.ts index 06e408f..a698ab7 100644 --- a/test/foreign-whitespace-test.ts +++ b/test/foreign-whitespace-test.ts @@ -19,11 +19,31 @@ describe('foreign text whitespace', () => { true ).convert(projectSummary); - const paragraph = results[0].content.model[0].children[0]; - const foreignText = paragraph.children.map( + const [boundaryParagraph, blankForeignParagraph] = + results[0].content.model[0].children; + const foreignText = boundaryParagraph.children.map( (foreign: any) => foreign.children[0].text ); expect(foreignText).toEqual(['Il est grand. ', ' Il ', 'est ', ' grand.']); + expect(blankForeignParagraph.children).toEqual([ + { + type: 'foreign', + children: [{ text: 'ellui', strong: true }], + 'xml:lang': 'fr', + }, + { text: ' as ' }, + { + type: 'foreign', + children: [{ text: 'iels', strong: true }], + 'xml:lang': 'fr', + }, + { text: ' ' }, + { + type: 'foreign', + children: [{ text: 'elleux', strong: true }], + 'xml:lang': 'fr', + }, + ]); }); }); From 55d77be90603d31d8c970c056cf099bf69ab9886 Mon Sep 17 00:00:00 2001 From: Anders Weinstein Date: Mon, 27 Jul 2026 21:00:37 -0400 Subject: [PATCH 2/2] move foreign processing to standardContentManipulations --- src/resources/common.ts | 8 ++++++++ src/resources/workbook.ts | 7 ------- test/resources/common-test.ts | 15 +++++++++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/resources/common.ts b/src/resources/common.ts index efa186a..51f5f47 100644 --- a/src/resources/common.ts +++ b/src/resources/common.ts @@ -112,6 +112,14 @@ export function failIfHasValue( export function standardContentManipulations($: any) { failIfPresent($, ['ipa', 'bdo']); + // A language course used whitespace-only foreign elements for foreign-text spacing. + // Torus authoring removes these, so we unwrap them into ordinary space text + $('foreign').each((_i: any, elem: any) => { + if ($(elem).children().length === 0 && $(elem).text().trim() === '') { + DOM.stripElement($, elem); + } + }); + handleJmolApplets($); handleCommandButtons($); diff --git a/src/resources/workbook.ts b/src/resources/workbook.ts index 1d24a8a..761594f 100644 --- a/src/resources/workbook.ts +++ b/src/resources/workbook.ts @@ -66,13 +66,6 @@ export function removeDoubleGroupHeaders($: any) { export function performRestructure($: any) { failIfPresent($, ['multipanel', 'dependency']); - - $('foreign').each((_i: any, elem: any) => { - if ($(elem).children().length === 0 && $(elem).text().trim() === '') { - DOM.stripElement($, elem); - } - }); - standardContentManipulations($); liftTitle($); diff --git a/test/resources/common-test.ts b/test/resources/common-test.ts index fc0990a..4ba9c4d 100644 --- a/test/resources/common-test.ts +++ b/test/resources/common-test.ts @@ -75,6 +75,21 @@ describe('cdata and codeblocks', () => { expect(img2.type).toBe('img_inline'); }); + test('should unwrap whitespace-only foreign elements', () => { + const content = + '

bonjour monde

'; + const $ = cheerio.load(content, { + normalizeWhitespace: true, + xmlMode: true, + }); + + standardContentManipulations($); + + expect($.xml()).toEqual( + '

bonjour monde

' + ); + }); + test('should reorder default alternative to be first', async () => { const content = `