2025SELab2-project-Dwengo/backend/src/services/learning-objects/processing/markdown/dwengo-marked-renderer.ts
2025-04-06 09:45:01 +02:00

108 lines
4.5 KiB
TypeScript

/**
* Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/markdown/learing_object_markdown_renderer.js [sic!]
*/
import PdfProcessor from '../pdf/pdf-processor.js';
import AudioProcessor from '../audio/audio-processor.js';
import ExternProcessor from '../extern/extern-processor.js';
import InlineImageProcessor from '../image/inline-image-processor.js';
import * as marked from 'marked';
import { getUrlStringForLearningObjectHTML, isValidHttpUrl } from '../../../../util/links.js';
import { ProcessingError } from '../processing-error.js';
import Image = marked.Tokens.Image;
import Heading = marked.Tokens.Heading;
import Link = marked.Tokens.Link;
import RendererObject = marked.RendererObject;
import { LearningObjectIdentifierDTO } from '@dwengo-1/common/interfaces/learning-content';
import { Language } from '@dwengo-1/common/util/language';
const prefixes = {
learningObject: '@learning-object',
pdf: '@pdf',
audio: '@audio',
extern: '@extern',
video: '@youtube',
notebook: '@notebook',
blockly: '@blockly',
};
function extractLearningObjectIdFromHref(href: string): LearningObjectIdentifierDTO {
const [hruid, language, version] = href.split(/\/(.+)/, 2)[1].split('/');
return {
hruid,
language: language as Language,
version: parseInt(version),
};
}
/**
* An extension for the renderer of the Marked Markdown renderer which adds support for
* - a custom heading,
* - links to other learning objects,
* - embeddings of other learning objects.
*/
const dwengoMarkedRenderer: RendererObject = {
heading(heading: Heading): string {
const text = heading.text;
const level = heading.depth;
const escapedText = text.toLowerCase().replace(/[^\w]+/g, '-');
return (
`<h${level}>\n` +
` <a name="${escapedText}" class="anchor" href="#${escapedText}">\n` +
` <span class="header-link"></span>\n` +
` </a>\n` +
` ${text}\n` +
`</h${level}>\n`
);
},
// When the syntax for a link is used => [text](href "title")
// Render a custom link when the prefix for a learning object is used.
link(link: Link): string {
const href = link.href;
const title = link.title || '';
const text = marked.parseInline(link.text); // There could for example be an image in the link.
if (href.startsWith(prefixes.learningObject)) {
// Link to learning-object
const learningObjectId = extractLearningObjectIdFromHref(href);
return `<a href="${getUrlStringForLearningObjectHTML(learningObjectId)}" target="_blank" title="${title}">${text}</a>`;
}
// Any other link
if (!isValidHttpUrl(href)) {
throw new ProcessingError('Link is not a valid HTTP URL!');
}
//<a href="https://kiks.ilabt.imec.be/hub/tmplogin?id=0101" title="Notebooks Werking"><img src="Knop.png" alt="" title="Knop"></a>
return `<a href="${href}" target="_blank" title="${title}">${text}</a>`;
},
// When the syntax for an image is used => ![text](href "title")
// Render a learning object, pdf, audio or video if a prefix is used.
image(img: Image): string {
const href = img.href;
if (href.startsWith(prefixes.learningObject)) {
// Embedded learning-object
const learningObjectId = extractLearningObjectIdFromHref(href);
return `
<learning-object hruid="${learningObjectId.hruid}" language="${learningObjectId.language}" version="${learningObjectId.version}"/>
`; // Placeholder for the learning object since we cannot fetch its HTML here (this has to be a sync function!)
} else if (href.startsWith(prefixes.pdf)) {
// Embedded pdf
const proc = new PdfProcessor();
return proc.render(href.split(/\/(.+)/, 2)[1]);
} else if (href.startsWith(prefixes.audio)) {
// Embedded audio
const proc = new AudioProcessor();
return proc.render(href.split(/\/(.+)/, 2)[1]);
} else if (href.startsWith(prefixes.extern) || href.startsWith(prefixes.video) || href.startsWith(prefixes.notebook)) {
// Embedded youtube video or notebook (or other extern content)
const proc = new ExternProcessor();
return proc.render(href.split(/\/(.+)/, 2)[1]);
}
// Embedded image
const proc = new InlineImageProcessor();
return proc.render(href);
},
};
export default dwengoMarkedRenderer;