Skip to content
Snippets Groups Projects
markable-support.ts 5.37 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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/markables-container/markables-container.component';
    
    export class MarkableSupport {
      private renderer: Renderer2;
      private applicationRef: ApplicationRef;
    
      // eslint-disable-next-line max-len
      private static wordsWithWhitespace: RegExp = /[^(\p{L}|\d)]*(\p{L}|\d)+[^(\p{L}|\d)]*|[^(\p{L}|\d)]+(\p{L}|\d)*[^(\p{L}|\d)]*|[^(\p{L}|\d)]*(\p{L}|\d)*[^(\p{L}|\d)]+/gu;
      private static prefix: RegExp = /[^(\p{L}|\d)]+(?=(\p{L}|\d)+)/u;
      private static word: RegExp = /(\p{L}|\d)+/u;
      private static suffix: RegExp = /[^(\p{L}|\d)]+$/u;
    
    jojohoch's avatar
    jojohoch committed
      constructor(
    
        renderer: Renderer2,
        applicationRef: ApplicationRef
      ) {
        this.renderer = renderer;
        this.applicationRef = applicationRef;
      }
    
      createMarkables(savedMarks: string[], elementComponent: TextComponent): void {
    
    jojohoch's avatar
    jojohoch committed
        const nodes = MarkableSupport.getNodes(elementComponent.textContainerRef.nativeElement.childNodes);
    
        const markablesContainers = MarkableSupport
          .getMarkablesContainers(nodes, MarkableSupport.expandSavedMarks(savedMarks));
    
    jojohoch's avatar
    jojohoch committed
        const markables = markablesContainers
    
    jojohoch's avatar
    jojohoch committed
          .flatMap((markablesContainer: MarkablesContainer) => markablesContainer.markables);
    
    jojohoch's avatar
    jojohoch committed
        this.createComponents(markablesContainers, elementComponent, markables);
    
      private static expandSavedMarks(savedMarks: string[]): string[] {
        return savedMarks.flatMap(range => {
          const [start, end, color] = range.split('-');
          const startIndex = parseInt(start, 10);
          const endIndex = parseInt(end, 10);
          return Array.from({ length: endIndex - startIndex + 1 }, (_, i) => {
            const currentIdx = startIndex + i;
            return `${currentIdx}-${currentIdx}-${color}`;
          });
        });
      }
    
    
    jojohoch's avatar
    jojohoch committed
      private createComponents(markablesContainers: MarkablesContainer[],
                               elementComponent: TextComponent,
                               markables: Markable[]): void {
        markablesContainers.map((markablesContainer: MarkablesContainer) => this
          .createMarkablesContainer(markablesContainer, elementComponent, markables)
        );
      }
    
      private createMarkablesContainer(markablesContainer: MarkablesContainer,
                                       elementComponent: TextComponent,
                                       markables: Markable[]): void {
        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;
        componentRef.instance.markablesChange.subscribe(() => {
          elementComponent.elementValueChanged.emit(
            {
              id: elementComponent.elementModel.id,
              value: markables
            }
          );
    
    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
        let offset = 0;
        return nodes.map((node: Node) => {
          const currentNodes = MarkableSupport.getMarkablesContainer(node, offset, savedMarks);
          offset += currentNodes.markables.length;
          return currentNodes;
    
    jojohoch's avatar
    jojohoch committed
      private static getMarkablesContainer(node: Node, offset: number, savedMarks: string[]): MarkablesContainer {
    
    jojohoch's avatar
    jojohoch committed
        return {
          node: node,
    
    jojohoch's avatar
    jojohoch committed
          markables: MarkableSupport.getMarkables(node.textContent || '', offset, savedMarks)
    
      private static getMarkables(text: string, startIndex: number, savedMarks: string[]): Markable[] {
    
        const wordsWithWhitespace = text?.match(MarkableSupport.wordsWithWhitespace);
    
    jojohoch's avatar
    jojohoch committed
        return wordsWithWhitespace?.map((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
          return {
            id: id,
            prefix: prefix ? prefix[0] : '',
            word: word ? word[0] : '',
            suffix: suffix ? suffix[0] : '',
            isActive: !!(word && word[0].length),
            color: color
          };
        }) || [];
    
    jojohoch's avatar
    jojohoch committed
      private static getNodes(childList: Node[] | NodeListOf<ChildNode>): Node[] {
        return Array.from(childList).reduce((nodes: Node[], 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) {
    
    jojohoch's avatar
    jojohoch committed
              nodes.push(...MarkableSupport.getNodes(node.childNodes));
    
    jojohoch's avatar
    jojohoch committed
          return nodes;
        }, []);
    
      private static getColorValueById(id: number, savedMarks: string[]): string | null {
    
    jojohoch's avatar
    jojohoch committed
        return savedMarks.map(savedMark => savedMark.split('-'))
          .find(mark => mark[0] === id.toString())?.[2] || null;
    
    jojohoch's avatar
    jojohoch committed
      }