Skip to content

Commit 210a3c7

Browse files
author
Andrea Barbasso
committed
[UXP-114] working directive
need to change markdown pipe to markdown directive and put everything in there
1 parent 7d33f5a commit 210a3c7

8 files changed

Lines changed: 167 additions & 5 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// import { MathDirective } from './math.directive';
2+
3+
describe('MathDirective', () => {
4+
it('should create an instance', () => {
5+
// const directive = new MathDirective();
6+
// expect(directive).toBeTruthy();
7+
});
8+
});
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Directive, OnChanges, OnInit, Input, ElementRef, OnDestroy, SimpleChanges } from '@angular/core';
2+
import { Subject } from 'rxjs';
3+
import { MathService } from './math.service';
4+
import { take, takeUntil } from 'rxjs/operators';
5+
6+
@Directive({
7+
selector: '[dsMath]'
8+
})
9+
export class MathDirective implements OnInit, OnChanges, OnDestroy {
10+
@Input() dsMath: string;
11+
private alive$ = new Subject<boolean>();
12+
private readonly el: HTMLElement;
13+
14+
constructor(private mathService: MathService, private elementRef: ElementRef) {
15+
this.el = elementRef.nativeElement;
16+
}
17+
18+
ngOnInit() {
19+
this.render();
20+
}
21+
22+
ngOnChanges(changes: SimpleChanges) {
23+
if (changes?.dsMath?.currentValue) {
24+
this.render();
25+
}
26+
}
27+
28+
private render() {
29+
this.mathService.ready().pipe(
30+
take(1),
31+
takeUntil(this.alive$)
32+
).subscribe(() => {
33+
// if this.dsMath begins with "The observation of the"
34+
if (this.dsMath.startsWith('The observation of the')) {
35+
console.warn('rendering math after ready');
36+
console.warn('this.dsMath', this.dsMath);
37+
console.warn('this.el', this.el);
38+
}
39+
this.mathService.render(this.el, this.dsMath);
40+
});
41+
}
42+
43+
ngOnDestroy() {
44+
this.alive$.next(false);
45+
}
46+
47+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { TestBed } from '@angular/core/testing';
2+
3+
import { MathService } from './math.service';
4+
5+
describe('MathService', () => {
6+
let service: MathService;
7+
8+
beforeEach(() => {
9+
TestBed.configureTestingModule({});
10+
service = TestBed.inject(MathService);
11+
});
12+
13+
it('should be created', () => {
14+
expect(service).toBeTruthy();
15+
});
16+
});
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { Injectable } from '@angular/core';
2+
import { Observable, ReplaySubject, Subject } from 'rxjs';
3+
4+
interface MathJaxConfig {
5+
source: string;
6+
integrity: string;
7+
id: string;
8+
}
9+
10+
declare global {
11+
interface Window {
12+
MathJax: any;
13+
}
14+
}
15+
16+
@Injectable({
17+
providedIn: 'root'
18+
})
19+
export class MathService {
20+
21+
private signal: Subject<boolean>;
22+
23+
private mathJaxOptions = {
24+
tex: {
25+
inlineMath: [['$', '$'], ['\\(', '\\)']]
26+
},
27+
svg: {
28+
fontCache: 'global'
29+
},
30+
startup: {
31+
typeset: false
32+
}
33+
};
34+
35+
private mathJax: MathJaxConfig = {
36+
source: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js',
37+
integrity: 'sha256-CnzfCXjFj1REmPHgWvm/OQv8gFaxwbLKUi41yCU7N2s=',
38+
id: 'MathJaxScript'
39+
};
40+
private mathJaxFallback: MathJaxConfig = {
41+
source: 'assets/mathjax/mml-chtml.js',
42+
integrity: 'sha256-CnzfCXjFj1REmPHgWvm/OQv8gFaxwbLKUi41yCU7N2s=',
43+
id: 'MathJaxBackupScript'
44+
};
45+
46+
constructor() {
47+
48+
this.signal = new ReplaySubject<boolean>();
49+
50+
void this.registerMathJaxAsync(this.mathJax)
51+
.then(() => this.signal.next(true))
52+
.catch(_ => {
53+
void this.registerMathJaxAsync(this.mathJaxFallback)
54+
.then(() => this.signal.next(true))
55+
.catch((error) => console.log(error));
56+
});
57+
}
58+
59+
private async registerMathJaxAsync(config: MathJaxConfig): Promise<any> {
60+
return new Promise<void>((resolve, reject) => {
61+
const optionsScript: HTMLScriptElement = document.createElement('script');
62+
optionsScript.type = 'text/javascript';
63+
optionsScript.text = `MathJax = ${JSON.stringify(this.mathJaxOptions)};`;
64+
document.head.appendChild(optionsScript);
65+
66+
const script: HTMLScriptElement = document.createElement('script');
67+
script.id = config.id;
68+
script.type = 'text/javascript';
69+
script.src = config.source;
70+
script.crossOrigin = 'anonymous';
71+
script.async = true;
72+
script.onload = () => resolve();
73+
script.onerror = error => reject(error);
74+
document.head.appendChild(script);
75+
});
76+
}
77+
78+
ready(): Observable<boolean> {
79+
return this.signal;
80+
}
81+
82+
render(element: HTMLElement, value: string) {
83+
// Take initial typesetting which MathJax performs into account
84+
// window.MathJax.startup.promise.then(() => {
85+
element.innerHTML = value;
86+
window.MathJax.typesetPromise([element]);
87+
// });
88+
}
89+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
<span class="dont-break-out" [innerHTML]="value | dsMarkdown: true | async">
22
</span>
3+
<span [dsMath]="value"></span>

src/app/shared/shared.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ import {
352352
import { ItemCollectionComponent } from './object-collection/shared/mydspace-item-collection/item-collection.component';
353353
import { ThemedItemListPreviewComponent } from './object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component';
354354
import { ItemListPreviewComponent } from './object-list/my-dspace-result-list-element/item-list-preview/item-list-preview.component';
355+
import { MathDirective } from '../core/shared/math.directive';
355356

356357
const MODULES = [
357358
CommonModule,
@@ -612,6 +613,7 @@ const DIRECTIVES = [
612613
HoverClassDirective,
613614
ContextHelpDirective,
614615
EntityIconDirective,
616+
MathDirective
615617
];
616618

617619
@NgModule({

src/app/shared/utils/markdown.pipe.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Inject, InjectionToken, Pipe, PipeTransform, SecurityContext } from '@angular/core';
22
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
3+
import { MathService } from 'src/app/core/shared/math.service';
34

45
import { environment } from '../../../environments/environment';
56
import { isEmpty } from '../empty.util';
@@ -48,6 +49,7 @@ export class MarkdownPipe implements PipeTransform {
4849
@Inject(MARKDOWN_IT) private markdownIt: LazyMarkdownIt,
4950
@Inject(MATHJAX) private mathjax: Mathjax,
5051
@Inject(SANITIZE_HTML) private sanitizeHtml: SanitizeHtml,
52+
private mathService: MathService
5153
) {
5254
}
5355

@@ -63,9 +65,6 @@ export class MarkdownPipe implements PipeTransform {
6365

6466
let html: string;
6567
if (environment.markdown.mathjax) {
66-
// TODO: instead of using md.use with mathjax, use ng-katex rendering from its service
67-
md.use(await this.mathjax);
68-
// TODO: keep this as is
6968
const sanitizeHtml = await this.sanitizeHtml;
7069
html = sanitizeHtml(md.render(value), {
7170
// sanitize-html doesn't let through SVG by default, so we extend its allowlists to cover MathJax SVG

src/config/default-app-config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -501,8 +501,8 @@ export class DefaultAppConfig implements AppConfig {
501501
// Whether to enable Markdown (https://commonmark.org/) and MathJax (https://www.mathjax.org/)
502502
// display in supported metadata fields. By default, only dc.description.abstract is supported.
503503
markdown: MarkdownConfig = {
504-
enabled: false,
505-
mathjax: false,
504+
enabled: true,
505+
mathjax: true,
506506
};
507507

508508
// Which vocabularies should be used for which search filters

0 commit comments

Comments
 (0)