Skip to content

Commit 149fc49

Browse files
Andrea Barbassoatarix83
authored andcommitted
Merged in DSC-1401-cherry-pick-UXP-114-maintenance (pull request DSpace#1575)
DSC-1401 cherry pick UXP 114 maintenance Approved-by: Giuseppe Digilio
2 parents a56e444 + 9467f4e commit 149fc49

28 files changed

Lines changed: 408 additions & 209 deletions
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { DOCUMENT } from '@angular/common';
2+
import {
3+
Inject,
4+
Injectable,
5+
} from '@angular/core';
6+
import { Observable, ReplaySubject, Subject } from 'rxjs';
7+
import { environment } from 'src/environments/environment';
8+
import {
9+
NativeWindowRef,
10+
NativeWindowService,
11+
} from '../services/window.service';import { MathJaxConfig, MathService } from './math.service';
12+
13+
@Injectable({
14+
providedIn: 'root'
15+
})
16+
/**
17+
* Provide the MathService for CSR
18+
*/
19+
export class ClientMathService extends MathService {
20+
21+
protected isReady$: Subject<boolean>;
22+
23+
protected mathJaxOptions = {
24+
tex: {
25+
inlineMath: [['$', '$'], ['\\(', '\\)']]
26+
},
27+
svg: {
28+
fontCache: 'global'
29+
},
30+
startup: {
31+
typeset: false
32+
}
33+
};
34+
35+
protected mathJax: MathJaxConfig = {
36+
source: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js',
37+
id: 'MathJaxScript'
38+
};
39+
protected mathJaxFallback: MathJaxConfig = {
40+
source: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-chtml.min.js',
41+
id: 'MathJaxBackupScript'
42+
};
43+
44+
constructor(
45+
@Inject(DOCUMENT) private _document: Document,
46+
@Inject(NativeWindowService) protected _window: NativeWindowRef,
47+
) {
48+
super();
49+
50+
this.isReady$ = new ReplaySubject<boolean>();
51+
52+
void this.registerMathJaxAsync(this.mathJax)
53+
.then(() => this.isReady$.next(true))
54+
.catch(_ => {
55+
void this.registerMathJaxAsync(this.mathJaxFallback)
56+
.then(() => this.isReady$.next(true));
57+
});
58+
}
59+
60+
/**
61+
* Register the specified MathJax script in the document
62+
*
63+
* @param config The configuration object for the script
64+
*/
65+
protected async registerMathJaxAsync(config: MathJaxConfig): Promise<any> {
66+
if (environment.markdown.mathjax) {
67+
return new Promise<void>((resolve, reject) => {
68+
69+
const optionsScript: HTMLScriptElement = this._document.createElement('script');
70+
optionsScript.type = 'text/javascript';
71+
optionsScript.text = `MathJax = ${JSON.stringify(this.mathJaxOptions)};`;
72+
this._document.head.appendChild(optionsScript);
73+
74+
const script: HTMLScriptElement = this._document.createElement('script');
75+
script.id = config.id;
76+
script.type = 'text/javascript';
77+
script.src = config.source;
78+
script.crossOrigin = 'anonymous';
79+
script.async = true;
80+
script.onload = () => resolve();
81+
script.onerror = error => reject(error);
82+
this._document.head.appendChild(script);
83+
});
84+
}
85+
return Promise.resolve();
86+
}
87+
88+
/**
89+
* Return the status of the script registration
90+
*/
91+
ready(): Observable<boolean> {
92+
return this.isReady$;
93+
}
94+
95+
/**
96+
* Render the specified element using the MathJax JavaScript
97+
*
98+
* @param element The element to render with MathJax
99+
*/
100+
render(element: HTMLElement) {
101+
if (environment.markdown.mathjax) {
102+
this._window.nativeWindow.MathJax.typesetPromise([element]);
103+
}
104+
}
105+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { TestBed } from '@angular/core/testing';
2+
import { Observable, of } from 'rxjs';
3+
import { MathService, MathJaxConfig } from './math.service';
4+
5+
export class MockMathService extends MathService {
6+
protected mathJaxOptions: any = {};
7+
protected mathJax: MathJaxConfig = { source: '', id: '' };
8+
protected mathJaxFallback: MathJaxConfig = { source: '', id: '' };
9+
10+
protected registerMathJaxAsync(config: MathJaxConfig): Promise<any> {
11+
return Promise.resolve();
12+
}
13+
14+
ready(): Observable<boolean> {
15+
return of(true);
16+
}
17+
18+
render(element: HTMLElement): void {
19+
return;
20+
}
21+
}
22+
23+
describe('MathService', () => {
24+
let service: MockMathService;
25+
26+
beforeEach(() => {
27+
TestBed.configureTestingModule({});
28+
service = new MockMathService();
29+
spyOn(service, 'render');
30+
});
31+
32+
it('should be created', () => {
33+
expect(service).toBeTruthy();
34+
});
35+
36+
it('should be ready', (done) => {
37+
service.ready().subscribe(isReady => {
38+
expect(isReady).toBe(true);
39+
done();
40+
});
41+
});
42+
43+
it('should render', () => {
44+
service.render(document.createElement('div'));
45+
expect(service.render).toHaveBeenCalled();
46+
});
47+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Observable } from 'rxjs';
2+
3+
export interface MathJaxConfig {
4+
source: string;
5+
id: string;
6+
}
7+
8+
/**
9+
* This service is used to provide the MathJax library with the ability to render markdown code
10+
*/
11+
export abstract class MathService {
12+
protected abstract mathJaxOptions: any;
13+
protected abstract mathJax: MathJaxConfig;
14+
protected abstract mathJaxFallback: MathJaxConfig;
15+
16+
protected abstract registerMathJaxAsync(config: MathJaxConfig): Promise<any>;
17+
abstract ready(): Observable<boolean>;
18+
abstract render(element: HTMLElement): void;
19+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Injectable } from '@angular/core';
2+
import { Observable, ReplaySubject, Subject } from 'rxjs';
3+
import { MathJaxConfig, MathService } from './math.service';
4+
5+
@Injectable({
6+
providedIn: 'root'
7+
})
8+
/**
9+
* Provide the MathService for SSR
10+
*/
11+
export class ServerMathService extends MathService {
12+
13+
protected signal: Subject<boolean>;
14+
15+
protected mathJaxOptions = {};
16+
17+
protected mathJax: MathJaxConfig = {
18+
source: '',
19+
id: ''
20+
};
21+
protected mathJaxFallback: MathJaxConfig = {
22+
source: '',
23+
id: ''
24+
};
25+
26+
constructor() {
27+
super();
28+
29+
this.signal = new ReplaySubject<boolean>();
30+
this.signal.next(true);
31+
}
32+
33+
protected async registerMathJaxAsync(config: MathJaxConfig): Promise<any> {
34+
return Promise.resolve();
35+
}
36+
37+
ready(): Observable<boolean> {
38+
return this.signal;
39+
}
40+
41+
render(element: HTMLElement) {
42+
return;
43+
}
44+
}

src/app/item-page/field-components/metadata-values/metadata-values.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
<!-- Render value as markdown -->
1414
<ng-template #markdown let-value="value">
15-
<span class="dont-break-out" [innerHTML]="value | dsMarkdown | async">
15+
<span class="dont-break-out" [dsMarkdown]="value">
1616
</span>
1717
</ng-template>
1818

src/app/item-page/field-components/metadata-values/metadata-values.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class MetadataValuesComponent implements OnChanges {
3737
@Input() label: string;
3838

3939
/**
40-
* Whether the {@link MarkdownPipe} should be used to render these metadata values.
40+
* Whether the {@link MarkdownDirective} should be used to render these metadata values.
4141
* This will only have effect if {@link MarkdownConfig#enabled} is true.
4242
* Mathjax will only be rendered if {@link MarkdownConfig#mathjax} is true.
4343
*/

src/app/item-page/item-page.module.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ import { ThemedItemAlertsComponent } from './alerts/themed-item-alerts.component
6161
import {
6262
ThemedFullFileSectionComponent
6363
} from './full/field-components/file-section/themed-full-file-section.component';
64+
import { MarkdownViewerModule } from '../shared/markdown-viewer/markdown-viewer.module';
65+
6466

6567
const ENTRY_COMPONENTS = [
6668
// put only entry components that use custom decorator
@@ -123,6 +125,7 @@ const DECLARATIONS = [
123125
CrisItemPageModule,
124126
ContextMenuModule.withEntryComponents(),
125127
MiradorViewerModule,
128+
MarkdownViewerModule,
126129
],
127130
declarations: [
128131
...DECLARATIONS,

src/app/item-page/simple/field-components/specific-field/abstract/item-page-abstract-field.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class ItemPageAbstractFieldComponent extends ItemPageFieldComponent {
3737
label = 'item.page.abstract';
3838

3939
/**
40-
* Use the {@link MarkdownPipe} to render dc.description.abstract values
40+
* Use the {@link MarkdownDirective} to render dc.description.abstract values
4141
*/
4242
enableMarkdown = true;
4343
}

src/app/item-page/simple/field-components/specific-field/generic/generic-item-page-field.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export class GenericItemPageFieldComponent extends ItemPageFieldComponent {
3636
@Input() label: string;
3737

3838
/**
39-
* Whether the {@link MarkdownPipe} should be used to render this metadata.
39+
* Whether the {@link MarkdownDirective} should be used to render this metadata.
4040
*/
4141
@Input() enableMarkdown = false;
4242

src/app/item-page/simple/field-components/specific-field/item-page-field.component.spec.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ import { MetadataMap, MetadataValue } from '../../../../core/shared/metadata.mod
99
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
1010
import { createPaginatedList } from '../../../../shared/testing/utils.test';
1111
import { environment } from '../../../../../environments/environment';
12-
import { MarkdownPipe } from '../../../../shared/utils/markdown.pipe';
1312
import { SharedModule } from '../../../../shared/shared.module';
1413
import { APP_CONFIG } from '../../../../../config/app-config.interface';
1514
import { By } from '@angular/platform-browser';
1615
import { BrowseDefinitionDataService } from '../../../../core/browse/browse-definition-data.service';
1716
import { BrowseDefinitionDataServiceStub } from '../../../../shared/testing/browse-definition-data-service.stub';
1817
import { RouterTestingModule } from '@angular/router/testing';
18+
import { MarkdownDirective } from '../../../../shared/utils/markdown.directive';
19+
import { MathService } from '../../../../core/shared/math.service';
1920

2021
let comp: ItemPageFieldComponent;
2122
let fixture: ComponentFixture<ItemPageFieldComponent>;
@@ -51,14 +52,15 @@ describe('ItemPageFieldComponent', () => {
5152
],
5253
providers: [
5354
{ provide: APP_CONFIG, useValue: appConfig },
54-
{ provide: BrowseDefinitionDataService, useValue: BrowseDefinitionDataServiceStub }
55+
{ provide: BrowseDefinitionDataService, useValue: BrowseDefinitionDataServiceStub },
56+
{ provide: MathService, useValue: {} }
5557
],
5658
declarations: [ItemPageFieldComponent, MetadataValuesComponent],
5759
schemas: [NO_ERRORS_SCHEMA]
5860
}).overrideComponent(ItemPageFieldComponent, {
5961
set: { changeDetection: ChangeDetectionStrategy.Default }
6062
}).compileComponents();
61-
markdownSpy = spyOn(MarkdownPipe.prototype, 'transform');
63+
markdownSpy = spyOn(MarkdownDirective.prototype, 'render');
6264
fixture = TestBed.createComponent(ItemPageFieldComponent);
6365
comp = fixture.componentInstance;
6466
comp.item = mockItemWithMetadataFieldsAndValue(mockFields, mockValue);

0 commit comments

Comments
 (0)