diff --git a/src/app/components/shared/comment-answer/comment-answer.component.html b/src/app/components/shared/comment-answer/comment-answer.component.html
index 8d133d0083abd17604e4bf9a2321dae7c941ea58..64440f23d58d80dd97437a359a57f9327186229a 100644
--- a/src/app/components/shared/comment-answer/comment-answer.component.html
+++ b/src/app/components/shared/comment-answer/comment-answer.component.html
@@ -13,7 +13,7 @@
       <app-write-comment [user]="user"
                          [onClose]="openDeleteAnswerDialog()"
                          [onSubmit]="saveAnswer()"
-                         [disableCancelButton]="!answer && commentComponent && commentComponent.commentBody && !commentComponent.commentBody.nativeElement.innerText"
+                         [disableCancelButton]="!answer && commentComponent && commentComponent.commentData.currentText.length > 0"
                          [confirmLabel]="'save-answer'"
                          [cancelLabel]="'delete-answer'"
                          [additionalTemplate]="editAnswer"
@@ -25,7 +25,7 @@
 
 <ng-template #editAnswer>
   <div *ngIf="(isStudent || !edit) && answer">
-    <app-custom-markdown class="imborder-answerages" [data]="answer"></app-custom-markdown>
+    <app-view-comment-data [currentData]="answer"></app-view-comment-data>
     <div fxLayout="row"
          fxLayoutAlign="end">
       <button mat-raised-button
