This is the codeAbility Sharing Platform! Learn more about the codeAbility Sharing Platform.

Skip to content
Snippets Groups Projects
markDownViewer.component.ts 3.38 KiB
Newer Older
import { Component, OnInit, Input, OnChanges, SimpleChanges } from '@angular/core';
import { Exercise } from 'app/shared/model/exercise.model';
import { ExerciseService } from '../service/exercise.service';
import { TranslateService } from '@ngx-translate/core';
import { MarkdownService } from 'ngx-markdown';
import { KatexOptions, errorKatexNotLoaded } from 'ngx-markdown';

declare let katex: any; // Magic
Michael Breu's avatar
Michael Breu committed

@Component({
  selector: 'jhi-markdown-viewer',
  templateUrl: './markDownViewer.component.html',
  styleUrls: ['./markDownViewer.component.scss'],
Michael Breu's avatar
Michael Breu committed
})
export class MarkDownViewerComponent implements OnInit, OnChanges {
  @Input() exercise: Exercise | undefined;
  public filename: string;
  public content: string;
  public baseURL: string;
  public katexOptions: KatexOptions = {
    displayMode: true,
    throwOnError: false,
    errorColor: '#cc0000',
  };
Michael Breu's avatar
Michael Breu committed

  constructor(private exerciseService: ExerciseService, private translate: TranslateService, private markdownService: MarkdownService) {
    this.filename = 'README.md';
    this.content = '';
    this.baseURL = '';
  }
Michael Breu's avatar
Michael Breu committed

  ngOnInit(): void {
    this.markdownService.renderKatex = (html: string, options?: KatexOptions) => this.renderKatex(html, options);
  }
Michael Breu's avatar
Michael Breu committed

  /** helper function for special latex rendering with katex */
  private renderKatex(html: string, options?: KatexOptions): string {
    if (typeof katex === 'undefined' || typeof katex.renderToString === 'undefined') {
      throw new Error(errorKatexNotLoaded);
    const reDollar = /\$([^\s][^$]*?[^\s])\$/gm;
    const html2 = html.replace(reDollar, (_, tex) => this.renderSanitizedLatex(tex, options));
    const reMath = /<code class="language-math">((.|\n|\r])*?)<\/code>/gi;
    return html2.replace(reMath, (_, tex) => this.renderSanitizedLatex(tex, options));
  }
Michael Breu's avatar
Michael Breu committed

  /** wrapper to sanitize latex before rendering with latex */
  private renderSanitizedLatex(latex: string, options?: KatexOptions): string {
    return katex.renderToString(this.replaceConfusingCharKatex(latex), options);
  }
Michael Breu's avatar
Michael Breu committed

  /** these html entities must be reverted, in order to work with katex :-( */
  private replaceConfusingCharKatex(html: string): string {
    return html
      .replace(/&amp;/gm, '&')
      .replace(/&lt;/gm, '<')
      .replace(/&gt;/gm, '>')
      .replace(/&#183;/gm, '·')
      .replace(/&#10;/gm, '·')
      .replace(/( \\ )+/gm, ' \\\\ ');
  }

  /** This is a hard core workaround, setting the baseUrl before content is rendered */

  getContentAndSetBaseURL(): string {
    this.markdownService.options.baseUrl = this.baseURL;
    return this.content;
  }

  // eslint-disable-next-line
  ngOnChanges(changes: SimpleChanges): void {
    if (!this.exercise) return;
    this.baseURL = this.exercise.originalResult.project.url + '/-/raw/master/README.md';
    this.exerciseService.loadExerciseFile(this.exercise.originalResult.project.project_id, 'README.md').subscribe(
      content => {
        this.content = content;
        this.filename = 'README.md';
      },
      () => {
        this.exerciseService.loadExerciseFile(this.exercise!.originalResult.project.project_id, 'exercise.md').subscribe(
          content => {
            this.content = content;
            this.filename = 'exercise.md';
          },
          () => {
            this.content = this.translate.instant('exercise.details.readmeNotFound');
            this.filename = '';
          }
        );
      }
    );
  }
Michael Breu's avatar
Michael Breu committed
}