Fix issue with images not loading on user-guide for local development builds (plus an optional innerHTML alternative (disabled by default))

This commit is contained in:
George Kalampokis 2020-12-31 16:34:36 +02:00
parent 0367aa0431
commit c56b7c59ed
3 changed files with 104 additions and 25 deletions

View File

@ -1,11 +1,6 @@
<!-- <div class="container-fluid">
<div class="row">
<span class="custom-header">User Guide Editor</span>
</div>
</div> -->
<!-- <div id='userguide' [innerHTML]="guideHTML"></div> -->
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<iframe *ngIf="guideHTMLUrl" class="iframe" id='userguide' [src]="guideHTMLUrl"></iframe> <iframe *ngIf="!useInnerHTML && guideHTMLUrl" class="iframe" id='userguide' [src]="guideHTMLUrl"></iframe>
<span #guide *ngIf="useInnerHTML" id='userguide' [innerHTML]="guideHTML"></span>
</div> </div>
</div> </div>

View File

@ -25,3 +25,7 @@
margin: 0px; margin: 0px;
border: none; border: none;
} }
:host :hover ::ng-deep .tocElement {
cursor: pointer !important;
}

View File

@ -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 { UserGuideService } from '@app/core/services/user-guide/user-guide.service';
import { BaseComponent } from '@common/base/base.component'; import { BaseComponent } from '@common/base/base.component';
import { takeUntil } from 'rxjs/internal/operators/takeUntil'; import { takeUntil } from 'rxjs/internal/operators/takeUntil';
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
import { LanguageService } from '@app/core/services/language/language.service'; import { LanguageService } from '@app/core/services/language/language.service';
import { SecurityContext } from '@angular/core';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { MatomoService } from '@app/core/services/matomo/matomo-service'; 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({ @Component({
selector: 'app-user-guide-content', selector: 'app-user-guide-content',
templateUrl: './user-guide-content.component.html', templateUrl: './user-guide-content.component.html',
styleUrls: ['./user-guide-content.component.scss'] 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; guideHTML: any;
guideHTMLUrl: SafeResourceUrl; guideHTMLUrl: SafeResourceUrl;
sanitizedGuideUrl: any; sanitizedGuideUrl: any;
private scrollEvent: EventListener; private scrollEvent: EventListener;
private tocScrollEvent: EventListener;
private _transformed: Subject<boolean> = new Subject();
private _parsed: Subject<boolean> = new Subject();
@ViewChild('guide', {static: false}) guide: ElementRef;
constructor( constructor(
private userGuideService: UserGuideService, private userGuideService: UserGuideService,
private sanitizer: DomSanitizer, private sanitizer: DomSanitizer,
private languageService: LanguageService, private languageService: LanguageService,
private httpClient: HttpClient, private httpClient: HttpClient,
private matomoService: MatomoService private matomoService: MatomoService,
private configurationService: ConfigurationService
) { super(); } ) { super(); }
ngOnInit() { ngOnInit() {
this.matomoService.trackPageView('User Guide Content'); this.matomoService.trackPageView('User Guide Content');
this.scrollEvent = ((ev) => this.scroll(ev)); this.scrollEvent = ((ev) => this.scroll(ev));
this.tocScrollEvent = ((ev) => {
this.activeToc(ev);
this.scroll(ev);
});
this.userGuideService.getUserGuide(this.languageService.getCurrentLanguage()) this.userGuideService.getUserGuide(this.languageService.getCurrentLanguage())
.pipe(takeUntil(this._destroyed)) .pipe(takeUntil(this._destroyed))
.subscribe(response => { .subscribe(response => {
const blob = new Blob([response.body], { type: 'text/html' }); const blob = new Blob([response.body], { type: 'text/html' });
if (this.useInnerHTML) {
this.readBlob(blob); this.readBlob(blob);
} else {
this.guideHTMLUrl = this.sanitizer.bypassSecurityTrustResourceUrl((window.URL ? URL : webkitURL).createObjectURL(blob)); 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); // this.sanitizedGuideUrl = this.sanitizer.sanitize(SecurityContext.URL, this.guideHTMLUrl);
// console.log(this.guideHTMLUrl); // console.log(this.guideHTMLUrl);
}); });
} }
ngAfterViewChecked(): void {
this.parse();
}
readBlob(blob: Blob) { readBlob(blob: Blob) {
const fr = new FileReader(); const fr = new FileReader();
fr.onload = ev => { fr.onload = ev => {
this.guideHTML = this.sanitizer.bypassSecurityTrustHtml(fr.result as string); const HTMLstring = fr.result as string;
this.parse(); 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); 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) { 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() { private parse() {
const specialElements: HTMLCollection = document.getElementsByClassName('href'); const specialElements: HTMLCollection = document.getElementsByTagName('a');
for (let i = 0; i < specialElements.length; i++) { for (let i = 0; i < specialElements.length; i++) {
const element = specialElements.item(i); const element = specialElements.item(i) as HTMLAnchorElement;
if(element.href.includes('#')) {
this.hrefToPath(element);
element.removeEventListener('click', this.scrollEvent); element.removeEventListener('click', this.scrollEvent);
element.addEventListener('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));
} }
} }