tags is preserved
trimValues: false,
// Parse correctly
unpairedTags: ['br'],
};
const richTextBuilderOptions = {
ignoreAttributes: false,
attributeNamePrefix: '@_',
suppressBooleanAttributes: false,
// 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);
this.richTextBuilder = new XMLBuilder(richTextBuilderOptions);
this.editorObject = problemState.editorObject;
this.problemState = problemState.problem;
}
/** addHints()
* The editorObject saved to the class constuctor is parsed for the attribute hints. No hints returns an empty object.
* The hints are parsed and appended to the hintsArray as object representations of the hint. The hints array is saved
* to the hint key in the demandHint object and returned.
* @return {object} demandhint object with atrribut hint with array of objects
*/
addHints() {
const hintsArray = [];
const { hints } = this.editorObject;
if (hints.length < 1) {
return hintsArray;
}
hints.forEach(hint => {
if (hint.length > 0) {
const parsedHint = this.richTextParser.parse(hint);
hintsArray.push({
hint: [...parsedHint],
});
}
});
const demandhint = [{ demandhint: hintsArray }];
return demandhint;
}
/** addSolution()
* The editorObject saved to the class constuctor is parsed for the attribute solution. If the soltuion is empty, it
* returns an empty object. The solution is parsed and checked if paragraph key's value is a string or array. Studio
* requires a div wrapper with a heading (Explanation). The heading is prepended to the parsed solution object. The
* solution object is returned with the updated div wrapper.
* @return {object} object representation of solution
*/
addSolution() {
const { solution } = this.editorObject;
if (!solution || solution.length <= 0) { return []; }
const solutionTitle = { p: [{ '#text': 'Explanation' }] };
const parsedSolution = this.richTextParser.parse(solution);
const withWrapper = [solutionTitle, ...parsedSolution];
const solutionObject = [{
solution: [{
':@': { '@_class': 'detailed-solution' },
div: [...withWrapper],
}],
}];
return solutionObject;
}
/** addMultiSelectAnswers(option)
* addMultiSelectAnswers takes option. Option is used to assign an answers to the
* correct OLX tag. This function is used for multiple choice, checkbox, and
* dropdown problems. The editorObject saved to the class constuctor is parsed for
* answers (titles only), selectFeedback, and unselectedFeedback. The problemState
* saved to the class constructor is parsed for the problemType and answers (full
* object). The answers are looped through to pair feedback with its respective
* OLX tags. While matching feedback tags, answers are also mapped to their
* respective OLX tags. he object representation of the answers is returned with
* the correct wrapping tags. For checkbox problems, compound hints are also returned.
* @param {string} option - string of answer tag name
* @return {object} object representation of answers
*/
addMultiSelectAnswers(option) {
const choice = [];
let compoundhint = [];
// eslint-disable-next-line prefer-const
let { answers, problemType } = this.problemState;
const answerTitles = this.editorObject?.answers;
const { selectedFeedback, unselectedFeedback } = this.editorObject;
/* todo */
/*
* the logic for general feedback is ot current being used.
* when component is updated will need to return to this code.
* general feedback replaces selected feedback if all incorrect selected feedback is the same.
* ******************************************
if (generalFeedback !== ''
&& answers.every(
answer => (
answer.correct
? true
: answer?.selectedFeedback === answers.find(a => a.correct === false).selectedFeedback
),
)) {
answers = answers.map(answer => (!answer?.correct
? { ...answer, selectedFeedback: generalFeedback }
: answer));
}
*/
answers.forEach((answer) => {
const feedback = [];
let singleAnswer = [];
const title = answerTitles ? this.richTextParser.parse(answerTitles[answer.id]) : [{ '#text': answer.title }];
const currentSelectedFeedback = selectedFeedback?.[answer.id] || null;
const currentUnselectedFeedback = unselectedFeedback?.[answer.id] || null;
let isEmpty;
if (answerTitles) {
isEmpty = Object.keys(title)?.length <= 0;
} else {
isEmpty = title['#text']?.length <= 0;
}
if (title && !isEmpty) {
if (currentSelectedFeedback && problemType === ProblemTypeKeys.MULTISELECT) {
const parsedSelectedFeedback = this.richTextParser.parse(currentSelectedFeedback);
feedback.push({
':@': { '@_selected': true },
[`${option}hint`]: parsedSelectedFeedback,
});
}
if (currentSelectedFeedback && problemType !== ProblemTypeKeys.MULTISELECT) {
const parsedSelectedFeedback = this.richTextParser.parse(currentSelectedFeedback);
feedback.push({
[`${option}hint`]: parsedSelectedFeedback,
});
}
if (currentUnselectedFeedback && problemType === ProblemTypeKeys.MULTISELECT) {
const parsedUnselectedFeedback = this.richTextParser.parse(currentUnselectedFeedback);
feedback.push({
':@': { '@_selected': false },
[`${option}hint`]: parsedUnselectedFeedback,
});
}
singleAnswer = {
':@': { '@_correct': answer.correct },
[option]: [...title, ...feedback],
};
choice.push(singleAnswer);
}
});
if (has(this.problemState, 'groupFeedbackList') && problemType === ProblemTypeKeys.MULTISELECT) {
compoundhint = this.addGroupFeedbackList();
choice.push(...compoundhint);
}
return choice;
}
/** addGroupFeedbackList()
* The problemState saved to the class constuctor is parsed for the attribute groupFeedbackList.
* No group feedback returns an empty array. Each groupFeedback in the groupFeedback list is
* mapped to a new object and appended to the compoundhint array.
* @return {object} object representation of compoundhints
*/
addGroupFeedbackList() {
const compoundhint = [];
const { groupFeedbackList } = this.problemState;
groupFeedbackList.forEach((element) => {
compoundhint.push({
compoundhint: [{ '#text': element.feedback }],
':@': { '@_value': element.answers.join(' ') },
});
});
return compoundhint;
}
/** addQuestion()
* The editorObject saved to the class constuctor is parsed for the attribute question. The question is parsed and
* checked for label tags. label tags are extracted from block-type tags like
or
, and the block-type tag is
* deleted while label is kept. For example,
becomes , while
*
Text
remains
Text
. The question is returned as an object representation.
* @return {object} object representaion of question
*/
addQuestion() {
const { question } = this.editorObject;
const questionObjectArray = this.richTextParser.parse(question);
/* Removes block tags like