UNPKG

codegradx

Version:
165 lines (160 loc) 5.9 kB
// xml2html.mjs // Time-stamp: "2021-09-14 14:24:38 queinnec" import { CodeGradX } from '../codegradx.mjs'; import { sax } from './sax.mjs'; import { htmlencode } from './htmlencode.mjs'; /** Conversion of texts (stems, reports) from XML to HTML. This function may be modified to accommodate your own desires. */ export function xml2html (s, options) { options = Object.assign({}, CodeGradX.xml2html.default, options); let result = ''; //const mark, totalMark; let mode = 'default'; let questionCounter = 0, sectionLevel = 0; // HTML tags to be left as they are: const htmlTagsRegExp = new RegExp('^(p|pre|code|ul|ol|li|em|it|i|sub|sup|strong|b)$'); // Tags to be converted into DIV: const divTagsRegExp = new RegExp('^(warning|error|introduction|conclusion|normal|stem|report)$'); // Tags to be converted into SPAN: const spanTagsRegExp = new RegExp("^(user|machine|lineNumber|summary)$"); // Tags with special hack: const specialTagRegExp = new RegExp("^(img|a)$"); // Tags to be ignored: const ignoreTagsRegExp = new RegExp("^(FW4EX|expectations|title|fw4ex)$"); function convertAttributes (attributes) { let s = ''; for ( let name of Object.keys(attributes) ) { let value = attributes[name]; s += ' ' + name + '="' + value + '"'; } return s; } const parser = sax.parser(true, { //trim: true }); parser.onerror = function (e) { throw e; }; parser.ontext= function (text) { if ( ! mode.match(/ignore/) ) { result += htmlencode(text); } }; function absolutize (node) { if ( options.exercise ) { const pathRegExp = new RegExp('^(./)?(path/.*)$'); if ( node.attributes.src ) { let matches = node.attributes.src.match(pathRegExp); if ( matches ) { node.attributes.src = options.exercise.server + '/exercisecontent/' + options.exercise.safecookie + '/' + matches[2]; } } if ( node.attributes.href ) { let matches = node.attributes.href.match(pathRegExp); if ( matches ) { node.attributes.href = options.exercise.server + '/exercisecontent/' + options.exercise.safecookie + '/' + matches[2]; } } } const tagname = node.name; const attributes = convertAttributes(node.attributes); return '<' + tagname + attributes + ' />'; } parser.onopentag = function (node) { const tagname = node.name; const attributes = convertAttributes(node.attributes); if ( tagname.match(ignoreTagsRegExp) ) { mode = 'ignore'; } else if ( tagname.match(htmlTagsRegExp) ) { result += '<' + tagname + attributes + '>'; } else if ( tagname.match(specialTagRegExp) ) { result += absolutize(node); } else if ( tagname.match(spanTagsRegExp) ) { result += '<span class="fw4ex_' + tagname + '"' + attributes + '>'; } else if ( tagname.match(divTagsRegExp) ) { result += '<div class="fw4ex_' + tagname + '"' + attributes + '>'; } else if ( tagname.match(/^mark$/) ) { const markOrig = CodeGradX._str2num(node.attributes.value); const mark = options.markFactor * CodeGradX._str2num2decimals(markOrig); result += '<span' + attributes + ' class="fw4ex_mark">' + mark + '<!-- ' + markOrig; } else if ( tagname.match(/^section$/) ) { result += '<div' + attributes + ' class="fw4ex_section' + (++sectionLevel) + '">'; } else if ( tagname.match(/^question$/) ) { const qname = node.attributes.name; const title = node.attributes.title || ''; result += '<div' + attributes + ' class="fw4ex_question">'; result += '<div class="fw4ex_question_title" data_counter="' + (++questionCounter) + '">' + qname + ": " + title + '</div>'; } else { result += '<div class="fw4ex_' + tagname + '"' + attributes + '>'; } }; parser.onclosetag = function (tagname) { if ( tagname.match(ignoreTagsRegExp) ) { mode = 'default'; } else if ( tagname.match(htmlTagsRegExp) ) { result += '</' + tagname + '>'; } else if ( tagname.match(specialTagRegExp) ) { /* result = result; */ } else if ( tagname.match(spanTagsRegExp) ) { result += '</span>'; } else if ( tagname.match(divTagsRegExp) ) { result += '</div>'; } else if ( tagname.match(/^mark$/) ) { result += ' --></span>'; } else if ( tagname.match(/^section$/) ) { --sectionLevel; result += '</div>'; } else if ( tagname.match(/^question$/) ) { result += '</div>'; } else { result += '</div>'; } }; parser.oncomment = function (text) { if ( ! mode.match(/ignore/) ) { result += '<!-- ' + text + ' -->'; } }; parser.oncdata = function (text) { if ( ! mode.match(/ignore/) ) { result += '<pre>' + htmlencode(text); } }; parser.cdata = function (text) { if ( ! mode.match(/ignore/) ) { result += htmlencode(text); } }; parser.onclosecdata = function () { if ( ! mode.match(/ignore/) ) { result += '</pre>'; } }; parser.write(s).close(); if ( questionCounter <= 1 ) { // If only one question, remove its title: let questionTitleRegExp = new RegExp( '<div class=.fw4ex_question_title. [^>]*>.*?</div>'); result = result.replace(questionTitleRegExp, ''); } return Promise.resolve(result); } CodeGradX.xml2html = xml2html; CodeGradX.xml2html.default = { markFactor: 100 }; // end of xml2html.js