Skip to content
Snippets Groups Projects
cloze-element.ts 5.02 KiB
Newer Older
  • Learn to ignore specific revisions
  •   CompoundElement,
      FontElement,
      FontProperties,
      InputElement,
      InputElementValue,
      LikertColumn,
      LikertRow,
      PositionedElement, PositionProperties,
      UIElement
    
    } from '../../models/uI-element';
    import { TextFieldElement } from '../text-field/text-field-element';
    import { TextAreaElement } from '../text-area/text-area-element';
    import { CheckboxElement } from '../checkbox/checkbox-element';
    import { DropdownElement } from '../dropdown/dropdown-element';
    import { DropListElement } from '../drop-list/drop-list';
    
    import { initFontElement, initPositionedElement } from '../../util/unit-interface-initializer';
    
    import { TextFieldSimpleElement } from '../textfield-simple/text-field-simple-element';
    
    import { DropListSimpleElement } from '../drop-list-simple/drop-list-simple';
    
    // TODO styles like em dont continue after inserted components
    
    
    export class ClozeElement extends CompoundElement implements PositionedElement, FontElement {
    
      text: string = '<p>Lorem ipsum dolor \\z sdfsdf \\i sdfsdf</p>';
    
      parts: {
        type: string;
        value: string | UIElement;
        style?: string;
      }[][] = [];
    
    rhenck's avatar
    rhenck committed
    
    
      childElements: InputElement[] = [];
    
    rhenck's avatar
    rhenck committed
    
    
      positionProps: PositionProperties;
    
      fontProps: FontProperties;
    
    rhenck's avatar
    rhenck committed
      constructor(serializedElement: UIElement) {
        super(serializedElement);
        Object.assign(this, serializedElement);
    
        this.positionProps = initPositionedElement(serializedElement);
    
        this.fontProps = initFontElement(serializedElement);
    
    rhenck's avatar
    rhenck committed
        this.height = 200;
    
        this.width = 500; // TODO
    
    rhenck's avatar
    rhenck committed
      }
    
      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':
    
    rhenck's avatar
    rhenck committed
            newElement = new TextFieldSimpleElement(elementModel);
    
    rhenck's avatar
    rhenck committed
            (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;
    
            newElement.onlyOneItem = true;
    
    rhenck's avatar
    rhenck committed
            break;
          default:
            throw new Error(`ElementType ${elementModel.type} not found!`);
        }
        return newElement;
      }
    }