Skip to content
Snippets Groups Projects
geometry.component.ts 4.71 KiB
Newer Older
import {
  AfterViewInit, Component, ElementRef, EventEmitter, Input, OnDestroy, Output, Renderer2
} from '@angular/core';
import { ElementComponent } from 'common/directives/element-component.directive';
import { GeometryElement } from 'common/models/elements/geometry/geometry';
import { ValueChangeElement } from 'common/models/elements/element';
import { ExternalResourceService } from 'common/services/external-resource.service';
import { debounceTime, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

declare const GGBApplet: any;

@Component({
  selector: 'aspect-geometry',
  template: `
    <div class="geogebra-container">
      <div [id]="elementModel.id" class="geogebra-applet"></div>
      <button *ngIf="this.elementModel.showResetIcon"
              mat-icon-button
              class="reset-button"
              (click)="reset()">
        <mat-icon class="reset-icon">restart_alt</mat-icon>
      </button>
    </div>
    <aspect-spinner [isLoaded]="isLoaded"></aspect-spinner>
  `,
  styles: [
    ':host {display: block; width: 100%; height: 100%;}',
    '.geogebra-applet {margin: auto;}',
    '.geogebra-container {position: relative;}',
    '.reset-button {position: absolute; top: 0; right: 0; z-index: 100; margin: 5px;}',
    '.reset-icon {font-size: 30px !important;}'
export class GeometryComponent extends ElementComponent implements AfterViewInit, OnDestroy {
  @Input() elementModel!: GeometryElement;
  @Input() appDefinition!: string;
  @Output() elementValueChanged = new EventEmitter<ValueChangeElement>();

  isLoaded: Subject<boolean> = new Subject();
  geoGebraApi!: any;
  private ngUnsubscribe = new Subject<void>();
  private geometryUpdated = new EventEmitter<void>();
  constructor(public elementRef: ElementRef,
              private renderer: Renderer2,
              private externalResourceService: ExternalResourceService) {
    super(elementRef);
    this.geometryUpdated
      .pipe(
        debounceTime(500),
        takeUntil(this.ngUnsubscribe)
      ).subscribe((): void => this.elementValueChanged
        .emit({
          id: this.elementModel.id,
          value: this.geoGebraApi.getBase64()
  ngAfterViewInit(): void {
    this.externalResourceService.initializeGeoGebra(this.renderer);
    this.externalResourceService.isGeoGebraLoaded()
      .pipe(takeUntil(this.ngUnsubscribe))
      .subscribe((isLoaded: boolean) => {
        if (isLoaded) this.initApplet();
      });
  refresh(): void {
    this.initApplet();
  }

  reset(): void {
    this.appDefinition = this.elementModel.appDefinition;
    this.initApplet();
    this.geometryUpdated.next();
  }

  private initApplet(): void {
    console.log('Initializing GeoGebra applet');
    if (!this.appDefinition) {
      console.error('Geogebra Applet definition not found.');
      return;
    }
    const params: any = {
      id: this.elementModel.id,
      width: this.elementModel.width,
      height: this.elementModel.height,
      showToolBar: this.elementModel.showToolbar,
      showResetIcon: false,
      enableShiftDragZoom: this.elementModel.enableShiftDragZoom,
      showZoomButtons: this.elementModel.showZoomButtons,
      showFullscreenButton: this.elementModel.showFullscreenButton,
      customToolBar: this.elementModel.customToolBar,
      enableUndoRedo: false,
      showMenuBar: false,
      showAlgebraInput: false,
      enableLabelDrags: false,
      enableRightClick: false,
      showToolBarHelp: false,
      errorDialogsActive: true,
      showLogging: false,
      useBrowserForJS: false,
      ggbBase64: this.appDefinition,
      appletOnLoad: (geoGebraApi: any) => {
        this.geoGebraApi = geoGebraApi;
        this.geoGebraApi.registerAddListener(() => {
          this.geometryUpdated.emit();
        this.geoGebraApi.registerRemoveListener(() => {
          this.geometryUpdated.emit();
        this.geoGebraApi.registerUpdateListener(() => {
          this.geometryUpdated.emit();
        this.geoGebraApi.registerRenameListener(() => {
          this.geometryUpdated.emit();
        this.geoGebraApi.registerClearListener(() => {
          this.geometryUpdated.emit();
        this.geoGebraApi.registerStoreUndoListener(() => {
          this.geometryUpdated.emit();
      }
    };
    const applet = new GGBApplet(params, '5.0');
    applet.setHTML5Codebase(this.externalResourceService.getGeoGebraHTML5URL());
    // Needs timeout for loading new unit file in editor. For unknown reasons the component
    // does not get re-initialized.
    setTimeout(() => applet.inject(this.elementModel.id));
  }

  ngOnDestroy(): void {
    this.ngUnsubscribe.next();
    this.ngUnsubscribe.complete();
  }