Skip to content
Snippets Groups Projects
element.ts 13.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • // eslint-disable-next-line max-classes-per-file
    
    import { ElementComponent } from 'common/directives/element-component.directive';
    import { Type } from '@angular/core';
    import { ClozeDocument } from 'common/models/elements/compound-elements/cloze/cloze';
    
    import { LikertRowElement } from 'common/models/elements/compound-elements/likert/likert-row';
    
    
    export type UIElementType = 'text' | 'button' | 'text-field' | 'text-field-simple' | 'text-area' | 'checkbox'
    
    jojohoch's avatar
    jojohoch committed
    | 'dropdown' | 'radio' | 'image' | 'audio' | 'video' | 'likert' | 'likert-row' | 'radio-group-images' | 'hotspot-image'
    
    rhenck's avatar
    rhenck committed
    | 'drop-list' | 'drop-list-simple' | 'cloze' | 'spell-correct' | 'slider' | 'frame' | 'toggle-button' | 'geometry'
    | 'math-field';
    
    
    export type UIElementValue = string | number | boolean | undefined | UIElementType | InputElementValue |
    
    jojohoch's avatar
    jojohoch committed
    TextLabel | TextLabel[] | ClozeDocument | LikertRowElement[] | Hotspot[] |
    
    PositionProperties | PlayerProperties | BasicStyles;
    
    export type InputAssistancePreset = null | 'french' | 'numbers' | 'numbersAndOperators' | 'numbersAndBasicOperators'
    
    | 'comparisonOperators' | 'squareDashDot' | 'placeValue' | 'space' | 'comma' | 'custom';
    
    
    export abstract class UIElement {
    
    rhenck's avatar
    rhenck committed
      [index: string]: unknown;
    
    rhenck's avatar
    rhenck committed
      id: string;
    
      type: UIElementType;
      width: number = 180;
      height: number = 60;
      position?: PositionProperties;
      styling?: BasicStyles & ExtendedStyles;
      player?: PlayerProperties;
    
    
    rhenck's avatar
    rhenck committed
      constructor(element: Partial<UIElement>) {
    
        if (!element.type) throw Error('Element has no type!');
        this.type = element.type;
    
    rhenck's avatar
    rhenck committed
        this.id = element.id || 'id_placeholder';
    
        if (element.width !== undefined) this.width = element.width;
        if (element.height !== undefined) this.height = element.height;
    
      }
    
      setProperty(property: string, value: UIElementValue): void {
    
    rhenck's avatar
    rhenck committed
        if (Array.isArray(this[property])) { // keep array reference intact
          (this[property] as UIElementValue[])
            .splice(0, (this[property] as UIElementValue[]).length, ...(value as UIElementValue[]));
        } else {
          this[property] = value;
        }
    
      }
    
      setStyleProperty(property: string, value: UIElementValue): void {
    
    rhenck's avatar
    rhenck committed
        (this.styling as BasicStyles & ExtendedStyles)[property] = value;
    
      }
    
      setPositionProperty(property: string, value: UIElementValue): void {
    
    rhenck's avatar
    rhenck committed
        (this.position as PositionProperties)[property] = value;
    
      }
    
      setPlayerProperty(property: string, value: UIElementValue): void {
    
    rhenck's avatar
    rhenck committed
        (this.player as PlayerProperties)[property] = value;
    
    rhenck's avatar
    rhenck committed
      getChildElements(): UIElement[] {
        return [];
    
      hasAnswerScheme(): boolean {
    
        return Boolean(this.getAnswerSchemeValues);
    
      abstract getElementComponent(): Type<ElementComponent>;
    
    rhenck's avatar
    rhenck committed
    
      static initPositionProps(defaults: Partial<PositionProperties> = {}): PositionProperties {
        return {
          fixedSize: defaults.fixedSize !== undefined ? defaults.fixedSize as boolean : false,
          dynamicPositioning: defaults.dynamicPositioning !== undefined ? defaults.dynamicPositioning as boolean : true,
          xPosition: defaults.xPosition !== undefined ? defaults.xPosition as number : 0,
          yPosition: defaults.yPosition !== undefined ? defaults.yPosition as number : 0,
          useMinHeight: defaults.useMinHeight !== undefined ? defaults.useMinHeight as boolean : false,
          gridColumn: defaults.gridColumn !== undefined ? defaults.gridColumn as number : null,
          gridColumnRange: defaults.gridColumnRange !== undefined ? defaults.gridColumnRange as number : 1,
          gridRow: defaults.gridRow !== undefined ? defaults.gridRow as number : null,
          gridRowRange: defaults.gridRowRange !== undefined ? defaults.gridRowRange as number : 1,
          marginLeft: defaults.marginLeft !== undefined ? defaults.marginLeft as number : 0,
          marginRight: defaults.marginRight !== undefined ? defaults.marginRight as number : 0,
          marginTop: defaults.marginTop !== undefined ? defaults.marginTop as number : 0,
          marginBottom: defaults.marginBottom !== undefined ? defaults.marginBottom as number : 0,
          zIndex: defaults.zIndex !== undefined ? defaults.zIndex as number : 0
        };
      }
    
      static initStylingProps<T>(defaults?: Partial<BasicStyles> & T): BasicStyles & T {
        return {
          ...defaults as T,
          fontColor: defaults?.fontColor !== undefined ? defaults.fontColor as string : '#000000',
          font: defaults?.font !== undefined ? defaults.font as string : 'Roboto',
          fontSize: defaults?.fontSize !== undefined ? defaults.fontSize as number : 20,
          bold: defaults?.bold !== undefined ? defaults.bold as boolean : false,
          italic: defaults?.italic !== undefined ? defaults.italic as boolean : false,
          underline: defaults?.underline !== undefined ? defaults.underline as boolean : false,
          backgroundColor: defaults?.backgroundColor !== undefined ? defaults.backgroundColor as string : '#d3d3d3'
        };
      }
    
      static createOptionLabel(optionText: string, addImg: boolean = false) {
        return {
          text: optionText,
          imgSrc: addImg ? null : undefined,
          imgPosition: addImg ? 'above' : undefined
        };
      }
    
    jojohoch's avatar
    jojohoch committed
    export type InputElementValue = string[] | string | number | boolean | TextLabel[] | null | Hotspot[] | boolean[];
    
    
    export abstract class InputElement extends UIElement {
      label: string = 'Beschriftung';
      value: InputElementValue = null;
      required: boolean = false;
      requiredWarnMessage: string = 'Eingabe erforderlich';
      readOnly: boolean = false;
    
    
    rhenck's avatar
    rhenck committed
      protected constructor(element: Partial<InputElement>) {
        super(element);
    
        if (element.label !== undefined) this.label = element.label;
        if (element.value !== undefined) this.value = element.value;
    
    rhenck's avatar
    rhenck committed
        if (element.required) this.required = element.required;
    
        if (element.requiredWarnMessage !== undefined) this.requiredWarnMessage = element.requiredWarnMessage;
    
    rhenck's avatar
    rhenck committed
        if (element.readOnly) this.readOnly = element.readOnly;
    
      abstract getAnswerScheme(options?: unknown): AnswerScheme;
    
    
      static stripHTML(htmlString: string): string {
        const parser = new DOMParser();
        const htmlDocument = parser.parseFromString(htmlString, 'text/html');
        return htmlDocument.documentElement.textContent || '';
      }
    
    export abstract class TextInputElement extends InputElement {
      inputAssistancePreset: InputAssistancePreset = null;
      inputAssistanceCustomKeys: string = '';
      inputAssistancePosition: 'floating' | 'right' = 'floating';
    
      inputAssistanceFloatingStartPosition: 'startBottom' | 'endCenter' = 'startBottom';
    
      restrictedToInputAssistanceChars: boolean = true;
      hasArrowKeys: boolean = false;
      showSoftwareKeyboard: boolean = false;
      softwareKeyboardShowFrench: boolean = false;
      protected constructor(element: Partial<TextInputElement>) {
        super(element);
        if (element.inputAssistancePreset) this.inputAssistancePreset = element.inputAssistancePreset;
        if (element.inputAssistanceCustomKeys !== undefined) {
          this.inputAssistanceCustomKeys = element.inputAssistanceCustomKeys;
        }
        if (element.inputAssistancePosition) this.inputAssistancePosition = element.inputAssistancePosition;
    
        if (element.inputAssistanceFloatingStartPosition) {
          this.inputAssistanceFloatingStartPosition = element.inputAssistanceFloatingStartPosition;
        }
    
        if (element.restrictedToInputAssistanceChars !== undefined) {
          this.restrictedToInputAssistanceChars = element.restrictedToInputAssistanceChars;
        }
        if (element.hasArrowKeys) this.hasArrowKeys = element.hasArrowKeys;
        if (element.showSoftwareKeyboard) this.showSoftwareKeyboard = element.showSoftwareKeyboard;
        if (element.softwareKeyboardShowFrench) this.softwareKeyboardShowFrench = element.softwareKeyboardShowFrench;
      }
    }
    
    
    export abstract class CompoundElement extends UIElement {
      abstract getChildElements(): UIElement[];
    }
    
    export abstract class PlayerElement extends UIElement {
      player: PlayerProperties;
    
    
    rhenck's avatar
    rhenck committed
      protected constructor(element: Partial<PlayerElement>) {
        super(element);
    
    rhenck's avatar
    rhenck committed
        this.player = {
    
          autostart: element.player?.autostart !== undefined ? element.player?.autostart as boolean : false,
          autostartDelay: element.player?.autostartDelay !== undefined ? element.player?.autostartDelay as number : 0,
          loop: element.player?.loop !== undefined ? element.player?.loop as boolean : false,
          startControl: element.player?.startControl !== undefined ? element.player?.startControl as boolean : true,
          pauseControl: element.player?.pauseControl !== undefined ? element.player?.pauseControl as boolean : false,
          progressBar: element.player?.progressBar !== undefined ? element.player?.progressBar as boolean : true,
          interactiveProgressbar: element.player?.interactiveProgressbar !== undefined ?
            element.player?.interactiveProgressbar as boolean :
    
    rhenck's avatar
    rhenck committed
            false,
    
          volumeControl: element.player?.volumeControl !== undefined ? element.player?.volumeControl as boolean : true,
          defaultVolume: element.player?.defaultVolume !== undefined ? element.player?.defaultVolume as number : 0.8,
          minVolume: element.player?.minVolume !== undefined ? element.player?.minVolume as number : 0,
          muteControl: element.player?.muteControl !== undefined ? element.player?.muteControl as boolean : true,
          interactiveMuteControl: element.player?.interactiveMuteControl !== undefined ?
            element.player?.interactiveMuteControl as boolean :
    
    rhenck's avatar
    rhenck committed
            false,
    
          hintLabel: element.player?.hintLabel !== undefined ? element.player?.hintLabel as string : '',
          hintLabelDelay: element.player?.hintLabelDelay !== undefined ? element.player?.hintLabelDelay as number : 0,
          activeAfterID: element.player?.activeAfterID !== undefined ? element.player?.activeAfterID as string : '',
          minRuns: element.player?.minRuns !== undefined ? element.player?.minRuns as number : 1,
          maxRuns: element.player?.maxRuns !== undefined ? element.player?.maxRuns as number | null : null,
          showRestRuns: element.player?.showRestRuns !== undefined ? element.player?.showRestRuns as boolean : false,
          showRestTime: element.player?.showRestTime !== undefined ? element.player?.showRestTime as boolean : true,
          playbackTime: element.player?.playbackTime !== undefined ? element.player?.playbackTime as number : 0
    
    rhenck's avatar
    rhenck committed
        };
    
      hasAnswerScheme(): boolean {
    
        return Boolean(this.getAnswerScheme);
    
      getAnswerScheme(): AnswerScheme {
    
        return {
          id: this.id,
          type: 'string',
          format: 'playback',
          multiple: false,
          nullable: true,
          values: [],
          valuesComplete: true
        };
      }
    
    export interface AnswerSchemeValue {
    
      value: string;
      label: string;
    }
    
    
    export interface AnswerScheme {
    
      id: string;
      type: string;
      format?: string;
      multiple?: boolean;
      nullable?: boolean;
    
      values?: AnswerSchemeValue[];
    
      valuesComplete?: boolean;
    
    }
    
    export interface PositionedUIElement extends UIElement {
      position: PositionProperties;
    }
    
    export interface PositionProperties {
    
    rhenck's avatar
    rhenck committed
      [index: string]: unknown;
    
      fixedSize: boolean;
      dynamicPositioning: boolean;
      xPosition: number;
      yPosition: number;
      useMinHeight: boolean;
      gridColumn: number | null;
      gridColumnRange: number;
      gridRow: number | null;
      gridRowRange: number;
      marginLeft: number;
      marginRight: number;
      marginTop: number;
      marginBottom: number;
      zIndex: number;
    }
    
    export interface BasicStyles {
    
    rhenck's avatar
    rhenck committed
      [index: string]: unknown;
    
      fontColor: string;
      font: string;
      fontSize: number;
      bold: boolean;
      italic: boolean;
      underline: boolean;
      backgroundColor: string;
    }
    
    export interface ExtendedStyles {
    
    rhenck's avatar
    rhenck committed
      [index: string]: unknown;
    
      lineHeight?: number;
      borderRadius?: number;
      itemBackgroundColor?: string;
      borderWidth?: number;
      borderColor?: string;
      borderStyle?: 'solid' | 'dotted' | 'dashed' | 'double' | 'groove' | 'ridge' | 'inset' | 'outset';
      lineColoring?: boolean;
      lineColoringColor?: string;
      selectionColor?: string;
    }
    
    export interface PlayerElement {
      player: PlayerProperties;
    }
    
    export interface PlayerProperties {
    
    rhenck's avatar
    rhenck committed
      [index: string]: unknown;
    
      autostart: boolean;
      autostartDelay: number;
      loop: boolean;
      startControl: boolean;
      pauseControl: boolean;
      progressBar: boolean;
      interactiveProgressbar: boolean;
      volumeControl: boolean;
      defaultVolume: number;
      minVolume: number;
      muteControl: boolean;
      interactiveMuteControl: boolean;
      hintLabel: string;
      hintLabelDelay: number;
      activeAfterID: string;
      minRuns: number;
      maxRuns: number | null;
      showRestRuns: boolean;
      showRestTime: boolean;
      playbackTime: number;
    }
    
    
    jojohoch's avatar
    jojohoch committed
    export interface Hotspot {
      top: number;
      left: number;
      width: number;
      height: number;
    
      shape: 'ellipse' | 'rectangle' | 'triangle';
    
    jojohoch's avatar
    jojohoch committed
      borderWidth: number;
      borderColor: string;
      backgroundColor: string;
      rotation: number;
      value: boolean;
      readOnly: boolean
    }
    
    
    export interface ValueChangeElement {
      id: string;
      value: InputElementValue;
    }
    
    
    export interface ButtonEvent {
      action: ButtonAction;
      param: UnitNavParam | number | string;
    
    export type ButtonAction = 'unitNav' | 'pageNav' | 'highlightText';
    
    export type UnitNavParam = 'previous' | 'next' | 'first' | 'last' | 'end';
    
    
    export interface OptionElement extends UIElement {
    
    rhenck's avatar
    rhenck committed
      getNewOptionLabel(optionText: string): Label;
    
    }
    
    export interface TextLabel {
    
      text: string;
    
    }
    
    export interface TextImageLabel extends TextLabel {
    
      imgSrc: string | null;
    
      imgPosition: 'above' | 'below' | 'left' | 'right';
    
    export interface DragNDropValueObject extends TextImageLabel {
    
      id: string;
    
      returnToOriginOnReplacement?: boolean;
      originListID?: string;
      originListIndex?: number;
    
    }
    
    export type Label = TextLabel | TextImageLabel | DragNDropValueObject;