diff --git a/src/app/components/shared/comment-answer/comment-answer.component.ts b/src/app/components/shared/comment-answer/comment-answer.component.ts
index c83a515698c36e09a325e13ef206fd558fe64d89..7a4174f7656ff0693dcaf6a9b7135859fc67dbea 100644
--- a/src/app/components/shared/comment-answer/comment-answer.component.ts
+++ b/src/app/components/shared/comment-answer/comment-answer.component.ts
@@ -84,7 +84,7 @@ export class CommentAnswerComponent implements OnInit {
   }
 
   deleteAnswer() {
-    this.commentComponent.clearHTML();
+    this.commentComponent.commentData.clear();
     this.answer = null;
     this.commentService.answer(this.comment, this.answer).subscribe();
     this.translateService.get('comment-page.answer-deleted').subscribe(msg => {
@@ -94,6 +94,6 @@ export class CommentAnswerComponent implements OnInit {
 
   onEditClick() {
     this.edit = true;
-    setTimeout(() => this.commentComponent.setHTML(this.answer));
+    setTimeout(() => this.commentComponent.commentData.set(this.answer));
   }
 }
diff --git a/src/app/components/shared/comment/comment.component.html b/src/app/components/shared/comment/comment.component.html
index bd009b1e970a3438c0d77feb16a6287ee4112155..0bd1627a2cb29871b30e05f34c6bfe9471fd0c49 100644
--- a/src/app/components/shared/comment/comment.component.html
+++ b/src/app/components/shared/comment/comment.component.html
@@ -264,7 +264,7 @@
            tabindex="0">
         <ars-row #commentBody>
           <ars-row #commentBodyInner>
-            <app-custom-markdown class="images" [data]="comment.body"></app-custom-markdown>
+            <app-view-comment-data class="images" [currentData]="comment.body"></app-view-comment-data>
           </ars-row>
         </ars-row>
         <span id="comment-{{ comment.id }}"
diff --git a/src/app/components/shared/custom-markdown/custom-markdown.component.ts b/src/app/components/shared/custom-markdown/custom-markdown.component.ts
index 7e7fb10142840e8bd61cf86fdf67c6365e66cc25..9b00e52d5a73d8c688ad723a29a30399cd68032e 100644
--- a/src/app/components/shared/custom-markdown/custom-markdown.component.ts
+++ b/src/app/components/shared/custom-markdown/custom-markdown.component.ts
@@ -14,7 +14,6 @@ export class CustomMarkdownComponent implements OnChanges {
   @Input() start: number | undefined;
   @Input() line: string | string[] | undefined;
   @Input() lineOffset: number | undefined;
-  @Input() isRawHTML = false;
   @Input() katexOptions: KatexOptions = {
     throwOnError: true
   };
@@ -89,10 +88,6 @@ export class CustomMarkdownComponent implements OnChanges {
   }
 
   private render(markdown: string, decodeHtml = false): void {
-    if (this.isRawHTML) {
-      this.element.nativeElement.innerHTML = this.renderKatex(markdown);
-      return;
-    }
     if (this.katex) {
       markdown = CustomMarkdownComponent.fixKatex(markdown);
     }
diff --git a/src/app/components/shared/shared.module.ts b/src/app/components/shared/shared.module.ts
index 3e435fad18f0ab647cd9aedfd366d481385c22a0..52a78cd959336e7bc6839c700a1bf873a3ec0c75 100644
--- a/src/app/components/shared/shared.module.ts
+++ b/src/app/components/shared/shared.module.ts
@@ -50,6 +50,7 @@ import { WriteCommentComponent } from './write-comment/write-comment.component';
 import { CustomMarkdownComponent } from './custom-markdown/custom-markdown.component';
 import { ScrollIntoViewDirective } from '../../directives/scroll-into-view.directive';
 import { QuillModule } from 'ngx-quill';
+import { ViewCommentDataComponent } from './view-comment-data/view-comment-data.component';
 
 @NgModule({
   imports: [
@@ -105,7 +106,8 @@ import { QuillModule } from 'ngx-quill';
     MatSpinnerOverlayComponent,
     WriteCommentComponent,
     CustomMarkdownComponent,
-    ScrollIntoViewDirective
+    ScrollIntoViewDirective,
+    ViewCommentDataComponent
   ],
   exports: [
     RoomJoinComponent,
diff --git a/src/app/components/shared/view-comment-data/view-comment-data.component.html b/src/app/components/shared/view-comment-data/view-comment-data.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..dbad03832ad80e76b87f56cbe672507c253b81e0
--- /dev/null
+++ b/src/app/components/shared/view-comment-data/view-comment-data.component.html
@@ -0,0 +1,22 @@
+<ars-row *ngIf="isEditor">
+  <div #editorErrorLayer id="editorErrorLayer"></div>
+  <quill-editor #editor
+                [maxLength]="user.role === 3 ? 1000 : 500"
+                placeholder="{{ 'comment-page.enter-comment' | translate }}"
+                [modules]="quillModules"
+                (document:click)="onDocumentClick($event)">
+  </quill-editor>
+  <div #tooltipContainer></div>
+  <div fxLayout="row" style="justify-content: space-between; padding: 0 5px">
+    <span aria-hidden="true" style="font-size: 75%">
+      {{ 'comment-page.Markdown-hint' | translate }}
+    </span>
+    <span aria-hidden="true" style="font-size: 75%">
+      {{currentData.length}} / {{user.role === 3 ? 1000 : 500}}
+    </span>
+  </div>
+</ars-row>
+<div *ngIf="!isEditor">
+  <quill-view #quillView [modules]="quillModules">
+  </quill-view>
+</div>
diff --git a/src/app/components/shared/view-comment-data/view-comment-data.component.scss b/src/app/components/shared/view-comment-data/view-comment-data.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..ff0d11f04b75d32fa8af760424ba22d0856a4b07
--- /dev/null
+++ b/src/app/components/shared/view-comment-data/view-comment-data.component.scss
@@ -0,0 +1,114 @@
+::ng-deep .ql-editor.ql-blank::before {
+  color: var(--on-surface);
+  filter: opacity(0.6);
+}
+
+::ng-deep .ql-snow {
+  :focus {
+    outline-offset: 0;
+  }
+
+  .ql-editor {
+    min-height: 8.8em;
+  }
+
+  .ql-stroke {
+    stroke: var(--on-surface);
+  }
+
+  .ql-picker {
+    color: var(--on-surface);
+  }
+
+  .ql-tooltip {
+    &[data-mode=formula]::before {
+      --quill-tooltip-label: var(--quill-tooltip-label-formula);
+    }
+    &[data-mode=video]::before {
+      --quill-tooltip-label: var(--quill-tooltip-label-video);
+    }
+    &[data-mode=image]::before {
+      --quill-tooltip-label: var(--quill-tooltip-label-image);
+    }
+    &[data-mode=link]::before {
+      --quill-tooltip-label: var(--quill-tooltip-label-link);
+    }
+    &::before {
+      content: var(--quill-tooltip-label) !important;
+    }
+
+    color: var(--on-surface);
+    background-color: var(--surface);
+
+    .ql-action::after {
+      padding: 7px !important;
+      background: var(--primary);
+      border-radius: 4px;
+      color: var(--on-primary);
+      content: var(--quill-tooltip-action) !important;
+    }
+
+    &.ql-editing .ql-action::after {
+      --quill-tooltip-action: var(--quill-tooltip-action-save);
+    }
+
+    .ql-remove::before {
+      content: var(--quill-tooltip-remove) !important;
+      padding: 7px !important;
+      background: var(--cancel);
+      border-radius: 4px;
+      color: var(--on-cancel);
+    }
+
+    &.ql-editing input[type=text] {
+      border-color: var(--on-surface);
+      color: var(--on-surface);
+      background-color: var(--dialog);
+    }
+  }
+
+  .ql-fill, .ql-stroke.ql-fill {
+    fill: var(--on-surface);
+  }
+
+  .ql-picker.ql-expanded .ql-picker-label {
+    color: var(--primary);
+
+    .ql-stroke {
+      stroke: var(--primary);
+    }
+  }
+
+  &.ql-container {
+    border-color: var(--on-surface);
+    height: 80%;
+  }
+
+  &.ql-toolbar, .ql-toolbar {
+    border-color: var(--on-surface);
+
+    .ql-picker.ql-expanded {
+      .ql-picker-label {
+        border-color: var(--on-surface);
+      }
+
+      .ql-picker-options {
+        background-color: var(--surface);
+      }
+    }
+
+    button:hover, button:focus, button.ql-active,
+    .ql-picker-label:hover, .ql-picker-label.ql-active,
+    .ql-picker-item:hover, .ql-picker-item.ql-selected {
+      color: var(--primary);
+
+      .ql-stroke {
+        stroke: var(--primary);
+      }
+
+      .ql-fill, .ql-stroke.ql-fill {
+        fill: var(--primary);
+      }
+    }
+  }
+}
diff --git a/src/app/components/shared/view-comment-data/view-comment-data.component.spec.ts b/src/app/components/shared/view-comment-data/view-comment-data.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1a2b65bc5abe829284a47c2feb6094a6929b74d0
--- /dev/null
+++ b/src/app/components/shared/view-comment-data/view-comment-data.component.spec.ts
@@ -0,0 +1,26 @@
+/*import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ViewCommentDataComponent } from './view-comment-data.component';
+
+describe('ViewCommentDataComponent', () => {
+  let component: ViewCommentDataComponent;
+  let fixture: ComponentFixture<ViewCommentDataComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ ViewCommentDataComponent ]
+    })
+    .compileComponents();
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ViewCommentDataComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
+ */
diff --git a/src/app/components/shared/view-comment-data/view-comment-data.component.ts b/src/app/components/shared/view-comment-data/view-comment-data.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..06d4e031d584dc0bc2b4720d7c46547f59183408
--- /dev/null
+++ b/src/app/components/shared/view-comment-data/view-comment-data.component.ts
@@ -0,0 +1,245 @@
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import { User } from '../../../models/user';
+import { QuillEditorComponent, QuillModules, QuillViewComponent } from 'ngx-quill';
+import Delta from 'quill-delta';
+import Quill from 'quill';
+import ImageResize from 'quill-image-resize-module';
+import 'quill-emoji/dist/quill-emoji.js';
+import { LanguageService } from '../../../services/util/language.service';
+import { TranslateService } from '@ngx-translate/core';
+
+Quill.register('modules/imageResize', ImageResize);
+
+const participantToolbar = [
+  ['bold', 'strike'],
+  ['blockquote', 'code-block'],
+  [{ list: 'ordered' }, { list: 'bullet' }],
+  ['link', 'formula'],
+  ['emoji']
+];
+
+const moderatorToolbar = [
+  ['bold', 'strike'],
+  ['blockquote', 'code-block'],
+  [{ header: 1 }, { header: 2 }],
+  [{ list: 'ordered' }, { list: 'bullet' }],
+  [{ indent: '-1' }, { indent: '+1' }],
+  [{ color: [] }],
+  [{ align: [] }],
+  ['link', 'image', 'video', 'formula'],
+  ['emoji']
+];
+
+@Component({
+  selector: 'app-view-comment-data',
+  templateUrl: './view-comment-data.component.html',
+  styleUrls: ['./view-comment-data.component.scss']
+})
+export class ViewCommentDataComponent implements OnInit, AfterViewInit {
+
+  @ViewChild('editor') editor: QuillEditorComponent;
+  @ViewChild('quillView') quillView: QuillViewComponent;
+  @ViewChild('editorErrorLayer') editorErrorLayer: ElementRef<HTMLDivElement>;
+  @ViewChild('tooltipContainer') tooltipContainer: ElementRef<HTMLDivElement>;
+  @Input() isEditor = false;
+  @Input() user: User;
+  @Input() currentData = '';
+  @Input() markEvents?: {
+    onCreate: (markContainer: HTMLDivElement, tooltipContainer: HTMLDivElement, editor: QuillEditorComponent) => void;
+    onChange: (delta: any) => void;
+    onEditorChange: () => void;
+    onDocumentClick: (e) => void;
+  };
+  currentText = '';
+
+  quillModules: QuillModules = {
+    toolbar: {
+      container: participantToolbar,
+      handlers: {
+        image: () => this.handle('image'),
+        video: () => this.handle('video'),
+        link: () => this.handleLink(),
+        formula: () => this.handle('formula')
+      }
+    },
+    'emoji-toolbar': true,
+    'emoji-shortname': true,
+    imageResize: {
+      modules: ['Resize', 'DisplaySize']
+    }
+  };
+
+  constructor(private languageService: LanguageService,
+              private translateService: TranslateService) {
+    this.languageService.langEmitter.subscribe(lang => {
+      this.translateService.use(lang);
+      if (this.isEditor) {
+        this.updateCSSVariables();
+      }
+    });
+  }
+
+  private static getDataFromDelta(contentDelta) {
+    return JSON.stringify(contentDelta.ops.map(op => {
+      let hasOnlyInsert = true;
+      for (const key in op) {
+        if (key !== 'insert') {
+          hasOnlyInsert = false;
+          break;
+        }
+      }
+      return hasOnlyInsert ? op['insert'] : op;
+    }));
+  }
+
+  private static getDeltaFromData(jsonData: string) {
+    return {
+      ops: JSON.parse(jsonData).map(elem => {
+        if (!elem['insert']) {
+          return { insert: elem };
+        } else {
+          return elem;
+        }
+      })
+    };
+  }
+
+  ngOnInit(): void {
+    if (this.user && this.user.role > 0) {
+      this.quillModules.toolbar['container'] = moderatorToolbar;
+    }
+    this.translateService.use(localStorage.getItem('currentLang'));
+    if (this.isEditor) {
+      this.updateCSSVariables();
+    }
+  }
+
+  ngAfterViewInit() {
+    if (this.isEditor) {
+      this.editor.onContentChanged.subscribe(e => {
+        if (this.markEvents && this.markEvents.onChange) {
+          this.markEvents.onChange(e.delta);
+        }
+        this.currentData = ViewCommentDataComponent.getDataFromDelta(e.content);
+        this.currentText = e.text;
+      });
+      this.editor.onEditorCreated.subscribe(_ => {
+        if (this.markEvents && this.markEvents.onCreate) {
+          this.markEvents.onCreate(this.editorErrorLayer.nativeElement, this.tooltipContainer.nativeElement, this.editor);
+        }
+        this.syncErrorLayer();
+        setTimeout(() => this.syncErrorLayer(), 200); // animations?
+      });
+      this.editor.onEditorChanged.subscribe(_ => {
+        if (this.markEvents && this.markEvents.onEditorChange) {
+          this.markEvents.onEditorChange();
+        }
+        this.syncErrorLayer();
+      });
+    } else {
+      this.quillView.onEditorCreated.subscribe(_ => {
+        this.set(this.currentData);
+      });
+    }
+  }
+
+  onDocumentClick(e) {
+    if (this.markEvents && this.markEvents.onDocumentClick) {
+      this.markEvents.onDocumentClick(e);
+    }
+  }
+
+  clear(): void {
+    const delta = new Delta();
+    if (this.isEditor) {
+      this.editor.quillEditor.setContents(delta);
+    } else {
+      this.quillView.quillEditor.setContents(delta);
+    }
+  }
+
+  set(jsonData: string): void {
+    const delta = ViewCommentDataComponent.getDeltaFromData(jsonData);
+    if (this.isEditor) {
+      this.editor.quillEditor.setContents(delta);
+    } else {
+      this.quillView.quillEditor.setContents(delta);
+    }
+  }
+
+  private syncErrorLayer(): void {
+    const pos = this.editor.elementRef.nativeElement.getBoundingClientRect();
+    const elem = this.editorErrorLayer.nativeElement;
+    elem.style.width = pos.width + 'px';
+    elem.style.height = pos.height + 'px';
+    elem.style.marginBottom = '-' + elem.style.height;
+  }
+
+  private handleLink(): void {
+    const quill = this.editor.quillEditor;
+    const selection = quill.getSelection(false);
+    if (!selection || !selection.length) {
+      return;
+    }
+    const tooltip = quill.theme.tooltip;
+    const originalSave = tooltip.save;
+    const originalHide = tooltip.hide;
+    tooltip.save = () => {
+      const value = tooltip.textbox.value;
+      if (value) {
+        const delta = new Delta()
+          .retain(selection.index)
+          .retain(selection.length, { link: value });
+        quill.updateContents(delta);
+        tooltip.hide();
+      }
+    };
+    // Called on hide and save.
+    tooltip.hide = () => {
+      tooltip.save = originalSave;
+      tooltip.hide = originalHide;
+      tooltip.hide();
+    };
+    tooltip.edit('link');
+    tooltip.textbox.value = quill.getText(selection.index, selection.length);
+    this.translateService.get('quill.tooltip-placeholder-link')
+      .subscribe(translation => tooltip.textbox.placeholder = translation);
+  }
+
+  private handle(type: string): void {
+    const quill = this.editor.quillEditor;
+    const tooltip = quill.theme.tooltip;
+    const originalSave = tooltip.save;
+    const originalHide = tooltip.hide;
+    tooltip.save = () => {
+      const range = quill.getSelection(true);
+      const value = tooltip.textbox.value;
+      if (value) {
+        quill.insertEmbed(range.index, type, value, 'user');
+      }
+    };
+    // Called on hide and save.
+    tooltip.hide = () => {
+      tooltip.save = originalSave;
+      tooltip.hide = originalHide;
+      tooltip.hide();
+    };
+    tooltip.edit(type);
+    this.translateService.get('quill.tooltip-placeholder-' + type)
+      .subscribe(translation => tooltip.textbox.placeholder = translation);
+  }
+
+  private updateCSSVariables() {
+    const variables = [
+      'quill.tooltip-remove', 'quill.tooltip-action-save', 'quill.tooltip-action', 'quill.tooltip-label',
+      'quill.tooltip-label-link', 'quill.tooltip-label-image', 'quill.tooltip-label-video',
+      'quill.tooltip-label-formula'
+    ];
+    for (const variable of variables) {
+      this.translateService.get(variable).subscribe(translation => {
+        document.body.style.setProperty('--' + variable.replace('.', '-'), JSON.stringify(translation));
+      });
+    }
+  }
+
+}
diff --git a/src/app/components/shared/write-comment/write-comment.component.html b/src/app/components/shared/write-comment/write-comment.component.html
index b931a1daaf5a5cafb7d813d73e1a848bc72312c5..1b180e34a6c5b10e3dd707d588b2d1aa90ff1b13 100644
--- a/src/app/components/shared/write-comment/write-comment.component.html
+++ b/src/app/components/shared/write-comment/write-comment.component.html
@@ -45,31 +45,17 @@
   </div>
 </ars-row>
 <ars-row [height]="12"></ars-row>
-<ars-row *ngIf="enabled">
-  <div #editorErrorLayer id="editorErrorLayer"></div>
-  <quill-editor #editor
-                [maxLength]="user.role === 3 ? 1000 : 500"
-                placeholder="{{ 'comment-page.enter-comment' | translate }}"
-                [modules]="quillModules"
-                (document:click)="onDocumentClick($event)">
-  </quill-editor>
-  <div #tooltipContainer></div>
-  <div fxLayout="row" style="justify-content: space-between; padding: 0 5px">
-    <span aria-hidden="true" style="font-size: 75%">
-      {{ 'comment-page.Markdown-hint' | translate }}
-    </span>
-    <span aria-hidden="true" style="font-size: 75%">
-      {{currentHTML.length}} / {{user.role === 3 ? 1000 : 500}}
-    </span>
-  </div>
-</ars-row>
+<app-view-comment-data *ngIf="enabled"
+                       [user]="user"
+                       [isEditor]="true"
+                       [markEvents]="getMarkEvents()"></app-view-comment-data>
 <ars-row ars-flex-box *ngIf="enabled" class="spellcheck">
   <ars-col>
     <button
-      [disabled]="currentText.length < 4"
+      [disabled]="!commentData || commentData.currentText.length < 4"
       mat-flat-button
       class="spell-button"
-      (click)="grammarCheck(currentText, langSelect && langSelect.nativeElement)">
+      (click)="checkGrammar()">
       {{ 'comment-page.grammar-check' | translate}}
       <mat-icon *ngIf="isSpellchecking" class="spinner-container">
         <app-mat-spinner-overlay diameter="20" strokeWidth="2" [color]="'on-primary'"></app-mat-spinner-overlay>
diff --git a/src/app/components/shared/write-comment/write-comment.component.scss b/src/app/components/shared/write-comment/write-comment.component.scss
index 2579d849f5dc293cd28563a01bd3209d250dda7c..0f303ef0146cb8af2965e175a00a2eadd31dd795 100644
--- a/src/app/components/shared/write-comment/write-comment.component.scss
+++ b/src/app/components/shared/write-comment/write-comment.component.scss
@@ -249,6 +249,10 @@ $borderOffset: 2px;
     outline-offset: 0;
   }
 
+  .ql-editor {
+    min-height: 8.8em;
+  }
+
   .ql-stroke {
     stroke: var(--on-surface);
   }
diff --git a/src/app/components/shared/write-comment/write-comment.component.ts b/src/app/components/shared/write-comment/write-comment.component.ts
index 1d4d08f4e13679ec0f3e251d5d91031a93a0e008..10352624d695b58fa7762d5961ca21fb2e9fbc10 100644
--- a/src/app/components/shared/write-comment/write-comment.component.ts
+++ b/src/app/components/shared/write-comment/write-comment.component.ts
@@ -5,46 +5,22 @@ import { Comment } from '../../../models/comment';
 import { User } from '../../../models/user';
 import { NotificationService } from '../../../services/util/notification.service';
 import { EventService } from '../../../services/util/event.service';
-import { QuillEditorComponent, QuillModules } from 'ngx-quill';
 import { CreateCommentKeywords } from '../../../utils/create-comment-keywords';
 import { Marks } from './write-comment.marks';
 import { LanguageService } from '../../../services/util/language.service';
-import Delta from 'quill-delta';
-import Quill from 'quill';
-import ImageResize from 'quill-image-resize-module';
-import 'quill-emoji/dist/quill-emoji.js';
+import { QuillEditorComponent } from 'ngx-quill';
+import { ViewCommentDataComponent } from '../view-comment-data/view-comment-data.component';
 
-Quill.register('modules/imageResize', ImageResize);
-
-const participantToolbar = [
-  ['bold', 'strike'],
-  ['blockquote', 'code-block'],
-  [{ list: 'ordered' }, { list: 'bullet' }],
-  ['link', 'formula']
-];
-
-const moderatorToolbar = [
-  ['bold', 'strike'],
-  ['blockquote', 'code-block'],
-  [{ header: 1 }, { header: 2 }],
-  [{ list: 'ordered' }, { list: 'bullet' }],
-  [{ indent: '-1' }, { indent: '+1' }],
-  [{ color: [] }],
-  [{ align: [] }],
-  ['link', 'image', 'video', 'formula']
-];
 
 @Component({
   selector: 'app-write-comment',
   templateUrl: './write-comment.component.html',
   styleUrls: ['./write-comment.component.scss']
 })
-export class WriteCommentComponent implements OnInit, AfterViewInit {
+export class WriteCommentComponent implements OnInit {
 
+  @ViewChild(ViewCommentDataComponent) commentData: ViewCommentDataComponent;
   @ViewChild('langSelect') langSelect: ElementRef<HTMLDivElement>;
-  @ViewChild('editor') editor: QuillEditorComponent;
-  @ViewChild('editorErrorLayer') editorErrorLayer: ElementRef<HTMLDivElement>;
-  @ViewChild('tooltipContainer') tooltipContainer: ElementRef<HTMLDivElement>;
   @Input() user: User;
   @Input() tags: string[];
   @Input() onClose: () => any;
@@ -57,32 +33,14 @@ export class WriteCommentComponent implements OnInit, AfterViewInit {
   @Input() enabled = true;
   comment: Comment;
   selectedTag: string;
-  currentHTML = '';
-  currentText = '';
-  //Grammarheck
+  // Grammarheck
   languages: Language[] = ['de-DE', 'en-US', 'fr', 'auto'];
   selectedLang: Language = 'auto';
   isSpellchecking = false;
   hasSpellcheckConfidence = true;
   newLang = 'auto';
+  // Marks
   marks: Marks;
-  quillModules: QuillModules = {
-    toolbar: {
-      container: participantToolbar,
-      handlers: {
-        image: () => this.handle('image'),
-        video: () => this.handle('video'),
-        link: () => this.handleLink(),
-        formula: () => this.handle('formula')
-      }
-    },
-    'emoji-toolbar': true,
-    'emoji-textarea': true,
-    'emoji-shortname': true,
-    imageResize: {
-      modules: ['Resize', 'DisplaySize', 'Toolbar']
-    }
-  };
 
   constructor(private notification: NotificationService,
               private languageService: LanguageService,
@@ -91,55 +49,11 @@ export class WriteCommentComponent implements OnInit, AfterViewInit {
               public languagetoolService: LanguagetoolService) {
     this.languageService.langEmitter.subscribe(lang => {
       this.translateService.use(lang);
-      this.updateCSSVariables();
     });
   }
 
   ngOnInit(): void {
     this.translateService.use(localStorage.getItem('currentLang'));
-    if (this.user && this.user.role > 0) {
-      this.quillModules.toolbar['container'] = moderatorToolbar;
-    }
-    this.translateService.use(localStorage.getItem('currentLang'));
-    this.updateCSSVariables();
-  }
-
-  ngAfterViewInit() {
-    this.editor.onContentChanged.subscribe(e => {
-      this.marks.onDataChange(e.delta);
-      this.currentHTML = e.html || '';
-      this.currentText = e.text;
-    });
-    this.editor.onEditorCreated.subscribe(_ => {
-      this.marks = new Marks(this.editorErrorLayer.nativeElement, this.tooltipContainer.nativeElement, this.editor);
-      this.syncErrorLayer();
-      setTimeout(() => this.syncErrorLayer(), 200); // animations?
-    });
-    this.editor.onEditorChanged.subscribe(_ => {
-      const elem: HTMLDivElement = document.querySelector('div.ql-tooltip');
-      if (elem) { // fix tooltip
-        setTimeout(() => {
-          const left = parseFloat(elem.style.left);
-          const right = left + elem.getBoundingClientRect().width;
-          const containerWidth = this.editor.editorElem.getBoundingClientRect().width;
-          if (left < 0) {
-            elem.style.left = '0';
-          } else if (right > containerWidth) {
-            elem.style.left = (containerWidth - right + left) + 'px';
-          }
-        });
-      }
-      this.syncErrorLayer();
-      this.marks.sync();
-    });
-  }
-
-  clearHTML(): void {
-    this.editor.editorElem.innerHTML = '';
-  }
-
-  setHTML(html: string): void {
-    this.editor.editorElem.innerHTML = html;
   }
 
   buildCloseDialogActionCallback(): () => void {
@@ -154,36 +68,22 @@ export class WriteCommentComponent implements OnInit, AfterViewInit {
       return undefined;
     }
     return () => {
-      if (this.checkInputData(this.currentHTML)) {
-        this.onSubmit(this.currentHTML, this.selectedTag);
+      if (this.checkInputData(this.commentData.currentData)) {
+        this.onSubmit(this.commentData.currentData, this.selectedTag);
       }
     };
   }
 
-  syncErrorLayer(): void {
-    const pos = this.editor.elementRef.nativeElement.getBoundingClientRect();
-    const elem = this.editorErrorLayer.nativeElement;
-    elem.style.width = pos.width + 'px';
-    elem.style.height = pos.height + 'px';
-    elem.style.marginBottom = '-' + elem.style.height;
-  }
-
   onDocumentClick(e) {
     if (!this.marks) {
       return;
     }
-    const range = this.editor.quillEditor.getSelection(false);
+    const range = this.commentData.editor.quillEditor.getSelection(false);
     this.marks.onClick(range && range.length === 0 ? range.index : null);
   }
 
-  maxLength(commentBody: HTMLDivElement, size: number): void {
-    if (commentBody.innerText.length > size) {
-      commentBody.innerText = commentBody.innerText.slice(0, size);
-    }
-    const body = commentBody.innerText;
-    if (body.length === 1 && body.charCodeAt(body.length - 1) === 10) {
-      commentBody.innerHTML = commentBody.innerHTML.replace('<br>', '');
-    }
+  checkGrammar() {
+    this.grammarCheck(this.commentData.currentText, this.langSelect && this.langSelect.nativeElement);
   }
 
   grammarCheck(rawText: string, langSelect: HTMLSpanElement): void {
@@ -233,6 +133,34 @@ export class WriteCommentComponent implements OnInit, AfterViewInit {
     return this.languagetoolService.checkSpellings(text, language);
   }
 
+  getMarkEvents() {
+    return {
+      onCreate: (markContainer: HTMLDivElement, tooltipContainer: HTMLDivElement, editor: QuillEditorComponent) => {
+        this.marks = new Marks(markContainer, tooltipContainer, editor);
+      },
+      onChange: (delta: any) => {
+        this.marks.onDataChange(delta);
+      },
+      onEditorChange: () => {
+        const elem: HTMLDivElement = document.querySelector('div.ql-tooltip');
+        if (elem) { // fix tooltip
+          setTimeout(() => {
+            const left = parseFloat(elem.style.left);
+            const right = left + elem.getBoundingClientRect().width;
+            const containerWidth = this.commentData.editor.editorElem.getBoundingClientRect().width;
+            if (left < 0) {
+              elem.style.left = '0';
+            } else if (right > containerWidth) {
+              elem.style.left = (containerWidth - right + left) + 'px';
+            }
+          });
+        }
+        this.marks.sync();
+      },
+      onDocumentClick: (e) => this.onDocumentClick(e)
+    };
+  }
+
   private checkInputData(body: string): boolean {
     body = body.trim();
     if (!body) {
@@ -244,71 +172,4 @@ export class WriteCommentComponent implements OnInit, AfterViewInit {
     return true;
   }
 
-  private updateCSSVariables() {
-    const variables = [
-      'quill.tooltip-remove', 'quill.tooltip-action-save', 'quill.tooltip-action', 'quill.tooltip-label',
-      'quill.tooltip-label-link', 'quill.tooltip-label-image', 'quill.tooltip-label-video',
-      'quill.tooltip-label-formula'
-    ];
-    for (const variable of variables) {
-      this.translateService.get(variable).subscribe(translation => {
-        document.body.style.setProperty('--' + variable.replace('.', '-'), JSON.stringify(translation));
-      });
-    }
-  }
-
-  private handleLink(): void {
-    const quill = this.editor.quillEditor;
-    const selection = quill.getSelection(false);
-    if (!selection || !selection.length) {
-      return;
-    }
-    const tooltip = quill.theme.tooltip;
-    const originalSave = tooltip.save;
-    const originalHide = tooltip.hide;
-    tooltip.save = () => {
-      const value = tooltip.textbox.value;
-      if (value) {
-        const delta = new Delta()
-          .retain(selection.index)
-          .retain(selection.length, { link: value });
-        quill.updateContents(delta);
-        tooltip.hide();
-      }
-    };
-    // Called on hide and save.
-    tooltip.hide = () => {
-      tooltip.save = originalSave;
-      tooltip.hide = originalHide;
-      tooltip.hide();
-    };
-    tooltip.edit('link');
-    tooltip.textbox.value = quill.getText(selection.index, selection.length);
-    this.translateService.get('quill.tooltip-placeholder-link')
-      .subscribe(translation => tooltip.textbox.placeholder = translation);
-  }
-
-  private handle(type: string): void {
-    const quill = this.editor.quillEditor;
-    const tooltip = quill.theme.tooltip;
-    const originalSave = tooltip.save;
-    const originalHide = tooltip.hide;
-    tooltip.save = () => {
-      const range = quill.getSelection(true);
-      const value = tooltip.textbox.value;
-      if (value) {
-        quill.insertEmbed(range.index, type, value, 'user');
-      }
-    };
-    // Called on hide and save.
-    tooltip.hide = () => {
-      tooltip.save = originalSave;
-      tooltip.hide = originalHide;
-      tooltip.hide();
-    };
-    tooltip.edit(type);
-    this.translateService.get('quill.tooltip-placeholder-' + type)
-      .subscribe(translation => tooltip.textbox.placeholder = translation);
-  }
-
 }
diff --git a/src/app/components/shared/write-comment/write-comment.marks.ts b/src/app/components/shared/write-comment/write-comment.marks.ts
index b387e18f949bd064eb99ca0ca1ba442870b4287c..d65fa2e88459694442ebd21cd1546dcda65b1b5f 100644
--- a/src/app/components/shared/write-comment/write-comment.marks.ts
+++ b/src/app/components/shared/write-comment/write-comment.marks.ts
@@ -1,6 +1,40 @@
 import { LanguagetoolResult } from '../../../services/http/languagetool.service';
 import { QuillEditorComponent } from 'ngx-quill';
 
+class ContentIndexFinder {
+
+  private opIndex = 0;
+  private contentOffset = 0;
+  private textOffset = 0;
+
+  constructor(private contentOps) {
+  }
+
+  adjustTextIndexes(startIndex: number, length: number): [number, number] {
+    const endIndex = startIndex + length;
+    let textLen = typeof this.contentOps[this.opIndex]['insert'] === 'string' ?
+      this.contentOps[this.opIndex]['insert'].length : 0;
+    while (textLen === 0 || this.textOffset + textLen < startIndex) {
+      this.textOffset += textLen;
+      this.contentOffset += textLen === 0 ? 1 : textLen;
+      ++this.opIndex;
+      textLen = typeof this.contentOps[this.opIndex]['insert'] === 'string' ?
+        this.contentOps[this.opIndex]['insert'].length : 0;
+    }
+    const diff = this.contentOffset - this.textOffset;
+    startIndex += diff;
+    while (this.textOffset + textLen < endIndex) {
+      this.textOffset += textLen;
+      this.contentOffset += textLen === 0 ? 1 : textLen;
+      ++this.opIndex;
+      textLen = typeof this.contentOps[this.opIndex]['insert'] === 'string' ?
+        this.contentOps[this.opIndex]['insert'].length : 0;
+    }
+    length += this.contentOffset - this.textOffset - diff;
+    return [startIndex, length];
+  }
+}
+
 export class Marks {
 
   private textErrors: Mark[] = [];
@@ -31,13 +65,13 @@ export class Marks {
     let index = 0;
     for (const op of delta.ops) {
       if (op['insert']) {
-        const len = op['insert'].length;
+        const len = typeof op['insert'] === 'string' ? op['insert'].length : 1;
         for (const textError of this.textErrors) {
           if (index > textError.startIndex + textError.markLength) {
             continue;
           }
-          textError.markLength += len;
           if (index >= textError.startIndex) {
+            textError.markLength += len;
             continue;
           }
           textError.startIndex += len;
@@ -62,7 +96,6 @@ export class Marks {
           }
           if (endDelete < textError.startIndex) {
             textError.startIndex -= len;
-            textError.markLength -= len;
             return true;
           }
           if (endDelete < textError.startIndex + textError.markLength) {
@@ -82,13 +115,15 @@ export class Marks {
   }
 
   buildErrors(initialText: string, wrongWords: string[], res: LanguagetoolResult): void {
+    const indexFinder = new ContentIndexFinder(this.editor.quillEditor.getContents().ops);
     for (let i = 0; i < res.matches.length; i++) {
       const match = res.matches[i];
       const foundWord = initialText.slice(match.offset, match.offset + match.length);
       if (!wrongWords.includes(foundWord)) {
         continue;
       }
-      const mark = new Mark(match.offset, match.length, this.markContainer, this.tooltipContainer, this.editor.quillEditor);
+      const [start, len] = indexFinder.adjustTextIndexes(match.offset, match.length);
+      const mark = new Mark(start, len, this.markContainer, this.tooltipContainer, this.editor.quillEditor);
       mark.setSuggestions(res, i, () => {
         const index = this.textErrors.findIndex(elem => elem === mark);
         if (index >= 0) {
@@ -207,19 +242,33 @@ class Mark {
   }
 
   private calculateBoundaries(): [start: number, length: number][] {
-    const text: string = this.quillEditor.getText(this.startIndex, this.markLength);
+    const ops = this.quillEditor.getContents(this.startIndex, this.markLength).ops;
     const bounds = [];
-    let i = text.indexOf('\n');
     let currentIndex = 0;
-    while (i >= 0) {
-      if (i > currentIndex) {
-        bounds.push([this.startIndex + currentIndex, i - currentIndex]);
+    for (const op of ops) {
+      if (typeof op['insert'] === 'string') {
+        const text = op['insert'];
+        let i = text.indexOf('\n');
+        let findIndex = 0;
+        while (i >= 0) {
+          if (i > findIndex) {
+            bounds.push([this.startIndex + findIndex + currentIndex, i - findIndex]);
+          }
+          findIndex = i + 1;
+          i = text.indexOf('\n', findIndex);
+        }
+        if (text.length + currentIndex < this.markLength) {
+          if (text.length > findIndex) {
+            bounds.push([this.startIndex + findIndex + currentIndex, text.length - findIndex]);
+          }
+        } else if (this.markLength > findIndex + currentIndex && text.length > findIndex) {
+          bounds.push([this.startIndex + findIndex + currentIndex, this.markLength - findIndex - currentIndex]);
+        }
+        currentIndex += text.length;
+      } else {
+        bounds.push([this.startIndex + currentIndex, 1]);
+        ++currentIndex;
       }
-      currentIndex = i + 1;
-      i = text.indexOf('\n', currentIndex);
-    }
-    if (this.markLength > currentIndex) {
-      bounds.push([this.startIndex + currentIndex, this.markLength - currentIndex]);
     }
     return bounds;
   }