Skip to content
Snippets Groups Projects
cloze-element.ts 4.69 KiB
Newer Older
  • Learn to ignore specific revisions
  • import {
      CompoundElement, InputElement, InputElementValue, UIElement
    } from '../uI-element';
    
    import {
      ClozePart, FontElement, LikertColumn, LikertRow
    } from '../../interfaces/UIElementInterfaces';
    
    rhenck's avatar
    rhenck committed
    import { TextFieldElement } from '../text-field-element';
    import { TextAreaElement } from '../text-area-element';
    import { CheckboxElement } from '../checkbox-element';
    import { DropdownElement } from '../dropdown-element';
    import { DropListElement } from './drop-list';
    
    import { initFontElement } from '../../util/unit-interface-initializer';
    
    
    // TODO styles like em dont continue after inserted components
    
    
    export class ClozeElement extends CompoundElement implements FontElement {
    
      text: string = '<p>Lorem ipsum dolor \\z</p>';
    
    rhenck's avatar
    rhenck committed
    
      parts: ClozePart[][] = [];
    
      childElements: InputElement[] = [];
    
    rhenck's avatar
    rhenck committed
    
    
      fontColor: string = 'black';
      font: string = 'Roboto';
      fontSize: number = 20;
      lineHeight: number = 120;
      bold: boolean = false;
      italic: boolean = false;
      underline: boolean = false;
    
    
    rhenck's avatar
    rhenck committed
      constructor(serializedElement: UIElement) {
        super(serializedElement);
        Object.assign(this, serializedElement);
    
        Object.assign(this, initFontElement(serializedElement));
    
    rhenck's avatar
    rhenck committed
        this.height = 200;
        this.width = 500;
      }
    
      setProperty(property: string, value: InputElementValue | string[] | LikertColumn[] | LikertRow[]): void {
        super.setProperty(property, value);
    
        if (property === 'text') {
          this.createParts(value as string);
        }
      }
    
      private createParts(htmlText: string): void {
    
        const elementList = ClozeElement.readElementArray(htmlText);
    
    rhenck's avatar
    rhenck committed
    
        this.parts = [];
    
        elementList.forEach((element: HTMLParagraphElement | HTMLHeadingElement, i: number) => {
          this.parseParagraphs(element, i);
    
    rhenck's avatar
    rhenck committed
        });
        // console.log('PARTS:', this.parts);
      }
    
    
      private static readElementArray(htmlText: string): (HTMLParagraphElement | HTMLHeadingElement)[] {
    
    rhenck's avatar
    rhenck committed
        const el = document.createElement('html');
        el.innerHTML = htmlText;
    
        return Array.from(el.children[1].children) as (HTMLParagraphElement | HTMLHeadingElement)[];
    
    rhenck's avatar
    rhenck committed
      }
    
    
      private parseParagraphs(element: HTMLParagraphElement | HTMLHeadingElement, partIndex: number): void {
    
    rhenck's avatar
    rhenck committed
        this.parts[partIndex] = []; // init array to be able to push
    
        let [nextSpecialElementIndex, nextElementType] = ClozeElement.getNextSpecialElement(element.innerHTML);
    
    rhenck's avatar
    rhenck committed
        let indexOffset = 0;
    
        while (nextSpecialElementIndex !== -1) {
          nextSpecialElementIndex += indexOffset;
    
          this.parts[partIndex].push({
            type: element.localName,
            value: element.innerHTML.substring(indexOffset, nextSpecialElementIndex),
            style: element.style.cssText
          });
    
    rhenck's avatar
    rhenck committed
    
          const newElement = ClozeElement.createElement(nextElementType);
          this.childElements.push(newElement);
          this.parts[partIndex].push({ type: nextElementType, value: newElement });
    
          indexOffset = nextSpecialElementIndex + 2; // + 2 to get rid of the marker, i.e. '\b'
          [nextSpecialElementIndex, nextElementType] =
    
            ClozeElement.getNextSpecialElement(element.innerHTML.substring(indexOffset));
    
    rhenck's avatar
    rhenck committed
        }
    
        this.parts[partIndex].push({
          type: element.localName,
          value: element.innerHTML.substring(indexOffset),
          style: element.style.cssText
        });
    
    rhenck's avatar
    rhenck committed
      }
    
      private static getNextSpecialElement(p: string): [number, string] {
        const x = [];
        if (p.indexOf('\\d') > 0) {
          x.push(p.indexOf('\\d'));
        }
        if (p.indexOf('\\i') > 0) {
          x.push(p.indexOf('\\i'));
        }
        if (p.indexOf('\\z') > 0) {
          x.push(p.indexOf('\\z'));
        }
    
        const y = Math.min(...x);
        let nextElementType = '';
        switch (p[y + 1]) {
          case 'd': nextElementType = 'dropdown'; break;
          case 'i': nextElementType = 'text-field'; break;
          case 'z': nextElementType = 'drop-list'; break;
          default: return [-1, 'unknown'];
        }
        return [y, nextElementType];
      }
    
    
      private static createElement(elementType: string): InputElement {
    
    rhenck's avatar
    rhenck committed
        const elementModel: UIElement = { type: elementType } as UIElement;
    
        let newElement: InputElement;
    
    rhenck's avatar
    rhenck committed
        switch (elementModel.type) {
          case 'text-field':
            newElement = new TextFieldElement(elementModel);
            (newElement as TextFieldElement).label = '';
            break;
          case 'text-area':
            newElement = new TextAreaElement(elementModel);
            break;
          case 'checkbox':
            newElement = new CheckboxElement(elementModel);
            break;
          case 'dropdown':
            newElement = new DropdownElement(elementModel);
            break;
          case 'drop-list':
            newElement = new DropListElement(elementModel);
            newElement.height = 30;
            newElement.width = 100;
            break;
          default:
            throw new Error(`ElementType ${elementModel.type} not found!`);
        }
        return newElement;
      }
    }