[DURACOM-240] working directive

need to change markdown pipe to markdown directive and put everything in there
This commit is contained in:
Andrea Barbasso
2024-02-26 16:03:12 +01:00
committed by Giuseppe Digilio
parent c6b72f6c64
commit 825308e223
6 changed files with 181 additions and 5 deletions

View File

@@ -0,0 +1,8 @@
// import { MathDirective } from './math.directive';
describe('MathDirective', () => {
it('should create an instance', () => {
// const directive = new MathDirective();
// expect(directive).toBeTruthy();
});
});

View File

@@ -0,0 +1,60 @@
import {
Directive,
ElementRef,
Input,
OnChanges,
OnDestroy,
OnInit,
SimpleChanges,
} from '@angular/core';
import { Subject } from 'rxjs';
import {
take,
takeUntil,
} from 'rxjs/operators';
import { MathService } from './math.service';
@Directive({
selector: '[dsMath]',
standalone: true,
})
export class MathDirective implements OnInit, OnChanges, OnDestroy {
@Input() dsMath: string;
private alive$ = new Subject<boolean>();
private readonly el: HTMLElement;
constructor(private mathService: MathService, private elementRef: ElementRef) {
this.el = elementRef.nativeElement;
}
ngOnInit() {
this.render();
}
ngOnChanges(changes: SimpleChanges) {
if (changes?.dsMath?.currentValue) {
this.render();
}
}
private render() {
this.mathService.ready().pipe(
take(1),
takeUntil(this.alive$),
).subscribe(() => {
// if this.dsMath begins with "The observation of the"
if (this.dsMath.startsWith('The observation of the')) {
console.warn('rendering math after ready');
console.warn('this.dsMath', this.dsMath);
console.warn('this.el', this.el);
}
this.mathService.render(this.el, this.dsMath);
});
}
ngOnDestroy() {
this.alive$.next(false);
}
}

View File

@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { MathService } from './math.service';
describe('MathService', () => {
let service: MathService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(MathService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@@ -0,0 +1,93 @@
import { Injectable } from '@angular/core';
import {
Observable,
ReplaySubject,
Subject,
} from 'rxjs';
interface MathJaxConfig {
source: string;
integrity: string;
id: string;
}
declare global {
interface Window {
MathJax: any;
}
}
@Injectable({
providedIn: 'root',
})
export class MathService {
private signal: Subject<boolean>;
private mathJaxOptions = {
tex: {
inlineMath: [['$', '$'], ['\\(', '\\)']],
},
svg: {
fontCache: 'global',
},
startup: {
typeset: false,
},
};
private mathJax: MathJaxConfig = {
source: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js',
integrity: 'sha256-CnzfCXjFj1REmPHgWvm/OQv8gFaxwbLKUi41yCU7N2s=',
id: 'MathJaxScript',
};
private mathJaxFallback: MathJaxConfig = {
source: 'assets/mathjax/mml-chtml.js',
integrity: 'sha256-CnzfCXjFj1REmPHgWvm/OQv8gFaxwbLKUi41yCU7N2s=',
id: 'MathJaxBackupScript',
};
constructor() {
this.signal = new ReplaySubject<boolean>(1);
void this.registerMathJaxAsync(this.mathJax)
.then(() => this.signal.next(true))
.catch(_ => {
void this.registerMathJaxAsync(this.mathJaxFallback)
.then(() => this.signal.next(true))
.catch((error) => console.log(error));
});
}
private async registerMathJaxAsync(config: MathJaxConfig): Promise<any> {
return new Promise<void>((resolve, reject) => {
const optionsScript: HTMLScriptElement = document.createElement('script');
optionsScript.type = 'text/javascript';
optionsScript.text = `MathJax = ${JSON.stringify(this.mathJaxOptions)};`;
document.head.appendChild(optionsScript);
const script: HTMLScriptElement = document.createElement('script');
script.id = config.id;
script.type = 'text/javascript';
script.src = config.source;
script.crossOrigin = 'anonymous';
script.async = true;
script.onload = () => resolve();
script.onerror = error => reject(error);
document.head.appendChild(script);
});
}
ready(): Observable<boolean> {
return this.signal;
}
render(element: HTMLElement, value: string) {
// Take initial typesetting which MathJax performs into account
// window.MathJax.startup.promise.then(() => {
element.innerHTML = value;
window.MathJax.typesetPromise([element]);
// });
}
}

View File

@@ -9,6 +9,7 @@ import {
DomSanitizer,
SafeHtml,
} from '@angular/platform-browser';
import { MathService } from 'src/app/core/shared/math.service';
import { environment } from '../../../environments/environment';
import { isEmpty } from '../empty.util';
@@ -58,6 +59,7 @@ export class MarkdownPipe implements PipeTransform {
@Inject(MARKDOWN_IT) private markdownIt: LazyMarkdownIt,
// @Inject(MATHJAX) private mathjax: Mathjax,
@Inject(SANITIZE_HTML) private sanitizeHtml: SanitizeHtml,
private mathService: MathService,
) {
}
@@ -73,9 +75,6 @@ export class MarkdownPipe implements PipeTransform {
let html: string;
if (environment.markdown.mathjax) {
// TODO: instead of using md.use with mathjax, use ng-katex rendering from its service
md.use(await this.mathjax);
// TODO: keep this as is
const sanitizeHtml = await this.sanitizeHtml;
html = sanitizeHtml(md.render(value), {
// sanitize-html doesn't let through SVG by default, so we extend its allowlists to cover MathJax SVG

View File

@@ -476,8 +476,8 @@ export class DefaultAppConfig implements AppConfig {
// Whether to enable Markdown (https://commonmark.org/) and MathJax (https://www.mathjax.org/)
// display in supported metadata fields. By default, only dc.description.abstract is supported.
markdown: MarkdownConfig = {
enabled: false,
mathjax: false,
enabled: true,
mathjax: true,
};
// Which vocabularies should be used for which search filters