From c56b7c59edcc0a50616811c94531a77334f97486 Mon Sep 17 00:00:00 2001 From: George Kalampokis Date: Thu, 31 Dec 2020 16:34:36 +0200 Subject: [PATCH] Fix issue with images not loading on user-guide for local development builds (plus an optional innerHTML alternative (disabled by default)) --- .../user-guide-content.component.html | 9 +- .../user-guide-content.component.scss | 4 + .../user-guide-content.component.ts | 116 +++++++++++++++--- 3 files changed, 104 insertions(+), 25 deletions(-) diff --git a/dmp-frontend/src/app/ui/user-guide/user-guide-content/user-guide-content.component.html b/dmp-frontend/src/app/ui/user-guide/user-guide-content/user-guide-content.component.html index 325bbfb73..e795bc016 100644 --- a/dmp-frontend/src/app/ui/user-guide/user-guide-content/user-guide-content.component.html +++ b/dmp-frontend/src/app/ui/user-guide/user-guide-content/user-guide-content.component.html @@ -1,11 +1,6 @@ - -
- + +
diff --git a/dmp-frontend/src/app/ui/user-guide/user-guide-content/user-guide-content.component.scss b/dmp-frontend/src/app/ui/user-guide/user-guide-content/user-guide-content.component.scss index 847345f67..e14027d67 100644 --- a/dmp-frontend/src/app/ui/user-guide/user-guide-content/user-guide-content.component.scss +++ b/dmp-frontend/src/app/ui/user-guide/user-guide-content/user-guide-content.component.scss @@ -25,3 +25,7 @@ margin: 0px; border: none; } + +:host :hover ::ng-deep .tocElement { + cursor: pointer !important; +} diff --git a/dmp-frontend/src/app/ui/user-guide/user-guide-content/user-guide-content.component.ts b/dmp-frontend/src/app/ui/user-guide/user-guide-content/user-guide-content.component.ts index ae21bbd20..b2f0ee14b 100644 --- a/dmp-frontend/src/app/ui/user-guide/user-guide-content/user-guide-content.component.ts +++ b/dmp-frontend/src/app/ui/user-guide/user-guide-content/user-guide-content.component.ts @@ -1,72 +1,152 @@ -import { Component, OnInit, AfterViewChecked } from '@angular/core'; +import { Component, OnInit, AfterViewChecked, ViewEncapsulation, ViewChild, AfterContentInit, AfterViewInit } from '@angular/core'; import { UserGuideService } from '@app/core/services/user-guide/user-guide.service'; import { BaseComponent } from '@common/base/base.component'; import { takeUntil } from 'rxjs/internal/operators/takeUntil'; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; import { LanguageService } from '@app/core/services/language/language.service'; -import { SecurityContext } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { MatomoService } from '@app/core/services/matomo/matomo-service'; +import { ElementRef } from '@angular/core'; +import { interval, Subject } from 'rxjs'; +import { ConfigurationService } from '@app/core/services/configuration/configuration.service'; @Component({ selector: 'app-user-guide-content', templateUrl: './user-guide-content.component.html', styleUrls: ['./user-guide-content.component.scss'] }) -export class UserGuideContentComponent extends BaseComponent implements OnInit, AfterViewChecked { - +export class UserGuideContentComponent extends BaseComponent implements OnInit { + readonly useInnerHTML: boolean = false; //GK: Change for TESTING PURPOSES ONLY guideHTML: any; guideHTMLUrl: SafeResourceUrl; sanitizedGuideUrl: any; private scrollEvent: EventListener; + private tocScrollEvent: EventListener; + private _transformed: Subject = new Subject(); + private _parsed: Subject = new Subject(); + + @ViewChild('guide', {static: false}) guide: ElementRef; constructor( private userGuideService: UserGuideService, private sanitizer: DomSanitizer, private languageService: LanguageService, private httpClient: HttpClient, - private matomoService: MatomoService + private matomoService: MatomoService, + private configurationService: ConfigurationService ) { super(); } ngOnInit() { this.matomoService.trackPageView('User Guide Content'); this.scrollEvent = ((ev) => this.scroll(ev)); + this.tocScrollEvent = ((ev) => { + this.activeToc(ev); + this.scroll(ev); + }); this.userGuideService.getUserGuide(this.languageService.getCurrentLanguage()) .pipe(takeUntil(this._destroyed)) .subscribe(response => { const blob = new Blob([response.body], { type: 'text/html' }); - this.readBlob(blob); - this.guideHTMLUrl = this.sanitizer.bypassSecurityTrustResourceUrl((window.URL ? URL : webkitURL).createObjectURL(blob)); + if (this.useInnerHTML) { + this.readBlob(blob); + } else { + this.guideHTMLUrl = this.sanitizer.bypassSecurityTrustResourceUrl((window.URL ? URL : webkitURL).createObjectURL(blob)); + //GK: In case the app is in localhost (dev/debug build) apply the following transformation + //in order to show the user guide's images + if (this.guideHTMLUrl && this.configurationService.app.includes('localhost')) { + interval(1000).pipe(takeUntil(this._transformed)).subscribe(() => this.transform()); + } + } + + // this.guideHTML = this.sanitizer.sanitize(SecurityContext.HTML, blob); // this.sanitizedGuideUrl = this.sanitizer.sanitize(SecurityContext.URL, this.guideHTMLUrl); // console.log(this.guideHTMLUrl); }); } - ngAfterViewChecked(): void { - this.parse(); - } - readBlob(blob: Blob) { const fr = new FileReader(); fr.onload = ev => { - this.guideHTML = this.sanitizer.bypassSecurityTrustHtml(fr.result as string); - this.parse(); + const HTMLstring = fr.result as string; + this.guideHTML = this.sanitizer.bypassSecurityTrustHtml(HTMLstring); + this.addScripts(HTMLstring); + if (this.guideHTML) { + interval(1000).pipe(takeUntil(this._transformed)).subscribe(() => this.transform()); + interval(1000).pipe(takeUntil(this._parsed)).subscribe(() => this.parse()); + } }; fr.readAsText(blob); } + activeToc(ev: Event) { + const current = document.getElementsByClassName('active'); + if (current.length > 0) { + current[0].classList.remove('active'); + } + (ev.currentTarget as Element).classList.add('active'); + } + scroll(ev: Event) { - document.getElementById((ev.srcElement as any).getAttribute('path')).scrollIntoView({ behavior: 'smooth', block: 'start' }); + document.getElementById((ev.currentTarget as Element).getAttribute('path')).scrollIntoView({ behavior: 'smooth', block: 'start' }); } private parse() { - const specialElements: HTMLCollection = document.getElementsByClassName('href'); + const specialElements: HTMLCollection = document.getElementsByTagName('a'); for (let i = 0; i < specialElements.length; i++) { - const element = specialElements.item(i); - element.removeEventListener('click', this.scrollEvent); - element.addEventListener('click', this.scrollEvent); + const element = specialElements.item(i) as HTMLAnchorElement; + if(element.href.includes('#')) { + this.hrefToPath(element); + element.removeEventListener('click', this.scrollEvent); + element.addEventListener('click', this.scrollEvent); + } + if (!this._parsed.isStopped) { + this._parsed.next(true); + this._parsed.complete(); + } + } + + } + + private addScripts(HTMLString: string) { + const fragment = document.createRange().createContextualFragment(HTMLString); + this.guide.nativeElement.appendChild(fragment); + } + + transform() { + if (this.useInnerHTML) { + const tocElements = document.getElementsByClassName('nav-link'); + for (let i = 0; i < tocElements.length; i++) { + const href = (tocElements[i] as HTMLAnchorElement).href; + if (href.includes('#')) { + this.hrefToPath(tocElements[i] as HTMLAnchorElement); + tocElements[i].addEventListener('click', this.tocScrollEvent); + tocElements[i].classList.add('tocElement'); + if (!this._transformed.isStopped) { + this._transformed.next(true); + this._transformed.complete(); + } + } + } + } else { + const userguide = document.getElementById('userguide') as HTMLIFrameElement; + const images = userguide.contentWindow.document.getElementsByTagName('img'); + for (let i = 0; i < images.length; i++) { + const image = images[i]; + image.src = `${this.configurationService.app}${image.src}`; + if (!this._transformed.isStopped) { + this._transformed.next(true); + this._transformed.complete(); + } + } } } + private hrefToPath(element: HTMLAnchorElement) { + const href = element.href; + const hashtagIndex = href.lastIndexOf('#'); + element.removeAttribute('href'); + element.setAttribute('path', href.slice(hashtagIndex + 1)); + } + }