diff --git a/projects/editor/src/app/app.component.ts b/projects/editor/src/app/app.component.ts
index ae09b257b27886800cdcb4cd118b55c8325d5c85..95ca2f6c011fc0c225146cb9812968be92b97375 100644
--- a/projects/editor/src/app/app.component.ts
+++ b/projects/editor/src/app/app.component.ts
@@ -3,7 +3,7 @@ import { TranslateService } from '@ngx-translate/core';
 import { registerLocaleData } from '@angular/common';
 import localeDe from '@angular/common/locales/de';
 import { VeronaAPIService, StartCommand } from './services/verona-api.service';
-import { UnitService } from './services/unit.service';
+import { UnitService } from './services/unit-services/unit.service';
 
 @Component({
   selector: 'aspect-editor',
diff --git a/projects/editor/src/app/components/canvas/canvas.component.html b/projects/editor/src/app/components/canvas/canvas.component.html
index 2db4c17e4210258f64c8caef7854b5403b2107f3..a3cc4691fa36d2105fc2334da974afe2e0349917 100644
--- a/projects/editor/src/app/components/canvas/canvas.component.html
+++ b/projects/editor/src/app/components/canvas/canvas.component.html
@@ -12,8 +12,8 @@
                              [allowMoveUp]="i != 0"
                              [allowMoveDown]="i < page.sections.length - 1"
                              [allowDelete]="page.sections.length > 1"
-                             (moveSection)="unitService.moveSection(section, page, $event)"
-                             (duplicateSection)="unitService.duplicateSection(section, page, i)"
+                             (moveSection)="sectionService.moveSection(section, page, $event)"
+                             (duplicateSection)="sectionService.duplicateSection(section, page, i)"
                              (selectElementComponent)="selectElementOverlay($event)">
         </aspect-section-menu>
         <aspect-section-static *ngIf="!section.dynamicPositioning"
diff --git a/projects/editor/src/app/components/canvas/canvas.component.ts b/projects/editor/src/app/components/canvas/canvas.component.ts
index b6ba5e7c2ff472c3f978a7abf8f834f33cb0571c..0a5e5e840ce64abd3780fe76dc4be8a572f75de3 100644
--- a/projects/editor/src/app/components/canvas/canvas.component.ts
+++ b/projects/editor/src/app/components/canvas/canvas.component.ts
@@ -5,11 +5,12 @@ import { CdkDragDrop } from '@angular/cdk/drag-drop';
 import { PositionedUIElement, UIElement } from 'common/models/elements/element';
 import { Page } from 'common/models/page';
 import { Section } from 'common/models/section';
-import { UnitService } from '../../services/unit.service';
+import { UnitService } from '../../services/unit-services/unit.service';
 import { SelectionService } from '../../services/selection.service';
 import { CanvasElementOverlay } from './overlays/canvas-element-overlay';
 import { SectionStaticComponent } from './section-static.component';
 import { SectionDynamicComponent } from './section-dynamic.component';
+import { SectionService } from 'editor/src/app/services/unit-services/section.service';
 
 @Component({
   selector: 'aspect-page-canvas',
@@ -42,10 +43,12 @@ export class CanvasComponent {
   @ViewChildren('sectionComponent')
     sectionComponents!: QueryList<SectionStaticComponent | SectionDynamicComponent>;
 
-  constructor(public selectionService: SelectionService, public unitService: UnitService) { }
+  constructor(public selectionService: SelectionService,
+              public unitService: UnitService,
+              public sectionService: SectionService) { }
 
   moveElementsBetweenSections(elements: UIElement[], previousSectionIndex: number, newSectionIndex: number): void {
-    this.unitService.transferElement(elements,
+    this.sectionService.transferElement(elements,
       this.page.sections[previousSectionIndex],
       this.page.sections[newSectionIndex]);
   }
@@ -86,7 +89,7 @@ export class CanvasComponent {
   }
 
   addSection(): void {
-    this.unitService.addSection(this.page);
+    this.sectionService.addSection(this.page);
     this.selectionService.selectedPageSectionIndex = this.page.sections.length - 1;
   }
 
diff --git a/projects/editor/src/app/components/canvas/dynamic-section-helper-grid.component.ts b/projects/editor/src/app/components/canvas/dynamic-section-helper-grid.component.ts
index e556a6e5d8fd3d97b7826f84fae06345af4d7f59..41df84a1d4a0cbe99a249832b99c80907a2cb80a 100644
--- a/projects/editor/src/app/components/canvas/dynamic-section-helper-grid.component.ts
+++ b/projects/editor/src/app/components/canvas/dynamic-section-helper-grid.component.ts
@@ -4,7 +4,8 @@ import {
 } from '@angular/core';
 import { UIElement, UIElementType } from 'common/models/elements/element';
 import { Section } from 'common/models/section';
-import { UnitService } from '../../services/unit.service';
+import { UnitService } from '../../services/unit-services/unit.service';
+import { ElementService } from 'editor/src/app/services/unit-services/element.service';
 
 @Component({
   selector: '[app-dynamic-section-helper-grid]',
@@ -44,7 +45,7 @@ export class DynamicSectionHelperGridComponent implements OnInit, OnChanges {
   columnCountArray: unknown[] = [];
   rowCountArray: unknown[] = [];
 
-  constructor(public unitService: UnitService) {}
+  constructor(public unitService: UnitService, private elementService: ElementService) {}
 
   ngOnInit(): void {
     this.calculateColumnCount();
@@ -134,7 +135,7 @@ export class DynamicSectionHelperGridComponent implements OnInit, OnChanges {
 
   newElementDropped(event: DragEvent, gridX: number, gridY: number): void {
     event.preventDefault();
-    this.unitService.addElementToSection(
+    this.elementService.addElementToSection(
       event.dataTransfer?.getData('elementType') as UIElementType,
       this.section,
       { x: gridX, y: gridY }
diff --git a/projects/editor/src/app/components/canvas/overlays/canvas-element-overlay.ts b/projects/editor/src/app/components/canvas/overlays/canvas-element-overlay.ts
index 4cf511684cbeacf2e519bc1f71f56c3cec496718..c3b19fc470919d1124696a664debc7d85fc27e47 100644
--- a/projects/editor/src/app/components/canvas/overlays/canvas-element-overlay.ts
+++ b/projects/editor/src/app/components/canvas/overlays/canvas-element-overlay.ts
@@ -12,8 +12,9 @@ import { UIElement } from 'common/models/elements/element';
 import { GeometryComponent } from 'common/components/geometry/geometry.component';
 import { FormElementComponent } from 'common/directives/form-element-component.directive';
 import { MathTableComponent } from 'common/components/input-elements/math-table.component';
-import { UnitService } from '../../../services/unit.service';
+import { UnitService } from '../../../services/unit-services/unit.service';
 import { SelectionService } from '../../../services/selection.service';
+import { ElementService } from 'editor/src/app/services/unit-services/element.service';
 
 @Directive()
 export abstract class CanvasElementOverlay implements OnInit, OnDestroy {
@@ -30,6 +31,7 @@ export abstract class CanvasElementOverlay implements OnInit, OnDestroy {
 
   constructor(public selectionService: SelectionService,
               protected unitService: UnitService,
+              protected elementService: ElementService,
               private changeDetectorRef: ChangeDetectorRef) { }
 
   ngOnInit(): void {
@@ -106,7 +108,7 @@ export abstract class CanvasElementOverlay implements OnInit, OnDestroy {
   }
 
   openEditDialog(): void {
-    this.unitService.showDefaultEditDialog(this.element);
+    this.elementService.showDefaultEditDialog(this.element);
   }
 
   setInteractionEnabled(isEnabled: boolean): void {
diff --git a/projects/editor/src/app/components/canvas/overlays/static-canvas-overlay.component.ts b/projects/editor/src/app/components/canvas/overlays/static-canvas-overlay.component.ts
index d3223609b7074437dc2ffa11ea67cb195cc88baa..006ba295e42b9044cf9368c96bc03ae7c1653599 100644
--- a/projects/editor/src/app/components/canvas/overlays/static-canvas-overlay.component.ts
+++ b/projects/editor/src/app/components/canvas/overlays/static-canvas-overlay.component.ts
@@ -66,12 +66,12 @@ export class StaticCanvasOverlayComponent extends CanvasElementOverlay {
   }
 
   updateModel(event: CdkDragEnd): void {
-    this.unitService.updateElementsProperty(
+    this.elementService.updateElementsProperty(
       this.selectionService.getSelectedElements(),
       'width',
       Math.max(this.oldX + event.distance.x, 0)
     );
-    this.unitService.updateElementsProperty(
+    this.elementService.updateElementsProperty(
       this.selectionService.getSelectedElements(),
       'height',
       Math.max(this.oldY + event.distance.y, 0)
@@ -82,7 +82,7 @@ export class StaticCanvasOverlayComponent extends CanvasElementOverlay {
     this.selectionService.selectedElements
       .pipe(take(1))
       .subscribe((selectedElements: UIElement[]) => {
-        this.unitService.deleteElements(selectedElements);
+        this.elementService.deleteElements(selectedElements);
         this.selectionService.clearElementSelection();
       })
       .unsubscribe();
diff --git a/projects/editor/src/app/components/canvas/section-menu.component.ts b/projects/editor/src/app/components/canvas/section-menu.component.ts
index 3f155a59988df0dd44af66cd5c69d0da8737ae76..3f84e88649d2f512556ee0e9c0885052d945f872 100644
--- a/projects/editor/src/app/components/canvas/section-menu.component.ts
+++ b/projects/editor/src/app/components/canvas/section-menu.component.ts
@@ -11,9 +11,10 @@ import { DropListElement } from 'common/models/elements/input-elements/drop-list
 import { IDService } from 'editor/src/app/services/id.service';
 import { VisibilityRule } from 'common/models/visibility-rule';
 import { ReferenceManager } from 'editor/src/app/services/reference-manager';
-import { UnitService } from '../../services/unit.service';
+import { UnitService } from '../../services/unit-services/unit.service';
 import { DialogService } from '../../services/dialog.service';
 import { SelectionService } from '../../services/selection.service';
+import { SectionService } from 'editor/src/app/services/unit-services/section.service';
 
 @Component({
   selector: 'aspect-section-menu',
@@ -184,6 +185,7 @@ export class SectionMenuComponent implements OnDestroy {
   private ngUnsubscribe = new Subject<void>();
 
   constructor(public unitService: UnitService,
+              private sectionService: SectionService,
               private selectionService: SelectionService,
               private dialogService: DialogService,
               private messageService: MessageService,
@@ -193,7 +195,7 @@ export class SectionMenuComponent implements OnDestroy {
   updateModel(
     property: string, value: string | number | boolean | VisibilityRule[] | { value: number; unit: string }[]
   ): void {
-    this.unitService.updateSectionProperty(this.section, property, value);
+    this.sectionService.updateSectionProperty(this.section, property, value);
   }
 
   selectElement(element: UIElement): void {
@@ -209,7 +211,7 @@ export class SectionMenuComponent implements OnDestroy {
         .subscribe((result: boolean) => {
           if (result) {
             ReferenceManager.deleteReferences(refs);
-            this.unitService.deleteSection(this.selectionService.selectedPageIndex, this.sectionIndex);
+            this.sectionService.deleteSection(this.selectionService.selectedPageIndex, this.sectionIndex);
             this.selectionService.selectedPageSectionIndex =
               Math.max(0, this.selectionService.selectedPageSectionIndex - 1);
           } else {
@@ -221,7 +223,7 @@ export class SectionMenuComponent implements OnDestroy {
         .pipe(takeUntil(this.ngUnsubscribe))
         .subscribe((result: boolean) => {
           if (result) {
-            this.unitService.deleteSection(this.selectionService.selectedPageIndex, this.sectionIndex);
+            this.sectionService.deleteSection(this.selectionService.selectedPageIndex, this.sectionIndex);
             this.selectionService.selectedPageSectionIndex =
               Math.max(0, this.selectionService.selectedPageSectionIndex - 1);
           }
@@ -284,7 +286,7 @@ export class SectionMenuComponent implements OnDestroy {
                 });
             }
           });
-          this.unitService.replaceSection(this.selectionService.selectedPageIndex, this.sectionIndex, newSection);
+          this.sectionService.replaceSection(this.selectionService.selectedPageIndex, this.sectionIndex, newSection);
         }
       });
   }
diff --git a/projects/editor/src/app/components/canvas/section-static.component.ts b/projects/editor/src/app/components/canvas/section-static.component.ts
index 79766c964a138d3ba56dc6fe6b0dca99481780f2..85e89eb3db886f26c0be8011afc168c59e1b6a02 100644
--- a/projects/editor/src/app/components/canvas/section-static.component.ts
+++ b/projects/editor/src/app/components/canvas/section-static.component.ts
@@ -3,8 +3,9 @@ import {
 } from '@angular/core';
 import { Section } from 'common/models/section';
 import { UIElementType } from 'common/models/elements/element';
-import { UnitService } from '../../services/unit.service';
+import { UnitService } from '../../services/unit-services/unit.service';
 import { CanvasElementOverlay } from './overlays/canvas-element-overlay';
+import { ElementService } from 'editor/src/app/services/unit-services/element.service';
 
 @Component({
   selector: 'aspect-section-static',
@@ -33,12 +34,12 @@ export class SectionStaticComponent {
   @ViewChild('sectionElement') sectionElement!: ElementRef;
   @ViewChildren('elementComponent') childElementComponents!: QueryList<CanvasElementOverlay>;
 
-  constructor(public unitService: UnitService) { }
+  constructor(public unitService: UnitService, private elementService: ElementService) { }
 
   newElementDropped(event: DragEvent): void {
     event.preventDefault();
     const sectionRect = this.sectionElement.nativeElement.getBoundingClientRect();
-    this.unitService.addElementToSection(
+    this.elementService.addElementToSection(
       event.dataTransfer?.getData('elementType') as UIElementType,
       this.section,
       { x: event.clientX - Math.round(sectionRect.left), y: event.clientY - Math.round(sectionRect.top) }
diff --git a/projects/editor/src/app/components/dialogs/player-edit-dialog.component.ts b/projects/editor/src/app/components/dialogs/player-edit-dialog.component.ts
index e8dca26463a276f19190451a15f869cefdff389c..005321174a09102049d5a4d808329268f41742cb 100644
--- a/projects/editor/src/app/components/dialogs/player-edit-dialog.component.ts
+++ b/projects/editor/src/app/components/dialogs/player-edit-dialog.component.ts
@@ -4,7 +4,7 @@ import {
 } from '@angular/core';
 import { MAT_DIALOG_DATA } from '@angular/material/dialog';
 import { PlayerProperties } from 'common/models/elements/property-group-interfaces';
-import { UnitService } from 'editor/src/app/services/unit.service';
+import { UnitService } from 'editor/src/app/services/unit-services/unit.service';
 
 @Component({
   selector: 'aspect-player-edit-dialog',
diff --git a/projects/editor/src/app/components/new-ui-element-panel/show-state-variables-button.component.ts b/projects/editor/src/app/components/new-ui-element-panel/show-state-variables-button.component.ts
index b65639baeade610bcb6dfa94d59f142d2285d176..c6e1eb14e3864c8248cc953319205f144cb30e1d 100644
--- a/projects/editor/src/app/components/new-ui-element-panel/show-state-variables-button.component.ts
+++ b/projects/editor/src/app/components/new-ui-element-panel/show-state-variables-button.component.ts
@@ -1,6 +1,6 @@
 import { Component, Input } from '@angular/core';
 import { DialogService } from 'editor/src/app/services/dialog.service';
-import { UnitService } from 'editor/src/app/services/unit.service';
+import { UnitService } from 'editor/src/app/services/unit-services/unit.service';
 
 @Component({
   selector: 'aspect-show-state-variables-button',
diff --git a/projects/editor/src/app/components/new-ui-element-panel/ui-element-toolbox.component.ts b/projects/editor/src/app/components/new-ui-element-panel/ui-element-toolbox.component.ts
index 829e135abecf66a880ef34a95a39717165e2b058..c4446d2b3e9b1e1398c9ce23a6bc67cbb3f0a951 100644
--- a/projects/editor/src/app/components/new-ui-element-panel/ui-element-toolbox.component.ts
+++ b/projects/editor/src/app/components/new-ui-element-panel/ui-element-toolbox.component.ts
@@ -1,7 +1,8 @@
 import { Component } from '@angular/core';
 import { UIElementType } from 'common/models/elements/element';
-import { UnitService } from '../../services/unit.service';
+import { UnitService } from '../../services/unit-services/unit.service';
 import { SelectionService } from '../../services/selection.service';
+import { ElementService } from 'editor/src/app/services/unit-services/element.service';
 
 @Component({
   selector: 'aspect-ui-element-toolbox',
@@ -11,10 +12,13 @@ import { SelectionService } from '../../services/selection.service';
 export class UiElementToolboxComponent {
   hoverRadioButton: boolean = false;
   hoverFormulaButton: boolean = false;
-  constructor(private selectionService: SelectionService, public unitService: UnitService) { }
+
+  constructor(private selectionService: SelectionService,
+              public unitService: UnitService,
+              private elementService: ElementService) { }
 
   async addUIElement(elementType: UIElementType): Promise<void> {
-    this.unitService.addElementToSectionByIndex(elementType,
+    this.elementService.addElementToSectionByIndex(elementType,
       this.selectionService.selectedPageIndex,
       this.selectionService.selectedPageSectionIndex);
   }
diff --git a/projects/editor/src/app/components/properties-panel/element-properties-panel.component.ts b/projects/editor/src/app/components/properties-panel/element-properties-panel.component.ts
index af88f64d8a5d8446a09b743051f03720fdd6e4dd..fb926868a0f389ce31b774839e84357fe02e4499 100644
--- a/projects/editor/src/app/components/properties-panel/element-properties-panel.component.ts
+++ b/projects/editor/src/app/components/properties-panel/element-properties-panel.component.ts
@@ -8,9 +8,11 @@ import { TranslateService } from '@ngx-translate/core';
 import { MessageService } from 'common/services/message.service';
 import { UIElement } from 'common/models/elements/element';
 import { LikertRowElement } from 'common/models/elements/compound-elements/likert/likert-row';
-import { UnitService } from '../../services/unit.service';
+import { UnitService } from '../../services/unit-services/unit.service';
 import { SelectionService } from '../../services/selection.service';
 import { CanvasElementOverlay } from '../canvas/overlays/canvas-element-overlay';
+import { ElementService } from 'editor/src/app/services/unit-services/element.service';
+import { SectionService } from 'editor/src/app/services/unit-services/section.service';
 
 export type CombinedProperties = UIElement & { idList?: string[] };
 
@@ -29,6 +31,8 @@ export class ElementPropertiesPanelComponent implements OnInit, OnDestroy {
 
   constructor(protected selectionService: SelectionService,
               public unitService: UnitService,
+              public sectionService: SectionService,
+              public elementService: ElementService,
               private messageService: MessageService,
               public sanitizer: DomSanitizer,
               private translateService: TranslateService) { }
@@ -105,7 +109,7 @@ export class ElementPropertiesPanelComponent implements OnInit, OnDestroy {
               value: unknown,
               isInputValid: boolean | null = true): void {
     if (isInputValid) {
-      this.unitService.updateElementsProperty(this.selectedElements, property, value);
+      this.elementService.updateElementsProperty(this.selectedElements, property, value);
     } else {
       this.messageService.showWarning(this.translateService.instant('inputInvalid'));
     }
@@ -118,11 +122,11 @@ export class ElementPropertiesPanelComponent implements OnInit, OnDestroy {
   }
 
   deleteElement(): void {
-    this.unitService.deleteElements(this.selectedElements);
+    this.elementService.deleteElements(this.selectedElements);
   }
 
   duplicateElement(): void {
-    this.unitService.duplicateSelectedElements();
+    this.sectionService.duplicateSelectedElements();
   }
 
   ngOnDestroy(): void {
diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.html b/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.html
index 2d10f975841c9494df9414b56c8be514813578f3..cbd13ef52d8d97be8f5b1528314c9beb9b38be68 100644
--- a/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.html
+++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.html
@@ -53,7 +53,7 @@
   <button *ngIf="combinedProperties.document"
           [style.align-self]="'center'"
           mat-raised-button
-          (click)="unitService.showDefaultEditDialog(selectedElements[0])">
+          (click)="elementService.showDefaultEditDialog(selectedElements[0])">
     Text und Elemente editieren
   </button>
 
@@ -66,7 +66,7 @@
   <button *ngIf="combinedProperties.player"
           [style.align-self]="'center'"
           mat-raised-button
-          (click)="unitService.showDefaultEditDialog(selectedElements[0])">
+          (click)="elementService.showDefaultEditDialog(selectedElements[0])">
     Medienoptionen anpassen
   </button>
 
diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.ts
index 4bfb98fae62b2122e3ad963d2b649599fcdedcdf..a38aa5542b4d7128cf0f89e6b33a4d39c877ea66 100644
--- a/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.ts
+++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/element-model-properties.component.ts
@@ -18,9 +18,10 @@ import { TextImageLabel, TextLabel } from 'common/models/elements/label-interfac
 import { Hotspot } from 'common/models/elements/input-elements/hotspot-image';
 import { StateVariable } from 'common/models/state-variable';
 import { GeometryComponent } from 'common/components/geometry/geometry.component';
-import { UnitService } from '../../../services/unit.service';
+import { UnitService } from '../../../services/unit-services/unit.service';
 import { SelectionService } from '../../../services/selection.service';
 import { DialogService } from '../../../services/dialog.service';
+import { ElementService } from 'editor/src/app/services/unit-services/element.service';
 
 @Component({
   selector: 'aspect-element-model-properties-component',
@@ -40,6 +41,7 @@ export class ElementModelPropertiesComponent implements OnDestroy {
   private ngUnsubscribe = new Subject<void>();
 
   constructor(public unitService: UnitService,
+              public elementService: ElementService,
               public selectionService: SelectionService,
               public dialogService: DialogService) { }
 
diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/action-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/action-properties.component.ts
index 97d71b492ccee1090f251c9f4e623ac08c0656b0..c5792be104a39216fccc7cd5b83d666893040fb7 100644
--- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/action-properties.component.ts
+++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/action-properties.component.ts
@@ -5,7 +5,7 @@ import {
 import { UIElement } from 'common/models/elements/element';
 import { StateVariable } from 'common/models/state-variable';
 import { TextComponent } from 'common/components/text/text.component';
-import { UnitService } from 'editor/src/app/services/unit.service';
+import { UnitService } from 'editor/src/app/services/unit-services/unit.service';
 import { SelectionService } from 'editor/src/app/services/selection.service';
 import { Page } from 'common/models/page';
 
diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/drop-list-properties.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/drop-list-properties.component.ts
index 90dd5f064b4b6fdffb83030bd6a18bd474916c2e..48b73eaec40a77766bb644706be83e0a4787dd03 100644
--- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/drop-list-properties.component.ts
+++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/ele-specific/drop-list-properties.component.ts
@@ -17,7 +17,7 @@ import { OptionListPanelComponent } from 'editor/src/app/components/properties-p
 import { FormsModule } from '@angular/forms';
 import { MatButtonModule } from '@angular/material/button';
 import { MatTooltipModule } from '@angular/material/tooltip';
-import { UnitService } from 'editor/src/app/services/unit.service';
+import { UnitService } from 'editor/src/app/services/unit-services/unit.service';
 import { DialogService } from 'editor/src/app/services/dialog.service';
 
 @Pipe({
diff --git a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/options-field-set.component.ts b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/options-field-set.component.ts
index 865e12ce51fa990b807ff6506ac7932ceed69862..a0037ed7c156ad2a389f3c3d17613c15be137f85 100644
--- a/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/options-field-set.component.ts
+++ b/projects/editor/src/app/components/properties-panel/model-properties-tab/input-groups/options-field-set.component.ts
@@ -3,13 +3,14 @@ import {
 } from '@angular/core';
 import { CombinedProperties } from 'editor/src/app/components/properties-panel/element-properties-panel.component';
 import { LikertRowElement, LikertRowProperties } from 'common/models/elements/compound-elements/likert/likert-row';
-import { UnitService } from 'editor/src/app/services/unit.service';
+import { UnitService } from 'editor/src/app/services/unit-services/unit.service';
 import { DialogService } from 'editor/src/app/services/dialog.service';
 import { moveItemInArray } from '@angular/cdk/drag-drop';
 import { SelectionService } from 'editor/src/app/services/selection.service';
 import { IDService } from 'editor/src/app/services/id.service';
 import { Label, TextImageLabel, TextLabel } from 'common/models/elements/label-interfaces';
 import { OptionElement } from 'common/models/elements/element';
+import { ElementService } from 'editor/src/app/services/unit-services/element.service';
 
 @Component({
   selector: 'aspect-options-field-set',
@@ -47,6 +48,7 @@ export class OptionsFieldSetComponent {
     }>();
 
   constructor(private unitService: UnitService,
+              private elementService: ElementService,
               private selectionService: SelectionService,
               public dialogService: DialogService,
               private idService: IDService) { }
@@ -56,7 +58,7 @@ export class OptionsFieldSetComponent {
 
     selectedElements.forEach(element => {
       const newValue = [...this.combinedProperties[property] as Label[], element.getNewOptionLabel(option)];
-      this.unitService.updateElementsProperty([element], property, newValue);
+      this.elementService.updateElementsProperty([element], property, newValue);
     });
   }
 
@@ -112,31 +114,31 @@ export class OptionsFieldSetComponent {
       .subscribe((result: LikertRowElement) => {
         if (result) {
           if (result.id !== row.id) {
-            this.unitService.updateElementsProperty(
+            this.elementService.updateElementsProperty(
               [row],
               'id',
               result.id
             );
           }
           if (result.rowLabel !== row.rowLabel) {
-            this.unitService.updateElementsProperty([row], 'rowLabel', result.rowLabel);
+            this.elementService.updateElementsProperty([row], 'rowLabel', result.rowLabel);
           }
           if (result.value !== row.value) {
-            this.unitService.updateElementsProperty(
+            this.elementService.updateElementsProperty(
               [row],
               'value',
               result.value
             );
           }
           if (result.verticalButtonAlignment !== row.verticalButtonAlignment) {
-            this.unitService.updateElementsProperty(
+            this.elementService.updateElementsProperty(
               [row],
               'verticalButtonAlignment',
               result.verticalButtonAlignment
             );
           }
           if (result.readOnly !== row.readOnly) {
-            this.unitService.updateElementsProperty(
+            this.elementService.updateElementsProperty(
               [row],
               'readOnly',
               result.readOnly
diff --git a/projects/editor/src/app/components/properties-panel/position-properties-tab/element-position-properties.component.ts b/projects/editor/src/app/components/properties-panel/position-properties-tab/element-position-properties.component.ts
index 3d9ca7b39306a25a156e1c6149cfac5f64a5f826..ed47e355b98cc5f09dca2cce41d8ea0bb7ae75a2 100644
--- a/projects/editor/src/app/components/properties-panel/position-properties-tab/element-position-properties.component.ts
+++ b/projects/editor/src/app/components/properties-panel/position-properties-tab/element-position-properties.component.ts
@@ -3,8 +3,9 @@ import {
 } from '@angular/core';
 import { PositionedUIElement } from 'common/models/elements/element';
 import { DimensionProperties, PositionProperties } from 'common/models/elements/property-group-interfaces';
-import { UnitService } from '../../../services/unit.service';
+import { UnitService } from '../../../services/unit-services/unit.service';
 import { SelectionService } from '../../../services/selection.service';
+import { ElementService } from 'editor/src/app/services/unit-services/element.service';
 
 @Component({
   selector: 'aspect-position-and-dimension-properties',
@@ -48,9 +49,11 @@ export class ElementPositionPropertiesComponent {
   @Input() positionProperties: PositionProperties | undefined;
   @Input() isZIndexDisabled: boolean = false;
 
-  constructor(public unitService: UnitService, public selectionService: SelectionService) { }
+  constructor(public unitService: UnitService,
+              public selectionService: SelectionService,
+              private elementService: ElementService) { }
 
   alignElements(direction: 'left' | 'right' | 'top' | 'bottom'): void {
-    this.unitService.alignElements(this.selectionService.getSelectedElements() as PositionedUIElement[], direction);
+    this.elementService.alignElements(this.selectionService.getSelectedElements() as PositionedUIElement[], direction);
   }
 }
diff --git a/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/dimension-field-set.component.ts b/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/dimension-field-set.component.ts
index a85e483a07184e50f7f3a54df1451000713a4436..2964d6a4b8a82aec911a72f49313958178262e44 100644
--- a/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/dimension-field-set.component.ts
+++ b/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/dimension-field-set.component.ts
@@ -1,5 +1,5 @@
 import { Component, Input } from '@angular/core';
-import { UnitService } from 'editor/src/app/services/unit.service';
+import { UnitService } from 'editor/src/app/services/unit-services/unit.service';
 import { SelectionService } from 'editor/src/app/services/selection.service';
 import { DimensionProperties, PositionProperties } from 'common/models/elements/property-group-interfaces';
 
diff --git a/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/position-field-set.component.ts b/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/position-field-set.component.ts
index 165423a9cf1912b60321325b58372f88bc77c020..40fc4153d33dcda81452710283e80679a1db40bc 100644
--- a/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/position-field-set.component.ts
+++ b/projects/editor/src/app/components/properties-panel/position-properties-tab/input-groups/position-field-set.component.ts
@@ -4,7 +4,7 @@ import {
 import { UIElementValue } from 'common/models/elements/element';
 import { PositionProperties } from 'common/models/elements/property-group-interfaces';
 import { SelectionService } from 'editor/src/app/services/selection.service';
-import { UnitService } from 'editor/src/app/services/unit.service';
+import { UnitService } from 'editor/src/app/services/unit-services/unit.service';
 
 @Component({
   selector: 'aspect-position-field-set',
diff --git a/projects/editor/src/app/components/properties-panel/style-properties-tab/element-style-properties.component.ts b/projects/editor/src/app/components/properties-panel/style-properties-tab/element-style-properties.component.ts
index dbd688ebad86b11966b145d887c6409d325b0a2e..2b16f38c33b47fb06bc4750cf4d514d333f30dd1 100644
--- a/projects/editor/src/app/components/properties-panel/style-properties-tab/element-style-properties.component.ts
+++ b/projects/editor/src/app/components/properties-panel/style-properties-tab/element-style-properties.component.ts
@@ -1,6 +1,7 @@
 import { Component, Input } from '@angular/core';
-import { UnitService } from 'editor/src/app/services/unit.service';
+import { UnitService } from 'editor/src/app/services/unit-services/unit.service';
 import { Stylings } from 'common/models/elements/property-group-interfaces';
+import { ElementService } from 'editor/src/app/services/unit-services/element.service';
 
 @Component({
   selector: 'aspect-element-style-properties',
@@ -10,7 +11,7 @@ import { Stylings } from 'common/models/elements/property-group-interfaces';
                       appearance="fill" class="mdInput textsingleline">
         <mat-label>{{'propertiesPanel.helperRowColor' | translate }}</mat-label>
         <input matInput type="text" [value]="styles.helperRowColor"
-               (input)="unitService.updateSelectedElementsStyleProperty(
+               (input)="elementService.updateSelectedElementsStyleProperty(
                           'helperRowColor', $any($event.target).value)">
         <button mat-icon-button matSuffix (click)="helperRowColorInput.click()">
           <mat-icon>edit</mat-icon>
@@ -18,11 +19,11 @@ import { Stylings } from 'common/models/elements/property-group-interfaces';
       </mat-form-field>
       <input matInput type="color" hidden #helperRowColorInput
              [value]="styles.helperRowColor"
-             (input)="unitService.updateSelectedElementsStyleProperty('helperRowColor', $any($event.target).value)">
+             (input)="elementService.updateSelectedElementsStyleProperty('helperRowColor', $any($event.target).value)">
 
       <mat-checkbox *ngIf="styles.lineColoring !== undefined"
                     [checked]="$any(styles.lineColoring)"
-                    (change)="unitService.updateSelectedElementsStyleProperty('lineColoring', $event.checked)">
+                    (change)="elementService.updateSelectedElementsStyleProperty('lineColoring', $event.checked)">
         {{'propertiesPanel.lineColoring' | translate }}
       </mat-checkbox>
 
@@ -31,7 +32,7 @@ import { Stylings } from 'common/models/elements/property-group-interfaces';
         <mat-label>{{'propertiesPanel.lineColoringColor' | translate }}</mat-label>
         <input matInput type="text" [value]="styles.lineColoringColor"
                [disabled]="!styles.lineColoring || styles.lineColoringColor === undefined"
-               (input)="unitService.updateSelectedElementsStyleProperty(
+               (input)="elementService.updateSelectedElementsStyleProperty(
                           'lineColoringColor', $any($event.target).value)">
         <button mat-icon-button matSuffix (click)="lineColorInput.click()">
           <mat-icon>edit</mat-icon>
@@ -39,11 +40,11 @@ import { Stylings } from 'common/models/elements/property-group-interfaces';
       </mat-form-field>
       <input matInput type="color" hidden #lineColorInput
              [value]="styles.lineColoringColor"
-             (input)="unitService.updateSelectedElementsStyleProperty('lineColoringColor', $any($event.target).value)">
+             (input)="elementService.updateSelectedElementsStyleProperty('lineColoringColor', $any($event.target).value)">
 
       <mat-checkbox *ngIf="styles.firstLineColoring !== undefined"
                     [checked]="$any(styles.firstLineColoring)"
-                    (change)="unitService.updateSelectedElementsStyleProperty('firstLineColoring', $event.checked)">
+                    (change)="elementService.updateSelectedElementsStyleProperty('firstLineColoring', $event.checked)">
         {{'propertiesPanel.firstLineColoring' | translate }}
       </mat-checkbox>
 
@@ -52,7 +53,7 @@ import { Stylings } from 'common/models/elements/property-group-interfaces';
         <mat-label>{{'propertiesPanel.firstLineColoringColor' | translate }}</mat-label>
         <input matInput type="text" [value]="styles.firstLineColoringColor"
                [disabled]="!styles.firstLineColoring || styles.firstLineColoringColor === undefined"
-               (input)="unitService.updateSelectedElementsStyleProperty(
+               (input)="elementService.updateSelectedElementsStyleProperty(
                           'firstLineColoringColor', $any($event.target).value)">
         <button mat-icon-button matSuffix (click)="firstLineColorInput.click()">
           <mat-icon>edit</mat-icon>
@@ -60,72 +61,72 @@ import { Stylings } from 'common/models/elements/property-group-interfaces';
       </mat-form-field>
       <input matInput type="color" hidden #firstLineColorInput
              [value]="styles.firstLineColoringColor"
-             (input)="unitService.updateSelectedElementsStyleProperty(
+             (input)="elementService.updateSelectedElementsStyleProperty(
                 'firstLineColoringColor', $any($event.target).value)">
 
       <mat-form-field *ngIf="styles.selectionColor !== undefined" appearance="fill">
         <mat-label>{{'propertiesPanel.selectionColor' | translate }}</mat-label>
         <input matInput type="text" [value]="styles.selectionColor"
-               (input)="unitService.updateSelectedElementsStyleProperty('selectionColor', $any($event.target).value)">
+               (input)="elementService.updateSelectedElementsStyleProperty('selectionColor', $any($event.target).value)">
         <button mat-icon-button matSuffix (click)="selectionColorInput.click()">
           <mat-icon>edit</mat-icon>
         </button>
       </mat-form-field>
       <input matInput type="color" hidden #selectionColorInput
              [value]="styles.selectionColor"
-             (input)="unitService.updateSelectedElementsStyleProperty('selectionColor', $any($event.target).value)">
+             (input)="elementService.updateSelectedElementsStyleProperty('selectionColor', $any($event.target).value)">
 
       <mat-form-field *ngIf="styles.itemBackgroundColor !== undefined"
                       appearance="fill" class="mdInput textsingleline">
         <mat-label>{{'propertiesPanel.itemBackgroundColor' | translate }}</mat-label>
         <input matInput type="text" [value]="styles.itemBackgroundColor"
-               (input)="unitService.updateSelectedElementsStyleProperty('itemBackgroundColor', $any($event.target).value)">
+               (input)="elementService.updateSelectedElementsStyleProperty('itemBackgroundColor', $any($event.target).value)">
         <button mat-icon-button matSuffix (click)="itembackgroundColorInput.click()">
           <mat-icon>edit</mat-icon>
         </button>
       </mat-form-field>
       <input matInput type="color" hidden #itembackgroundColorInput
              [value]="styles.itemBackgroundColor"
-             (input)="unitService.updateSelectedElementsStyleProperty('itemBackgroundColor', $any($event.target).value)">
+             (input)="elementService.updateSelectedElementsStyleProperty('itemBackgroundColor', $any($event.target).value)">
 
       <mat-form-field *ngIf="styles.backgroundColor !== undefined"
                       appearance="fill" class="mdInput textsingleline">
         <mat-label>{{'propertiesPanel.backgroundColor' | translate }}</mat-label>
         <input matInput type="text" [value]="styles.backgroundColor"
-               (input)="unitService.updateSelectedElementsStyleProperty('backgroundColor', $any($event.target).value)">
+               (input)="elementService.updateSelectedElementsStyleProperty('backgroundColor', $any($event.target).value)">
         <button mat-icon-button matSuffix (click)="backgroundColorInput.click()">
           <mat-icon>edit</mat-icon>
         </button>
       </mat-form-field>
       <input matInput type="color" hidden #backgroundColorInput
              [value]="styles.backgroundColor"
-             (input)="unitService.updateSelectedElementsStyleProperty('backgroundColor', $any($event.target).value)">
+             (input)="elementService.updateSelectedElementsStyleProperty('backgroundColor', $any($event.target).value)">
 
       <mat-form-field *ngIf="styles.fontColor !== undefined"
                       appearance="fill" class="mdInput textsingleline">
         <mat-label>{{'propertiesPanel.fontColor' | translate }}</mat-label>
         <input matInput type="text" [value]="styles.fontColor"
-               (input)="unitService.updateSelectedElementsStyleProperty('fontColor', $any($event.target).value)">
+               (input)="elementService.updateSelectedElementsStyleProperty('fontColor', $any($event.target).value)">
         <button mat-icon-button matSuffix (click)="fontColorInput.click()">
           <mat-icon>edit</mat-icon>
         </button>
       </mat-form-field>
       <input matInput type="color" hidden #fontColorInput
              [value]="styles.fontColor"
-             (input)="unitService.updateSelectedElementsStyleProperty('fontColor', $any($event.target).value)">
+             (input)="elementService.updateSelectedElementsStyleProperty('fontColor', $any($event.target).value)">
 
 <!--      <mat-form-field *ngIf="styles.font !== undefined"-->
 <!--                      appearance="fill" class="mdInput textsingleline">-->
 <!--        <mat-label>{{'propertiesPanel.font' | translate }}</mat-label>-->
 <!--        <input matInput type="text" [value]="styles.font" disabled-->
-<!--               (input)="unitService.updateSelectedElementsStyleProperty('font', $any($event.target).value)">-->
+<!--               (input)="elementService.updateSelectedElementsStyleProperty('font', $any($event.target).value)">-->
 <!--      </mat-form-field>-->
       <mat-form-field *ngIf="styles.fontSize !== undefined"
                       appearance="fill" class="mdInput textsingleline">
         <mat-label>{{'propertiesPanel.fontSize' | translate }}</mat-label>
         <input matInput type="number" #fontSize="ngModel" min="0"
                [ngModel]="styles.fontSize"
-               (ngModelChange)="unitService.updateSelectedElementsStyleProperty('fontSize', $event)"
+               (ngModelChange)="elementService.updateSelectedElementsStyleProperty('fontSize', $event)"
                (change)="styles.fontSize = styles.fontSize ? styles.fontSize : 0">
       </mat-form-field>
       <mat-form-field *ngIf="styles.lineHeight !== undefined"
@@ -133,23 +134,23 @@ import { Stylings } from 'common/models/elements/property-group-interfaces';
         <mat-label>{{'propertiesPanel.lineHeight' | translate }}</mat-label>
         <input matInput type="number" #lineHeight="ngModel" min="0"
                [ngModel]="styles.lineHeight"
-               (ngModelChange)="unitService.updateSelectedElementsStyleProperty('lineHeight', $event)"
+               (ngModelChange)="elementService.updateSelectedElementsStyleProperty('lineHeight', $event)"
                (change)="styles.lineHeight = styles.lineHeight ? styles.lineHeight : 0">
       </mat-form-field>
 
       <mat-checkbox *ngIf="styles.bold !== undefined"
                     [checked]="$any(styles.bold)"
-                    (change)="unitService.updateSelectedElementsStyleProperty('bold', $event.checked)">
+                    (change)="elementService.updateSelectedElementsStyleProperty('bold', $event.checked)">
         {{'propertiesPanel.bold' | translate }}
       </mat-checkbox>
       <mat-checkbox *ngIf="styles.italic !== undefined"
                     [checked]="$any(styles.italic)"
-                    (change)="unitService.updateSelectedElementsStyleProperty('italic', $event.checked)">
+                    (change)="elementService.updateSelectedElementsStyleProperty('italic', $event.checked)">
         {{'propertiesPanel.italic' | translate }}
       </mat-checkbox>
       <mat-checkbox *ngIf="styles.underline !== undefined"
                     [checked]="$any(styles.underline)"
-                    (change)="unitService.updateSelectedElementsStyleProperty('underline', $event.checked)">
+                    (change)="elementService.updateSelectedElementsStyleProperty('underline', $event.checked)">
         {{'propertiesPanel.underline' | translate }}
       </mat-checkbox>
 
@@ -161,7 +162,7 @@ import { Stylings } from 'common/models/elements/property-group-interfaces';
       <mat-form-field *ngIf="styles.borderRadius !== undefined" appearance="fill">
         <mat-label>{{'propertiesPanel.borderRadius' | translate }}</mat-label>
         <input matInput type="number" [ngModel]="styles.borderRadius"
-               (ngModelChange)="unitService.updateSelectedElementsStyleProperty('borderRadius', $event)"
+               (ngModelChange)="elementService.updateSelectedElementsStyleProperty('borderRadius', $event)"
                (change)="styles.borderRadius = styles.borderRadius ? styles.borderRadius : 0">
       </mat-form-field>
 
@@ -169,20 +170,20 @@ import { Stylings } from 'common/models/elements/property-group-interfaces';
                       appearance="fill" class="mdInput textsingleline">
         <mat-label>{{'propertiesPanel.borderColor' | translate }}</mat-label>
         <input matInput type="text" [value]="styles.borderColor"
-               (input)="unitService.updateSelectedElementsStyleProperty('borderColor', $any($event.target).value)">
+               (input)="elementService.updateSelectedElementsStyleProperty('borderColor', $any($event.target).value)">
         <button mat-icon-button matSuffix (click)="borderColorInput.click()">
           <mat-icon>edit</mat-icon>
         </button>
       </mat-form-field>
       <input matInput type="color" hidden #borderColorInput
              [value]="styles.borderColor"
-             (input)="unitService.updateSelectedElementsStyleProperty('borderColor', $any($event.target).value)">
+             (input)="elementService.updateSelectedElementsStyleProperty('borderColor', $any($event.target).value)">
 
       <mat-form-field *ngIf="styles.borderStyle !== undefined"
                       appearance="fill">
         <mat-label>{{'propertiesPanel.borderStyle' | translate }}</mat-label>
         <mat-select [value]="styles.borderStyle"
-                    (selectionChange)="unitService.updateSelectedElementsStyleProperty('borderStyle', $event.value)">
+                    (selectionChange)="elementService.updateSelectedElementsStyleProperty('borderStyle', $event.value)">
           <mat-option *ngFor="let option of ['solid', 'dotted', 'dashed',
                                          'double', 'groove', 'ridge', 'inset', 'outset']"
                       [value]="option">
@@ -195,7 +196,7 @@ import { Stylings } from 'common/models/elements/property-group-interfaces';
         <mat-label>{{'propertiesPanel.borderWidth' | translate }}</mat-label>
         <input matInput type="number" #borderWidth="ngModel"
                [ngModel]="styles.borderWidth"
-               (ngModelChange)="unitService.updateSelectedElementsStyleProperty('borderWidth', $event)"
+               (ngModelChange)="elementService.updateSelectedElementsStyleProperty('borderWidth', $event)"
                (change)="styles.borderRadius = styles.borderRadius ? styles.borderRadius : 0">
       </mat-form-field>
     </fieldset>
@@ -204,5 +205,5 @@ import { Stylings } from 'common/models/elements/property-group-interfaces';
 export class ElementStylePropertiesComponent {
   @Input() styles!: Stylings | undefined;
 
-  constructor(public unitService: UnitService) { }
+  constructor(public elementService: ElementService) { }
 }
diff --git a/projects/editor/src/app/components/toolbar/toolbar.component.ts b/projects/editor/src/app/components/toolbar/toolbar.component.ts
index 0aba1eaa2595a31a0a35aadd60a89bc0d4a30a6f..b08fab15059b5fe39de3d3d9963c22801dbd9cc8 100644
--- a/projects/editor/src/app/components/toolbar/toolbar.component.ts
+++ b/projects/editor/src/app/components/toolbar/toolbar.component.ts
@@ -1,5 +1,5 @@
 import { Component } from '@angular/core';
-import { UnitService } from '../../services/unit.service';
+import { UnitService } from '../../services/unit-services/unit.service';
 
 @Component({
   selector: 'aspect-toolbar',
diff --git a/projects/editor/src/app/components/unit-view/page-menu.component.html b/projects/editor/src/app/components/unit-view/page-menu.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..0e87e011d4af2abb2cba7f77e7fb2d272580236a
--- /dev/null
+++ b/projects/editor/src/app/components/unit-view/page-menu.component.html
@@ -0,0 +1,88 @@
+<div [style]="'display: flex;'">
+  <button [disabled]="page.alwaysVisible ||
+                      pageIndex == 0 ||
+                      (pageIndex == 1 && unitService.unit.pages[0].alwaysVisible)"
+          [style]="'justify-content: center'"
+          [matTooltip]="'Seite nach vorn verschieben'"
+          mat-menu-item (click)="movePage('left')">
+    <mat-icon>west</mat-icon>
+  </button>
+  <button [disabled]="page.alwaysVisible ||
+                      pageIndex == unitService.unit.pages.length - 1"
+          [style]="'justify-content: center;'"
+          [matTooltip]="'Seite nach hinten verschieben'"
+          mat-menu-item (click)="movePage('right')">
+    <mat-icon>east</mat-icon>
+  </button>
+</div>
+
+<button mat-menu-item class="delete-button"
+        [matTooltip]="'Seite löschen'"
+        (click)="deletePage()">
+  <mat-icon>delete</mat-icon>
+</button>
+
+<mat-divider></mat-divider>
+
+<fieldset class="fx-column-start-stretch">
+  <legend>Seitenbreite</legend>
+  <mat-checkbox class="menuItem"
+                [matTooltip]="'Abgewählt wird die verfügbare Bildschirmbreite voll ausgenutzt.'"
+                [checked]="page.hasMaxWidth"
+                (click)="$event.stopPropagation()"
+                (change)="updateModel(page, 'hasMaxWidth', $event.source.checked)">
+    Seitenbreite begrenzen
+  </mat-checkbox>
+  <p class="menuItem" [style.margin-top.px]="5" [style.margin-left.px]="10">
+    effektive Seitenbreite: <br>{{page.hasMaxWidth ? page.maxWidth + 2 * page.margin + 'px' : '∞'}}
+  </p>
+  <mat-form-field class="menuItem" appearance="fill">
+    <mat-label>Seitenbreite in px</mat-label>
+    <input matInput type="number" min="0" #maxWidth="ngModel"
+           [disabled]="!page.hasMaxWidth"
+           [ngModel]="page.hasMaxWidth ? page.maxWidth : null"
+           (click)="$event.stopPropagation()"
+           (ngModelChange)="updateModel(page,'maxWidth', $event || 0, maxWidth.valid)">
+  </mat-form-field>
+  <mat-form-field class="menuItem" appearance="fill">
+    <mat-label>Randbreite in px</mat-label>
+    <input matInput type="number" min="0" #margin="ngModel"
+           [ngModel]="page.margin"
+           (click)="$event.stopPropagation()"
+           (ngModelChange)="updateModel(page,'margin', $event || 0, margin.valid)">
+  </mat-form-field>
+</fieldset>
+
+<mat-form-field class="menuItem" appearance="fill" [style.margin-top.px]="16">
+  <mat-label>{{'pageProperties.backgroundColor' | translate }}</mat-label>
+  <input matInput type="color" #backgroundColor="ngModel"
+         [ngModel]="page.backgroundColor"
+         (ngModelChange)="updateModel(page,'backgroundColor', $event, backgroundColor.valid)">
+</mat-form-field>
+<mat-checkbox class="menuItem"
+              [disabled]="unitService.unit.pages.length < 2 || unitService.unit.pages[0].alwaysVisible && pageIndex != 0"
+              [ngModel]="page.alwaysVisible"
+              (click)="$event.stopPropagation()"
+              (change)="updateModel(page, 'alwaysVisible', $event.source.checked)">
+  Seite dauerhaft sichtbar
+</mat-checkbox>
+<mat-form-field class="menuItem" appearance="fill">
+  <mat-label>{{'pageProperties.position' | translate }}</mat-label>
+  <mat-select [disabled]="!page.alwaysVisible"
+              [value]="page.alwaysVisiblePagePosition"
+              (click)="$event.stopPropagation()"
+              (selectionChange)="updateModel(page, 'alwaysVisiblePagePosition', $event.value)">
+    <mat-option *ngFor="let option of ['left', 'right', 'top', 'bottom']"
+                [value]="option">
+      {{option | translate}}
+    </mat-option>
+  </mat-select>
+</mat-form-field>
+<mat-form-field class="menuItem" appearance="fill">
+  <mat-label>{{'pageProperties.alwaysVisibleAspectRatio' | translate }}</mat-label>
+  <input matInput type="number" min="0" max="100"
+         [disabled]="!page.alwaysVisible"
+         [ngModel]="page.alwaysVisibleAspectRatio"
+         (click)="$event.stopPropagation()"
+         (ngModelChange)="updateModel(page, 'alwaysVisibleAspectRatio', $event || 0)">
+</mat-form-field>
diff --git a/projects/editor/src/app/components/unit-view/page-menu.component.ts b/projects/editor/src/app/components/unit-view/page-menu.component.ts
index 1b17001e8d156333832bbe22d6996a5642e0ebd1..6604a1c637f0632efe7065a48a27668c3ee5b446 100644
--- a/projects/editor/src/app/components/unit-view/page-menu.component.ts
+++ b/projects/editor/src/app/components/unit-view/page-menu.component.ts
@@ -1,4 +1,4 @@
-import { Component, Input, OnDestroy } from '@angular/core';
+import { ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
 import { MatCheckboxModule } from '@angular/material/checkbox';
 import { MatDividerModule } from '@angular/material/divider';
 import { MatFormFieldModule } from '@angular/material/form-field';
@@ -14,11 +14,12 @@ import { Page } from 'common/models/page';
 import { takeUntil } from 'rxjs/operators';
 import { ReferenceManager } from 'editor/src/app/services/reference-manager';
 import { SelectionService } from 'editor/src/app/services/selection.service';
-import { UnitService } from 'editor/src/app/services/unit.service';
+import { UnitService } from 'editor/src/app/services/unit-services/unit.service';
 import { DialogService } from 'editor/src/app/services/dialog.service';
 import { MessageService } from 'common/services/message.service';
 import { Subject } from 'rxjs';
 import { MAT_TOOLTIP_DEFAULT_OPTIONS, MatTooltipDefaultOptions, MatTooltipModule } from '@angular/material/tooltip';
+import { PageService } from 'editor/src/app/services/unit-services/page.service';
 
 /** Custom options the configure the tooltip's default show/hide delays. */
 export const myCustomTooltipDefaults: MatTooltipDefaultOptions = {
@@ -48,93 +49,7 @@ export const myCustomTooltipDefaults: MatTooltipDefaultOptions = {
     MatTooltipModule
   ],
   providers: [{provide: MAT_TOOLTIP_DEFAULT_OPTIONS, useValue: myCustomTooltipDefaults}],
-  template: `
-    <div [style]="'display: flex;'">
-      <button [disabled]="page.alwaysVisible"
-              [style]="'justify-content: center'"
-              [matTooltip]="'Seite nach vorn verschieben'"
-              mat-menu-item (click)="movePage(page,'left')">
-        <mat-icon>west</mat-icon>
-      </button>
-      <button [disabled]="page.alwaysVisible"
-              [style]="'justify-content: center;'"
-              [matTooltip]="'Seite nach hinten verschieben'"
-              mat-menu-item (click)="movePage(page, 'right')">
-        <mat-icon>east</mat-icon>
-      </button>
-    </div>
-
-    <button mat-menu-item class="delete-button"
-            [matTooltip]="'Seite löschen'"
-            (click)="deletePage()">
-      <mat-icon>delete</mat-icon>
-    </button>
-
-    <mat-divider></mat-divider>
-
-    <fieldset class="fx-column-start-stretch">
-      <legend>Seitenbreite</legend>
-      <mat-checkbox class="menuItem"
-                    [matTooltip]="'Abgewählt wird die verfügbare Bildschirmbreite voll ausgenutzt.'"
-                    [checked]="page.hasMaxWidth"
-                    (click)="$event.stopPropagation()"
-                    (change)="updateModel(page, 'hasMaxWidth', $event.source.checked)">
-        Seitenbreite begrenzen
-      </mat-checkbox>
-      <p class="menuItem" [style.margin-top.px]="5" [style.margin-left.px]="10">
-        effektive Seitenbreite: <br>{{page.hasMaxWidth ? page.maxWidth + 2 * page.margin + 'px' : '∞'}}
-      </p>
-      <mat-form-field class="menuItem" appearance="fill">
-        <mat-label>Seitenbreite in px</mat-label>
-        <input matInput type="number" min="0" #maxWidth="ngModel"
-               [disabled]="!page.hasMaxWidth"
-               [ngModel]="page.hasMaxWidth ? page.maxWidth : null"
-               (click)="$event.stopPropagation()"
-               (ngModelChange)="updateModel(page,'maxWidth', $event || 0, maxWidth.valid)">
-      </mat-form-field>
-      <mat-form-field class="menuItem" appearance="fill">
-        <mat-label>Randbreite in px</mat-label>
-        <input matInput type="number" min="0" #margin="ngModel"
-               [ngModel]="page.margin"
-               (click)="$event.stopPropagation()"
-               (ngModelChange)="updateModel(page,'margin', $event || 0, margin.valid)">
-      </mat-form-field>
-    </fieldset>
-
-    <mat-form-field class="menuItem" appearance="fill" [style.margin-top.px]="16">
-      <mat-label>{{'pageProperties.backgroundColor' | translate }}</mat-label>
-      <input matInput type="color" #backgroundColor="ngModel"
-             [ngModel]="page.backgroundColor"
-             (ngModelChange)="updateModel(page,'backgroundColor', $event, backgroundColor.valid)">
-    </mat-form-field>
-    <mat-checkbox class="menuItem"
-                  [disabled]="unitService.unit.pages.length < 2 || unitService.unit.pages[0].alwaysVisible && pageIndex != 0"
-                  [ngModel]="page.alwaysVisible"
-                  (click)="$event.stopPropagation()"
-                  (change)="updateModel(page, 'alwaysVisible', $event.source.checked)">
-      Seite dauerhaft sichtbar
-    </mat-checkbox>
-    <mat-form-field class="menuItem" appearance="fill">
-      <mat-label>{{'pageProperties.position' | translate }}</mat-label>
-      <mat-select [disabled]="!page.alwaysVisible"
-                  [value]="page.alwaysVisiblePagePosition"
-                  (click)="$event.stopPropagation()"
-                  (selectionChange)="updateModel(page, 'alwaysVisiblePagePosition', $event.value)">
-        <mat-option *ngFor="let option of ['left', 'right', 'top', 'bottom']"
-                    [value]="option">
-          {{option | translate}}
-        </mat-option>
-      </mat-select>
-    </mat-form-field>
-    <mat-form-field class="menuItem" appearance="fill">
-      <mat-label>{{'pageProperties.alwaysVisibleAspectRatio' | translate }}</mat-label>
-      <input matInput type="number" min="0" max="100"
-             [disabled]="!page.alwaysVisible"
-             [ngModel]="page.alwaysVisibleAspectRatio"
-             (click)="$event.stopPropagation()"
-             (ngModelChange)="updateModel(page, 'alwaysVisibleAspectRatio', $event || 0)">
-    </mat-form-field>
-  `,
+  templateUrl: 'page-menu.component.html',
   styles: `
     :host {
       display: flex;
@@ -161,16 +76,18 @@ export const myCustomTooltipDefaults: MatTooltipDefaultOptions = {
 export class PageMenu implements OnDestroy {
   @Input() page!: Page;
   @Input() pageIndex!: number;
+  @Output() pageOrderChanged = new EventEmitter<void>();
   private ngUnsubscribe = new Subject<void>();
 
   constructor(public unitService: UnitService,
+              public pageService: PageService,
               public selectionService: SelectionService,
               private dialogService: DialogService,
               private messageService: MessageService) {}
 
-  movePage(page: Page, direction: 'left' | 'right'): void {
-    this.unitService.moveSelectedPage(direction);
-    this.refreshTabs();
+  movePage(direction: 'left' | 'right'): void {
+    this.pageService.moveSelectedPage(direction);
+    this.pageOrderChanged.emit();
   }
 
   deletePage(): void {
@@ -189,7 +106,7 @@ export class PageMenu implements OnDestroy {
         .subscribe((result: boolean) => {
           if (result) {
             ReferenceManager.deleteReferences(refs);
-            this.unitService.deletePage(this.selectionService.selectedPageIndex);
+            this.pageService.deletePage(this.selectionService.selectedPageIndex);
             this.selectionService.selectPreviousPage();
           } else {
             this.messageService.showReferencePanel(refs);
@@ -200,7 +117,7 @@ export class PageMenu implements OnDestroy {
         .pipe(takeUntil(this.ngUnsubscribe))
         .subscribe((result: boolean) => {
           if (result) {
-            this.unitService.deletePage(this.selectionService.selectedPageIndex);
+            this.pageService.deletePage(this.selectionService.selectedPageIndex);
             this.selectionService.selectPreviousPage();
           }
         });
@@ -213,7 +130,7 @@ export class PageMenu implements OnDestroy {
         this.movePageToFront(page);
         page.alwaysVisible = true;
         this.selectionService.selectedPageIndex = 0;
-        this.refreshTabs();
+        this.pageOrderChanged.emit();
       }
       page[property] = value;
       this.unitService.updateUnitDefinition(); // TODO
@@ -230,15 +147,6 @@ export class PageMenu implements OnDestroy {
     }
   }
 
-  /* This is a hack. The tab element gets bugged when changing the underlying array.
-   With this we can temporarily remove it from the DOM and then add it again, re-initializing it. */
-  private refreshTabs(): void { // TODO seems unnecessary (?); moving pages works fine
-    // this.pagesLoaded = false;
-    // setTimeout(() => {
-    //   this.pagesLoaded = true;
-    // });
-  }
-
   ngOnDestroy(): void {
     this.ngUnsubscribe.next();
     this.ngUnsubscribe.complete();
diff --git a/projects/editor/src/app/components/unit-view/unit-view.component.html b/projects/editor/src/app/components/unit-view/unit-view.component.html
index 9adaaff538c26131197086a50d65933ac17de4b2..06ea1ffaf18d25c58c0c92e09aa80f8232984477 100644
--- a/projects/editor/src/app/components/unit-view/unit-view.component.html
+++ b/projects/editor/src/app/components/unit-view/unit-view.component.html
@@ -40,7 +40,8 @@
             </button>
             <mat-menu #pageMenu="matMenu">
               <div (click)="$event.stopPropagation()">
-                <aspect-unit-view-page-menu [page]="page" [pageIndex]="i"></aspect-unit-view-page-menu>
+                <aspect-unit-view-page-menu [page]="page" [pageIndex]="i"
+                                            (pageOrderChanged)="refreshTabs()"></aspect-unit-view-page-menu>
               </div>
             </mat-menu>
           </ng-template>
diff --git a/projects/editor/src/app/components/unit-view/unit-view.component.ts b/projects/editor/src/app/components/unit-view/unit-view.component.ts
index 9e33783beacef2146cf047d854036fae0b7a8504..71ee86a6431a9825656de7ad1d1c02de85bdf9bc 100644
--- a/projects/editor/src/app/components/unit-view/unit-view.component.ts
+++ b/projects/editor/src/app/components/unit-view/unit-view.component.ts
@@ -1,7 +1,9 @@
 import { Component } from '@angular/core';
 import { PageChangeService } from 'common/services/page-change.service';
-import { UnitService } from '../../services/unit.service';
+import { UnitService } from '../../services/unit-services/unit.service';
 import { SelectionService } from '../../services/selection.service';
+import { HistoryService } from 'editor/src/app/services/history.service';
+import { PageService } from 'editor/src/app/services/unit-services/page.service';
 
 @Component({
   selector: 'aspect-unit-view',
@@ -13,15 +15,24 @@ export class UnitViewComponent {
 
   constructor(public selectionService: SelectionService,
               public unitService: UnitService,
+              public pageService: PageService,
               public pageChangeService: PageChangeService,
-              private dialogService: DialogService,
-              private messageService: MessageService) { }
+              public historyService: HistoryService) { }
 
   selectPage(newIndex: number): void {
     this.selectionService.selectPage(newIndex);
   }
 
   addPage(): void {
-    this.unitService.addPage();
+    this.pageService.addPage();
+  }
+
+  /* This is a hack. The tab element gets bugged when changing the underlying array.
+     With this we can temporarily remove it from the DOM and then add it again, re-initializing it. */
+  refreshTabs(): void {
+    this.pagesLoaded = false;
+    setTimeout(() => {
+      this.pagesLoaded = true;
+    });
   }
 }
diff --git a/projects/editor/src/app/services/unit-services/element.service.ts b/projects/editor/src/app/services/unit-services/element.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c7e25ef9ced89a75bcba0223c2d0a6643bf61589
--- /dev/null
+++ b/projects/editor/src/app/services/unit-services/element.service.ts
@@ -0,0 +1,325 @@
+import { Injectable } from '@angular/core';
+import { UnitService } from 'editor/src/app/services/unit-services/unit.service';
+import { SelectionService } from 'editor/src/app/services/selection.service';
+import { IDService } from 'editor/src/app/services/id.service';
+import {
+  InputElement, PlayerElement,
+  PositionedUIElement,
+  UIElement,
+  UIElementProperties,
+  UIElementType, UIElementValue
+} from 'common/models/elements/element';
+import { Section } from 'common/models/section';
+import { GeometryProperties } from 'common/models/elements/geometry/geometry';
+import { firstValueFrom } from 'rxjs';
+import { FileService } from 'common/services/file.service';
+import { AudioProperties } from 'common/models/elements/media-elements/audio';
+import { VideoProperties } from 'common/models/elements/media-elements/video';
+import { ImageProperties } from 'common/models/elements/media-elements/image';
+import {
+  PlayerProperties,
+  PositionProperties,
+  PropertyGroupGenerators
+} from 'common/models/elements/property-group-interfaces';
+import { ElementFactory } from 'common/util/element.factory';
+import { ReferenceManager } from 'editor/src/app/services/reference-manager';
+import { DialogService } from 'editor/src/app/services/dialog.service';
+import { MessageService } from 'common/services/message.service';
+import { TextElement } from 'common/models/elements/text/text';
+import { ClozeDocument, ClozeElement } from 'common/models/elements/compound-elements/cloze/cloze';
+import { DomSanitizer } from '@angular/platform-browser';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class ElementService {
+  unit = this.unitService.unit;
+
+  constructor(private unitService: UnitService,
+              private selectionService: SelectionService,
+              private dialogService: DialogService,
+              private messageService: MessageService,
+              private idService: IDService,
+              private sanitizer: DomSanitizer) { }
+
+  addElementToSectionByIndex(elementType: UIElementType,
+                             pageIndex: number,
+                             sectionIndex: number): void {
+    this.addElementToSection(elementType, this.unit.pages[pageIndex].sections[sectionIndex]);
+  }
+
+  async addElementToSection(elementType: UIElementType, section: Section,
+                            coordinates?: { x: number, y: number }): Promise<void> {
+    const newElementProperties: Partial<UIElementProperties> = {};
+    if (['geometry'].includes(elementType)) {
+      (newElementProperties as GeometryProperties).appDefinition =
+        await firstValueFrom(this.dialogService.showGeogebraAppDefinitionDialog());
+      if (!(newElementProperties as GeometryProperties).appDefinition) return; // dialog canceled
+    }
+    if (['audio', 'video', 'image', 'hotspot-image'].includes(elementType)) {
+      let mediaSrc = '';
+      switch (elementType) {
+        case 'hotspot-image':
+        case 'image':
+          mediaSrc = await FileService.loadImage();
+          break;
+        case 'audio':
+          mediaSrc = await FileService.loadAudio();
+          break;
+        case 'video':
+          mediaSrc = await FileService.loadVideo();
+          break;
+        // no default
+      }
+      (newElementProperties as AudioProperties | VideoProperties | ImageProperties).src = mediaSrc;
+    }
+
+    // Coordinates are given if an element is dragged directly into a cell
+    if (coordinates) {
+      newElementProperties.position = {
+        ...(section.dynamicPositioning && { gridColumn: coordinates.x }),
+        ...(section.dynamicPositioning && { gridRow: coordinates.y }),
+        ...(!section.dynamicPositioning && { yPosition: coordinates.y }),
+        ...(!section.dynamicPositioning && { yPosition: coordinates.y })
+      } as PositionProperties;
+    }
+
+    // Use z-index -1 for frames
+    newElementProperties.position = {
+      zIndex: elementType === 'frame' ? -1 : 0,
+      ...newElementProperties.position
+    } as PositionProperties;
+
+    section.addElement(ElementFactory.createElement({
+      type: elementType,
+      position: PropertyGroupGenerators.generatePositionProps(newElementProperties.position),
+      ...newElementProperties,
+      id: this.idService.getAndRegisterNewID(elementType)
+    }) as PositionedUIElement);
+    this.unitService.updateUnitDefinition();
+  }
+
+  deleteElements(elements: UIElement[]): void {
+    const refs =
+      this.unitService.referenceManager.getElementsReferences(elements);
+    // console.log('element refs', refs);
+    if (refs.length > 0) {
+      this.dialogService.showDeleteReferenceDialog(refs)
+        .subscribe((result: boolean) => {
+          if (result) {
+            ReferenceManager.deleteReferences(refs);
+            this.unitService.unregisterIDs(elements);
+            this.unit.pages[this.selectionService.selectedPageIndex].sections.forEach(section => {
+              section.elements = section.elements.filter(element => !elements.includes(element));
+            });
+            this.unitService.updateUnitDefinition();
+          } else {
+            this.messageService.showReferencePanel(refs);
+          }
+        });
+    } else {
+      this.dialogService.showConfirmDialog('Element(e) löschen?')
+        .subscribe((result: boolean) => {
+          if (result) {
+            this.unitService.unregisterIDs(elements);
+            this.unit.pages[this.selectionService.selectedPageIndex].sections.forEach(section => {
+              section.elements = section.elements.filter(element => !elements.includes(element));
+            });
+            this.unitService.updateUnitDefinition();
+          }
+        });
+    }
+  }
+
+  updateElementsProperty(elements: UIElement[], property: string, value: unknown): void {
+    console.log('updateElementProperty', elements, property, value);
+    elements.forEach(element => {
+      if (property === 'id') {
+        if (this.idService.validateAndAddNewID(value as string, element.id)) {
+          element.setProperty('id', value);
+        }
+      } else if (element.type === 'text' && property === 'text') {
+        this.handleTextElementChange(element as TextElement, value as string);
+      } else if (property === 'document') {
+        this.handleClozeDocumentChange(element as ClozeElement, value as ClozeDocument);
+      } else {
+        element.setProperty(property, value);
+        if (element.type === 'geometry' && property !== 'trackedVariables') this.unitService.geometryElementPropertyUpdated.next(element.id);
+        if (element.type === 'math-table') this.unitService.mathTableElementPropertyUpdated.next(element.id);
+      }
+    });
+    this.unitService.elementPropertyUpdated.next();
+    this.unitService.updateUnitDefinition();
+  }
+
+  private handleTextElementChange(element: TextElement, value: string): void {
+    const deletedAnchorIDs = UnitService.getRemovedTextAnchorIDs(element, value);
+    const refs = this.unitService.referenceManager.getTextAnchorReferences(deletedAnchorIDs);
+    if (refs.length > 0) {
+      this.dialogService.showDeleteReferenceDialog(refs)
+        .subscribe((result: boolean) => {
+          if (result) {
+            ReferenceManager.deleteReferences(refs);
+            element.setProperty('text', value);
+          } else {
+            this.messageService.showReferencePanel(refs);
+          }
+        });
+    } else {
+      element.setProperty('text', value);
+    }
+  }
+
+  private handleClozeDocumentChange(element: ClozeElement, newValue: ClozeDocument): void {
+    const deletedElements = UnitService.getRemovedClozeElements(element, newValue);
+    const refs = this.unitService.referenceManager.getElementsReferences(deletedElements);
+    if (refs.length > 0) {
+      this.dialogService.showDeleteReferenceDialog(refs)
+        .subscribe((result: boolean) => {
+          if (result) {
+            ReferenceManager.deleteReferences(refs);
+            this.applyClozeDocumentChange(element, newValue);
+          } else {
+            this.messageService.showReferencePanel(refs);
+          }
+        });
+    } else {
+      this.applyClozeDocumentChange(element, newValue);
+    }
+  }
+
+  private applyClozeDocumentChange(element: ClozeElement, value: ClozeDocument): void {
+    element.setProperty('document', value);
+    ClozeElement.getDocumentChildElements(value as ClozeDocument).forEach(clozeChild => {
+      if (clozeChild.id === 'cloze-child-id-placeholder') {
+        clozeChild.id = this.idService.getAndRegisterNewID(clozeChild.type);
+        delete clozeChild.position;
+      }
+    });
+  }
+
+  alignElements(elements: PositionedUIElement[], alignmentDirection: 'left' | 'right' | 'top' | 'bottom'): void {
+    switch (alignmentDirection) {
+      case 'left':
+        this.updateElementsProperty(
+          elements,
+          'xPosition',
+          Math.min(...elements.map(element => element.position.xPosition))
+        );
+        break;
+      case 'right':
+        this.updateElementsProperty(
+          elements,
+          'xPosition',
+          Math.max(...elements.map(element => element.position.xPosition))
+        );
+        break;
+      case 'top':
+        this.updateElementsProperty(
+          elements,
+          'yPosition',
+          Math.min(...elements.map(element => element.position.yPosition))
+        );
+        break;
+      case 'bottom':
+        this.updateElementsProperty(
+          elements,
+          'yPosition',
+          Math.max(...elements.map(element => element.position.yPosition))
+        );
+        break;
+      // no default
+    }
+    this.unitService.elementPropertyUpdated.next();
+    this.unitService.updateUnitDefinition();
+  }
+
+  showDefaultEditDialog(element: UIElement): void {
+    switch (element.type) {
+      case 'button':
+      case 'dropdown':
+      case 'checkbox':
+      case 'radio':
+        this.dialogService.showTextEditDialog(element.label as string).subscribe((result: string) => {
+          if (result) {
+            this.updateElementsProperty([element], 'label', result);
+          }
+        });
+        break;
+      case 'text':
+        this.dialogService.showRichTextEditDialog(
+          (element as TextElement).text,
+          (element as TextElement).styling.fontSize
+        ).subscribe((result: string) => {
+          if (result) {
+            // TODO add proper sanitization
+            this.updateElementsProperty(
+              [element],
+              'text',
+              (this.sanitizer.bypassSecurityTrustHtml(result) as any).changingThisBreaksApplicationSecurity as string
+            );
+          }
+        });
+        break;
+      case 'cloze':
+        this.dialogService.showClozeTextEditDialog(
+          (element as ClozeElement).document!,
+          (element as ClozeElement).styling.fontSize
+        ).subscribe((result: string) => {
+          if (result) {
+            // TODO add proper sanitization
+            this.updateElementsProperty(
+              [element],
+              'document',
+              (this.sanitizer.bypassSecurityTrustHtml(result) as any).changingThisBreaksApplicationSecurity as string
+            );
+          }
+        });
+        break;
+      case 'text-field':
+        this.dialogService.showTextEditDialog((element as InputElement).value as string)
+          .subscribe((result: string) => {
+            if (result) {
+              this.updateElementsProperty([element], 'value', result);
+            }
+          });
+        break;
+      case 'text-area':
+        this.dialogService.showMultilineTextEditDialog((element as InputElement).value as string)
+          .subscribe((result: string) => {
+            if (result) {
+              this.updateElementsProperty([element], 'value', result);
+            }
+          });
+        break;
+      case 'audio':
+      case 'video':
+        this.dialogService.showPlayerEditDialog(element.id, (element as PlayerElement).player)
+          .subscribe((result: PlayerProperties) => {
+            if (!result) return;
+            Object.keys(result).forEach(
+              key => this.updateElementsPlayerProperty([element], key, result[key] as UIElementValue)
+            );
+          });
+        break;
+      // no default
+    }
+  }
+
+  updateSelectedElementsStyleProperty(property: string, value: UIElementValue): void {
+    const elements = this.selectionService.getSelectedElements();
+    elements.forEach(element => {
+      element.setStyleProperty(property, value);
+    });
+    this.unitService.elementPropertyUpdated.next();
+    this.unitService.updateUnitDefinition();
+  }
+
+  updateElementsPlayerProperty(elements: UIElement[], property: string, value: UIElementValue): void {
+    elements.forEach(element => {
+      element.setPlayerProperty(property, value);
+    });
+    this.unitService.elementPropertyUpdated.next();
+    this.unitService.updateUnitDefinition();
+  }
+}
diff --git a/projects/editor/src/app/services/unit-services/page.service.ts b/projects/editor/src/app/services/unit-services/page.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..28a7b82b8817bfa259cad42ee84e1830f18ee66c
--- /dev/null
+++ b/projects/editor/src/app/services/unit-services/page.service.ts
@@ -0,0 +1,56 @@
+import { Injectable } from '@angular/core';
+import { Page } from 'common/models/page';
+import { UnitService } from 'editor/src/app/services/unit-services/unit.service';
+import { MessageService } from 'common/services/message.service';
+import { SelectionService } from 'editor/src/app/services/selection.service';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class PageService {
+  unit = this.unitService.unit;
+
+  constructor(private unitService: UnitService,
+              private messageService: MessageService,
+              private selectionService: SelectionService) { }
+
+  addPage(): void {
+    this.unitService.updateUnitDefinition({
+      title: 'Seite hinzugefügt',
+      command: () => {
+        this.unit.pages.push(new Page());
+        this.selectionService.selectedPageIndex = this.unit.pages.length - 1; // TODO selection stuff here is not good
+        return {};
+      },
+      rollback: () => {
+        this.unit.pages.splice(this.unit.pages.length - 1, 1);
+        this.selectionService.selectPreviousPage();
+      }
+    });
+  }
+
+  deletePage(pageIndex: number): void {
+    this.unitService.updateUnitDefinition({
+      title: 'Seite gelöscht',
+      command: () => {
+        const deletedpage = this.unitService.unit.pages.splice(pageIndex, 1)[0];
+        return {
+          pageIndex,
+          deletedpage
+        };
+      },
+      rollback: (deletedData: Record<string, unknown>) => {
+        this.unit.pages.splice(deletedData['pageIndex'] as number, 0, deletedData['deletedpage'] as Page);
+      }
+    });
+  }
+
+  moveSelectedPage(direction: 'left' | 'right') {
+    if (this.unit.canPageBeMoved(this.selectionService.selectedPageIndex, direction)) {
+      this.unit.movePage(this.selectionService.selectedPageIndex, direction);
+      this.unitService.updateUnitDefinition();
+    } else {
+      this.messageService.showWarning('Seite kann nicht verschoben werden.');
+    }
+  }
+}
diff --git a/projects/editor/src/app/services/unit-services/section.service.ts b/projects/editor/src/app/services/unit-services/section.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b559cf6315e9fc06ba310ba103236a682bef7038
--- /dev/null
+++ b/projects/editor/src/app/services/unit-services/section.service.ts
@@ -0,0 +1,99 @@
+import { Injectable } from '@angular/core';
+import { UnitService } from 'editor/src/app/services/unit-services/unit.service';
+import { SelectionService } from 'editor/src/app/services/selection.service';
+import { Page } from 'common/models/page';
+import { Section } from 'common/models/section';
+import { PositionedUIElement, UIElement } from 'common/models/elements/element';
+import { DropListElement } from 'common/models/elements/input-elements/drop-list';
+import { ArrayUtils } from 'common/util/array';
+import { IDService } from 'editor/src/app/services/id.service';
+import { VisibilityRule } from 'common/models/visibility-rule';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class SectionService {
+  unit = this.unitService.unit;
+
+  constructor(private unitService: UnitService,
+              private selectionService: SelectionService,
+              private idService: IDService) { }
+
+  updateSectionProperty(section: Section, property: string, value: string | number | boolean | VisibilityRule[] | { value: number; unit: string }[]): void {
+    section.setProperty(property, value);
+    this.unitService.elementPropertyUpdated.next();
+    this.unitService.updateUnitDefinition();
+  }
+
+  addSection(page: Page, section?: Section): void {
+    // register section IDs
+    if (section) {
+      section.elements.forEach(element => {
+        if (['drop-list', 'drop-list-simple'].includes((element as UIElement).type as string)) {
+          (element as DropListElement).value.forEach(value => this.idService.addID(value.id));
+        }
+        if (['likert', 'cloze'].includes((element as UIElement).type as string)) {
+          element.getChildElements().forEach(el => {
+            this.idService.addID(el.id);
+            if ((element as UIElement).type === 'drop-list') {
+              (element as DropListElement).value.forEach(value => this.idService.addID(value.id));
+            }
+          });
+        }
+        this.idService.addID(element.id);
+      });
+    }
+    page.sections.push(
+      section || new Section()
+    );
+    this.unitService.updateUnitDefinition();
+  }
+
+  deleteSection(pageIndex: number, sectionIndex: number): void {
+    this.unitService.unregisterIDs(this.unit.pages[pageIndex].sections[sectionIndex].getAllElements());
+    this.unit.pages[pageIndex].sections.splice(sectionIndex, 1);
+    this.unitService.updateUnitDefinition();
+  }
+
+  duplicateSection(section: Section, page: Page, sectionIndex: number): void {
+    const newSection: Section = new Section({
+      ...section,
+      elements: section.elements.map(element => this.unitService.duplicateElement(element) as PositionedUIElement)
+    });
+    page.sections.splice(sectionIndex + 1, 0, newSection);
+    this.unitService.updateUnitDefinition();
+  }
+
+  moveSection(section: Section, page: Page, direction: 'up' | 'down'): void {
+    ArrayUtils.moveArrayItem(section, page.sections, direction);
+    if (direction === 'up' && this.selectionService.selectedPageSectionIndex > 0) {
+      this.selectionService.selectedPageSectionIndex -= 1;
+    } else if (direction === 'down') {
+      this.selectionService.selectedPageSectionIndex += 1;
+    }
+    this.unitService.updateUnitDefinition();
+  }
+
+  replaceSection(pageIndex: number, sectionIndex: number, newSection: Section): void {
+    this.deleteSection(pageIndex, sectionIndex);
+    this.addSection(this.unit.pages[pageIndex], newSection);
+  }
+
+  /* Move element between sections */
+  transferElement(elements: UIElement[], previousSection: Section, newSection: Section): void {
+    previousSection.elements = previousSection.elements.filter(element => !elements.includes(element));
+    elements.forEach(element => {
+      newSection.elements.push(element as PositionedUIElement);
+    });
+    this.unitService.updateUnitDefinition();
+  }
+
+  duplicateSelectedElements(): void {
+    const selectedSection =
+      this.unit.pages[this.selectionService.selectedPageIndex].sections[this.selectionService.selectedPageSectionIndex];
+    this.selectionService.getSelectedElements().forEach((element: UIElement) => {
+      selectedSection.elements.push(this.unitService.duplicateElement(element, true) as PositionedUIElement);
+    });
+    this.unitService.updateUnitDefinition();
+  }
+}
diff --git a/projects/editor/src/app/services/unit-services/unit.service.ts b/projects/editor/src/app/services/unit-services/unit.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b62fc2252b02a7c2e0d69c4ea90b80759782de8c
--- /dev/null
+++ b/projects/editor/src/app/services/unit-services/unit.service.ts
@@ -0,0 +1,253 @@
+import { Injectable } from '@angular/core';
+import { DomSanitizer } from '@angular/platform-browser';
+import { firstValueFrom, Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+import { FileService } from 'common/services/file.service';
+import { MessageService } from 'common/services/message.service';
+import { Unit, UnitProperties } from 'common/models/unit';
+import {
+  PlayerProperties,
+  PositionProperties,
+  PropertyGroupGenerators
+} from 'common/models/elements/property-group-interfaces';
+import { DragNDropValueObject } from 'common/models/elements/label-interfaces';
+import {
+  CompoundElement, InputElement, PlayerElement, PositionedUIElement,
+  UIElement, UIElementProperties, UIElementType, UIElementValue
+} from 'common/models/elements/element';
+import { ClozeDocument, ClozeElement } from 'common/models/elements/compound-elements/cloze/cloze';
+import { TextElement } from 'common/models/elements/text/text';
+import { DropListElement } from 'common/models/elements/input-elements/drop-list';
+import { Section } from 'common/models/section';
+import { ElementFactory } from 'common/util/element.factory';
+import { GeometryProperties } from 'common/models/elements/geometry/geometry';
+import { AudioProperties } from 'common/models/elements/media-elements/audio';
+import { VideoProperties } from 'common/models/elements/media-elements/video';
+import { ImageProperties } from 'common/models/elements/media-elements/image';
+import { StateVariable } from 'common/models/state-variable';
+import { VisibilityRule } from 'common/models/visibility-rule';
+import { VersionManager } from 'common/services/version-manager';
+import { ReferenceManager } from 'editor/src/app/services/reference-manager';
+import { DialogService } from '../dialog.service';
+import { VeronaAPIService } from '../verona-api.service';
+import { SelectionService } from '../selection.service';
+import { IDService } from '../id.service';
+import { UnitDefinitionSanitizer } from '../sanitizer';
+import { HistoryService, UnitUpdateCommand } from 'editor/src/app/services/history.service';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class UnitService {
+  unit: Unit;
+  elementPropertyUpdated: Subject<void> = new Subject<void>();
+  geometryElementPropertyUpdated: Subject<string> = new Subject<string>();
+  mathTableElementPropertyUpdated: Subject<string> = new Subject<string>();
+  referenceManager: ReferenceManager;
+  private ngUnsubscribe = new Subject<void>();
+
+  constructor(private selectionService: SelectionService,
+              private veronaApiService: VeronaAPIService,
+              private messageService: MessageService,
+              private dialogService: DialogService,
+              private historyService: HistoryService,
+
+              private idService: IDService) {
+    this.unit = new Unit();
+    this.referenceManager = new ReferenceManager(this.unit);
+  }
+
+  loadUnitDefinition(unitDefinition: string): void {
+    if (unitDefinition) {
+      try {
+        let unitDef = JSON.parse(unitDefinition);
+        if (!VersionManager.hasCompatibleVersion(unitDef)) {
+          if (VersionManager.isNewer(unitDef)) {
+            throw Error('Unit-Version ist neuer als dieser Editor. Bitte mit der neuesten Version öffnen.');
+          }
+          if (!VersionManager.needsSanitization(unitDef)) {
+            throw Error('Unit-Version ist veraltet. Sie kann mit Version 1.38/1.39 aktualisiert werden.');
+          }
+          this.dialogService.showSanitizationDialog().subscribe(() => {
+            unitDef = UnitDefinitionSanitizer.sanitizeUnit(unitDef);
+            this.loadUnit(unitDef);
+            this.updateUnitDefinition();
+          });
+        } else {
+          this.loadUnit(unitDef);
+        }
+      } catch (e) {
+        // eslint-disable-next-line no-console
+        console.error(e);
+        if (e instanceof Error) this.dialogService.showUnitDefErrorDialog(e.message);
+      }
+    } else {
+      this.idService.reset();
+      this.unit = new Unit();
+      this.referenceManager = new ReferenceManager(this.unit);
+    }
+  }
+
+  private loadUnit(parsedUnitDefinition?: string): void {
+    this.idService.reset();
+    this.unit = new Unit(parsedUnitDefinition as unknown as UnitProperties);
+    this.idService.registerUnitIds(this.unit);
+    this.referenceManager = new ReferenceManager(this.unit);
+
+    const invalidRefs = this.referenceManager.getAllInvalidRefs();
+    if (invalidRefs.length > 0) {
+      this.referenceManager.removeInvalidRefs(invalidRefs);
+      this.messageService.showFixedReferencePanel(invalidRefs);
+      this.updateUnitDefinition();
+    }
+  }
+
+  updateUnitDefinition(command?: UnitUpdateCommand): void {
+    if (command) {
+      const deletedData = command.command();
+      this.historyService.addCommand(command, deletedData);
+    }
+    this.veronaApiService.sendChanged(this.unit);
+  }
+
+  rollback(): void {
+    this.historyService.rollback();
+    this.veronaApiService.sendChanged(this.unit);
+  }
+
+  freeUpIds(elements: UIElement[]): void {
+    elements.forEach(element => {
+      if (element.type === 'drop-list') {
+        ((element as DropListElement).value as DragNDropValueObject[]).forEach((value: DragNDropValueObject) => {
+          this.idService.removeId(value.id);
+        });
+      }
+      if (element instanceof CompoundElement) {
+        element.getChildElements().forEach((childElement: UIElement) => {
+          this.idService.removeId(childElement.id);
+        });
+      }
+      this.idService.removeId(element.id);
+    });
+  }
+
+  /* - Also changes position of the element to not cover copied element.
+     - Also changes and registers all copied IDs. */
+  duplicateElement(element: UIElement, adjustPosition: boolean = false): UIElement {
+    const newElement = element.getDuplicate();
+
+    if (newElement.position && adjustPosition) {
+      newElement.position.xPosition += 10;
+      newElement.position.yPosition += 10;
+      newElement.position.gridRow = null;
+      newElement.position.gridColumn = null;
+    }
+
+    newElement.id = this.idService.getAndRegisterNewID(newElement.type);
+    if (newElement instanceof CompoundElement) {
+      newElement.getChildElements().forEach((child: UIElement) => {
+        child.id = this.idService.getAndRegisterNewID(child.type);
+        if (child.type === 'drop-list') {
+          (child.value as DragNDropValueObject[]).forEach(valueObject => {
+            valueObject.id = this.idService.getAndRegisterNewID('value');
+          });
+        }
+      });
+    }
+
+    // Special care with DropLists as they are no CompoundElement yet still have children with IDs
+    if (newElement.type === 'drop-list') {
+      (newElement.value as DragNDropValueObject[]).forEach(valueObject => {
+        valueObject.id = this.idService.getAndRegisterNewID('value');
+      });
+    }
+    return newElement;
+  }
+
+  static getRemovedTextAnchorIDs(element: TextElement, newValue: string): string[] {
+    return TextElement.getAnchorIDs(element.text)
+      .filter(el => !TextElement.getAnchorIDs(newValue).includes(el));
+  }
+
+  static getRemovedClozeElements(cloze: ClozeElement, newClozeDoc: ClozeDocument): UIElement[] {
+    const newElements = ClozeElement.getDocumentChildElements(newClozeDoc);
+    return cloze.getChildElements()
+      .filter(element => !newElements.includes(element));
+  }
+
+  updateSelectedElementsPositionProperty(property: string, value: UIElementValue): void {
+    this.updateElementsPositionProperty(this.selectionService.getSelectedElements(), property, value);
+  }
+
+  updateElementsPositionProperty(elements: UIElement[], property: string, value: UIElementValue): void {
+    elements.forEach(element => {
+      element.setPositionProperty(property, value);
+    });
+    this.reorderElements();
+    this.elementPropertyUpdated.next();
+    this.updateUnitDefinition();
+  }
+
+  updateElementsDimensionsProperty(elements: UIElement[], property: string, value: number | null): void {
+    console.log('updateElementsDimensionsProperty', property, value);
+    elements.forEach(element => {
+      element.setDimensionsProperty(property, value);
+    });
+    this.elementPropertyUpdated.next();
+    this.updateUnitDefinition();
+  }
+
+  /* Reorder elements by their position properties, so the tab order is correct */
+  reorderElements() {
+    const sectionElementList = this.unit.pages[this.selectionService.selectedPageIndex]
+      .sections[this.selectionService.selectedPageSectionIndex].elements;
+    const isDynamicPositioning = this.unit.pages[this.selectionService.selectedPageIndex]
+      .sections[this.selectionService.selectedPageSectionIndex].dynamicPositioning;
+    const sortDynamicPositioning = (a: PositionedUIElement, b: PositionedUIElement) => {
+      const rowSort =
+        (a.position.gridRow !== null ? a.position.gridRow : Infinity) -
+        (b.position.gridRow !== null ? b.position.gridRow : Infinity);
+      if (rowSort === 0) {
+        return a.position.gridColumn! - b.position.gridColumn!;
+      }
+      return rowSort;
+    };
+    const sortStaticPositioning = (a: PositionedUIElement, b: PositionedUIElement) => {
+      const ySort = a.position.yPosition! - b.position.yPosition!;
+      if (ySort === 0) {
+        return a.position.xPosition! - b.position.xPosition!;
+      }
+      return ySort;
+    };
+    if (isDynamicPositioning) {
+      sectionElementList.sort(sortDynamicPositioning);
+    } else {
+      sectionElementList.sort(sortStaticPositioning);
+    }
+  }
+
+  saveUnit(): void {
+    FileService.saveUnitToFile(JSON.stringify(this.unit));
+  }
+
+  async loadUnitFromFile(): Promise<void> {
+    this.loadUnitDefinition(await FileService.loadFile(['.json', '.voud']));
+  }
+
+  getNewValueID(): string {
+    return this.idService.getAndRegisterNewID('value');
+  }
+
+  /* Used by props panel to show available dropLists to connect */
+  getAllDropListElementIDs(): string[] {
+    const allDropLists = [
+      ...this.unit.getAllElements('drop-list'),
+      ...this.unit.getAllElements('drop-list-simple')];
+    return allDropLists.map(dropList => dropList.id);
+  }
+
+  updateStateVariables(stateVariables: StateVariable[]): void {
+    this.unit.stateVariables = stateVariables;
+    this.updateUnitDefinition();
+  }
+}
diff --git a/projects/editor/src/app/services/unit.service.ts b/projects/editor/src/app/services/unit.service.ts
deleted file mode 100644
index f589e9b6ed962358305ae94308fedc8097b788c9..0000000000000000000000000000000000000000
--- a/projects/editor/src/app/services/unit.service.ts
+++ /dev/null
@@ -1,629 +0,0 @@
-import { Injectable } from '@angular/core';
-import { DomSanitizer } from '@angular/platform-browser';
-import { firstValueFrom, Subject } from 'rxjs';
-import { takeUntil } from 'rxjs/operators';
-import { TranslateService } from '@ngx-translate/core';
-import { FileService } from 'common/services/file.service';
-import { MessageService } from 'common/services/message.service';
-import { ArrayUtils } from 'common/util/array';
-import { Unit, UnitProperties } from 'common/models/unit';
-import {
-  PlayerProperties,
-  PositionProperties,
-  PropertyGroupGenerators
-} from 'common/models/elements/property-group-interfaces';
-import { DragNDropValueObject } from 'common/models/elements/label-interfaces';
-import {
-  CompoundElement, InputElement, PlayerElement, PositionedUIElement,
-  UIElement, UIElementProperties, UIElementType, UIElementValue
-} from 'common/models/elements/element';
-import { ClozeDocument, ClozeElement } from 'common/models/elements/compound-elements/cloze/cloze';
-import { TextElement } from 'common/models/elements/text/text';
-import { DropListElement } from 'common/models/elements/input-elements/drop-list';
-import { Page } from 'common/models/page';
-import { Section } from 'common/models/section';
-import { ElementFactory } from 'common/util/element.factory';
-import { GeometryProperties } from 'common/models/elements/geometry/geometry';
-import { AudioProperties } from 'common/models/elements/media-elements/audio';
-import { VideoProperties } from 'common/models/elements/media-elements/video';
-import { ImageProperties } from 'common/models/elements/media-elements/image';
-import { StateVariable } from 'common/models/state-variable';
-import { VisibilityRule } from 'common/models/visibility-rule';
-import { VersionManager } from 'common/services/version-manager';
-import { ReferenceManager } from 'editor/src/app/services/reference-manager';
-import { DialogService } from './dialog.service';
-import { VeronaAPIService } from './verona-api.service';
-import { SelectionService } from './selection.service';
-import { IDService } from './id.service';
-import { UnitDefinitionSanitizer } from './sanitizer';
-
-@Injectable({
-  providedIn: 'root'
-})
-export class UnitService {
-  unit: Unit;
-  elementPropertyUpdated: Subject<void> = new Subject<void>();
-  geometryElementPropertyUpdated: Subject<string> = new Subject<string>();
-  mathTableElementPropertyUpdated: Subject<string> = new Subject<string>();
-  referenceManager: ReferenceManager;
-  private ngUnsubscribe = new Subject<void>();
-
-  constructor(private selectionService: SelectionService,
-              private veronaApiService: VeronaAPIService,
-              private messageService: MessageService,
-              private dialogService: DialogService,
-              private sanitizer: DomSanitizer,
-              private translateService: TranslateService,
-              private idService: IDService) {
-    this.unit = new Unit();
-    this.referenceManager = new ReferenceManager(this.unit);
-  }
-
-  loadUnitDefinition(unitDefinition: string): void {
-    if (unitDefinition) {
-      try {
-        let unitDef = JSON.parse(unitDefinition);
-        if (!VersionManager.hasCompatibleVersion(unitDef)) {
-          if (VersionManager.isNewer(unitDef)) {
-            throw Error('Unit-Version ist neuer als dieser Editor. Bitte mit der neuesten Version öffnen.');
-          }
-          if (!VersionManager.needsSanitization(unitDef)) {
-            throw Error('Unit-Version ist veraltet. Sie kann mit Version 1.38/1.39 aktualisiert werden.');
-          }
-          this.dialogService.showSanitizationDialog().subscribe(() => {
-            unitDef = UnitDefinitionSanitizer.sanitizeUnit(unitDef);
-            this.loadUnit(unitDef);
-            this.unitUpdated();
-          });
-        } else {
-          this.loadUnit(unitDef);
-        }
-      } catch (e) {
-        // eslint-disable-next-line no-console
-        console.error(e);
-        if (e instanceof Error) this.dialogService.showUnitDefErrorDialog(e.message);
-      }
-    } else {
-      this.idService.reset();
-      this.unit = new Unit();
-      this.referenceManager = new ReferenceManager(this.unit);
-    }
-  }
-
-  private loadUnit(parsedUnitDefinition?: string): void {
-    this.idService.reset();
-    this.unit = new Unit(parsedUnitDefinition as unknown as UnitProperties);
-    this.idService.registerUnitIds(this.unit);
-    this.referenceManager = new ReferenceManager(this.unit);
-
-    const invalidRefs = this.referenceManager.getAllInvalidRefs();
-    if (invalidRefs.length > 0) {
-      this.referenceManager.removeInvalidRefs(invalidRefs);
-      this.messageService.showFixedReferencePanel(invalidRefs);
-      this.unitUpdated();
-    }
-  }
-
-  unitUpdated(): void {
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  addPage(): void {
-    this.unit.pages.push(new Page());
-    this.selectionService.selectedPageIndex = this.unit.pages.length - 1;
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  deletePage(pageIndex: number): void {
-    this.unit.pages.splice(pageIndex, 1);
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  moveSelectedPage(direction: 'left' | 'right') {
-    if (this.unit.canPageBeMoved(this.selectionService.selectedPageIndex, direction)) {
-      this.unit.movePage(this.selectionService.selectedPageIndex, direction);
-      this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-    } else {
-      this.messageService.showWarning('Seite kann nicht verschoben werden.');
-    }
-  }
-
-  addSection(page: Page, section?: Section): void {
-    // register section IDs
-    if (section) {
-      section.elements.forEach(element => {
-        if (['drop-list', 'drop-list-simple'].includes((element as UIElement).type as string)) {
-          (element as DropListElement).value.forEach(value => this.idService.addID(value.id));
-        }
-        if (['likert', 'cloze'].includes((element as UIElement).type as string)) {
-          element.getChildElements().forEach(el => {
-            this.idService.addID(el.id);
-            if ((element as UIElement).type === 'drop-list') {
-              (element as DropListElement).value.forEach(value => this.idService.addID(value.id));
-            }
-          });
-        }
-        this.idService.addID(element.id);
-      });
-    }
-    page.sections.push(
-      section || new Section()
-    );
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  deleteSection(pageIndex: number, sectionIndex: number): void {
-    this.freeUpIds(this.unit.pages[pageIndex].sections[sectionIndex].getAllElements());
-    this.unit.pages[pageIndex].sections.splice(sectionIndex, 1);
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  duplicateSection(section: Section, page: Page, sectionIndex: number): void {
-    const newSection: Section = new Section({
-      ...section,
-      elements: section.elements.map(element => this.duplicateElement(element) as PositionedUIElement)
-    });
-    page.sections.splice(sectionIndex + 1, 0, newSection);
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  moveSection(section: Section, page: Page, direction: 'up' | 'down'): void {
-    ArrayUtils.moveArrayItem(section, page.sections, direction);
-    if (direction === 'up' && this.selectionService.selectedPageSectionIndex > 0) {
-      this.selectionService.selectedPageSectionIndex -= 1;
-    } else if (direction === 'down') {
-      this.selectionService.selectedPageSectionIndex += 1;
-    }
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  addElementToSectionByIndex(elementType: UIElementType,
-                             pageIndex: number,
-                             sectionIndex: number): void {
-    this.addElementToSection(elementType, this.unit.pages[pageIndex].sections[sectionIndex]);
-  }
-
-  async addElementToSection(elementType: UIElementType, section: Section,
-                            coordinates?: { x: number, y: number }): Promise<void> {
-    const newElementProperties: Partial<UIElementProperties> = {};
-    if (['geometry'].includes(elementType)) {
-      (newElementProperties as GeometryProperties).appDefinition =
-        await firstValueFrom(this.dialogService.showGeogebraAppDefinitionDialog());
-      if (!(newElementProperties as GeometryProperties).appDefinition) return; // dialog canceled
-    }
-    if (['audio', 'video', 'image', 'hotspot-image'].includes(elementType)) {
-      let mediaSrc = '';
-      switch (elementType) {
-        case 'hotspot-image':
-        case 'image':
-          mediaSrc = await FileService.loadImage();
-          break;
-        case 'audio':
-          mediaSrc = await FileService.loadAudio();
-          break;
-        case 'video':
-          mediaSrc = await FileService.loadVideo();
-          break;
-        // no default
-      }
-      (newElementProperties as AudioProperties | VideoProperties | ImageProperties).src = mediaSrc;
-    }
-
-    // Coordinates are given if an element is dragged directly into a cell
-    if (coordinates) {
-      newElementProperties.position = {
-        ...(section.dynamicPositioning && { gridColumn: coordinates.x }),
-        ...(section.dynamicPositioning && { gridRow: coordinates.y }),
-        ...(!section.dynamicPositioning && { yPosition: coordinates.y }),
-        ...(!section.dynamicPositioning && { yPosition: coordinates.y })
-      } as PositionProperties;
-    }
-
-    // Use z-index -1 for frames
-    newElementProperties.position = {
-      zIndex: elementType === 'frame' ? -1 : 0,
-      ...newElementProperties.position
-    } as PositionProperties;
-
-    section.addElement(ElementFactory.createElement({
-      type: elementType,
-      position: PropertyGroupGenerators.generatePositionProps(newElementProperties.position),
-      ...newElementProperties,
-      id: this.idService.getAndRegisterNewID(elementType)
-    }) as PositionedUIElement);
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  deleteElements(elements: UIElement[]): void {
-    const refs =
-      this.referenceManager.getElementsReferences(elements);
-    // console.log('element refs', refs);
-    if (refs.length > 0) {
-      this.dialogService.showDeleteReferenceDialog(refs)
-        .pipe(takeUntil(this.ngUnsubscribe))
-        .subscribe((result: boolean) => {
-          if (result) {
-            ReferenceManager.deleteReferences(refs);
-            this.freeUpIds(elements);
-            this.unit.pages[this.selectionService.selectedPageIndex].sections.forEach(section => {
-              section.elements = section.elements.filter(element => !elements.includes(element));
-            });
-            this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-          } else {
-            this.messageService.showReferencePanel(refs);
-          }
-        });
-    } else {
-      this.dialogService.showConfirmDialog('Element(e) löschen?')
-        .pipe(takeUntil(this.ngUnsubscribe))
-        .subscribe((result: boolean) => {
-          if (result) {
-            this.freeUpIds(elements);
-            this.unit.pages[this.selectionService.selectedPageIndex].sections.forEach(section => {
-              section.elements = section.elements.filter(element => !elements.includes(element));
-            });
-            this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-          }
-        });
-    }
-  }
-
-  private freeUpIds(elements: UIElement[]): void {
-    elements.forEach(element => {
-      if (element.type === 'drop-list') {
-        ((element as DropListElement).value as DragNDropValueObject[]).forEach((value: DragNDropValueObject) => {
-          this.idService.removeId(value.id);
-        });
-      }
-      if (element instanceof CompoundElement) {
-        element.getChildElements().forEach((childElement: UIElement) => {
-          this.idService.removeId(childElement.id);
-        });
-      }
-      this.idService.removeId(element.id);
-    });
-  }
-
-  /* Move element between sections */
-  transferElement(elements: UIElement[], previousSection: Section, newSection: Section): void {
-    previousSection.elements = previousSection.elements.filter(element => !elements.includes(element));
-    elements.forEach(element => {
-      newSection.elements.push(element as PositionedUIElement);
-    });
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  duplicateSelectedElements(): void {
-    const selectedSection =
-      this.unit.pages[this.selectionService.selectedPageIndex].sections[this.selectionService.selectedPageSectionIndex];
-    this.selectionService.getSelectedElements().forEach((element: UIElement) => {
-      selectedSection.elements.push(this.duplicateElement(element, true) as PositionedUIElement);
-    });
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  /* - Also changes position of the element to not cover copied element.
-     - Also changes and registers all copied IDs. */
-  private duplicateElement(element: UIElement, adjustPosition: boolean = false): UIElement {
-    const newElement = element.getDuplicate();
-
-    if (newElement.position && adjustPosition) {
-      newElement.position.xPosition += 10;
-      newElement.position.yPosition += 10;
-      newElement.position.gridRow = null;
-      newElement.position.gridColumn = null;
-    }
-
-    newElement.id = this.idService.getAndRegisterNewID(newElement.type);
-    if (newElement instanceof CompoundElement) {
-      newElement.getChildElements().forEach((child: UIElement) => {
-        child.id = this.idService.getAndRegisterNewID(child.type);
-        if (child.type === 'drop-list') {
-          (child.value as DragNDropValueObject[]).forEach(valueObject => {
-            valueObject.id = this.idService.getAndRegisterNewID('value');
-          });
-        }
-      });
-    }
-
-    // Special care with DropLists as they are no CompoundElement yet still have children with IDs
-    if (newElement.type === 'drop-list') {
-      (newElement.value as DragNDropValueObject[]).forEach(valueObject => {
-        valueObject.id = this.idService.getAndRegisterNewID('value');
-      });
-    }
-    return newElement;
-  }
-
-  updateSectionProperty(section: Section, property: string, value: string | number | boolean | VisibilityRule[] | { value: number; unit: string }[]): void {
-    section.setProperty(property, value);
-    this.elementPropertyUpdated.next();
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  updateElementsProperty(elements: UIElement[], property: string, value: unknown): void {
-    console.log('updateElementProperty', elements, property, value);
-    elements.forEach(element => {
-      if (property === 'id') {
-        if (this.idService.validateAndAddNewID(value as string, element.id)) {
-          element.setProperty('id', value);
-        }
-      } else if (element.type === 'text' && property === 'text') {
-        this.handleTextElementChange(element as TextElement, value as string);
-      } else if (property === 'document') {
-        this.handleClozeDocumentChange(element as ClozeElement, value as ClozeDocument);
-      } else {
-        element.setProperty(property, value);
-        if (element.type === 'geometry' && property !== 'trackedVariables') this.geometryElementPropertyUpdated.next(element.id);
-        if (element.type === 'math-table') this.mathTableElementPropertyUpdated.next(element.id);
-      }
-    });
-    this.elementPropertyUpdated.next();
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  handleTextElementChange(element: TextElement, value: string): void {
-    const deletedAnchorIDs = UnitService.getRemovedTextAnchorIDs(element, value);
-    const refs = this.referenceManager.getTextAnchorReferences(deletedAnchorIDs);
-    if (refs.length > 0) {
-      this.dialogService.showDeleteReferenceDialog(refs)
-        .pipe(takeUntil(this.ngUnsubscribe))
-        .subscribe((result: boolean) => {
-          if (result) {
-            ReferenceManager.deleteReferences(refs);
-            element.setProperty('text', value);
-          } else {
-            this.messageService.showReferencePanel(refs);
-          }
-        });
-    } else {
-      element.setProperty('text', value);
-    }
-  }
-
-  static getRemovedTextAnchorIDs(element: TextElement, newValue: string): string[] {
-    return TextElement.getAnchorIDs(element.text)
-      .filter(el => !TextElement.getAnchorIDs(newValue).includes(el));
-  }
-
-  handleClozeDocumentChange(element: ClozeElement, newValue: ClozeDocument): void {
-    const deletedElements = UnitService.getRemovedClozeElements(element, newValue);
-    const refs = this.referenceManager.getElementsReferences(deletedElements);
-    if (refs.length > 0) {
-      this.dialogService.showDeleteReferenceDialog(refs)
-        .pipe(takeUntil(this.ngUnsubscribe))
-        .subscribe((result: boolean) => {
-          if (result) {
-            ReferenceManager.deleteReferences(refs);
-            this.applyClozeDocumentChange(element, newValue);
-          } else {
-            this.messageService.showReferencePanel(refs);
-          }
-        });
-    } else {
-      this.applyClozeDocumentChange(element, newValue);
-    }
-  }
-
-  applyClozeDocumentChange(element: ClozeElement, value: ClozeDocument): void {
-    element.setProperty('document', value);
-    ClozeElement.getDocumentChildElements(value as ClozeDocument).forEach(clozeChild => {
-      if (clozeChild.id === 'cloze-child-id-placeholder') {
-        clozeChild.id = this.idService.getAndRegisterNewID(clozeChild.type);
-        delete clozeChild.position;
-      }
-    });
-  }
-
-  static getRemovedClozeElements(cloze: ClozeElement, newClozeDoc: ClozeDocument): UIElement[] {
-    const newElements = ClozeElement.getDocumentChildElements(newClozeDoc);
-    return cloze.getChildElements()
-      .filter(element => !newElements.includes(element));
-  }
-
-  updateSelectedElementsPositionProperty(property: string, value: UIElementValue): void {
-    this.updateElementsPositionProperty(this.selectionService.getSelectedElements(), property, value);
-  }
-
-  updateElementsPositionProperty(elements: UIElement[], property: string, value: UIElementValue): void {
-    elements.forEach(element => {
-      element.setPositionProperty(property, value);
-    });
-    this.reorderElements();
-    this.elementPropertyUpdated.next();
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  updateElementsDimensionsProperty(elements: UIElement[], property: string, value: number | null): void {
-    console.log('updateElementsDimensionsProperty', property, value);
-    elements.forEach(element => {
-      element.setDimensionsProperty(property, value);
-    });
-    this.elementPropertyUpdated.next();
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  /* Reorder elements by their position properties, so the tab order is correct */
-  reorderElements() {
-    const sectionElementList = this.unit.pages[this.selectionService.selectedPageIndex]
-      .sections[this.selectionService.selectedPageSectionIndex].elements;
-    const isDynamicPositioning = this.unit.pages[this.selectionService.selectedPageIndex]
-      .sections[this.selectionService.selectedPageSectionIndex].dynamicPositioning;
-    const sortDynamicPositioning = (a: PositionedUIElement, b: PositionedUIElement) => {
-      const rowSort =
-        (a.position.gridRow !== null ? a.position.gridRow : Infinity) -
-        (b.position.gridRow !== null ? b.position.gridRow : Infinity);
-      if (rowSort === 0) {
-        return a.position.gridColumn! - b.position.gridColumn!;
-      }
-      return rowSort;
-    };
-    const sortStaticPositioning = (a: PositionedUIElement, b: PositionedUIElement) => {
-      const ySort = a.position.yPosition! - b.position.yPosition!;
-      if (ySort === 0) {
-        return a.position.xPosition! - b.position.xPosition!;
-      }
-      return ySort;
-    };
-    if (isDynamicPositioning) {
-      sectionElementList.sort(sortDynamicPositioning);
-    } else {
-      sectionElementList.sort(sortStaticPositioning);
-    }
-  }
-
-  updateSelectedElementsStyleProperty(property: string, value: UIElementValue): void {
-    const elements = this.selectionService.getSelectedElements();
-    elements.forEach(element => {
-      element.setStyleProperty(property, value);
-    });
-    this.elementPropertyUpdated.next();
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  updateElementsPlayerProperty(elements: UIElement[], property: string, value: UIElementValue): void {
-    elements.forEach(element => {
-      element.setPlayerProperty(property, value);
-    });
-    this.elementPropertyUpdated.next();
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  alignElements(elements: PositionedUIElement[], alignmentDirection: 'left' | 'right' | 'top' | 'bottom'): void {
-    switch (alignmentDirection) {
-      case 'left':
-        this.updateElementsProperty(
-          elements,
-          'xPosition',
-          Math.min(...elements.map(element => element.position.xPosition))
-        );
-        break;
-      case 'right':
-        this.updateElementsProperty(
-          elements,
-          'xPosition',
-          Math.max(...elements.map(element => element.position.xPosition))
-        );
-        break;
-      case 'top':
-        this.updateElementsProperty(
-          elements,
-          'yPosition',
-          Math.min(...elements.map(element => element.position.yPosition))
-        );
-        break;
-      case 'bottom':
-        this.updateElementsProperty(
-          elements,
-          'yPosition',
-          Math.max(...elements.map(element => element.position.yPosition))
-        );
-        break;
-      // no default
-    }
-    this.elementPropertyUpdated.next();
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-
-  saveUnit(): void {
-    FileService.saveUnitToFile(JSON.stringify(this.unit));
-  }
-
-  async loadUnitFromFile(): Promise<void> {
-    this.loadUnitDefinition(await FileService.loadFile(['.json', '.voud']));
-  }
-
-  showDefaultEditDialog(element: UIElement): void {
-    switch (element.type) {
-      case 'button':
-      case 'dropdown':
-      case 'checkbox':
-      case 'radio':
-        this.dialogService.showTextEditDialog(element.label as string).subscribe((result: string) => {
-          if (result) {
-            this.updateElementsProperty([element], 'label', result);
-          }
-        });
-        break;
-      case 'text':
-        this.dialogService.showRichTextEditDialog(
-          (element as TextElement).text,
-          (element as TextElement).styling.fontSize
-        ).subscribe((result: string) => {
-          if (result) {
-            // TODO add proper sanitization
-            this.updateElementsProperty(
-              [element],
-              'text',
-              (this.sanitizer.bypassSecurityTrustHtml(result) as any).changingThisBreaksApplicationSecurity as string
-            );
-          }
-        });
-        break;
-      case 'cloze':
-        this.dialogService.showClozeTextEditDialog(
-          (element as ClozeElement).document!,
-          (element as ClozeElement).styling.fontSize
-        ).subscribe((result: string) => {
-          if (result) {
-            // TODO add proper sanitization
-            this.updateElementsProperty(
-              [element],
-              'document',
-              (this.sanitizer.bypassSecurityTrustHtml(result) as any).changingThisBreaksApplicationSecurity as string
-            );
-          }
-        });
-        break;
-      case 'text-field':
-        this.dialogService.showTextEditDialog((element as InputElement).value as string)
-          .subscribe((result: string) => {
-            if (result) {
-              this.updateElementsProperty([element], 'value', result);
-            }
-          });
-        break;
-      case 'text-area':
-        this.dialogService.showMultilineTextEditDialog((element as InputElement).value as string)
-          .subscribe((result: string) => {
-            if (result) {
-              this.updateElementsProperty([element], 'value', result);
-            }
-          });
-        break;
-      case 'audio':
-      case 'video':
-        this.dialogService.showPlayerEditDialog(element.id, (element as PlayerElement).player)
-          .subscribe((result: PlayerProperties) => {
-            if (!result) return;
-            Object.keys(result).forEach(
-              key => this.updateElementsPlayerProperty([element], key, result[key] as UIElementValue)
-            );
-          });
-        break;
-      // no default
-    }
-  }
-
-  getNewValueID(): string {
-    return this.idService.getAndRegisterNewID('value');
-  }
-
-  /* Used by props panel to show available dropLists to connect */
-  getAllDropListElementIDs(): string[] {
-    const allDropLists = [
-      ...this.unit.getAllElements('drop-list'),
-      ...this.unit.getAllElements('drop-list-simple')];
-    return allDropLists.map(dropList => dropList.id);
-  }
-
-  replaceSection(pageIndex: number, sectionIndex: number, newSection: Section): void {
-    this.deleteSection(pageIndex, sectionIndex);
-    this.addSection(this.unit.pages[pageIndex], newSection);
-  }
-
-  updateStateVariables(stateVariables: StateVariable[]): void {
-    this.unit.stateVariables = stateVariables;
-    this.veronaApiService.sendVoeDefinitionChangedNotification(this.unit);
-  }
-}