Skip to content
Snippets Groups Projects
markable-support.ts 4.67 KiB
Newer Older
import { ApplicationRef, createComponent, Renderer2 } from '@angular/core';
import { TextComponent } from 'common/components/text/text.component';
import { Markable, MarkablesContainer } from 'player/src/app/models/markable.interface';
jojohoch's avatar
jojohoch committed
import {
  MarkablesContainerComponent
} from 'player/src/app/components/elements/markables-container/markables-container.component';

export class MarkableSupport {
  private renderer: Renderer2;
  private applicationRef: ApplicationRef;
  static wordsWithWhitespace: RegExp = /(\s*\S+\s*)|(s+\S*\s*)|(s*\S*\s+)/g;
  static prefix: RegExp = /\W+(?=\w+)/u;
  static word: RegExp = /\w+/u;
  static suffix: RegExp = /\W+$/u;

jojohoch's avatar
jojohoch committed
  constructor(
    renderer: Renderer2,
    applicationRef: ApplicationRef
  ) {
    this.renderer = renderer;
    this.applicationRef = applicationRef;
  }
  createMarkables(savedMarks: string[], elementComponent: TextComponent): void {
    const nodes = MarkableSupport.findNodes(elementComponent.textContainerRef.nativeElement.childNodes);
    const markablesContainers = MarkableSupport.getMarkablesContainers(nodes, savedMarks);
    this.markables = markablesContainers
jojohoch's avatar
jojohoch committed
      .flatMap((markablesContainer: MarkablesContainer) => markablesContainer.markables);
    this.createComponents(markablesContainers, elementComponent);
  private createComponents(markablesContainers: MarkablesContainer[], elementComponent: TextComponent): void {
jojohoch's avatar
jojohoch committed
    markablesContainers.forEach((markablesContainer: MarkablesContainer) => {
      const node = markablesContainer.node;
      const markableContainerElement = this.renderer.createElement('markable-container');
      node.parentNode?.replaceChild(markableContainerElement, node);
      const environmentInjector = this.applicationRef.injector;
      const componentRef = createComponent(MarkablesContainerComponent, {
        environmentInjector,
        hostElement: markableContainerElement
      });
      componentRef.instance.markables = markablesContainer.markables;
      componentRef.instance.selectedColor = elementComponent.selectedColor;
jojohoch's avatar
jojohoch committed
      componentRef.instance.markablesChange.subscribe(() => {
        elementComponent.elementValueChanged.emit(
jojohoch's avatar
jojohoch committed
          {
            id: elementComponent.elementModel.id,
jojohoch's avatar
jojohoch committed
          }
        );
      });
jojohoch's avatar
jojohoch committed
      this.applicationRef.attachView(componentRef.hostView);
    });
  }

  private static getMarkablesContainers(nodes: Node[], savedMarks: string[]): MarkablesContainer[] {
jojohoch's avatar
jojohoch committed
    const markablesContainers: MarkablesContainer[] = [];
    let wordsCount = 0;
    nodes.forEach((node: Node) => {
      const currentNodes = MarkableSupport.getMarkablesContainer(node, wordsCount, savedMarks);
jojohoch's avatar
jojohoch committed
      wordsCount += currentNodes.markables.length;
      markablesContainers.push(currentNodes);
    });
    return markablesContainers;
  }

  private static getMarkablesContainer(node: Node, wordsCount: number, savedMarks: string[]): MarkablesContainer {
jojohoch's avatar
jojohoch committed
    return {
      node: node,
      markables: MarkableSupport.getMarkables(node.textContent || '', wordsCount, savedMarks)
  private static getMarkables(text: string, startIndex: number, savedMarks: string[]): Markable[] {
jojohoch's avatar
jojohoch committed
    const markables: Markable[] = [];
    const wordsWithWhitespace = text?.match(MarkableSupport.wordsWithWhitespace);
jojohoch's avatar
jojohoch committed
    wordsWithWhitespace?.forEach((wordWithWhitespace: string, index: number) => {
      const prefix = wordWithWhitespace.match(MarkableSupport.prefix);
      const word = wordWithWhitespace.match(MarkableSupport.word);
      const suffix = wordWithWhitespace.match(MarkableSupport.suffix);
jojohoch's avatar
jojohoch committed
      const id = startIndex + index;
      const color = MarkableSupport.getColorValueById(id, savedMarks);
jojohoch's avatar
jojohoch committed
      markables.push(
        {
jojohoch's avatar
jojohoch committed
          id: id,
jojohoch's avatar
jojohoch committed
          prefix: prefix ? prefix[0] : '',
          word: word ? word[0] : '',
jojohoch's avatar
jojohoch committed
          suffix: suffix ? suffix[0] : '',
          isActive: !!(word && word[0].length),
          color: color
jojohoch's avatar
jojohoch committed
        }
      );
    });
    return markables;
  }

  private static findNodes(childList: Node[] | NodeListOf<ChildNode>): Node[] {
jojohoch's avatar
jojohoch committed
    const nodes: Node[] = [];
    childList.forEach((node: Node) => {
      if (node.nodeType === Node.TEXT_NODE && !nodes.includes(node) && node.textContent) {
jojohoch's avatar
jojohoch committed
        nodes.push(node);
      }
      if (node.nodeType === Node.ELEMENT_NODE) {
        if (node.childNodes.length) {
          nodes.push(...MarkableSupport.findNodes(node.childNodes));
jojohoch's avatar
jojohoch committed
        }
      }
    });
    return nodes;
  }
  private static getColorValueById(id: number, savedMarks: string[]): string | null {
    const savedMarkById = savedMarks.map(savedMark => savedMark.split('-'))
      .find(mark => mark[0] === id.toString());
    return savedMarkById ? savedMarkById[2] : null;
jojohoch's avatar
jojohoch committed
  }