diff --git a/src/main/java/org/olat/core/util/Formatter.java b/src/main/java/org/olat/core/util/Formatter.java index 3b172294d014173a5c2ecb472901c304b73a41ef..a4356f856ae82b665312ec876973eaab32a45142 100644 --- a/src/main/java/org/olat/core/util/Formatter.java +++ b/src/main/java/org/olat/core/util/Formatter.java @@ -663,11 +663,25 @@ public class Formatter { sb.append("<").append(elem).append(" id=\"").append(domid).append("\">"); sb.append(htmlFragment); sb.append("</").append(elem).append(">"); - sb.append("\n<script type='text/javascript'>\n/* <![CDATA[ */\n setTimeout(function() { BFormatter.formatLatexFormulas('").append(domid).append("');}, 100);\n/* ]]> */\n</script>"); + sb.append(elementLatexFormattingScript(domid)); return sb.toString(); } return htmlFragment; } + + /** + * Html code script to render the latex formulas of a given element id + * @param domid Id of the DOM node containing the elements to render. + */ + public static String elementLatexFormattingScript(String domid) { + return String.format("%n" + + "<script type='text/javascript'>%n" + + "/* <![CDATA[ */%n" + + " jQuery(function() {setTimeout(function() { BFormatter.formatLatexFormulas('%s');}, 100); }); %n" + + "/* ]]> */%n" + + "</script>", + domid); + } // Pattern to find URL's in text diff --git a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentObjectComponentRenderer.java b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentObjectComponentRenderer.java index b4a38444a428ea6756e1279e762f0827b812e34a..d56f17be9db0aa7b57e5db10247cf2f657b0b695 100644 --- a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentObjectComponentRenderer.java +++ b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentObjectComponentRenderer.java @@ -83,6 +83,7 @@ import org.olat.core.helpers.Settings; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.CodeHelper; +import org.olat.core.util.Formatter; import org.olat.core.util.StringHelper; import org.olat.core.util.Util; import org.olat.ims.qti21.QTI21Constants; @@ -97,6 +98,7 @@ import uk.ac.ed.ph.jqtiplus.attribute.Attribute; import uk.ac.ed.ph.jqtiplus.attribute.AttributeList; import uk.ac.ed.ph.jqtiplus.attribute.ForeignAttribute; import uk.ac.ed.ph.jqtiplus.attribute.value.IntegerAttribute; +import uk.ac.ed.ph.jqtiplus.attribute.value.StringAttribute; import uk.ac.ed.ph.jqtiplus.attribute.value.StringMultipleAttribute; import uk.ac.ed.ph.jqtiplus.node.ForeignElement; import uk.ac.ed.ph.jqtiplus.node.QtiNode; @@ -470,15 +472,29 @@ public abstract class AssessmentObjectComponentRenderer extends DefaultComponent } case RubricBlock.QTI_CLASS_NAME: break; //never rendered automatically case Math.QTI_CLASS_NAME: { + String domid = "mw_" + CodeHelper.getRAMUniqueID(); + sb.append("<div id=\"").append(domid).append("\">"); renderMath(renderer, sb, component, resolvedAssessmentItem, itemSessionState, (Math)block); + sb.append("</div>") + .append(Formatter.elementLatexFormattingScript(domid)); break; } - case Div.QTI_CLASS_NAME: + case Div.QTI_CLASS_NAME: { + String domid = null; + if (containsClass(block, "math")) { + domid = "mw_" + CodeHelper.getRAMUniqueID(); + sb.append("<div id=\"").append(domid).append("\">"); + } renderStartHtmlTag(sb, component, resolvedAssessmentItem, block, null); ((Div)block).getFlows().forEach((flow) -> renderFlow(renderer, sb, component, resolvedAssessmentItem, itemSessionState, flow, ubu, translator)); renderEndTag(sb, block); + if (domid != null) { + sb.append("</div>") + .append(Formatter.elementLatexFormattingScript(domid)); + } break; + } case Ul.QTI_CLASS_NAME: renderStartHtmlTag(sb, component, resolvedAssessmentItem, block, null); ((Ul)block).getLis().forEach((li) @@ -672,7 +688,11 @@ public abstract class AssessmentObjectComponentRenderer extends DefaultComponent break; } case Math.QTI_CLASS_NAME: { + String domid = "mw_" + CodeHelper.getRAMUniqueID(); + sb.append("<span id=\"").append(domid).append("\">"); renderMath(renderer, sb, component, resolvedAssessmentItem, itemSessionState, (Math)inline); + sb.append("</span>") + .append(Formatter.elementLatexFormattingScript(domid)); break; } case Img.QTI_CLASS_NAME: { @@ -709,9 +729,7 @@ public abstract class AssessmentObjectComponentRenderer extends DefaultComponent protected final void renderSpan(AssessmentRenderer renderer, StringOutput sb, Span span, AssessmentObjectComponent component, ResolvedAssessmentItem resolvedAssessmentItem, ItemSessionState itemSessionState, URLBuilder ubu, Translator translator) { - StringMultipleAttribute attrClass = span.getAttributes().getStringMultipleAttribute("class"); - - if (attrClass != null && attrClass.getValue() != null && attrClass.getValue().contains("math")) { + if (containsClass(span,"math")) { String domid = "mw_" + CodeHelper.getRAMUniqueID(); sb.append("<span id=\"").append(domid).append("\">"); @@ -721,7 +739,7 @@ public abstract class AssessmentObjectComponentRenderer extends DefaultComponent renderEndTag(sb, span); sb.append("</span>") - .append("\n<script type='text/javascript'>\n/* <![CDATA[ */\n jQuery(function() {setTimeout(function() { BFormatter.formatLatexFormulas('").append(domid).append("');}, 100); }); \n/* ]]> */\n</script>"); + .append(Formatter.elementLatexFormattingScript(domid)); } else { renderStartHtmlTag(sb, component, resolvedAssessmentItem, span, null); span.getInlines().forEach((child) @@ -1391,10 +1409,15 @@ public abstract class AssessmentObjectComponentRenderer extends DefaultComponent renderer.setMathXsltDisabled(true); try(StringOutput mathOutput = StringOutputPool.allocStringBuilder(2048)) { - mathOutput.append("<math xmlns=\"http://www.w3.org/1998/Math/MathML\">"); + if (!math.getAttributes().contains("xmlns")) { + StringAttribute xmlnsAttribute = new StringAttribute(math, "xmlns", false); + xmlnsAttribute.setValue("http://www.w3.org/1998/Math/MathML"); + math.getAttributes().add(xmlnsAttribute); + } + renderStartHtmlTag(mathOutput, component, resolvedAssessmentItem, math, null); math.getContent().forEach((foreignElement) -> renderMath(renderer, mathOutput, component, resolvedAssessmentItem, itemSessionState, foreignElement)); - mathOutput.append("</math>"); + renderEndTag(mathOutput, math); String enrichedMathML = StringOutputPool.freePop(mathOutput); renderer.setMathXsltDisabled(false); transformMathmlAsString(sb, enrichedMathML); @@ -1449,7 +1472,7 @@ public abstract class AssessmentObjectComponentRenderer extends DefaultComponent renderEndTag(out, fElement); } } else if(mathElement instanceof TextRun) { - out.append(((TextRun)mathElement).getTextContent()); + out.append(StringEscapeUtils.escapeXml(((TextRun)mathElement).getTextContent())); } } @@ -1588,7 +1611,7 @@ public abstract class AssessmentObjectComponentRenderer extends DefaultComponent sb.append("<span class='o_error'>ERROR MATHML</span>"); } } - + protected boolean containsClass(QtiNode element, String marker) { AttributeList attributes = element.getAttributes(); for(int i=attributes.size(); i-->0; ) { diff --git a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentRenderFunctions.java b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentRenderFunctions.java index d883dcd785e58af4534becf44922ff6311e69209..af2273133dd3efcdc73b4baf1db5fa9dc2b1917a 100644 --- a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentRenderFunctions.java +++ b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentRenderFunctions.java @@ -593,6 +593,8 @@ public class AssessmentRenderFunctions { case "class": case "contextmenu": case "dir": + case "display": + case "download": case "hidden": case "name": case "id": @@ -603,6 +605,7 @@ public class AssessmentRenderFunctions { case "style": case "width": case "height": + case "xmlns": value = getDomAttributeValue(attribute); break; case "href": diff --git a/src/test/java/org/olat/core/util/FormatterTest.java b/src/test/java/org/olat/core/util/FormatterTest.java index 60073f1557c7ec2b6a156e688fbd7a0d6b82ac7a..54db23da304b7717546b2c5174c20340bc2cad97 100644 --- a/src/test/java/org/olat/core/util/FormatterTest.java +++ b/src/test/java/org/olat/core/util/FormatterTest.java @@ -22,6 +22,7 @@ package org.olat.core.util; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; +import java.util.UUID; import org.apache.commons.lang.StringEscapeUtils; import org.junit.Assert; @@ -132,4 +133,12 @@ public class FormatterTest { Assert.assertEquals("32:23:45", Formatter.formatTimecode(116625000l)); Assert.assertEquals("532:23:45", Formatter.formatTimecode(1916625000l)); } + + @Test + public void elementLatexFormattingScript() { + String domId = UUID.randomUUID().toString(); + String latextFormatterJs = Formatter.elementLatexFormattingScript(domId); + Assert.assertNotNull(latextFormatterJs); + Assert.assertTrue(latextFormatterJs.contains(domId)); + } }