Newer
Older
Component, OnInit, Input, ComponentFactoryResolver, ViewChild, ViewContainerRef
} from '@angular/core';
import {
FormBuilder, FormControl, FormGroup, ValidatorFn
} from '@angular/forms';
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import * as ElementFactory from '../../../../../common/util/element.factory';
import { KeyboardService } from '../../services/keyboard.service';
import { FormService } from '../../services/form.service';
import { UnitStateService } from '../../services/unit-state.service';
import { MarkingService } from '../../services/marking.service';
InputElement, InputElementValue,
UIElement,
ValueChangeElement
} from '../../../../../common/models/uI-element';
import { FormElementComponent } from '../../../../../common/form-element-component.directive';
import { CompoundElementComponent }
from '../../../../../common/element-components/compound-elements/compound-element.directive';
import { TextElement } from '../../../../../common/models/text-element';
import { VideoElement } from '../../../../../common/models/video-element';
import { AudioElement } from '../../../../../common/models/audio-element';
import { ImageElement } from '../../../../../common/models/image-element';
import { VeronaPostService } from '../../services/verona-post.service';
import { MediaPlayerElementComponent } from '../../../../../common/media-player-element-component.directive';
import { MediaPlayerService } from '../../services/media-player.service';
import { TextComponent } from '../../../../../common/element-components/text.component';
import { TextFieldElement } from '../../../../../common/models/text-field-element';
@Component({
selector: 'app-element-container',
templateUrl: './element-container.component.html',
styleUrls: ['./element-container.component.css']
export class ElementContainerComponent implements OnInit {
@Input() elementModel!: UIElement;
@Input() parentArrayIndex!: number;
isKeyboardOpen!: boolean;
keyboardLayout!: 'french' | 'numbers' | 'numbersAndOperators' | 'none';
private ngUnsubscribe = new Subject<void>();
@ViewChild('elementComponentContainer',
{ read: ViewContainerRef, static: true }) private elementComponentContainer!: ViewContainerRef;
constructor(public keyboardService: KeyboardService,
private componentFactoryResolver: ComponentFactoryResolver,
private formService: FormService,
private unitStateService: UnitStateService,
private veronaPostService: VeronaPostService,
private mediaPlayerService: MediaPlayerService,
private markingService: MarkingService) {
ngOnInit(): void {
ElementFactory.getComponentFactory(this.elementModel.type, this.componentFactoryResolver);
const elementComponent = this.elementComponentContainer.createComponent(elementComponentFactory).instance;
elementComponent.elementModel = this.restoreUnitStateValue(this.elementModel);
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
this.registerElement(elementComponent);
this.subscribeCompoundChildren(elementComponent);
this.subscribeStartSelection(elementComponent);
this.subscribeApplySelection(elementComponent);
this.subscribeMediaStatusChanged(elementComponent);
this.subscribeNavigationRequested(elementComponent);
this.subscribeElementValueChanged(elementComponent);
this.subscribeForKeyboardEvents(elementComponent);
const elementForm = this.formBuilder.group({});
if (elementComponent instanceof FormElementComponent) {
elementComponent.parentForm = elementForm;
this.subscribeSetValidators(elementComponent, elementForm);
this.registerFormGroup(elementForm);
this.formService.registerFormControl({
id: this.elementModel.id,
formControl: new FormControl((this.elementModel as InputElement).value),
formGroup: elementForm
});
} else if (elementComponent instanceof CompoundElementComponent) {
elementComponent.parentForm = elementForm;
elementComponent.getFormElementModelChildren()
.forEach((element: InputElement) => {
this.registerFormGroup(elementForm);
this.formService.registerFormControl({
id: element.id,
formControl: new FormControl(element.value),
formGroup: elementForm
});
});
} else if (elementComponent instanceof MediaPlayerElementComponent) {
this.mediaPlayerService.registerMediaElement(elementComponent);
}
}
private registerElement(elementComponent: any): void {
if (elementComponent.domElement) {
this.unitStateService.registerElement(
this.initUnitStateValue(elementComponent.elementModel),
elementComponent.domElement,
this.pageIndex
private subscribeStartSelection(elementComponent: any): void {
if (elementComponent.startSelection) {
elementComponent.startSelection
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((mouseEvent: MouseEvent) => {
const selection = window.getSelection();
if (mouseEvent.ctrlKey && selection?.rangeCount) {
selection.removeAllRanges();
}
});
}
private subscribeCompoundChildren(elementComponent: any): void {
if (elementComponent.startSelection) {
elementComponent.startSelection
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((mouseEvent: MouseEvent) => {
const selection = window.getSelection();
if (mouseEvent.ctrlKey && selection?.rangeCount) {
selection.removeAllRanges();
}
private subscribeApplySelection(elementComponent: any): void {
if (elementComponent.applySelection) {
elementComponent.applySelection
.pipe(takeUntil(this.ngUnsubscribe))
{ mode: 'mark' | 'underline' | 'delete',
color: string;
element: HTMLElement;
clear: boolean }) => {
this.markingService
.applySelection(selection.mode, selection.color, selection.element, elementComponent as TextComponent);
}
private subscribeMediaStatusChanged(elementComponent: any): void {
if (elementComponent.mediaPlayStatusChanged) {
elementComponent.mediaPlayStatusChanged
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((playStatus: string | null) => {
this.mediaPlayerService.broadCastPlayChanges(playStatus);
});
}
}
private subscribeNavigationRequested(elementComponent: any): void {
if (elementComponent.navigationRequested) {
elementComponent.navigationRequested
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((target: 'previous' | 'next' | 'first' | 'last' | 'end') => {
this.veronaPostService.sendVopUnitNavigationRequestedNotification(target);
});
private subscribeElementValueChanged(elementComponent: any): void {
if (elementComponent.elementValueChanged) {
elementComponent.elementValueChanged
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((playbackTimeChanged: ValueChangeElement) => {
this.unitStateService.changeElementValue(playbackTimeChanged);
});
}
private subscribeSetValidators(elementComponent: any, elementForm: FormGroup): void {
if (elementComponent.setValidators) {
elementComponent.setValidators
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((validators: ValidatorFn[]) => {
this.formService.setValidators({
id: this.elementModel.id,
validators: validators,
formGroup: elementForm
});
});
}
private subscribeForKeyboardEvents(elementComponent: any): void {
if (elementComponent.onFocus) {
elementComponent.onFocus
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((focussedInputControl: HTMLElement): void => {
const inputElement = this.elementModel.type === 'text-area' ?
focussedInputControl as HTMLTextAreaElement :
focussedInputControl as HTMLInputElement;
this.keyboardLayout = (this.elementModel as TextFieldElement).inputAssistancePreset;
this.isKeyboardOpen = this.keyboardService.openKeyboard(inputElement);
});
}
if (elementComponent.onBlur) {
elementComponent.onBlur
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe((): void => {
this.isKeyboardOpen = this.keyboardService.closeKeyboard();
});
}
private restoreUnitStateValue(elementModel: UIElement): UIElement {
const unitStateElementCode = this.unitStateService.getUnitStateElement(elementModel.id);
if (unitStateElementCode && unitStateElementCode.value !== undefined) {
switch (elementModel.type) {
case 'text':
elementModel.text = unitStateElementCode.value;
break;
case 'image':
elementModel.magnifierUsed = unitStateElementCode.value;
break;
case 'video':
case 'audio':
elementModel.playbackTime = unitStateElementCode.value;
break;
default:
elementModel.value = unitStateElementCode.value;
}
}
return elementModel;
}
private initUnitStateValue = (elementModel: UIElement): { id: string, value: InputElementValue } => {
switch (elementModel.type) {
case 'text':
return { id: elementModel.id, value: (elementModel as TextElement).text };
case 'image':
return { id: elementModel.id, value: (elementModel as ImageElement).magnifierUsed };
case 'video':
return { id: elementModel.id, value: (elementModel as VideoElement).playbackTime };
case 'audio':
return { id: elementModel.id, value: (elementModel as AudioElement).playbackTime };
default:
return { id: elementModel.id, value: (elementModel as InputElement).value };
}
};
private registerFormGroup(elementForm: FormGroup): void {
this.formService.registerFormGroup({
formGroup: elementForm,
parentForm: this.parentForm,
parentArray: 'elements',
parentArrayIndex: this.parentArrayIndex
});
}
ngOnDestroy(): void {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}