From 25e4e39953b8d2fafc6685bab678bbd7d0545f1e Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Mon, 11 Sep 2023 18:47:04 +0400 Subject: [PATCH] fix: preformatted content being re-formatted (#376) The issue had to do with how Firefox handles pasting newlines inside a
 tag (TinyMCE's editor works via contenteditable) and
fast-xml-parser's parsing. In Firefox, newlines are converted to 
when pasted, while Chrome preserves them. The parser by default trims spaces in text nodes. In Firefox, the parser creates individual text nodes between the
elements, and those have leading spaces in the example. In Chrome, there are no
elements and the entire content is a single text node as-is. Setting trimValues to false disables the trimming and resolves this issue in Firefox. While investigating this, I noticed the builder also mishandles
tags emitted by the editor, converting them to

. The unpairedTags option in the builder ensures they are output correctly as a single tag, and setting suppressUnpairedNode to false ensures that single tag is
rather than
to remain XML compatible. While trying to resolve this, I was looking into the paste plugin in TinyMCE. It changes the behavior of pasting, making it more consistent between Chrome and Firefox (i.e. both emit
) and is incorporated into TinyMCE 6 core. Unfortunately, it seems to mangle pasting inside a
 tag by inserting
redundant nbsp characters (tinymce/tinymce#9017). TinyMCE 6 also outputs 
rather than
- adding unpairedTags to the parser options is meant to handle this, but it does not seem to behave entirely correct (NaturalIntelligence/fast-xml-parser#609). These should be kept in mind if/when upgrading to TinyMCE 6 (the different behaviors can be seen easily at https://fiddle.tiny.cloud). --- .../ProblemEditor/data/ReactStateOLXParser.js | 11 ++++++++++- .../ProblemEditor/data/ReactStateOLXParser.test.js | 10 ++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/editors/containers/ProblemEditor/data/ReactStateOLXParser.js b/src/editors/containers/ProblemEditor/data/ReactStateOLXParser.js index d6bfba43c..4532308b0 100644 --- a/src/editors/containers/ProblemEditor/data/ReactStateOLXParser.js +++ b/src/editors/containers/ProblemEditor/data/ReactStateOLXParser.js @@ -13,17 +13,26 @@ class ReactStateOLXParser { hex: false, }, preserveOrder: true, + // Ensure whitespace inside
 tags is preserved
+      trimValues: false,
+      // Parse 
correctly + unpairedTags: ['br'], }; const richTextBuilderOptions = { ignoreAttributes: false, attributeNamePrefix: '@_', suppressBooleanAttributes: false, - format: true, + // Avoid formatting as it adds unwanted newlines and whitespace, + // breaking
 tags
+      format: false,
       numberParseOptions: {
         leadingZeros: false,
         hex: false,
       },
       preserveOrder: true,
+      unpairedTags: ['br'],
+      // Output 
rather than
+ suppressUnpairedNode: false, }; this.richTextParser = new XMLParser(richTextParserOptions); diff --git a/src/editors/containers/ProblemEditor/data/ReactStateOLXParser.test.js b/src/editors/containers/ProblemEditor/data/ReactStateOLXParser.test.js index 34c2e7b0e..8c09ceccc 100644 --- a/src/editors/containers/ProblemEditor/data/ReactStateOLXParser.test.js +++ b/src/editors/containers/ProblemEditor/data/ReactStateOLXParser.test.js @@ -104,5 +104,15 @@ describe('Check React State OLXParser problem', () => { const buildOLX = stateParser.buildOLX(); expect(buildOLX.replace(/\s/g, '')).toEqual(numberParseTestOLX.buildOLX.replace(/\s/g, '')); }); + test('correctly preserves whitespace inside pre tags', () => { + const stateParser = new ReactStateOLXParser({ + problem: { problemType: 'optionresponse', answers: [] }, + editorObject: { question: '
  1  a
2 b
', hints: [] }, + }); + const buildOLX = stateParser.buildOLX(); + expect(buildOLX).toEqual( + '\n
  1  a
2 b
\n
', + ); + }); }); });