edition-data.service.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import { HttpClient } from '@angular/common/http';
  2. import { Injectable } from '@angular/core';
  3. import { forkJoin, Observable, of } from 'rxjs';
  4. import { catchError, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators';
  5. import { AppConfig } from '../app.config';
  6. import { OriginalEncodingNodeType } from '../models/evt-models';
  7. import { parseXml } from '../utils/xml-utils';
  8. @Injectable({
  9. providedIn: 'root',
  10. })
  11. export class EditionDataService {
  12. private editionUrls = AppConfig.evtSettings.files.editionUrls || [];
  13. public parsedEditionSource$: Observable<OriginalEncodingNodeType> = this.loadAndParseEditionData();
  14. constructor(
  15. private http: HttpClient,
  16. ) {
  17. }
  18. private loadAndParseEditionData() {
  19. const editionUrl = this.editionUrls[0];
  20. return this.http.get(editionUrl, { responseType: 'text' }).pipe(
  21. map(source => parseXml(source)),
  22. mergeMap((editionData) => this.loadXIinclude(editionData, editionUrl.substring(0, editionUrl.lastIndexOf('/') + 1))),
  23. publishReplay(1),
  24. refCount(),
  25. catchError(() => this.handleLoadingError()),
  26. );
  27. }
  28. loadXIinclude(doc: HTMLElement, baseUrlPath: string) {
  29. const filesToInclude = Array.from(doc.getElementsByTagName('xi:include'));
  30. const xiIncludeLoadsSubs = filesToInclude.map(element =>
  31. this.http.get(baseUrlPath + element.getAttribute('href'), { responseType: 'text' })
  32. .pipe(
  33. tap((fileData) => {
  34. const includedDoc = parseXml(fileData);
  35. const fileXpointer = element.getAttribute('xpointer');
  36. let includedTextElem: Node;
  37. if (fileXpointer) {
  38. includedTextElem = doc.querySelector(`[*|id="${fileXpointer}"]`) || includedDoc.querySelector('text');
  39. } else {
  40. includedTextElem = includedDoc.querySelector('text');
  41. }
  42. // element.parentNode.replaceChild(includedTextElem, element);
  43. element.parentNode.appendChild(includedTextElem);
  44. }),
  45. catchError(_ => {
  46. Array.from(element.getElementsByTagName('xi:fallback')).map((el) => {
  47. const divEl = document.createElement('div');
  48. divEl.classList.add('xiinclude-fallback');
  49. divEl.setAttribute('xml:id', element.getAttribute('xpointer'));
  50. divEl.innerHTML = `<p>${el.innerHTML}</p>`;
  51. return divEl;
  52. }).forEach((el) => element.parentNode.replaceChild(el, element));
  53. return of(doc);
  54. }),
  55. ));
  56. if (xiIncludeLoadsSubs.length > 0) {
  57. return forkJoin(xiIncludeLoadsSubs).pipe(map(() => doc));
  58. }
  59. return of(doc);
  60. }
  61. private handleLoadingError() {
  62. // TODO: TEMP
  63. const errorEl: HTMLElement = document.createElement('div');
  64. if (!this.editionUrls || this.editionUrls.length === 0) {
  65. errorEl.textContent = 'Missing configuration for edition files. Data cannot be loaded.';
  66. } else {
  67. errorEl.textContent = 'There was an error in loading edition files.';
  68. }
  69. return of(errorEl);
  70. }
  71. }