@@ -16950,7 +16950,7 @@ jme.display = /** @lends Numbas.jme.display */ {
1695016950 }
1695116951 if(tree.args) {
1695216952 var args = tree.args.map(replace_subvars);
16953- return {tok: tree.tok, args: args};
16953+ return {tok: tree.tok, args: args, bracketed: tree.bracketed };
1695416954 }
1695516955 return tree;
1695616956 }
@@ -21514,6 +21514,7 @@ if(res) { \
2151421514 this.stagedAnswer = answer;
2151521515 this.setDirty(true);
2151621516 this.removeWarnings();
21517+ this.display && this.display.showWarnings();
2151721518
2151821519 if(!dontStore) {
2151921520 if(!this.question || !this.question.exam || !this.question.exam.loading) {
@@ -23224,9 +23225,7 @@ Question.prototype = /** @lends Numbas.Question.prototype */
2322423225 tryGetAttribute(q.variablesTest,q.xml,'variables',['condition','maxRuns'],[]);
2322523226 q.signals.trigger('variableDefinitionsLoaded');
2322623227 q.signals.on('variablesGenerated',function() {
23227- var doc = Sarissa.getDomDocument();
23228- doc.appendChild(q.originalXML.cloneNode(true)); //get a fresh copy of the original XML, to sub variables into
23229- q.xml = doc.selectSingleNode('question');
23228+ q.xml = q.originalXML.cloneNode(true); //get a fresh copy of the original XML, to sub variables into
2323023229 q.xml.setAttribute('number',q.number);
2323123230 });
2323223231 q.signals.on(['variablesGenerated', 'rulesetsMade'], function() {
@@ -26282,6 +26281,9 @@ Numbas.queueScript('diagnostic',['util','jme','localisation','jme-variables'], f
2628226281 topic[x[0]] = x[1];
2628326282 });
2628426283 var group = dc.exam.question_groups.find(function(g) { return g.settings.name==topic_name; })
26284+ if(!group) {
26285+ return;
26286+ }
2628526287 topic.questions = [];
2628626288 for(var i=0;i<group.numQuestions;i++) {
2628726289 topic.questions.push({
@@ -26826,6 +26828,17 @@ Numbas.queueScript('marking',['util', 'jme','localisation','jme-variables','math
2682626828 } else {
2682726829 var part_result = part.mark_answer(answer, part.getScope());
2682826830 }
26831+ if(part_result.waiting_for_pre_submit) {
26832+ return jme.wrapValue({
26833+ marks: part.availableMarks(),
26834+ credit: 0,
26835+ feedback: [],
26836+ valid: false,
26837+ states: {},
26838+ state_valid: {},
26839+ values: {}
26840+ });
26841+ }
2682926842 var result = marking.finalise_state(part_result.states.mark);
2683026843 return jme.wrapValue({
2683126844 marks: part.availableMarks(),
@@ -27323,7 +27336,7 @@ Copyright 2011-14 Newcastle University
2732327336*/
2732427337/** @file Start the exam */
2732527338// 'base' gives the third-party libraries on which Numbas depends
27326- Numbas.queueScript('base',['jquery','localisation','seedrandom','knockout','sarissa' ],function() {
27339+ Numbas.queueScript('base',['jquery','localisation','seedrandom','knockout'],function() {
2732727340});
2732827341Numbas.queueScript('start-exam',['base','util', 'exam','settings'],function() {
2732927342 for(var name in Numbas.custom_part_types) {
@@ -28979,7 +28992,7 @@ storage.init();
2897928992});
2898028993
2898128994/*
28982- Copyright 2011-14 Newcastle University
28995+ Copyright 2011-25 Newcastle University
2898328996 Licensed under the Apache License, Version 2.0 (the "License");
2898428997 you may not use this file except in compliance with the License.
2898528998 You may obtain a copy of the License at
@@ -29014,6 +29027,68 @@ Numbas.queueScript('xml',['base','jme'],function() {
2901429027 * @type {Object<XMLDocument>}
2901529028 */
2901629029
29030+
29031+ /**
29032+ * Extends the XMLDocument to emulate IE's selectNodes.
29033+ *
29034+ * @param {string} xpath_selector - The XPath expression to use.
29035+ * @param {Node} contextNode - The top node to match against.
29036+ * @returns {Array.<Node>} - The nodes matching the XPath expression.
29037+ */
29038+ XMLDocument.prototype.selectNodes = function(xpath_selector, contextNode){
29039+ var oResult = this.evaluate(
29040+ xpath_selector,
29041+ contextNode || this,
29042+ this.documentElement,
29043+ XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
29044+ null
29045+ );
29046+ var nodeList = new Array(oResult.snapshotLength);
29047+ for(var i=0; i<nodeList.length; i++){
29048+ nodeList[i] = oResult.snapshotItem(i);
29049+ }
29050+ return nodeList;
29051+ }
29052+
29053+ /**
29054+ * Extends the XMLDocument to emulate IE's `selectSingleNode`.
29055+ *
29056+ * @param {string} xpath_selector - The XPath expression to use.
29057+ * @param {Node} contextNode - this is for internal use only by the same method when called on Elements.
29058+ * @returns {Node} - The first node matching the XPath expression.
29059+ */
29060+ XMLDocument.prototype.selectSingleNode = function(xpath_selector, contextNode) {
29061+ return this.evaluate(
29062+ xpath_selector,
29063+ contextNode || this,
29064+ this.documentElement,
29065+ XPathResult.FIRST_ORDERED_NODE_TYPE,
29066+ null
29067+ ).singleNodeValue;
29068+ };
29069+
29070+
29071+ /**
29072+ * Extends the Element to emulate IE's selectNodes.
29073+ * @param {string} xpath_selector - The XPath expression to use.
29074+ * @returns {Array.<Node>} - The result of the XPath search.
29075+ */
29076+ Element.prototype.selectNodes = function(xpath_selector){
29077+ return this.ownerDocument.selectNodes(xpath_selector, this);
29078+ };
29079+
29080+
29081+ /**
29082+ * Extends the Element to emulate IE's `selectSingleNode`.
29083+ *
29084+ * @param {string} xpath_selector - The XPath expression to use.
29085+ * @returns {Node} - The first node matching the XPath expression.
29086+ */
29087+ Element.prototype.selectSingleNode = function(xpath_selector){
29088+ return this.ownerDocument.selectSingleNode(xpath_selector, this);
29089+ };
29090+
29091+
2901729092/** @namespace Numbas.xml */
2901829093var xml = Numbas.xml = {
2901929094 /** DOM parser to use to parse XML.
@@ -29040,14 +29115,13 @@ var xml = Numbas.xml = {
2904029115 loadXML: function(xmlstring)
2904129116 {
2904229117 //parse the XML document
29043- var doc = xml.dp.parseFromString(xmlstring,'text/xml');
29118+ const parser = new DOMParser();
29119+ var doc = parser.parseFromString(xmlstring,'text/xml');
2904429120 //check for errors
29045- if(Sarissa.getParseErrorText(doc) != Sarissa.PARSED_OK)
29046- {
29047- throw(new Numbas.Error('xml.could not load',{message: Numbas.util.escapeHTML(Sarissa.getParseErrorText(doc) )}));
29121+ const errorNode = doc.querySelector("parsererror");
29122+ if (errorNode) {
29123+ throw(new Numbas.Error('xml.could not load',{message: Numbas.util.escapeHTML(errorNode.textContent )}));
2904829124 }
29049- //allow XPath to be used to select nodes
29050- doc.setProperty('SelectionLanguage','XPath');
2905129125 //convert all the attribute names to lower case
2905229126 var es = doc.selectNodes('descendant::*');
2905329127 for(var i=0; i<es.length; i++)
@@ -29255,33 +29329,26 @@ var xml = Numbas.xml = {
2925529329 $(this).replaceWith(localString);
2925629330 });
2925729331 return template;
29258- },
29259- /** Transform an XML node using the given XSL template, returning a string representation of the transformed XML.
29260- *
29261- * @param {Element} template
29262- * @param {Element} xml
29263- * @returns {string}
29264- */
29265- transform: function(template,xml) {
29266- /** Is the browser Internet Explorer?
29267- *
29268- * @returns {boolean}
29269- */
29270- function isIE() {
29271- var ua = window.navigator.userAgent; //Check the userAgent property of the window.navigator object
29272- var msie = ua.indexOf('MSIE '); // IE 10 or older
29273- var trident = ua.indexOf('Trident/'); //IE 11
29274-
29275- return (msie > 0 || trident > 0);
29276- }
29277- var r;
29278- if(!isIE()) {
29279- r = $.xsl.transform(template,xml);
29280- } else {
29281- var s = xml.transformNode(template);
29282- r = {string: s, error: ''};
29283- }
29284- return r.string;
29332+ },
29333+ /** Transform an XML node using the given XSL template, returning a string representation of the transformed XML.
29334+ *
29335+ * @param {Element} template
29336+ * @param {Element} xml
29337+ * @returns {string}
29338+ */
29339+ transform: function(template,xml) {
29340+ const container = xml.ownerDocument.createElement('container');
29341+ xml = xml.cloneNode(true);
29342+ container.append(xml);
29343+
29344+ const processor = new XSLTProcessor();
29345+ processor.importStylesheet(template);
29346+ const doc = processor.transformToDocument(container);
29347+ const serializer = new XMLSerializer();
29348+
29349+ const string = serializer.serializeToString(doc);
29350+
29351+ return string;
2928529352 },
2928629353 /** Is the given node empty? True if it has no children.
2928729354 *
@@ -32728,6 +32795,16 @@ JMEPart.prototype = /** @lends Numbas.JMEPart.prototype */
3272832795 var settings = this.settings;
3272932796 var answerSimplification = Numbas.jme.collectRuleset(settings.answerSimplificationString,scope.allRulesets());
3273032797 var tree = jme.display.subvars(settings.correctAnswerString, scope);
32798+ tree = scope.expandJuxtapositions(
32799+ tree,
32800+ {
32801+ singleLetterVariables: settings.singleLetterVariables,
32802+ noUnknownFunctions: !settings.allowUnknownFunctions,
32803+ implicitFunctionComposition: settings.implicitFunctionComposition,
32804+ normaliseSubscripts: true
32805+ }
32806+ );
32807+
3273132808 if(!tree && this.marks>0) {
3273232809 this.error('part.jme.answer missing');
3273332810 }
0 commit comments