diff --git a/src/app/components/creator/_dialogs/room-edit/room-edit.component.html b/src/app/components/creator/_dialogs/room-edit/room-edit.component.html
index b79de81de353d5e43d26e68356f38d5a7ad9514b..653cd63864da0ce78aebe22d7ea65e077c1db828 100644
--- a/src/app/components/creator/_dialogs/room-edit/room-edit.component.html
+++ b/src/app/components/creator/_dialogs/room-edit/room-edit.component.html
@@ -56,9 +56,12 @@
       </mat-tab>
     </mat-tab-group>
     <div fxLayoutAlign="center center">
-      <label for="myCheck" style="background: darkgreen;  font-size: larger;">{{ 'room-page.block' | translate }} </label> 
-      <input type="checkbox" id= "myCheck" [(ngModel)]= "check" >
-      
+      <mat-slide-toggle [(ngModel)]= "check">{{ 'room-page.block' | translate }}</mat-slide-toggle>
+    </div>
+    <div fxLayoutAlign="center center">
+      <!-- <input type="checkbox" id= "myCheck" [(ngModel)]= "check" > -->
+   <!-- <mat-label for="myCheck" ><mat-icon>block</mat-icon>{{ 'room-page.block' | translate }} </mat-label> -->
+
       <button mat-raised-button
               class="delete"
               (click)="openDeleteRoomDialog()"
diff --git a/src/app/components/creator/_dialogs/room-edit/room-edit.component.scss b/src/app/components/creator/_dialogs/room-edit/room-edit.component.scss
index 55edafc7f35014143fef054a1140837b8a7dbfad..e892ab2a99e1c2270b59a4c029ea574abfb4d000 100644
--- a/src/app/components/creator/_dialogs/room-edit/room-edit.component.scss
+++ b/src/app/components/creator/_dialogs/room-edit/room-edit.component.scss
@@ -51,3 +51,11 @@ mat-hint {
   color: var(--on-surface) !important;
   margin-right: 1%;
 }
+
+mat-slide-toggle {
+  margin-top: 20px;
+}
+
+
+
+
diff --git a/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.ts b/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.ts
index 0e29ee970bd2b16919df89f5644df6a2e054d98d..a43c004ddd59b02b636c793290dabb893e6181f0 100644
--- a/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.ts
+++ b/src/app/components/moderator/moderator-comment-list/moderator-comment-list.component.ts
@@ -329,6 +329,7 @@ export class ModeratorCommentListComponent implements OnInit, OnDestroy {
         c.id = payload.id;
         c.timestamp = payload.timestamp;
         c.creatorId = payload.creatorId;
+        c.keywordsFromQuestioner = JSON.parse(payload.keywordsFromQuestioner);
         c.userNumber = this.commentService.hashCode(c.creatorId);
         this.comments = this.comments.concat(c);
         break;
diff --git a/src/app/components/shared/_dialogs/cloud-configuration/cloud-configuration.component.html b/src/app/components/shared/_dialogs/cloud-configuration/cloud-configuration.component.html
index 6a920d6894ebbfa54966e72a5903e14ddbcfbacf..41e49a5bc57b18438a544a3cc421afe68e006f68 100644
--- a/src/app/components/shared/_dialogs/cloud-configuration/cloud-configuration.component.html
+++ b/src/app/components/shared/_dialogs/cloud-configuration/cloud-configuration.component.html
@@ -8,161 +8,170 @@
                           [ngModelOptions]="{standalone: true}">Demo Cloud</mat-slide-toggle>
       </div>
     </div>
-    <div class="input-row specialButtons special-settings" fxLayout="row" fxLayoutGap="5px" fxLayout.xs="column">
-      <div class="button-row weight-class-buttons">
-        <button (click)="toggleExtendedView()" mat-button class="primary" id="extendedViewButton">{{'tag-cloud-config.extended-btn' | translate}}</button>
-        <button (click)="toggleCleanupView()" mat-button class="primary">Tag-Cleanup</button>
-      </div>
-    </div>
-    <h3>{{'tag-cloud-config.general' | translate}}</h3>
-    <div class="input-row" fxLayout="row" fxLayoutGap="5px" fxLayout.xs="column">
-      <mat-slide-toggle (change)="valueChanged()" fxFlex [(ngModel)]="cloudParameters.randomAngles"
-        [ngModelOptions]="{standalone: true}">{{'tag-cloud-config.random-angle' | translate}}</mat-slide-toggle>
-    </div>
-    <div style="margin: 10px;"></div>
-    <div class="input-row" fxLayout="column" fxLayoutGap="5px">
-      <div class="input-row" fxLayout="column" fxLayoutGap="5px">
-        <div fxLayout="row" fxLayoutGap="5px">
-          <mat-form-field fxFlex="100%">
-            <mat-label>{{'tag-cloud-config.background' | translate}}</mat-label>
-            <input matInput [value]="cloudParameters.backgroundColor" [colorPicker]="cloudParameters.backgroundColor"
-              (colorPickerChange)="backgroundColorChanged($event)" class="custom-color-picker" />
-            <p matTooltip="{{'tag-cloud-config.background-tooltip' | translate}}" class="custom-color-picker-text">
-              {{'tag-cloud-config.select-color' | translate}}</p>
+    <br>
+    <mat-accordion id="accordion">
+      <mat-expansion-panel [expanded]="step == 0" (opened)="setStep(0)" class="matExpansionPanel" hideToggle>
+        <mat-expansion-panel-header>
+          <mat-panel-title>
+            <h3>{{'tag-cloud-config.general' | translate}}</h3>
+          </mat-panel-title>
+        </mat-expansion-panel-header>
+        <div class="input-row firstElementOfExpansionPanel" fxLayout="row" fxLayoutGap="5px" fxLayout.xs="column">
+          <mat-slide-toggle matTooltip="{{'tag-cloud-config.random-angle-tooltip' | translate}}" (change)="valueChanged()" fxFlex [(ngModel)]="cloudParameters.randomAngles"
+            [ngModelOptions]="{standalone: true}">{{'tag-cloud-config.random-angle' | translate}}</mat-slide-toggle>
+        </div>
+        <div class="input-row special-settings automatic-spelling" fxLayout="row" fxLayoutGap="5px" fxLayout.xs="column">
+          <div class="input-row" fxLayout="row" fxLayoutGap="5px" fxLayout.xs="column">
+            <mat-slide-toggle matTooltip="{{'tag-cloud-config.automatic-spelling-tooltip' | translate}}" [(ngModel)]="cloudParameters.checkSpelling" [checked]="cloudParameters.checkSpelling" [ngModelOptions]="{standalone: true}" (change)="valueChanged()">{{'tag-cloud-config.automatic-spelling' | translate}}</mat-slide-toggle>
+          </div>
+        </div>
+          <div class="input-row special-settings automatic-spelling" fxLayout="row" fxLayoutGap="5px" fxLayout.xs="column">
+            <mat-radio-group matTooltip="{{'tag-cloud-config.notation-tooltip' | translate}}" aria-label="Notation:"> {{'tag-cloud-config.notation' | translate}}
+              <div><mat-radio-button value="1" (change)="textStyleChanged(1)" [checked]="cloudParameters.textTransform == 1">{{'tag-cloud-config.lowerCase' | translate}}</mat-radio-button> </div>
+              <div><mat-radio-button value="2" (change)="textStyleChanged(2)" [checked]="cloudParameters.textTransform == 2">{{'tag-cloud-config.capitalization' | translate}}</mat-radio-button> </div>
+              <div><mat-radio-button value="0" (change)="textStyleChanged(0)" [checked]="cloudParameters.textTransform == 0">{{'tag-cloud-config.standard' | translate}}</mat-radio-button> </div>
+            </mat-radio-group>
+          </div>
+          <div class="input-row special-settings alphabetical-sorting" fxLayout="row" fxLayoutGap="5px" fxLayout.xs="column">
+            <div class="input-row" fxLayout="row" fxLayoutGap="5px" fxLayout.xs="column">
+              <mat-slide-toggle matTooltip="{{'tag-cloud-config.alphabetical-sorting-tooltip' | translate}}" [(ngModel)]="cloudParameters.sortAlphabetically" [checked]="cloudParameters.sortAlphabetically" [ngModelOptions]="{standalone: true}"(change)="valueChanged()">{{'tag-cloud-config.alphabetical-sorting' | translate}}</mat-slide-toggle>
+            </div>
+          </div>
+        <div style="margin: 10px;"></div>
+        <div class="input-row" fxLayout="column" fxLayoutGap="5px">
+          <div class="input-row" fxLayout="column" fxLayoutGap="5px">
+            <div fxLayout="row" fxLayoutGap="5px">
+              <mat-form-field fxFlex="100%">
+                <mat-label>{{'tag-cloud-config.background' | translate}}</mat-label>
+                <input matInput [value]="cloudParameters.backgroundColor" [colorPicker]="cloudParameters.backgroundColor"
+                  (colorPickerChange)="backgroundColorChanged($event)" class="custom-color-picker" />
+                <p matTooltip="{{'tag-cloud-config.background-tooltip' | translate}}" class="custom-color-picker-text">
+                  {{'tag-cloud-config.select-color' | translate}}</p>
+              </mat-form-field>
+              <div fxFlex="35px" class="color-box" [style.background]="cloudParameters.backgroundColor"></div>
+            </div>
+          </div>
+        </div>
+        <div class="input-row" fxLayout="column" fxLayoutGap="5px">
+          <mat-label class="label-text">{{'tag-cloud-config.word-delay' | translate}}</mat-label>
+          <mat-slider #delaySlider min="0" max="1000" step="50" [(ngModel)]="cloudParameters.delayWord"
+            [ngModelOptions]="{standalone: true}" (change)="valueChanged()" [thumbLabel]="true"
+            [value]="cloudParameters.delayWord" matTooltip="{{'tag-cloud-config.word-delay-tooltip' | translate}}">
+          </mat-slider>
+        </div>
+        <div class="input-row" fxLayout="column" fxLayoutGap="5px">
+          <mat-form-field fxFlex fxLayout.xs="column">
+            <mat-label>{{'tag-cloud-config.font-size-min' | translate}}</mat-label>
+            <input #minFont [value]="cloudParameters.fontSizeMin" matInput type="number"
+              [(ngModel)]="cloudParameters.fontSizeMin" [ngModelOptions]="{standalone: true}" (change)="valueChanged()"
+              min="10" max="300" step="10" matTooltip="{{'tag-cloud-config.font-size-min-tooltip' | translate}}" />
           </mat-form-field>
-          <div fxFlex="35px" class="color-box" [style.background]="cloudParameters.backgroundColor"></div>
         </div>
-      </div>
-    </div>
-    <div class="input-row" fxLayout="column" fxLayoutGap="5px">
-      <mat-label class="label-text">{{'tag-cloud-config.word-delay' | translate}}</mat-label>
-      <mat-slider #delaySlider min="0" max="1000" step="50" [(ngModel)]="cloudParameters.delayWord"
-        [ngModelOptions]="{standalone: true}" (change)="valueChanged()" [thumbLabel]="true"
-        [value]="cloudParameters.delayWord" matTooltip="{{'tag-cloud-config.word-delay-tooltip' | translate}}">
-      </mat-slider>
-    </div>
-    <div class="input-row" fxLayout="column" fxLayoutGap="5px">
-      <mat-form-field fxFlex fxLayout.xs="column">
-        <mat-label>{{'tag-cloud-config.font-size-min' | translate}}</mat-label>
-        <input #minFont [value]="cloudParameters.fontSizeMin" matInput type="number"
-          [(ngModel)]="cloudParameters.fontSizeMin" [ngModelOptions]="{standalone: true}" (change)="valueChanged()"
-          min="10" max="300" step="10" matTooltip="{{'tag-cloud-config.font-size-min-tooltip' | translate}}" />
-      </mat-form-field>
-    </div>
-    <div class="input-row" fxLayout="column" fxLayoutGap="5px">
-      <mat-form-field fxFlex fxLayout.xs="column">
-        <mat-label>{{'tag-cloud-config.font-size-max' | translate}}</mat-label>
-        <input #maxFont matInput type="number" [(ngModel)]="cloudParameters.fontSizeMax"
-          [ngModelOptions]="{standalone: true}" (change)="valueChanged()" min="10" max="1000" step="10"
-          matTooltip="{{'tag-cloud-config.font-size-max-tooltip' | translate}}" />
-      </mat-form-field>
-    </div>
-    <h3>{{'tag-cloud-config.hover-title' | translate}}</h3>
-    <div>
-      <div class="input-row" fxLayout="column" fxLayoutGap="5px">
-        <div fxLayout="row" fxLayoutGap="5px">
-          <mat-form-field fxFlex="100%">
-            <mat-label>{{'tag-cloud-config.hover-color' | translate}}</mat-label>
-            <input matInput [value]="cloudParameters.fontColor" [colorPicker]="cloudParameters.fontColor"
-              (colorPickerChange)="fontColorChanged($event)" class="custom-color-picker" />
-            <p class="custom-color-picker-text" matTooltip="{{'tag-cloud-config.select-color-tooltip' | translate}}">
-              {{'tag-cloud-config.select-color' | translate}}</p>
+        <div class="input-row" fxLayout="column" fxLayoutGap="5px">
+          <mat-form-field fxFlex fxLayout.xs="column">
+            <mat-label>{{'tag-cloud-config.font-size-max' | translate}}</mat-label>
+            <input #maxFont matInput type="number" [(ngModel)]="cloudParameters.fontSizeMax"
+              [ngModelOptions]="{standalone: true}" (change)="valueChanged()" min="10" max="1000" step="10"
+              matTooltip="{{'tag-cloud-config.font-size-max-tooltip' | translate}}" />
           </mat-form-field>
-          <div fxFlex="35px" class="color-box" [style.background]="cloudParameters.fontColor"></div>
         </div>
-      </div>
-      <div class="input-row" fxLayout="column" fxLayoutGap="5px">
-        <mat-label class="label-text">{{'tag-cloud-config.hover-scale' | translate}}</mat-label>
-        <mat-slider #hoverScaleSlider [value]="cloudParameters.hoverScale" min="1" max="10" step="0.2"
-          [(ngModel)]="cloudParameters.hoverScale" [ngModelOptions]="{standalone: true}" (change)="valueChanged()"
-          [thumbLabel]="true" matTooltip="{{'tag-cloud-config.hover-scale-tooltip' | translate}}"></mat-slider>
-      </div>
-      <div class="input-row" fxLayout="column" fxLayoutGap="5px">
-        <mat-label class="label-text">{{'tag-cloud-config.hover-time' | translate}}</mat-label>
-        <mat-slider #transitonSlider [value]="cloudParameters.hoverTime" min="0" max="2" step="0.2"
-          [(ngModel)]="cloudParameters.hoverTime" [ngModelOptions]="{standalone: true}" (change)="valueChanged()"
-          [thumbLabel]="true" matTooltip="{{'tag-cloud-config.hover-time-tooltip' | translate}}"></mat-slider>
-      </div>
-      <div class="input-row" fxLayout="column" fxLayoutGap="5px">
-        <mat-label class="label-text">{{'tag-cloud-config.hover-delay' | translate}}</mat-label>
-        <mat-slider #hoverDelaySlider [value]="cloudParameters.hoverDelay" min="0" max="2" step="0.1"
-          [(ngModel)]="cloudParameters.hoverDelay" [ngModelOptions]="{standalone: true}" (change)="valueChanged()"
-          [thumbLabel]="true" matTooltip="{{'tag-cloud-config.hover-delay-tooltip' | translate}}"></mat-slider>
-      </div>
-    </div>
+        <mat-action-row>
+          <button mat-icon-button (click)="nextStep()"><mat-icon>expand_more</mat-icon></button>        </mat-action-row>
+      </mat-expansion-panel>
+        <mat-expansion-panel [expanded]="step == 1" (opened)="setStep(1)" class="matExpansionPanel" hideToggle>
+          <mat-expansion-panel-header>
+            <mat-panel-title>
+              <h3>{{'tag-cloud-config.hover-title' | translate}}</h3>
+            </mat-panel-title>
+          </mat-expansion-panel-header>
+          <div>
+            <div class="input-row firstElementOfExpansionPanel" fxLayout="column" fxLayoutGap="5px">
+              <div fxLayout="row" fxLayoutGap="5px">
+                <mat-form-field fxFlex="100%">
+                  <mat-label>{{'tag-cloud-config.hover-color' | translate}}</mat-label>
+                  <input matInput [value]="cloudParameters.fontColor" [colorPicker]="cloudParameters.fontColor"
+                    (colorPickerChange)="fontColorChanged($event)" class="custom-color-picker" />
+                  <p class="custom-color-picker-text" matTooltip="{{'tag-cloud-config.select-color-tooltip' | translate}}">
+                    {{'tag-cloud-config.select-color' | translate}}</p>
+                </mat-form-field>
+                <div fxFlex="35px" class="color-box" [style.background]="cloudParameters.fontColor"></div>
+              </div>
+            </div>
+            <div class="input-row" fxLayout="column" fxLayoutGap="5px">
+              <mat-label class="label-text">{{'tag-cloud-config.hover-scale' | translate}}</mat-label>
+              <mat-slider #hoverScaleSlider [value]="cloudParameters.hoverScale" min="1" max="10" step="0.2"
+                [(ngModel)]="cloudParameters.hoverScale" [ngModelOptions]="{standalone: true}" (change)="valueChanged()"
+                [thumbLabel]="true" matTooltip="{{'tag-cloud-config.hover-scale-tooltip' | translate}}"></mat-slider>
+            </div>
+            <div class="input-row" fxLayout="column" fxLayoutGap="5px">
+              <mat-label class="label-text">{{'tag-cloud-config.hover-time' | translate}}</mat-label>
+              <mat-slider #transitonSlider [value]="cloudParameters.hoverTime" min="0" max="2" step="0.2"
+                [(ngModel)]="cloudParameters.hoverTime" [ngModelOptions]="{standalone: true}" (change)="valueChanged()"
+                [thumbLabel]="true" matTooltip="{{'tag-cloud-config.hover-time-tooltip' | translate}}"></mat-slider>
+            </div>
+            <div class="input-row" fxLayout="column" fxLayoutGap="5px">
+              <mat-label class="label-text">{{'tag-cloud-config.hover-delay' | translate}}</mat-label>
+              <mat-slider #hoverDelaySlider [value]="cloudParameters.hoverDelay" min="0" max="2" step="0.1"
+                [(ngModel)]="cloudParameters.hoverDelay" [ngModelOptions]="{standalone: true}" (change)="valueChanged()"
+                [thumbLabel]="true" matTooltip="{{'tag-cloud-config.hover-delay-tooltip' | translate}}"></mat-slider>
+            </div>
+          </div>
+          <mat-action-row>
+            <button mat-icon-button (click)="prevStep()"><mat-icon>expand_less</mat-icon></button>
+            <button mat-icon-button (click)="nextStep()"><mat-icon>expand_more</mat-icon></button>
+          </mat-action-row>
+      </mat-expansion-panel>
+      <mat-expansion-panel [expanded]="step == 2" (opened)="setStep(2)" class="matExpansionPanel" hideToggle>
+        <mat-expansion-panel-header>
+          <mat-panel-title>
+            <h3>{{'tag-cloud-config.weight-class-settings' | translate}}</h3>
+          </mat-panel-title>
+        </mat-expansion-panel-header>
+        <div class="input-row" fxLayout="row" fxLayoutGap="5px" fxLayout.xs="column">
+        <div class="cloud-configuration-form" fxLayout="column">
+          <div *ngFor="let weightClass of weightClasses" class="weight-class-setting">
+            <div class ="weight-class-setting-content">
+            <h3 class="weight-class-heading">{{'tag-cloud-config.weight-class' | translate}} {{weightClasses.indexOf(weightClass) + 1}}</h3>
+              <div class="input-row" fxLayout="column" fxLayoutGap="5px">
+                <div fxLayout="row" fxLayoutGap="5px">
+                  <mat-form-field fxFlex="100%">
+                    <mat-label>{{'tag-cloud-config.weight-color' | translate}}</mat-label>
+                    <input matInput [value]="weightClass.tagColor" [colorPicker]="weightClass.tagColor"
+                           (colorPickerChange)="weightColorChanged(weightClasses.indexOf(weightClass), $event)" class="custom-color-picker" />
+                    <p matTooltip="{{'tag-cloud-config.weight-color-tooltip' | translate}}" class="custom-color-picker-text">
+                      {{'tag-cloud-config.select-color' | translate}}</p>
+                  </mat-form-field>
+                  <div fxFlex="35px" class="color-box" [style.background]="weightClass.tagColor"></div>
+                </div>
+              </div>
+              <div class="input-row" fxLayout="column" fxLayoutGap="5px" *ngIf="weightClass.actualTagNumber > 0 && !parent.dataManager.demoActive">
+                <div class="input-row" fxLayout="column" fxLayoutGap="5px">
+                <mat-label class="label-text" >{{'tag-cloud-config.weight-number' | translate}}</mat-label>
+                <mat-slider [value]="weightClass.maxTagNumber" min="0" [max]="weightClass.actualTagNumber" step="1"
+                            [(ngModel)]="weightClass.maxTagNumber" [ngModelOptions]="{standalone: true}" (change)="valueChanged()"
+                            [thumbLabel]="true" matTooltip="{{'tag-cloud-config.weight-number-tooltip' | translate}}"></mat-slider>
+              </div>
+              </div>
+              <div class="input-row" fxLayout="column" fxLayoutGap="5px">
+                <mat-label class="label-text">{{'tag-cloud-config.rotate-weight' | translate}}</mat-label>
+                <mat-slider [value]="weightClass.rotationAngle" min="-180" max="180" step="1"
+                            [(ngModel)]="weightClass.rotationAngle" [ngModelOptions]="{standalone: true}" (change)="valueChanged()"
+                            [thumbLabel]="true" matTooltip="{{'tag-cloud-config.rotate-weight-tooltip' | translate}}"></mat-slider>
+              </div>
+            </div>
+          </div>
+        </div>
+        </div>
+        <mat-action-row>
+          <button mat-icon-button (click)="prevStep()"><mat-icon>expand_less</mat-icon></button>
+          <button class="close-btn" mat-icon-button (click)="nextStep()"><mat-icon>close</mat-icon></button>
+        </mat-action-row>
+      </mat-expansion-panel>
+    </mat-accordion>
     <br>
     <div class="button-row">
       <button (click)="cancel()" mat-button class="secondary">{{'tag-cloud-config.cancel-btn' | translate}}</button>
       <button (click)="save()" mat-button class="primary">{{'tag-cloud-config.save-btn' | translate}}</button>
     </div>
   </div>
-  <div class="cloud-configuration-form" fxLayout="column" *ngIf="extendedView">
-    <div  class="weight-class-button button-row">
-      <button (click)="toggleExtendedView()" mat-button class="primary">{{'tag-cloud-config.back-btn' | translate}}</button>
-    </div>
-    <h2>{{'tag-cloud-config.weight-class-settings' | translate}}</h2>
-    <div *ngFor="let weightClass of weightClasses" class="weight-class-setting">
-      <div class ="weight-class-setting-content">
-      <h3 class="weight-class-heading">{{'tag-cloud-config.weight-class' | translate}} {{weightClasses.indexOf(weightClass) + 1}}</h3>
-      <div class="input-row" fxLayout="column" fxLayoutGap="5px">
-        <div class="input-row" fxLayout="column" fxLayoutGap="5px">
-          <div fxLayout="row" fxLayoutGap="5px">
-            <mat-form-field fxFlex="100%">
-              <mat-label>{{'tag-cloud-config.weight-color' | translate}}</mat-label>
-              <input matInput [value]="weightClass.tagColor" [colorPicker]="weightClass.tagColor"
-                     (colorPickerChange)="weightColorChanged(weightClasses.indexOf(weightClass), $event)" class="custom-color-picker" />
-              <p matTooltip="{{'tag-cloud-config.weight-color-tooltip' | translate}}" class="custom-color-picker-text">
-                {{'tag-cloud-config.select-color' | translate}}</p>
-            </mat-form-field>
-            <div fxFlex="35px" class="color-box" [style.background]="weightClass.tagColor"></div>
-          </div>
-        </div>
-      </div>
-      
-      <div class="input-row" fxLayout="column" fxLayoutGap="5px" *ngIf="weightClass.actualTagNumber > 0 && !parent.dataManager.demoActive">
-        <mat-label class="label-text" >{{'tag-cloud-config.weight-number' | translate}}</mat-label>
-        <mat-slider [value]="weightClass.maxTagNumber" min="0" [max]="weightClass.actualTagNumber" step="1"
-                    [(ngModel)]="weightClass.maxTagNumber" [ngModelOptions]="{standalone: true}" (change)="valueChanged()"
-                    [thumbLabel]="true" matTooltip="{{'tag-cloud-config.weight-number-tooltip' | translate}}"></mat-slider>
-      </div>
-      <div class="input-row" fxLayout="column" fxLayoutGap="5px">
-        <mat-label class="label-text">{{'tag-cloud-config.rotate-weight' | translate}}</mat-label>
-        <mat-slider [value]="weightClass.rotationAngle" min="-180" max="180" step="1"
-                    [(ngModel)]="weightClass.rotationAngle" [ngModelOptions]="{standalone: true}" (change)="valueChanged()"
-                    [thumbLabel]="true" matTooltip="{{'tag-cloud-config.weight-number-tooltip' | translate}}"></mat-slider>
-      </div>
-    </div>
-  </div>
-  </div>
-  <div class="cloud-configuration-form" fxLayout="column" *ngIf="cleanUpView">
-    <div  class="weight-class-button button-row">
-      <button (click)="toggleCleanupView()" mat-button class="primary">{{'tag-cloud-config.back-btn' | translate}}</button>
-    </div>
-    <h2>{{'tag-cloud-config.cleanUpView' | translate}}</h2>
-    <div class="input-row special-settings automatic-spelling" fxLayout="row" fxLayoutGap="5px" fxLayout.xs="column">
-      <div class="input-row" fxLayout="row" fxLayoutGap="5px" fxLayout.xs="column">
-        <mat-slide-toggle matTooltip="{{'tag-cloud-config.automatic-spelling-tooltip' | translate}}" [(ngModel)]="cloudParameters.checkSpelling" [checked]="cloudParameters.checkSpelling" [ngModelOptions]="{standalone: true}" (change)="valueChanged()">{{'tag-cloud-config.automatic-spelling' | translate}}</mat-slide-toggle>
-      </div>
-    </div>
-      <div class="input-row special-settings automatic-spelling" fxLayout="row" fxLayoutGap="5px" fxLayout.xs="column">
-        <mat-radio-group matTooltip="{{'tag-cloud-config.notation-tooltip' | translate}}" aria-label="Notation:"> {{'tag-cloud-config.notation' | translate}}
-          <div><mat-radio-button value="1" (change)="textStyleChanged(1)" [checked]="cloudParameters.textTransform == 1">{{'tag-cloud-config.lowerCase' | translate}}</mat-radio-button> </div>
-          <div><mat-radio-button value="2" (change)="textStyleChanged(2)" [checked]="cloudParameters.textTransform == 2">{{'tag-cloud-config.capitalization' | translate}}</mat-radio-button> </div>
-          <div><mat-radio-button value="0" (change)="textStyleChanged(0)" [checked]="cloudParameters.textTransform == 0">{{'tag-cloud-config.standard' | translate}}</mat-radio-button> </div>
-        </mat-radio-group>
-      </div>
-      <div class="input-row special-settings alphabetical-sorting" fxLayout="row" fxLayoutGap="5px" fxLayout.xs="column">
-        <div class="input-row" fxLayout="row" fxLayoutGap="5px" fxLayout.xs="column">
-          <mat-slide-toggle matTooltip="{{'tag-cloud-config.alphabetical-sorting-tooltip' | translate}}" [(ngModel)]="cloudParameters.sortAlphabetically" [checked]="cloudParameters.sortAlphabetically" [ngModelOptions]="{standalone: true}"(change)="valueChanged()">{{'tag-cloud-config.alphabetical-sorting' | translate}}</mat-slide-toggle>
-        </div>
-      </div>
-      <div class="input-row" fxLayout="column" fxLayoutGap="5px">
-        <mat-form-field fxFlex fxLayout.xs="column">
-          <mat-label>{{'tag-cloud-config.highestWeight' | translate}}</mat-label>
-          <input #highestWeight matTooltip="{{'tag-cloud-config.highestWeight-tooltip' | translate}}" [value]="cloudParameters.highestWeight" matInput type="number"
-            [(ngModel)]="cloudParameters.highestWeight" [ngModelOptions]="{standalone: true}" (change)="valueChanged()"
-            min="0" max="150" step="1"/>
-        </mat-form-field>
-      </div>
-  </div>
 </div>
diff --git a/src/app/components/shared/_dialogs/cloud-configuration/cloud-configuration.component.scss b/src/app/components/shared/_dialogs/cloud-configuration/cloud-configuration.component.scss
index faaf8bd4ab99c5b9349ad95b3e931b10116e778f..85ba0670d217f1caee2f1dace0c92582a07ecf4f 100644
--- a/src/app/components/shared/_dialogs/cloud-configuration/cloud-configuration.component.scss
+++ b/src/app/components/shared/_dialogs/cloud-configuration/cloud-configuration.component.scss
@@ -8,7 +8,7 @@ h2{
 }
 
 h3 {
-  border-bottom: 1px solid var(--primary);
+
   color: var(--primary);
   padding-bottom: 6px;
 }
@@ -35,10 +35,15 @@ div {
   color: var(--on-secondary);
 }
 
-button {
+::ng-deep .mat-button {
   min-width: 105px;
 }
 
+.close-btn mat-icon{
+  font-size: 20px;
+  margin-top: 2px;
+}
+
 mat-icon {
   margin-right: 10px;
 }
@@ -69,8 +74,10 @@ mat-divider {
   border: 2px solid var(--on-dialog);
   height: 30px;
   width: 20px;
+  bottom: 8px;
   border-radius: 50%;
   margin-top: 13px;
+  margin-bottom: 10px;
   transform: translateY(-4px);
   display: block;
   position: relative;
@@ -157,6 +164,7 @@ mat-divider {
 }
 .custom-color-picker-text{
   position: absolute;
+  top: 25px;
   transform: translateY(-35px);
 }
 
@@ -186,6 +194,11 @@ mat-divider {
   border-bottom: none;
 }
 
+#accordion {
+  margin-bottom: 10px;
+  margin-top: 10px;
+}
+
 .weight-class-buttons {
   margin-bottom: 0;
 }
@@ -198,4 +211,15 @@ mat-divider {
 
 #rotation{
   margin-top: 10px;
-}
\ No newline at end of file
+}
+
+.matExpansionPanel{
+  background-color: var(--dialog);
+  margin: 2px;
+  padding: 5px;
+}
+
+.firstElementOfExpansionPanel{
+  border-top: 2px solid var(--primary);
+  padding-top: 10px;
+}
diff --git a/src/app/components/shared/_dialogs/cloud-configuration/cloud-configuration.component.ts b/src/app/components/shared/_dialogs/cloud-configuration/cloud-configuration.component.ts
index 94f2117a5e6498a1234e130d54c39cc4b0d2b192..a2be830f62756dc06140d3f07ae6f96cb0ca4bdd 100644
--- a/src/app/components/shared/_dialogs/cloud-configuration/cloud-configuration.component.ts
+++ b/src/app/components/shared/_dialogs/cloud-configuration/cloud-configuration.component.ts
@@ -1,9 +1,9 @@
 import { Component, Input, OnInit } from '@angular/core';
 import { TranslateService } from '@ngx-translate/core';
 import { TagCloudComponent } from '../../tag-cloud/tag-cloud.component';
-import { TagCloudMetaDataCount } from '../../tag-cloud/tag-cloud.data-manager';
 import { CloudParameters, CloudTextStyle } from '../../tag-cloud/tag-cloud.interface';
 import { WeightClass } from './weight-class.interface';
+import { TagCloudMetaDataCount } from '../../../../services/util/tag-cloud-data.service';
 
 @Component({
   selector: 'app-cloud-configuration',
@@ -26,6 +26,7 @@ export class CloudConfigurationComponent implements OnInit {
   alphabeticalSorting: boolean;
   rotation: number;
   highestWeight: number;
+  step:number = 0;
   weightClasses: WeightClass[] = [
     {
       maxTagNumber: 20,
@@ -148,12 +149,16 @@ export class CloudConfigurationComponent implements OnInit {
     this.parent.tagCloudDataManager.demoActive = false;
     this.parent.setCloudParameters(this.defaultCloudParameters);
     this.parent.configurationOpen = false;
+    this.setStep(0)
+
   }
 
   save() {
     this.parent.tagCloudDataManager.demoActive = false;
     this.parent.setCloudParameters(this.cloudParameters);
-    this.parent.configurationOpen = false;
+    this.parent.configurationOpen = false;    
+   this.setStep(0)
+
   }
 
   toggleExtendedView() {
@@ -176,4 +181,16 @@ export class CloudConfigurationComponent implements OnInit {
     this.valueChanged();
   }
 
+  setStep(index: number) {
+    this.step = index;
+  }
+
+  nextStep() {
+    this.step++;
+  }
+
+  prevStep() {
+    this.step--;
+  }
+
 }
diff --git a/src/app/components/shared/_dialogs/create-comment/create-comment.component.html b/src/app/components/shared/_dialogs/create-comment/create-comment.component.html
index eb4b8d88af2d0af963d0e30a5ac2b763dfb28580..1c94986def026458b15144bc5e4b223bcaac346f 100644
--- a/src/app/components/shared/_dialogs/create-comment/create-comment.component.html
+++ b/src/app/components/shared/_dialogs/create-comment/create-comment.component.html
@@ -46,6 +46,8 @@
           <mat-form-field style="width:100%;">
             <input [disabled]="true" matInput>
             <div [contentEditable]="true"
+            (paste)="clearHTML($event);
+                maxLength(commentBody)"
                       [spellcheck]="false"
                       spellcheck="false"
                       (focus)="eventService.makeFocusOnInputTrue()"
@@ -70,6 +72,9 @@
                 {{inputText.length}} / {{user.role === 3 ? 1000 : 500}}
               </span>
             </mat-hint>
+            <span *ngIf="!this.hasSpellcheckConfidence">
+              <p>{{ 'spacy-dialog.force-language-selection' | translate }}</p>
+            </span>
           </mat-form-field>
         </ars-row>
       </mat-tab>
@@ -92,7 +97,12 @@
   </ars-row>
   <ars-row ars-flex-box>
     <ars-col>
-      <button class="mat-flat-button spell-button"  (click)="grammarCheck(commentBody)">{{ 'comment-page.grammar-check' | translate}}</button>
+      <button mat-button class="spell-button" (click)="grammarCheck(commentBody)">
+        {{ 'comment-page.grammar-check' | translate}}
+        <mat-icon *ngIf="isSpellchecking" style="margin: 0;">
+          <mat-spinner diameter="20"></mat-spinner>
+        </mat-icon>
+      </button>
     </ars-col>
     <ars-col>
       <app-dialog-action-buttons
diff --git a/src/app/components/shared/_dialogs/create-comment/create-comment.component.scss b/src/app/components/shared/_dialogs/create-comment/create-comment.component.scss
index 41d5b82dbc9498fee7f9f9c119a673d3708c554a..d7ea6685b22ad2d3dc4a1996b1d2e7335a0be616 100644
--- a/src/app/components/shared/_dialogs/create-comment/create-comment.component.scss
+++ b/src/app/components/shared/_dialogs/create-comment/create-comment.component.scss
@@ -123,3 +123,12 @@ mat-hint {
 .mat-option {
   color: var(--on-surface);
 }
+
+::ng-deep .mat-tab-body-content {
+  max-width: 540px !important;
+  overflow-x: hidden !important;
+}
+
+::ng-deep .mat-spinner circle {
+  stroke: var(--on-primary);
+}
diff --git a/src/app/components/shared/_dialogs/create-comment/create-comment.component.ts b/src/app/components/shared/_dialogs/create-comment/create-comment.component.ts
index cb679c21af613760a779abe073e0d26a80fa9718..8191bdb32c6625bf4a489cc8f837df27293d9885 100644
--- a/src/app/components/shared/_dialogs/create-comment/create-comment.component.ts
+++ b/src/app/components/shared/_dialogs/create-comment/create-comment.component.ts
@@ -31,6 +31,9 @@ export class CreateCommentComponent implements OnInit, OnDestroy {
 
   bodyForm = new FormControl('', [Validators.required]);
 
+  isSpellchecking = false;
+  hasSpellcheckConfidence = true;
+
   @ViewChild('commentBody', { static: true }) commentBody: HTMLDivElement;
 
   constructor(
@@ -69,6 +72,11 @@ export class CreateCommentComponent implements OnInit, OnDestroy {
   onNoClick(): void {
     this.dialogRef.close();
   }
+  clearHTML(e){
+    e.preventDefault();
+    const text = e.clipboardData.getData('text');
+    document.getElementById('answer-input').innerText += text.replace(/<[^>]*>?/gm, '');
+  }
 
   checkInputData(body: string): boolean {
     body = body.trim();
@@ -97,8 +105,9 @@ export class CreateCommentComponent implements OnInit, OnDestroy {
     this.checkSpellings(this.inputText).subscribe((res) => {
       const words: string[] = this.inputText.trim().split(' ');
       const errorQuotient = (res.matches.length * 100) / words.length;
+      const hasSpellcheckConfidence = this.checkLanguageConfidence(res);
 
-      if (errorQuotient <= 20) {
+      if (hasSpellcheckConfidence && errorQuotient <= 20) {
         let commentBodyChecked = this.inputText;
         const commentLang = this.languagetoolService.mapLanguageToSpacyModel(res.language.code);
 
@@ -148,19 +157,29 @@ export class CreateCommentComponent implements OnInit, OnDestroy {
   }
 
   maxLength(commentBody: HTMLDivElement): void {
-    this.inputText = commentBody.innerText;
     if (this.user.role === 3 && commentBody.innerText.length > 1000) {
       commentBody.innerText = commentBody.innerText.slice(0, 1000);
     } else if (this.user.role !== 3 && commentBody.innerText.length > 500) {
       commentBody.innerText = commentBody.innerText.slice(0, 500);
     }
     this.body = commentBody.innerText;
+    if(this.body.length === 1 && this.body.charCodeAt(this.body.length - 1) === 10){
+      commentBody.innerHTML = commentBody.innerHTML.replace('<br>','');
+    }
+    this.inputText = commentBody.innerText;
   }
 
   grammarCheck(commentBody: HTMLDivElement): void {
     const wrongWords: string[] = [];
     commentBody.innerHTML = this.inputText;
+    this.isSpellchecking = true;
+    this.hasSpellcheckConfidence = true;
     this.checkSpellings(commentBody.innerText).subscribe((wordsCheck) => {
+      if(!this.checkLanguageConfidence(wordsCheck)) {
+        this.hasSpellcheckConfidence = false;
+        return;
+      }
+
       if (wordsCheck.matches.length > 0) {
         wordsCheck.matches.forEach(grammarError => {
           const wrongWord = commentBody.innerText.slice(grammarError.offset, grammarError.offset + grammarError.length);
@@ -192,13 +211,13 @@ export class CreateCommentComponent implements OnInit, OnDestroy {
 
               const replacement =
                 '<div class="markUp" data-id="'+i+'" style="position: relative; display: inline-block; border-bottom: 1px dotted black">' +
-                '   <span data-id="' + i + '" style="text-decoration: underline wavy red; cursor: pointer;">' +
-                wrongWord +
-                '   </span>' +
-                // eslint-disable-next-line max-len
-                '     <div class="dropdownBlock" style="display: none; width: 160px; background-color: white; border-style: solid; border-color: var(--primary); color: #fff; text-align: center; border-radius: 6px; padding: 5px 0; position: absolute; z-index: 1000; bottom: 100%; left: 50%; margin-left: -80px;">' +
-                suggestionsHTML +
-                '     </div>' +
+                  '<span data-id="' + i + '" style="text-decoration: underline wavy red; cursor: pointer;">' +
+                          wrongWord +
+                  '</span>' +
+                  // eslint-disable-next-line max-len
+                  '<div class="dropdownBlock" style="display: none; width: 160px; background-color: white; border-style: solid; border-color: var(--primary); color: #fff; text-align: center; border-radius: 6px; padding: 5px 0; position: absolute; z-index: 1000; bottom: 100%;">' +
+                        suggestionsHTML +
+                  '</div>' +
                 '</div>';
 
               commentBody.innerHTML = commentBody.innerHTML.substr(0, res.matches[i].offset) +
@@ -207,13 +226,26 @@ export class CreateCommentComponent implements OnInit, OnDestroy {
           }
 
           setTimeout(() => {
-            Array.from(document.getElementsByClassName('markUp')).forEach(marked => {
-              marked.addEventListener('click', () => {
-                ((marked as HTMLElement).lastChild as HTMLElement).style.display = 'block';
+            Array.from(document.getElementsByClassName('markUp')).forEach(markup => {
+              markup.addEventListener('click', () => {
+                ((markup as HTMLElement).lastChild as HTMLElement).style.display = 'block';
+                const rectdiv = (document.getElementById('answer-input')).getBoundingClientRect();
+                const rectmarkup = markup.getBoundingClientRect();
+                let offset;
+                if (rectmarkup.x + rectmarkup.width / 2 > rectdiv.right - 80) {
+                  offset = rectdiv.right - rectmarkup.x - rectmarkup.width;
+                  ((markup as HTMLElement).lastChild as HTMLElement).style.right = -offset + 'px';
+                } else if (rectmarkup.x + rectmarkup.width / 2 < rectdiv.left + 80) {
+                  offset = rectmarkup.x - rectdiv.left;
+                  ((markup as HTMLElement).lastChild as HTMLElement).style.left = -offset + 'px';
+                } else {
+                  ((markup as HTMLElement).lastChild as HTMLElement).style.left = '50%';
+                  ((markup as HTMLElement).lastChild as HTMLElement).style.marginLeft = '-80px';
+                }
                 setTimeout(() => {
-                  Array.from(document.getElementsByClassName('suggestions')).forEach(e => {
-                    e.addEventListener('click', () => {
-                      e.parentElement.parentElement.outerHTML = e.innerHTML;
+                  Array.from(document.getElementsByClassName('suggestions')).forEach(suggestion => {
+                    suggestion.addEventListener('click', () => {
+                      suggestion.parentElement.parentElement.outerHTML = suggestion.innerHTML;
                       this.inputText = commentBody.innerText;
                     });
                   });
@@ -223,6 +255,12 @@ export class CreateCommentComponent implements OnInit, OnDestroy {
           }, 500);
         });
       }
+    }, () => {}, () => {
+      this.isSpellchecking = false;
     });
   }
+
+  checkLanguageConfidence(wordsCheck: any) {
+    return this.selectedLang === 'auto' ? wordsCheck.language.detectedLanguage.confidence >= 0.5 : true;
+  }
 }
diff --git a/src/app/components/shared/_dialogs/room-create/room-create.component.ts b/src/app/components/shared/_dialogs/room-create/room-create.component.ts
index 5c83def0486ec96696029d41e50134b895e0378e..1e49b428fcd067fe53775b70b635676eac133457 100644
--- a/src/app/components/shared/_dialogs/room-create/room-create.component.ts
+++ b/src/app/components/shared/_dialogs/room-create/room-create.component.ts
@@ -74,6 +74,7 @@ export class RoomCreateComponent implements OnInit {
     newRoom.name = longRoomName;
     newRoom.abbreviation = '00000000';
     newRoom.description = '';
+    newRoom.blacklist = '[]';
     if (this.hasCustomShortId && this.customShortIdName && this.customShortIdName.length > 0) {
       if (!new RegExp('[1-9a-z,A-Z,\s,\-,\.,\_,\~]+').test(this.customShortIdName)
         || this.customShortIdName.startsWith(' ') || this.customShortIdName.endsWith(' ')) {
diff --git a/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.html b/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.html
index 3a2cab8045ff60eeb87392acaf297dbc199f95ce..66a7e1f1efe14fc99d2c6a26ae55b71ad4ab80e2 100644
--- a/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.html
+++ b/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.html
@@ -55,8 +55,8 @@
         </mat-list-item>
       </mat-list>
     </ars-row>
-    <span *ngIf="keywords.length<=0">
-      <p>{{ 'spacy-dialog.empty-nouns' | translate}}</p>
+    <span *ngIf="keywords.length <= 0 && !this.isLoading">
+      <p>{{ 'spacy-dialog.empty-nouns' | translate }}</p>
     </span>
 
   </div>
diff --git a/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.ts b/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.ts
index 150aa70e3fb88fa7e2c3c0a09afcf6b4f90a6650..4a69313c912f7eafc40aff1070177819db8c91c6 100644
--- a/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.ts
+++ b/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.ts
@@ -25,6 +25,7 @@ export class SpacyDialogComponent implements OnInit, AfterContentInit {
   commentBodyChecked: string;
   keywords: Keyword[] = [];
   keywordsOriginal: Keyword[] = [];
+  isLoading = false;
 
   constructor(
     protected langService: LanguageService,
@@ -68,6 +69,9 @@ export class SpacyDialogComponent implements OnInit, AfterContentInit {
     } else {
       regex = new RegExp('(?!au|de|la|le|en|un)[A-ZÀ-Ÿ]{2,}', 'gi');
     }
+
+    this.isLoading = true;
+
     // N at first pos = all Nouns(NN de/en) including singular(NN, NNP en), plural (NNPS, NNS en), proper Noun(NNE, NE de)
     this.spacyService.getKeywords(this.commentBodyChecked, model)
       .subscribe(words => {
@@ -89,6 +93,8 @@ export class SpacyDialogComponent implements OnInit, AfterContentInit {
       }, () => {
         this.keywords = [];
         this.keywordsOriginal = [];
+      }, () => {
+        this.isLoading = false;
       });
   }
 
diff --git a/src/app/components/shared/_dialogs/topic-cloud-administration/TopicCloudAdminData.ts b/src/app/components/shared/_dialogs/topic-cloud-administration/TopicCloudAdminData.ts
index 43437d2f46dd6320149bfce02e6b02ccf39310ed..5d0dbabc488342451031004c7321b514b6b9b60d 100644
--- a/src/app/components/shared/_dialogs/topic-cloud-administration/TopicCloudAdminData.ts
+++ b/src/app/components/shared/_dialogs/topic-cloud-administration/TopicCloudAdminData.ts
@@ -1,13 +1,58 @@
-export interface TopicCloudAdminData{
+export interface TopicCloudAdminData {
     blacklist: string[];
+    wantedLabels: {
+      de: string[];
+      en: string[];
+    };
     considerVotes: boolean;
     profanityFilter: boolean;
     blacklistIsActive: boolean;
     keywordORfulltext: KeywordOrFulltext;
 }
 
-export enum KeywordOrFulltext{
+export enum KeywordOrFulltext {
     keyword,
     fulltext,
     both
 }
+
+export interface Label {
+    readonly tag: string;
+    readonly label: string;
+}
+
+export class Labels {
+    readonly de: Label[];
+    readonly en: Label[];
+
+    constructor(_de: Label[], _en: Label[]) {
+        this.de = _de;
+        this.en = _en;
+    }
+}
+
+const deLabels: Label[] = [
+    {tag: 'sb',  label: 'Subjekt'},
+    {tag: 'pd',  label: 'Prädikat'},
+    {tag: 'og',  label: 'Genitivobjekt'},
+    {tag: 'ag',  label: 'Genitivattribut'},
+    {tag: 'app', label: 'Apposition'},
+    {tag: 'da',  label: 'Dativobjekt'},
+    {tag: 'oa',  label: 'Akkusativobjekt'},
+    {tag: 'nk',  label: 'Noun Kernel Element'},
+    {tag: 'mo',  label: 'Modifikator'},
+    {tag: 'cj',  label: 'Konjunktor'}
+];
+
+const enLabels: Label[] = [
+    {tag: 'no',   label: 'Noun'},
+    {tag: 'pro',  label: 'Pronoun'},
+    {tag: 've',   label: 'Verb'},
+    {tag: 'adj',  label: 'Adjective'},
+    {tag: 'adv',  label: 'AdverbDVERB'},
+    {tag: 'pre',  label: 'Preposition'},
+    {tag: 'con',  label: 'Conjunction'},
+    {tag: 'int',  label: 'Interjection'}
+];
+
+export const spacyLabels = new Labels(deLabels, enLabels);
diff --git a/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.html b/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.html
index bc7471ca4f14d7ec2bb3e8d56cb80218bd7086de..d1e5756904aa02809fb953506f86cf83ef7b7e75 100644
--- a/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.html
+++ b/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.html
@@ -11,9 +11,7 @@
         <mat-label class="color-on-surface">
           {{"topic-cloud-dialog.select-choice" | translate}}
         </mat-label>
-        <mat-radio-group
-        class="radio-button-group"
-        [(ngModel)]="keywordORfulltext">
+        <mat-radio-group class="radio-button-group" [(ngModel)]="keywordORfulltext">
           <mat-radio-button checked="true" [value]="keywordOrFulltextENUM[0]" class="radio-button-item">
             {{"topic-cloud-dialog.keyword" | translate}}
           </mat-radio-button>
@@ -37,7 +35,7 @@
       </mat-slide-toggle>
 
       <mat-accordion class="new-profanity-word" multi>
-        <mat-expansion-panel class="color-background" (opened)="enterProfanityWord=true; focusInput('profanity-word-input')"
+        <mat-expansion-panel class="color-background" (opened)="enterProfanityWord=true; focusInput('bad-word-input')"
           (closed)="enterProfanityWord = false">
           <mat-expansion-panel-header class="color-background">
             <mat-panel-title>
@@ -65,12 +63,13 @@
           <div>
             <button mat-raised-button *ngIf="getProfanityList().length > 0" class="primaryBackground"
               (click)="showProfanityList=!showProfanityList">
-              {{showProfanityList ? ('topic-cloud-dialog.hide-profanity-list' | translate) : ('topic-cloud-dialog.show-profanity-list' | translate)}}
+              {{showProfanityList ? ('topic-cloud-dialog.hide-profanity-list' | translate) :
+              ('topic-cloud-dialog.show-profanity-list' | translate)}}
             </button>
           </div>
         </mat-expansion-panel>
 
-        <mat-expansion-panel class="color-background"
+        <mat-expansion-panel class="color-background margin-bottom"
           (opened)="enterBlacklistWord = true; focusInput('blacklist-word-input')"
           (closed)="enterBlacklistWord = false">
           <mat-expansion-panel-header class="color-background">
@@ -87,9 +86,8 @@
           <button mat-stroked-button color="primary" class="margin-left" (click)="addBlacklistWord()">
             {{'topic-cloud-dialog.add-word' | translate}}
           </button>
-
-          <mat-list role="list" *ngIf="showBlacklistWordList" class="margin-bottom">
-            <mat-list-item class="color-on-surface" *ngFor="let word of getBlacklist()" role="listitem">{{word}}
+          <mat-list role="list" *ngIf="showBlacklistWordList && blacklist.length > 0" class="margin-bottom">
+            <mat-list-item class="color-on-surface" *ngFor="let word of blacklist" role="listitem">{{word}}
               <button style="margin-left: auto" mat-icon-button class="red" (click)="removeWordFromBlacklist(word)">
                 <mat-icon mat-list-icon style="margin-bottom: 6px;">delete</mat-icon>
               </button>
@@ -97,12 +95,53 @@
           </mat-list>
 
           <div>
-            <button mat-raised-button *ngIf="getBlacklist().length > 0" class="primaryBackground"
+            <button mat-raised-button class="primaryBackground" *ngIf="blacklist.length > 0"
               (click)="showBlacklistWordList=!showBlacklistWordList">
-              {{showBlacklistWordList ? ('topic-cloud-dialog.hide-blacklist' | translate) : ('topic-cloud-dialog.show-blacklist' | translate)}}
+              {{showBlacklistWordList ? ('topic-cloud-dialog.hide-blacklist' | translate) :
+              ('topic-cloud-dialog.show-blacklist' | translate)}}
             </button>
           </div>
         </mat-expansion-panel>
+        <mat-expansion-panel class="color-background">
+          <mat-expansion-panel-header class="color-background">
+            <mat-panel-title>
+              Spacy labels
+            </mat-panel-title>
+          </mat-expansion-panel-header>
+
+          <mat-tab-group animationDuration="0ms" mat-stretch-tabs mat-align-tabs="center">
+            <mat-tab label="{{'topic-cloud-dialog.german' | translate}}">
+              <mat-selection-list *ngIf="wantedLabels" [(ngModel)]="wantedLabels.de">
+
+                <mat-option class="color-on-surface" (click)="selectAllDE(); allSelectedDE = !allSelectedDE">
+                  <mat-label>
+                    <mat-icon>playlist_add_check</mat-icon>
+                    {{'topic-cloud-dialog.select-all' | translate}}
+                  </mat-label>
+                </mat-option>
+
+                <mat-list-option [value]="label.tag" class="color-on-surface" *ngFor="let label of spacyLabels.de">
+                  {{label.label + " (" + label.tag + ")"}}
+                </mat-list-option>
+              </mat-selection-list>
+            </mat-tab>
+            <mat-tab label="{{'topic-cloud-dialog.english' | translate}}">
+              <mat-selection-list *ngIf="wantedLabels" [(ngModel)]="wantedLabels.en">
+
+                <mat-option class="color-on-surface" (click)="selectAllEN(); allSelectedEN = !allSelectedEN">
+                  <mat-label>
+                    <mat-icon>playlist_add_check</mat-icon>
+                    {{'topic-cloud-dialog.select-all' | translate}}
+                  </mat-label>
+                </mat-option>
+
+                <mat-list-option [value]="label.tag" class="color-on-surface" *ngFor="let label of spacyLabels.en">
+                  {{label.label + " (" + label.tag + ")"}}
+                </mat-list-option>
+              </mat-selection-list>
+            </mat-tab>
+          </mat-tab-group>
+        </mat-expansion-panel>
       </mat-accordion>
     </mat-expansion-panel>
   </mat-accordion>
@@ -123,12 +162,14 @@
     </div>
 
     <div fxLayoutAlign="center center" style="margin-left: auto; font-weight: bold;">
-      <mat-icon [ngClass]="{'animation-blink': searchMode}">sell</mat-icon>
-      <p class="margin-left" [ngClass]="{'animation-blink': searchMode}">{{searchMode ? filteredKeywords.length : keywords.length}}</p>
+      <mat-icon svgIcon="comment_tag"
+                [ngClass]="{'animation-blink': searchMode}"
+                class="oldtypo-h2 comment_tag-icon"></mat-icon>
+      <p [ngClass]="{'animation-blink': searchMode}">{{searchMode ? filteredKeywords.length :
+        keywords.length}}</p>
     </div>
     <div class="margin-left vertical-center">
-      <button [ngClass]="{'animation-blink': sortMode!=='alphabetic'}" mat-icon-button
-              [matMenuTriggerFor]="sortMenu">
+      <button [ngClass]="{'animation-blink': sortMode!=='alphabetic'}" mat-icon-button [matMenuTriggerFor]="sortMenu">
         <mat-icon>sort</mat-icon>
       </button>
     </div>
@@ -151,15 +192,13 @@
     </button>
   </mat-menu>
 
-  <mat-accordion>
-    <mat-expansion-panel class="color-surface" hideToggle *ngIf="searchMode && filteredKeywords.length === 0">
-      <mat-expansion-panel-header class="color-surface">
-        <mat-panel-title fxLayoutAlign="center">
-          {{'topic-cloud-dialog.no-keywords-note' | translate}}
-        </mat-panel-title>
-      </mat-expansion-panel-header>
-    </mat-expansion-panel>
+  <mat-card class="color-surface" *ngIf="keywords.length === 0 || (searchMode && filteredKeywords.length === 0)">
+    <p class="color-on-surface" fxLayoutAlign="center">
+      {{'topic-cloud-dialog.no-keywords-note' | translate}}
+    </p>
+  </mat-card>
 
+  <mat-accordion>
     <mat-expansion-panel class="color-surface" (opened)="panelOpenState = true" (closed)="panelOpenState = edit = false"
       *ngFor="let keyword of
                         (searchMode ? filteredKeywords : keywords); let i = index" [attr.data-index]="i">
@@ -168,13 +207,13 @@
           {{profanityFilter ? getKeywordWithoutProfanity(keyword.keyword) : keyword.keyword}}
         </mat-panel-title>
         <mat-panel-description>
-          {{keyword.questions.length}}
-          {{'topic-cloud-dialog.question-count-'+(keyword.questions.length > 1 ? 'plural' : 'singular') | translate}}
+          {{keyword.comments.length}}
+          {{'topic-cloud-dialog.question-count-'+(keyword.comments.length > 1 ? 'plural' : 'singular') | translate}}
         </mat-panel-description>
       </mat-expansion-panel-header>
-      <div *ngFor="let question of keyword.questions">
+      <div *ngFor="let question of keyword.comments">
         <mat-divider></mat-divider>
-        <app-topic-dialog-comment [question]="question" [keyword]="keyword.keyword" [maxShowedCharachters]="140"
+        <app-topic-dialog-comment [question]="question.body" [keyword]="keyword.keyword" [maxShowedCharachters]="140"
           [isCollapsed]="!panelOpenState" [profanityFilter]="profanityFilter"></app-topic-dialog-comment>
       </div>
 
diff --git a/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.scss b/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.scss
index f44532d248f95cfcc58ff91faad7bb06a4d2e4e3..c283bcb67cc19596c035738be223da967e7815ce 100644
--- a/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.scss
+++ b/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.scss
@@ -16,6 +16,10 @@
   margin-bottom: 16px;
 }
 
+.margin-top {
+  margin-top: 16px;
+}
+
 .primary {
   color: var(--primary);
   background: none;
@@ -134,3 +138,6 @@ mat-dialog-content {
   max-height: 80vh!important;
 }
 
+.comment_tag-icon {
+  height: 18px !important;
+}
diff --git a/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.ts b/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.ts
index b5b55d9ca573918ab7ab771be1fb8778b82eb7d7..4540506808389e7cdfe49e046a9a3562d4b4b4d9 100644
--- a/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.ts
+++ b/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.ts
@@ -1,15 +1,18 @@
-import { Component, OnDestroy, OnInit } from '@angular/core';
-import { MatDialog, MatDialogRef } from '@angular/material/dialog';
-import { TagCloudComponent } from '../../tag-cloud/tag-cloud.component';
+import { Component, Inject, OnDestroy, OnInit, ViewChild } from '@angular/core';
+import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
 import { NotificationService } from '../../../../services/util/notification.service';
 import { TopicCloudConfirmDialogComponent } from '../topic-cloud-confirm-dialog/topic-cloud-confirm-dialog.component';
-import { AuthenticationService } from '../../../../services/http/authentication.service';
 import { UserRole } from '../../../../models/user-roles.enum';
 import { TranslateService } from '@ngx-translate/core';
 import { LanguageService } from '../../../../services/util/language.service';
 import { TopicCloudAdminService } from '../../../../services/util/topic-cloud-admin.service';
-import { TopicCloudAdminData } from './TopicCloudAdminData';
+import { TopicCloudAdminData, Labels, spacyLabels } from './TopicCloudAdminData';
 import { KeywordOrFulltext } from './TopicCloudAdminData';
+import { User } from '../../../../models/user';
+import { Comment } from '../../../../models/comment';
+import { CommentService } from '../../../../services/http/comment.service';
+import { WsCommentServiceService } from '../../../../services/websockets/ws-comment-service.service';
+import { TSMap } from 'typescript-map';
 
 @Component({
   selector: 'app-topic-cloud-administration',
@@ -21,6 +24,8 @@ export class TopicCloudAdministrationComponent implements OnInit, OnDestroy {
   public considerVotes: boolean;
   public profanityFilter: boolean;
   public blacklistIsActive: boolean;
+  blacklist: string[] = [];
+  blacklistSubscription = undefined;
   keywordOrFulltextENUM = KeywordOrFulltext;
   newKeyword = undefined;
   edit = false;
@@ -38,95 +43,87 @@ export class TopicCloudAdministrationComponent implements OnInit, OnDestroy {
   showSettingsPanel = false;
   keywordORfulltext: string = undefined;
   userRole: UserRole;
-  keywords: Keyword[] = [
-    {
-      keywordID: 1,
-      keyword: 'Cloud',
-      questions: [
-        'Wieviel speicherplatz steht mir in der Cloud zur verfügung?',
-        'Sollen wir die Tag Cloud implementieren?',
-        // eslint-disable-next-line max-len
-        'Wie genau ist die Cloud aufgebaut? Wieviel speicherplatz steht mir in der Cloud zur verfuegungWie genau ist die Cloud aufgebaut? Wieviel speicherplatz steht mir in der Cloud zur verfuegungWie genau ist die Cloud aufgebaut? Wieviel speicherplatz steht mir in der Cloud zur verfuegungWie genau ist die Cloud aufgebaut? Wieviel speicherplatz steht mir in der Cloud zur verfuegungWie genau ist die Cloud aufgebaut? Wieviel speicherplatz steht mir in der Cloud zur verfuegungWie genau ist die Cloud aufgebaut? Wieviel speicherplatz steht mir in der Cloud zur verfuegung',
-      ]
-    },
-    {
-      keywordID: 2,
-      keyword: 'SWT',
-      questions: [
-        'Muss man fuer das Modul SWT bestanden haben?'
-      ]
-    },
-    {
-      keywordID: 3,
-      keyword: 'Frage',
-      questions: [
-        'Das ist eine Lange Frage mit dem Thema \'frage\'',
-        'Ich habe eine Frage, sind Fragen zum thema \'Frage\' auch erlaubt?',
-        'Ich wollte Fragen ob sie gerne Sachen gefragt werden',
-        'Langsam geht mir die Fragerei mit den ganzen Fragen auf den Geist Frage'
-      ]
-    },
-    {
-      keywordID: 4,
-      keyword: 'Klausur',
-      questions: [
-        'Darf man in der Klausur hilfmittel verwenden?',
-        'An welchem Termin findet die Klausur statt?'
-      ]
-    },
-    {
-      keywordID: 5,
-      keyword: 'Diskrete Math',
-      questions: [
-        'wann wird die nächste veranstaltung stattfinden?',
-        'gibt es heute übung?'
-      ]
-    },
-    {
-      keywordID: 6,
-      keyword: 'Arsch',
-      questions: [
-        'Das ist eine Testfrage fuer den Profanity Filter, du Arschloch',
-        'Englisch: Fuck you!',
-        'Deutsch: Fick dich!',
-        'Französisch: Gros con!',
-        'Türkisch: Orospu çocuğu!',
-        'Arabisch: عاهرة!',
-        'Russisch: Муда!',
-        'Multi language: Ficken, Fuck, con',
-        'Custom: Nieder mit KQC'
-      ]
-    },
-  ];
+  allSelectedDE = true;
+  allSelectedEN = true;
+  spacyLabels: Labels;
+  wantedLabels: {
+    de: string[];
+    en: string[];
+  };
+
+  keywords: Keyword[] = [];
   private topicCloudAdminData: TopicCloudAdminData;
 
-  constructor(public cloudDialogRef: MatDialogRef<TagCloudComponent>,
+  constructor(
+    @Inject(MAT_DIALOG_DATA) public data: Data,
+    public cloudDialogRef: MatDialogRef<TopicCloudAdministrationComponent>,
     public confirmDialog: MatDialog,
     private notificationService: NotificationService,
-    private authenticationService: AuthenticationService,
     private translateService: TranslateService,
     private langService: LanguageService,
-    private topicCloudAdminService: TopicCloudAdminService) {
+    private topicCloudAdminService: TopicCloudAdminService,
+    private commentService: CommentService,
+    private wsCommentServiceService: WsCommentServiceService) {
       this.langService.langEmitter.subscribe(lang => {
         this.translateService.use(lang);
       });
     }
 
   ngOnInit(): void {
+    this.wsCommentServiceService.getCommentStream(localStorage.getItem('roomId')).subscribe(_ => this.initKeywords());
+    this.blacklistSubscription = this.topicCloudAdminService.getBlacklist().subscribe(list => this.blacklist = list);
+    this.isCreatorOrMod = this.data ? (this.data.user.role !== UserRole.PARTICIPANT) : true;
     this.translateService.use(localStorage.getItem('currentLang'));
-    this.checkIfUserIsModOrCreator();
-    this.checkIfThereAreQuestions();
-    this.sortQuestions();
+    this.spacyLabels = spacyLabels;
+    this.wantedLabels = undefined;
     this.setDefaultAdminData();
+    this.initKeywords();
   }
 
   ngOnDestroy(){
     this.setAdminData();
+    if(this.blacklistSubscription !== undefined){
+      this.blacklistSubscription.unsubscribe();
+    }
+  }
+
+  initKeywords(){
+    this.commentService.getFilteredComments(localStorage.getItem('roomId')).subscribe(comments => {
+      this.keywords = [];
+      comments.map(comment => {
+        const keywords = this.keywordORfulltext === KeywordOrFulltext[0] ? comment.keywordsFromQuestioner : comment.keywordsFromSpacy;
+        keywords.map(_keyword => {
+          const existingKey = this.checkIfKeywordExists(_keyword);
+          if (existingKey){
+            existingKey.vote += comment.score;
+            if (this.checkIfCommentExists(existingKey.comments, comment.id)){
+              existingKey.comments.push(comment);
+            }
+          } else {
+            const keyword: Keyword = {
+              keyword: _keyword,
+              comments: [comment],
+              vote: comment.score
+            };
+            this.keywords.push(keyword);
+          }
+        });
+      });
+      this.sortQuestions();
+    });
+  }
+
+  checkIfCommentExists(comments: Comment[], id: string): boolean{
+    return comments.filter(comment => comment.id === id).length === 0;
   }
 
-  setAdminData(){
+  setAdminData() {
     this.topicCloudAdminData = {
-      blacklist: this.topicCloudAdminService.getBlacklistWords(this.profanityFilter, this.blacklistIsActive),
+      blacklist: [],
+      wantedLabels: {
+        de: this.wantedLabels.de,
+        en: this.wantedLabels.en
+      },
       considerVotes: this.considerVotes,
       profanityFilter: this.profanityFilter,
       blacklistIsActive: this.blacklistIsActive,
@@ -136,12 +133,16 @@ export class TopicCloudAdministrationComponent implements OnInit, OnDestroy {
   }
 
   setDefaultAdminData() {
-    this.topicCloudAdminData = this.topicCloudAdminService.getAdminData;
+    this.topicCloudAdminData = this.topicCloudAdminService.getDefaultAdminData;
     if (this.topicCloudAdminData) {
       this.considerVotes = this.topicCloudAdminData.considerVotes;
       this.profanityFilter = this.topicCloudAdminData.profanityFilter;
       this.blacklistIsActive = this.topicCloudAdminData.blacklistIsActive;
       this.keywordORfulltext = KeywordOrFulltext[this.topicCloudAdminData.keywordORfulltext];
+      this.wantedLabels = {
+        de: this.topicCloudAdminData.wantedLabels.de,
+        en: this.topicCloudAdminData.wantedLabels.en
+      };
     }
   }
 
@@ -150,11 +151,7 @@ export class TopicCloudAdministrationComponent implements OnInit, OnDestroy {
   }
 
   getProfanityList() {
-    return this.topicCloudAdminService.getProfanityList();
-  }
-
-  getBlacklist() {
-    return this.topicCloudAdminService.getBlacklist();
+    return this.topicCloudAdminService.getCustomProfanityList();
   }
 
   sortQuestions(sortMode?: string) {
@@ -167,27 +164,22 @@ export class TopicCloudAdministrationComponent implements OnInit, OnDestroy {
         this.keywords.sort((a, b) => a.keyword.localeCompare(b.keyword));
         break;
       case 'questionsCount':
-        this.keywords.sort((a, b) => b.questions.length - a.questions.length);
+        this.keywords.sort((a, b) => b.comments.length - a.comments.length);
         break;
       case 'voteCount':
-        console.log('not implemented!, sorting with question count');
-        this.keywords.sort((a, b) => b.questions.length - a.questions.length);
+        this.keywords.sort((a, b) => b.vote - a.vote);
         break;
     }
   }
 
-  checkIfUserIsModOrCreator() {
-    this.isCreatorOrMod = this.authenticationService.getRole() === UserRole.CREATOR ||
-                          this.authenticationService.getRole() === UserRole.EDITING_MODERATOR ||
-                          this.authenticationService.getRole() === UserRole.EXECUTIVE_MODERATOR;
-  }
-
   checkIfThereAreQuestions() {
     if (this.keywords.length === 0){
-      this.translateService.get('topic-cloud-dialog.nokeyword-note').subscribe(msg => {
+      this.translateService.get('topic-cloud-dialog.no-keywords-note').subscribe(msg => {
         this.notificationService.show(msg);
       });
-      this.cloudDialogRef.close();
+      setTimeout(() => {
+        this.cloudDialogRef.close();
+      }, 0);
     }
   }
 
@@ -198,38 +190,68 @@ export class TopicCloudAdministrationComponent implements OnInit, OnDestroy {
     }, 0);
   }
 
-  deleteKeyword(key: Keyword): void{
-    this.keywords.map(keyword => {
-      if (keyword.keywordID === key.keywordID) {
-          this.keywords.splice(this.keywords.indexOf(keyword, 0), 1);
-      }
+  deleteKeyword(key: Keyword, message?: string): void{
+    key.comments.map(comment => {
+      const changes = new TSMap<string, any>();
+      let keywords = comment.keywordsFromQuestioner;
+      keywords.splice(keywords.indexOf(key.keyword, 0), 1);
+      changes.set('keywordsFromQuestioner', JSON.stringify(keywords));
+      keywords = comment.keywordsFromSpacy;
+      keywords.splice(keywords.indexOf(key.keyword, 0), 1);
+      changes.set('keywordsFromSpacy', JSON.stringify(keywords));
+      this.updateComment(comment, changes, message);
     });
-    if (this.keywords.length === 0) {
-      this.cloudDialogRef.close();
-    }
+
     if (this.searchMode === true){
-      /* update filtered array if it is searchmode */
       this.searchKeyword();
     }
   }
 
+  updateComment(updatedComment: Comment, changes: TSMap<string, any>, messageTranslate?: string){
+    this.commentService.patchComment(updatedComment, changes).subscribe(_ => {
+      if (messageTranslate){
+        this.translateService.get('topic-cloud-dialog.' + messageTranslate).subscribe(msg => {
+          this.notificationService.show(msg);
+        });
+      }
+    },
+      error => {
+        this.translateService.get('topic-cloud-dialog.changes-gone-wrong').subscribe(msg => {
+          this.notificationService.show(msg);
+        });
+    });
+  }
+
   cancelEdit(): void {
     this.edit = false;
     this.newKeyword = undefined;
   }
 
   confirmEdit(key: Keyword): void {
-    for (const keyword of this.keywords){
-      if (keyword.keywordID === key.keywordID) {
-        const key2 = this.checkIfKeywordExists(this.newKeyword.trim().toLowerCase());
-        if (key2){
-          this.openConfirmDialog('merge-message', 'merge', keyword, key2);
-        } else {
-          keyword.keyword = this.newKeyword.trim();
+    const key2 = this.checkIfKeywordExists(this.newKeyword);
+    if (key2){
+      this.openConfirmDialog('merge-message', 'merge', key, key2);
+    } else {
+      key.comments.map(comment => {
+        const changes = new TSMap<string, any>();
+        let keywords = comment.keywordsFromQuestioner;
+        for (let i = 0; i < keywords.length; i++){
+          if (keywords[i].toLowerCase() === key.keyword.toLowerCase()){
+            keywords[i] = this.newKeyword.trim();
+          }
         }
-        break;
-      }
+        changes.set('keywordsFromQuestioner', JSON.stringify(keywords));
+        keywords = comment.keywordsFromSpacy;
+        for (let i = 0; i < keywords.length; i++){
+          if (keywords[i].toLowerCase() === key.keyword.toLowerCase()){
+            keywords[i] = this.newKeyword.trim();
+          }
+        }
+        changes.set('keywordsFromSpacy', JSON.stringify(keywords));
+        this.updateComment(comment, changes, 'keyword-edit');
+      });
     }
+
     this.edit = false;
     this.newKeyword = undefined;
     this.sortQuestions();
@@ -246,7 +268,7 @@ export class TopicCloudAdministrationComponent implements OnInit, OnDestroy {
 
     confirmDialogRef.afterClosed().subscribe(result => {
       if (result === 'delete') {
-        this.deleteKeyword(keyword);
+        this.deleteKeyword(keyword, 'keyword-delete');
       } else if (result === 'merge') {
         this.mergeKeywords(keyword, mergeTarget);
       }
@@ -266,8 +288,17 @@ export class TopicCloudAdministrationComponent implements OnInit, OnDestroy {
 
   mergeKeywords(key1: Keyword, key2: Keyword) {
     if (key1 !== undefined && key2 !== undefined){
-      key1.questions.map(question => {
-        key2.questions.push(question);
+      key1.comments.map(comment => {
+        if (this.checkIfCommentExists(key2.comments, comment.id)){
+          const changes = new TSMap<string, any>();
+          let keywords = comment.keywordsFromQuestioner;
+          keywords.push(key2.keyword);
+          changes.set('keywordsFromQuestioner', JSON.stringify(keywords));
+          keywords = comment.keywordsFromSpacy;
+          keywords.push(key2.keyword);
+          changes.set('keywordsFromSpacy', JSON.stringify(keywords));
+          this.updateComment(comment, changes);
+        }
       });
       this.deleteKeyword(key1);
     }
@@ -275,7 +306,7 @@ export class TopicCloudAdministrationComponent implements OnInit, OnDestroy {
 
   checkIfKeywordExists(key: string): Keyword {
     for(const keyword of this.keywords){
-      if(keyword.keyword.toLowerCase() === key){
+      if(keyword.keyword.toLowerCase() === key.trim().toLowerCase()){
         return keyword;
       }
     }
@@ -297,7 +328,7 @@ export class TopicCloudAdministrationComponent implements OnInit, OnDestroy {
   }
 
   addBlacklistWord() {
-    this.topicCloudAdminService.addToBlacklistWordList(this.newBlacklistWord);
+    this.topicCloudAdminService.addWordToBlacklist(this.newBlacklistWord);
     this.newBlacklistWord = undefined;
     if (this.searchMode){
       this.searchKeyword();
@@ -312,13 +343,40 @@ export class TopicCloudAdministrationComponent implements OnInit, OnDestroy {
     this.topicCloudAdminService.removeWordFromBlacklist(word);
   }
 
-  refreshAllLists(){
+  refreshAllLists() {
     this.searchKeyword();
   }
+
+  selectAllDE() {
+    if (this.allSelectedDE) {
+      this.wantedLabels.de = []
+    } else {
+      this.wantedLabels.de = [];
+      this.spacyLabels.de.forEach(label => {
+        this.wantedLabels.de.push(label.tag);
+      });
+    }
+  }
+
+  selectAllEN() {
+    if (this.allSelectedEN) {
+      this.wantedLabels.en = [];
+      this.spacyLabels.en.forEach(label => {
+        this.wantedLabels.en.push(label.tag);
+      });
+    } else {
+      this.wantedLabels.en = []
+    }
+  }
 }
 
 interface Keyword {
-  keywordID: number;
   keyword: string;
-  questions: string[];
+  comments: Comment[];
+  vote: number;
 }
+
+export interface Data{
+  user: User;
+}
+
diff --git a/src/app/components/shared/_dialogs/topic-cloud-filter/topic-cloud-filter.component.html b/src/app/components/shared/_dialogs/topic-cloud-filter/topic-cloud-filter.component.html
index b01a4c381f7168427e04dec18bdec40dd125fca3..aca94ca9f9645153ea046c0973328fea0cc8ae4e 100644
--- a/src/app/components/shared/_dialogs/topic-cloud-filter/topic-cloud-filter.component.html
+++ b/src/app/components/shared/_dialogs/topic-cloud-filter/topic-cloud-filter.component.html
@@ -12,11 +12,11 @@
         <div class="elementIcons">
           <mat-icon [inline]="true">comment</mat-icon> {{allCommentsCount}} 
           <mat-icon [inline]="true">person</mat-icon> {{allCommentsUsers}}
-          <mat-icon svgIcon="comment_tag" class="comment_tag-icon"></mat-icon> {{allCommentsKeywords}}
+          <mat-icon svgIcon="hashtag" class="comment_tag-icon"></mat-icon> {{allCommentsKeywords}}
         </div>
       </div>
     </mat-radio-button>
-    <mat-radio-button checked="true" value="continueWithCurr">
+    <mat-radio-button checked="true" value="continueWithCurr" *ngIf="!disableCurrentFiltersOptions">
       <div class="elementRow">
         <div class="elementText">
           {{'content.continue-with-current-questions' | translate}}
@@ -24,7 +24,7 @@
         <div class="elementIcons">
           <mat-icon [inline]="true">comment</mat-icon> {{filteredCommentsCount}}
           <mat-icon [inline]="true">person</mat-icon> {{filteredCommentsUsers}}
-          <mat-icon svgIcon="comment_tag" class="comment_tag-icon"></mat-icon> {{filteredCommentsKeywords}}
+          <mat-icon svgIcon="hashtag" class="comment_tag-icon"></mat-icon> {{filteredCommentsKeywords}}
         </div>
       </div>
     </mat-radio-button>
diff --git a/src/app/components/shared/_dialogs/topic-cloud-filter/topic-cloud-filter.component.ts b/src/app/components/shared/_dialogs/topic-cloud-filter/topic-cloud-filter.component.ts
index 1fd01557fbe4c4e1e12024680b5cf91a4e7787a7..1947f59790d21fa08eb9d9084eebf891d00c0be1 100644
--- a/src/app/components/shared/_dialogs/topic-cloud-filter/topic-cloud-filter.component.ts
+++ b/src/app/components/shared/_dialogs/topic-cloud-filter/topic-cloud-filter.component.ts
@@ -39,6 +39,9 @@ export class TopicCloudFilterComponent implements OnInit {
   filteredCommentsUsers : number;
   filteredCommentsKeywords : number;
 
+  disableCurrentFiltersOptions : boolean = false;
+  commentsLoaded : boolean = false;
+
   constructor(public dialogRef: MatDialogRef<RoomCreatorPageComponent>,
               public dialog: MatDialog,
               public notificationService: NotificationService,
@@ -63,16 +66,32 @@ export class TopicCloudFilterComponent implements OnInit {
         this.allCommentsCount = counts.comments;
         this.allCommentsUsers = counts.users;
         this.allCommentsKeywords = counts.keywords;
+        this.commentsLoadedCallback();
       });
       this.commentService.getFilteredComments(room.id).subscribe(comments => {
         const counts = this.getCommentCounts(comments);
         this.filteredCommentsCount = counts.comments;
         this.filteredCommentsUsers = counts.users;
         this.filteredCommentsKeywords = counts.keywords;
-      });
+        this.commentsLoadedCallback();
+      });      
     });
   }
 
+  commentsLoadedCallback() {
+    if (!this.commentsLoaded) {
+      this.commentsLoaded = true;
+    } else {
+      this.disableCurrentFiltersOptions = ((this.allCommentsCount == this.filteredCommentsCount) &&
+                                          (this.allCommentsUsers == this.filteredCommentsUsers) && 
+                                          (this.allCommentsKeywords == this.filteredCommentsKeywords));
+
+      if (this.disableCurrentFiltersOptions) {
+        this.continueFilter = 'continueWithAll';
+      }
+    }
+  }
+
   closeDialog(): void {
   }
 
diff --git a/src/app/components/shared/comment-list/comment-list.component.ts b/src/app/components/shared/comment-list/comment-list.component.ts
index 2e25c141dbb9a8e7b2d708368cc82f26b369c115..9c3ef6cf85eca46ec4d7f812f56a19f6f8fdcb99 100644
--- a/src/app/components/shared/comment-list/comment-list.component.ts
+++ b/src/app/components/shared/comment-list/comment-list.component.ts
@@ -140,7 +140,7 @@ export class CommentListComponent implements OnInit, OnDestroy {
       dialogRef.componentInstance.roomId = this.room.id;
     });
     this.eventService.on<string>('setTagConfig').subscribe(tag => {
-      this.clickedOnTag(tag);
+      this.clickedOnKeyword(tag);
     });
     nav('tags', () => {
       const updRoom = JSON.parse(JSON.stringify(this.room));
@@ -236,14 +236,18 @@ export class CommentListComponent implements OnInit, OnDestroy {
             this.roomService.addToHistory(this.room.id);
             this.authenticationService.setAccess(this.shortId, UserRole.PARTICIPANT);
           }
-          this.getModeratorIds();
-          this.subscribeCommentStream();
-          this.commentService.getAckComments(this.room.id)
-            .subscribe(comments => {
-              this.comments = comments;
-              this.getComments();
-              this.eventService.broadcast('commentListCreated', null);
-            });
+          this.moderatorService.get(this.roomId).subscribe(list => {
+            this.moderatorIds = list.map(m => m.accountId);
+            this.moderatorIds.push(this.room.ownerId);
+
+            this.subscribeCommentStream();
+            this.commentService.getAckComments(this.room.id)
+              .subscribe(comments => {
+                this.comments = comments;
+                this.getComments();
+                this.eventService.broadcast('commentListCreated', null);
+              });
+          });
           /**
            if (this.userRole === UserRole.PARTICIPANT) {
             this.openCreateDialog();
@@ -279,13 +283,6 @@ export class CommentListComponent implements OnInit, OnDestroy {
     return filter;
   }
 
-  getModeratorIds() {
-    this.moderatorService.get(this.roomId).subscribe(list => {
-      this.moderatorIds = list.map(m => m.accountId);
-      this.moderatorIds.push(this.room.ownerId);
-    });
-  }
-
   ngOnDestroy() {
     if (!this.freeze && this.commentStream) {
       this.commentStream.unsubscribe();
@@ -369,6 +366,7 @@ export class CommentListComponent implements OnInit, OnDestroy {
         c.timestamp = payload.timestamp;
         c.tag = payload.tag;
         c.creatorId = payload.creatorId;
+        c.keywordsFromQuestioner = JSON.parse(payload.keywordsFromQuestioner);
         c.userNumber = this.commentService.hashCode(c.creatorId);
         this.commentService.getComment(c.id).subscribe(e => {
           c.number = e.number;
@@ -456,10 +454,12 @@ export class CommentListComponent implements OnInit, OnDestroy {
       this.filteredComments = this.commentsFilteredByTime;
       this.hideCommentsList = false;
       this.currentFilter = '';
+      this.selectedTag = '';
+      this.selectedKeyword = '';
       this.sortComments(this.currentSort);
+      CommentFilterOptions.writeFilterStatic(this.getCurrentFilter());
       return;
     }
-    console.log(compare);
     this.filteredComments = this.commentsFilteredByTime.filter(c => {
       switch (type) {
         case this.correct:
@@ -481,7 +481,7 @@ export class CommentListComponent implements OnInit, OnDestroy {
           return c.userNumber === compare;
         case this.keyword:
           this.selectedKeyword = compare;
-          return c.keywordsFromQuestioner != null ? c.keywordsFromQuestioner.includes(compare) : false;
+          return c.keywordsFromQuestioner != null && c.keywordsFromQuestioner.length > 0 ? c.keywordsFromQuestioner.includes(compare) : false;
         case this.answer:
           return c.answer;
         case this.unanswered:
diff --git a/src/app/components/shared/comment/comment.component.html b/src/app/components/shared/comment/comment.component.html
index eb3945740c95b6bf9670cb879d4c0647dd38145e..647ea69f0394223b9b5231cd8f3ae17c3de2f8cc 100644
--- a/src/app/components/shared/comment/comment.component.html
+++ b/src/app/components/shared/comment/comment.component.html
@@ -324,14 +324,14 @@
            [mat-menu-trigger-for]="keywordsMenu"
            *ngIf="(comment.keywordsFromQuestioner != undefined && comment.keywordsFromQuestioner.length > 0)"
            class="comment-keywords">
-        <mat-icon svgIcon="comment_tag"></mat-icon>
+        <mat-icon svgIcon="hashtag" class="keyword-icon"></mat-icon>
         <span>
           &nbsp;{{ this.selectedKeyword === '' ? ('comment-page.keywords' | translate ) : this.selectedKeyword}}
         </span>
         <mat-menu #keywordsMenu>
 
           <mat-list dense class="keywords-list">
-            <mat-list-item  *ngFor="let keyword of comment.keywordsFromQuestioner; let odd = odd; let even = even"
+            <mat-list-item *ngFor="let keyword of comment.keywordsFromQuestioner; let odd = odd; let even = even"
                             [class.keywords-alternate]="odd"
                             [class.keywords-even]="even">                  
                             <span (click)="this.clickedOnKeyword.emit(keyword)" class="keyword-span">{{keyword}}</span>
diff --git a/src/app/components/shared/comment/comment.component.scss b/src/app/components/shared/comment/comment.component.scss
index 921bc0a869dd690eb03022a9e30c7d856143d180..2f933346af8f6a1b33e24a35866f0c273593fd33 100644
--- a/src/app/components/shared/comment/comment.component.scss
+++ b/src/app/components/shared/comment/comment.component.scss
@@ -201,6 +201,10 @@ mat-card-content > :first-child {
   opacity: 1 !important;
 }
 
+.keyword-icon {
+  height: 18px !important;
+}
+
 .user-number {
   cursor: pointer;
   color: var(--on-surface);
diff --git a/src/app/components/shared/comment/comment.component.ts b/src/app/components/shared/comment/comment.component.ts
index 9e316cf823c1665625ed416eb3a20b7cd41cf157..768371bf740d8c98d3e7b4ed3fd4f2b7dbf74970 100644
--- a/src/app/components/shared/comment/comment.component.ts
+++ b/src/app/components/shared/comment/comment.component.ts
@@ -18,6 +18,7 @@ import { UserRole } from '../../../models/user-roles.enum';
 import { Rescale } from '../../../models/rescale';
 import { RowComponent } from '../../../../../projects/ars/src/lib/components/layout/frame/row/row.component';
 import { User } from '../../../models/user';
+import { Observable } from 'rxjs';
 
 @Component({
   selector: 'app-comment',
@@ -130,7 +131,10 @@ export class CommentComponent implements OnInit, AfterViewInit {
   }
 
   setRead(comment: Comment): void {
-    this.commentService.toggleRead(comment).subscribe(c => this.comment = c);
+      // @ts-ignore
+
+    this.commentService.toggleRead(comment).subscribe(c => {this.comment = c; this.comment.keywordsFromQuestioner = JSON.parse(c.keywordsFromQuestioner)});
+    // @ts-ignore
   }
 
   markCorrect(comment: Comment, type: CorrectWrong): void {
@@ -139,11 +143,16 @@ export class CommentComponent implements OnInit, AfterViewInit {
       } else {
         comment.correct = type;
       }
-    this.commentService.markCorrect(comment).subscribe(c => this.comment = c);
+    // @ts-ignore
+    this.commentService.markCorrect(comment).subscribe(c => {this.comment = c; this.comment.keywordsFromQuestioner = JSON.parse(c.keywordsFromQuestioner)});
+   // @ts-ignore
   }
 
+
   setFavorite(comment: Comment): void {
-    this.commentService.toggleFavorite(comment).subscribe(c => this.comment = c);
+      // @ts-ignore
+    this.commentService.toggleFavorite(comment).subscribe(c => {this.comment = c; this.comment.keywordsFromQuestioner = JSON.parse(c.keywordsFromQuestioner)});
+      // @ts-ignore
   }
 
   voteUp(comment: Comment): void {
@@ -210,11 +219,15 @@ export class CommentComponent implements OnInit, AfterViewInit {
   }
 
   setAck(comment: Comment): void {
-    this.commentService.toggleAck(comment).subscribe(c => this.comment = c);
+    //@ts-ignore
+    this.commentService.toggleAck(comment).subscribe(c => {this.comment = c; this.comment.keywordsFromQuestioner = JSON.parse(c.keywordsFromQuestioner)});
+    //@ts-ignore
   }
 
   setBookmark(comment: Comment): void {
-    this.commentService.toggleBookmark(comment).subscribe(c => this.comment = c);
+    //@ts-ignore
+    this.commentService.toggleBookmark(comment).subscribe(c => {this.comment = c; this.comment.keywordsFromQuestioner = JSON.parse(c.keywordsFromQuestioner)});
+    //@ts-ignore  
   }
 
   goToFullScreen(element: Element): void {
diff --git a/src/app/components/shared/header/header.component.html b/src/app/components/shared/header/header.component.html
index aabc5d3cea8c901a47d013f5a39e27b728d2673c..d9537f9fbdd2b85e007feb6d143d01a42339938e 100644
--- a/src/app/components/shared/header/header.component.html
+++ b/src/app/components/shared/header/header.component.html
@@ -47,7 +47,7 @@
                 class="pseudo-button"
                 matRipple
                 matTooltip="{{'header.overview-keywords-tooltip' | translate}}">
-          <mat-icon svgIcon="comment_tag"
+          <mat-icon svgIcon="hashtag"
                     class="oldtypo-h2 comment_tag-icon"></mat-icon>
           <p style="margin-left: 0.25em;">{{commentsCountKeywords}}</p>
         </button>
diff --git a/src/app/components/shared/header/header.component.ts b/src/app/components/shared/header/header.component.ts
index 65d387666d2b9ac11a2935dfbf73971d1b319a73..c3842163ab6ec31498753ccb4abf41f0ae106357 100644
--- a/src/app/components/shared/header/header.component.ts
+++ b/src/app/components/shared/header/header.component.ts
@@ -23,7 +23,7 @@ import { MotdService } from '../../../services/http/motd.service';
 import { TopicCloudFilterComponent } from '../_dialogs/topic-cloud-filter/topic-cloud-filter.component';
 import { RoomService } from '../../../services/http/room.service';
 import { Room } from '../../../models/room';
-import { TagCloudMetaData } from '../tag-cloud/tag-cloud.data-manager';
+import { TagCloudMetaData } from '../../../services/util/tag-cloud-data.service';
 
 @Component({
   selector: 'app-header',
diff --git a/src/app/components/shared/shared.module.ts b/src/app/components/shared/shared.module.ts
index 0fe7715ac88a1eada7caf515b44614ca0fbeed45..9e1fa76e707e4fe232456bbe490910b5f1b96b51 100644
--- a/src/app/components/shared/shared.module.ts
+++ b/src/app/components/shared/shared.module.ts
@@ -36,6 +36,7 @@ import { TopicCloudAdministrationComponent } from './_dialogs/topic-cloud-admini
 import { TopicDialogCommentComponent } from './dialog/topic-dialog-comment/topic-dialog-comment.component';
 import { TopicCloudFilterComponent } from './_dialogs/topic-cloud-filter/topic-cloud-filter.component';
 import { SpacyDialogComponent } from './_dialogs/spacy-dialog/spacy-dialog.component';
+import { TagCloudPopUpComponent } from './tag-cloud/tag-cloud-pop-up/tag-cloud-pop-up.component';
 
 @NgModule({
   imports: [
@@ -77,7 +78,8 @@ import { SpacyDialogComponent } from './_dialogs/spacy-dialog/spacy-dialog.compo
     TopicCloudAdministrationComponent,
     TopicDialogCommentComponent,
     TopicCloudFilterComponent,
-    SpacyDialogComponent
+    SpacyDialogComponent,
+    TagCloudPopUpComponent
   ],
     exports: [
         RoomJoinComponent,
@@ -93,7 +95,8 @@ import { SpacyDialogComponent } from './_dialogs/spacy-dialog/spacy-dialog.compo
         CommentComponent,
         DialogActionButtonsComponent,
         UserBonusTokenComponent,
-        CloudConfigurationComponent
+        CloudConfigurationComponent,
+        TagCloudPopUpComponent
     ]
 })
 export class SharedModule {
diff --git a/src/app/components/shared/tag-cloud/tag-cloud-pop-up/tag-cloud-pop-up.component.html b/src/app/components/shared/tag-cloud/tag-cloud-pop-up/tag-cloud-pop-up.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..65e367adb959663a8e22fe6966624e26368b9c44
--- /dev/null
+++ b/src/app/components/shared/tag-cloud/tag-cloud-pop-up/tag-cloud-pop-up.component.html
@@ -0,0 +1,39 @@
+<div #popupContainer
+     class="popupContainer"
+     (focusout)="onFocus($event)"
+      tabindex="0">
+  <span>
+    <mat-icon matTooltip="{{'tag-cloud.overview-question-topic-tooltip' | translate}}">comment</mat-icon>
+    <p>
+      {{tagData && tagData.comments.length}}
+    </p>
+    <mat-icon matTooltip="{{'tag-cloud.overview-questioners-topic-tooltip' | translate}}">person</mat-icon>
+    <p>
+      {{tagData && tagData.distinctUsers.size}}
+    </p>
+    <mat-icon matTooltip="{{'tag-cloud.upvote-topic' | translate}}">thumb_up</mat-icon>
+    <p>
+      {{tagData && tagData.cachedUpVotes}}
+    </p>
+    <mat-icon matTooltip="{{'tag-cloud.downvote-topic' | translate}}">thumb_down</mat-icon>
+    <p>
+      {{tagData && tagData.cachedDownVotes}}
+    </p>
+    <button *ngIf="user && user.role >= 1" mat-button (click)="addBlacklistWord()">
+      <mat-icon matTooltip="{{'tag-cloud.blacklist-topic' | translate}}">gavel</mat-icon>
+    </button>
+  </span>
+  <br>
+  <span>
+    <mat-icon matTooltip="{{'tag-cloud.period-since-first-comment' | translate}}">date_range</mat-icon>
+    <p>
+      {{timePeriodText}}
+    </p>
+  </span>
+  <div *ngIf="categories && categories.length">
+    <p>Kategorien:</p>
+    <ul>
+      <li *ngFor="let category of categories">{{category}}</li>
+    </ul>
+  </div>
+</div>
diff --git a/src/app/components/shared/tag-cloud/tag-cloud-pop-up/tag-cloud-pop-up.component.scss b/src/app/components/shared/tag-cloud/tag-cloud-pop-up/tag-cloud-pop-up.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..057e27b57a667a064649da4332cadcb7b5a40733
--- /dev/null
+++ b/src/app/components/shared/tag-cloud/tag-cloud-pop-up/tag-cloud-pop-up.component.scss
@@ -0,0 +1,118 @@
+$popup-arrow-size: 20px;
+$popup-arrow-border-size: 2px;
+$popup-arrow-half-size: $popup-arrow-size / 2;
+$popup-arrow-offset: $popup-arrow-half-size + $popup-arrow-border-size;
+$header-size: 67px;
+
+.popupContainer {
+  visibility: hidden;
+  border-radius: 25px;
+  border: 2px solid #000;
+  background-color: var(--dialog);
+  padding: $popup-arrow-half-size;
+  position: absolute;
+  box-shadow: 0 0 10px var(--dialog);
+  box-sizing: border-box;
+  z-index: 3;
+  color: var(--on-dialog);
+  transform: translateY(-$header-size);
+
+  &:focus {
+    outline: none;
+  }
+
+  &::after {
+    position: absolute;
+    content: '';
+    width: $popup-arrow-size;
+    height: $popup-arrow-size;
+    background-color: var(--dialog);
+    border-top: 0 solid #000;
+    border-right: $popup-arrow-border-size solid #000;
+    border-left: 0 solid #000;
+    border-bottom: $popup-arrow-border-size solid #000;
+  }
+
+  &.down {
+    visibility: unset;
+    transform: translate(-50%, calc(-100% - #{$header-size + $popup-arrow-offset}));
+
+    &::after {
+      top: 100%;
+      left: 50%;
+      margin-top: -$popup-arrow-half-size;
+      margin-left: -$popup-arrow-half-size;
+      transform: rotate(45deg);
+    }
+  }
+
+  &.left {
+    visibility: unset;
+    transform: translate($popup-arrow-offset, calc(-50% - #{$header-size}));
+
+    &::after {
+      top: 50%;
+      right: 100%;
+      margin-top: -$popup-arrow-half-size;
+      margin-right: -$popup-arrow-half-size;
+      transform: rotate(135deg);
+    }
+  }
+
+  &.up {
+    visibility: unset;
+    transform: translate(-50%, $popup-arrow-offset - $header-size);
+
+    &::after {
+      bottom: 100%;
+      left: 50%;
+      margin-bottom: -$popup-arrow-half-size;
+      margin-left: -$popup-arrow-half-size;
+      transform: rotate(225deg);
+    }
+  }
+
+  &.right {
+    visibility: unset;
+    transform: translate(calc(-100% - #{$popup-arrow-offset}), calc(-50% - #{$header-size}));
+
+    &::after {
+      top: 50%;
+      left: 100%;
+      margin-top: -$popup-arrow-half-size;
+      margin-left: -$popup-arrow-half-size;
+      transform: rotate(315deg);
+    }
+  }
+}
+
+div > p {
+  margin-bottom: 1px;
+}
+
+span {
+  margin-right: 5px;
+
+  & > mat-icon {
+    margin: -1px 0px 0px 12px;
+    vertical-align: middle;
+  }
+
+  & > p {
+    display: inline;
+    font-weight: 600;
+    vertical-align: middle;
+  }
+}
+
+ul {
+  margin: 0;
+}
+
+button {
+  color: var(--red);
+  min-width: unset;
+  margin-left: 0;
+  padding-left: 10px;
+  padding-right: 10px;
+}
diff --git a/src/app/components/shared/tag-cloud/tag-cloud-pop-up/tag-cloud-pop-up.component.spec.ts b/src/app/components/shared/tag-cloud/tag-cloud-pop-up/tag-cloud-pop-up.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..be4c0a85dfd377daf9509432ffd793ae52fed9fe
--- /dev/null
+++ b/src/app/components/shared/tag-cloud/tag-cloud-pop-up/tag-cloud-pop-up.component.spec.ts
@@ -0,0 +1,26 @@
+/*import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TagCloudPopUpComponent } from './tag-cloud-pop-up.component';
+
+describe('TagCloudPopUpComponent', () => {
+  let component: TagCloudPopUpComponent;
+  let fixture: ComponentFixture<TagCloudPopUpComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ TagCloudPopUpComponent ]
+    })
+    .compileComponents();
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(TagCloudPopUpComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
+*/
diff --git a/src/app/components/shared/tag-cloud/tag-cloud-pop-up/tag-cloud-pop-up.component.ts b/src/app/components/shared/tag-cloud/tag-cloud-pop-up/tag-cloud-pop-up.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2b3210c4330c47da7851821ffbf8b9e268d176f0
--- /dev/null
+++ b/src/app/components/shared/tag-cloud/tag-cloud-pop-up/tag-cloud-pop-up.component.ts
@@ -0,0 +1,233 @@
+import { AfterViewInit, Component, ElementRef, Input, OnInit, ViewChild } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+import { LanguageService } from '../../../../services/util/language.service';
+import { TagCloudComponent } from '../tag-cloud.component';
+import { AuthenticationService } from '../../../../services/http/authentication.service';
+import { User } from '../../../../models/user';
+import { TagCloudDataTagEntry } from '../../../../services/util/tag-cloud-data.service';
+
+const CLOSE_TIME = 1500;
+
+@Component({
+  selector: 'app-tag-cloud-pop-up',
+  templateUrl: './tag-cloud-pop-up.component.html',
+  styleUrls: ['./tag-cloud-pop-up.component.scss']
+})
+export class TagCloudPopUpComponent implements OnInit, AfterViewInit {
+
+  @Input() parent: TagCloudComponent;
+  @ViewChild('popupContainer') popupContainer: ElementRef;
+  tag: string;
+  tagData: TagCloudDataTagEntry;
+  categories: string[];
+  timePeriodText: string;
+  user: User;
+  private _popupHoverTimer: number;
+  private _popupCloseTimer: number;
+
+  constructor(private langService: LanguageService,
+              private translateService: TranslateService,
+              private authenticationService: AuthenticationService) {
+    this.langService.langEmitter.subscribe(lang => {
+      this.translateService.use(lang);
+    });
+  }
+
+  ngOnInit(): void {
+    this.timePeriodText = '...';
+    this.authenticationService.watchUser.subscribe(newUser => {
+      if (newUser) {
+        this.user = newUser;
+      }
+    });
+  }
+
+  ngAfterViewInit() {
+    const html = this.popupContainer.nativeElement as HTMLDivElement;
+    html.addEventListener('mouseenter', () => {
+      clearTimeout(this._popupCloseTimer);
+    });
+    html.addEventListener('mouseleave', () => {
+      this._popupCloseTimer = setTimeout(() => {
+        this.close();
+      }, CLOSE_TIME);
+    });
+  }
+
+  onFocus(event) {
+    if (!this.popupContainer.nativeElement.contains(event.target)) {
+      this.close();
+    }
+  }
+
+  leave(): void {
+    clearTimeout(this._popupHoverTimer);
+    this._popupCloseTimer = setTimeout(() => {
+      this.close();
+    }, CLOSE_TIME);
+  }
+
+  enter(elem: HTMLElement, tag: string, tagData: TagCloudDataTagEntry, hoverDelayInMs: number): void {
+    clearTimeout(this._popupCloseTimer);
+    clearTimeout(this._popupHoverTimer);
+    this._popupHoverTimer = setTimeout(() => {
+      this.tag = tag;
+      this.tagData = tagData;
+      this.categories = Array.from(tagData.categories.keys());
+      this.calculateDateText(() => {
+        this.position(elem);
+      });
+    }, hoverDelayInMs);
+  }
+
+  addBlacklistWord(): void {
+    this.parent.dataManager.blockWord(this.tag);
+    this.close();
+  }
+
+  close(): void {
+    const html = this.popupContainer.nativeElement as HTMLDivElement;
+    html.classList.remove('up', 'down', 'right', 'left');
+  }
+
+  private position(elem: HTMLElement) {
+    const html = this.popupContainer.nativeElement as HTMLDivElement;
+    const popup = html.getBoundingClientRect();
+    const tag = elem.getBoundingClientRect();
+    const boundingBox = elem.parentElement.getBoundingClientRect();
+    // calculate the free space to the left, right, top and bottom from tag
+    const spaceLeft = tag.x + tag.width / 2;
+    const spaceRight = boundingBox.right - tag.right + tag.width / 2;
+    const spaceTop = tag.y - boundingBox.y;
+    const spaceBottom = boundingBox.bottom - tag.bottom;
+    // set flags if tag is near bounding box
+    const isLeft = spaceLeft <= popup.width / 2.0;
+    const isRight = spaceRight <= popup.width / 2.0;
+    const isTop = spaceTop <= popup.height;
+    const isBottom = spaceBottom <= popup.height;
+
+    // try to make a decision where to place the popup outgoing from tag with checks if we are at a border of the viewport
+    enum PopupPosition {
+      top,
+      bottom,
+      left,
+      right
+    }
+
+    let dockingPosition;
+    if (isLeft && isTop && !isBottom && !isRight) {
+      dockingPosition = PopupPosition.right;
+    } else if (isTop && !isLeft && !isRight && !isBottom) {
+      dockingPosition = PopupPosition.bottom;
+    } else if (isRight && isTop && !isLeft && !isBottom) {
+      dockingPosition = PopupPosition.left;
+    } else if (isLeft && !isTop && !isRight && !isBottom) {
+      dockingPosition = PopupPosition.right;
+    } else if (!isLeft && !isTop && !isRight && !isBottom) {
+      // default docking when all sides offer enough space
+      dockingPosition = PopupPosition.top;
+    } else if (isRight && !isTop && !isLeft && !isBottom) {
+      dockingPosition = PopupPosition.left;
+    } else if (isLeft && isBottom && !isTop && !isRight) {
+      dockingPosition = PopupPosition.right;
+    } else if (!isLeft && isBottom && !isTop && !isRight) {
+      dockingPosition = PopupPosition.top;
+    } else if (!isLeft && isBottom && isTop && !isRight) {
+      dockingPosition = PopupPosition.left;
+    } else {
+      /*
+       * Find solution for small screens when all sides produce unpleasant results
+       */
+      dockingPosition = PopupPosition.top;
+    }
+    html.classList.remove('left', 'right', 'up', 'down');
+    if (dockingPosition === PopupPosition.bottom) {
+      html.style.top = tag.bottom + 'px';
+      html.style.left = tag.x + tag.width / 2 + 'px';
+      html.classList.add('up');
+    } else if (dockingPosition === PopupPosition.top) {
+      html.style.top = tag.y + 'px';
+      html.style.left = tag.x + tag.width / 2 + 'px';
+      html.classList.add('down');
+    } else if (dockingPosition === PopupPosition.left) {
+      html.style.top = tag.top + tag.height / 2 + 'px';
+      html.style.left = tag.x + 'px';
+      html.classList.add('right');
+    } else if (dockingPosition === PopupPosition.right) {
+      html.style.top = tag.top + tag.height / 2 + 'px';
+      html.style.left = tag.right + 'px';
+      html.classList.add('left');
+    }
+    html.focus();
+  }
+
+  private calculateDateText(afterInit: () => void): void {
+    const subscriber = (e: string) => {
+      this.timePeriodText = e;
+      if (afterInit) {
+        setTimeout(afterInit);
+      }
+    };
+    // @ts-ignore
+    const diffMs = Date.now() - Date.parse(this.tagData.firstTimeStamp);
+    const seconds = Math.floor(diffMs / 1_000);
+    if (seconds < 60) {
+      // few seconds
+      this.translateService.get('tag-cloud-popup.few-seconds').subscribe(subscriber);
+      return;
+    }
+    const minutes = Math.floor(seconds / 60);
+    if (minutes < 5) {
+      // few minutes
+      this.translateService.get('tag-cloud-popup.few-minutes').subscribe(subscriber);
+      return;
+    } else if (minutes < 60) {
+      // x minutes
+      this.translateService.get('tag-cloud-popup.some-minutes', {
+        minutes
+      }).subscribe(subscriber);
+      return;
+    }
+    const hours = Math.floor(minutes / 60);
+    if (hours === 1) {
+      // 1 hour
+      this.translateService.get('tag-cloud-popup.one-hour').subscribe(subscriber);
+      return;
+    } else if (hours < 24) {
+      // x hours
+      this.translateService.get('tag-cloud-popup.some-hours', {
+        hours
+      }).subscribe(subscriber);
+      return;
+    }
+    const days = Math.floor(hours / 24);
+    if (days === 1) {
+      // 1 day
+      this.translateService.get('tag-cloud-popup.one-day').subscribe(subscriber);
+      return;
+    } else if (days < 7) {
+      // x days
+      this.translateService.get('tag-cloud-popup.some-days', {
+        days
+      }).subscribe(subscriber);
+      return;
+    }
+    const weeks = Math.floor(days / 7);
+    if (weeks === 1) {
+      // 1 week
+      this.translateService.get('tag-cloud-popup.one-week').subscribe(subscriber);
+      return;
+    } else if (weeks < 12) {
+      // x weeks
+      this.translateService.get('tag-cloud-popup.some-weeks', {
+        weeks
+      }).subscribe(subscriber);
+      return;
+    }
+    const months = Math.floor(weeks / 4);
+    // x months
+    this.translateService.get('tag-cloud-popup.some-months', {
+      months
+    }).subscribe(subscriber);
+  }
+}
diff --git a/src/app/components/shared/tag-cloud/tag-cloud.component.html b/src/app/components/shared/tag-cloud/tag-cloud.component.html
index 509971aa8bae17c92eb112e1b9e6ad0cf07c01fe..6f6afd01783495837b2d1ac015d6a7644755fc2c 100644
--- a/src/app/components/shared/tag-cloud/tag-cloud.component.html
+++ b/src/app/components/shared/tag-cloud/tag-cloud.component.html
@@ -5,13 +5,14 @@
     </mat-drawer>
     <mat-drawer-content>
       <ars-fill ars-flex-box>
+        <app-tag-cloud-pop-up [parent]="this"></app-tag-cloud-pop-up>
         <div [ngClass]="{'hidden': !isLoading}" fxLayout="row" fxLayoutAlign="center center" fxFill>
           <mat-progress-spinner *ngIf="isLoading" mode="indeterminate"></mat-progress-spinner>
         </div>
         <angular-tag-cloud
+          id="tagCloudComponent"
           class="spacyTagCloud"
           (window:resize)="onResize($event)"
-          (afterInit)="initTagCloud()"
           (clicked)="openTags($event)"
           [data]="data"
           [width]="options.width"
diff --git a/src/app/components/shared/tag-cloud/tag-cloud.component.scss b/src/app/components/shared/tag-cloud/tag-cloud.component.scss
index adfc5f5d2534942cfe624e704b87e16e83ba393d..7304a3c38ffadd188d72504dc7e86d20eef15cb3 100644
--- a/src/app/components/shared/tag-cloud/tag-cloud.component.scss
+++ b/src/app/components/shared/tag-cloud/tag-cloud.component.scss
@@ -1,3 +1,6 @@
+$header-size: 67px;
+$margin: 15px;
+
 .mat-drawer.mat-drawer-push {
   background-color: var(--background);
 }
@@ -7,16 +10,16 @@ mat-drawer {
 }
 
 ars-fill {
-  width: calc(100% - 30px);
-  height: calc(100% - 30px);
-  margin: 15px;
+  width: calc(100% - #{2 * $margin});
+  height: calc(100% - #{2 * $margin});
+  margin: $margin;
 }
 
 mat-drawer-container {
-  height: calc(100% - 67px);
+  height: calc(100% - #{$header-size});
   width: 100%;
   position: fixed;
-  margin-top: 67px;
+  margin-top: $header-size;
 }
 
 mat-drawer-content {
@@ -31,3 +34,12 @@ mat-drawer-content {
 ::ng-deep mat-progress-spinner circle {
   stroke: var(--on-background) !important;
 }
+
+app-tag-cloud-pop-up {
+  width: max-content;
+  height: max-content;
+}
+
+::ng-deep .spacyTagCloud span {
+  user-select: none !important;
+}
diff --git a/src/app/components/shared/tag-cloud/tag-cloud.component.ts b/src/app/components/shared/tag-cloud/tag-cloud.component.ts
index 74565ecbcdfea3530e546eb9ffd9743ec8139562..451e5bb905137f85500b9aa7824f08e4aef690dc 100644
--- a/src/app/components/shared/tag-cloud/tag-cloud.component.ts
+++ b/src/app/components/shared/tag-cloud/tag-cloud.component.ts
@@ -23,8 +23,10 @@ import { ThemeService } from '../../../../theme/theme.service';
 import { cloneParameters, CloudParameters, CloudTextStyle, CloudWeightSettings } from './tag-cloud.interface';
 import { TopicCloudAdministrationComponent } from '../_dialogs/topic-cloud-administration/topic-cloud-administration.component';
 import { WsCommentServiceService } from '../../../services/websockets/ws-comment-service.service';
-import { TagCloudDataManager } from './tag-cloud.data-manager';
 import { CreateCommentWrapper } from '../../../utils/CreateCommentWrapper';
+import { TopicCloudAdminService } from '../../../services/util/topic-cloud-admin.service';
+import { TagCloudPopUpComponent } from './tag-cloud-pop-up/tag-cloud-pop-up.component';
+import { TagCloudDataService, TagCloudDataTagEntry } from '../../../services/util/tag-cloud-data.service';
 
 class CustomPosition implements Position {
   left: number;
@@ -34,23 +36,26 @@ class CustomPosition implements Position {
               public relativeTop: number) {
   }
 
-  updatePosition(width: number, height: number, text: string, style: CSSStyleDeclaration) {
-    const offsetY = parseFloat(style.height) / 2;
-    const offsetX = parseFloat(style.width) / 2;
+  updatePosition(width: number, height: number, metrics: TextMetrics) {
+    const offsetY = (metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent) / 2;
+    const offsetX = metrics.width / 2;
     this.left = width * this.relativeLeft - offsetX;
     this.top = height * this.relativeTop - offsetY;
   }
 }
 
 class TagComment implements CloudData {
-  constructor(public color: string,
-              public external: boolean,
-              public link: string,
-              public position: Position,
+
+  constructor(public text: string,
               public rotate: number,
-              public text: string,
-              public tooltip: string,
-              public weight: number) {
+              public weight: number,
+              public tagData: TagCloudDataTagEntry,
+              public index: number,
+              public color: string = null,
+              public external: boolean = false,
+              public link: string = null,
+              public position: Position = null,
+              public tooltip: string = null) {
   }
 }
 
@@ -129,6 +134,7 @@ const getDefaultCloudParameters = (): CloudParameters => {
 export class TagCloudComponent implements OnInit, AfterViewInit, OnDestroy {
 
   @ViewChild(TCloudComponent, {static: false}) child: TCloudComponent;
+  @ViewChild(TagCloudPopUpComponent) popup: TagCloudPopUpComponent;
   @Input() user: User;
   @Input() roomId: string;
   room: Room;
@@ -157,10 +163,12 @@ export class TagCloudComponent implements OnInit, AfterViewInit, OnDestroy {
   //Subscriptions
   headerInterface = null;
   themeSubscription = null;
-  readonly dataManager: TagCloudDataManager;
   private _currentSettings: CloudParameters;
   private _createCommentWrapper: CreateCommentWrapper = null;
   private _subscriptionCommentlist = null;
+  private _calcCanvas: HTMLCanvasElement = null;
+  private _calcRenderContext: CanvasRenderingContext2D = null;
+  private _calcFont: string = null;
 
   constructor(private commentService: CommentService,
               private langService: LanguageService,
@@ -173,13 +181,16 @@ export class TagCloudComponent implements OnInit, AfterViewInit, OnDestroy {
               protected roomService: RoomService,
               private themeService: ThemeService,
               private wsCommentService: WsCommentServiceService,
-              private router: Router) {
+              private topicCloudAdmin: TopicCloudAdminService,
+              private router: Router,
+              public dataManager: TagCloudDataService) {
     this.roomId = localStorage.getItem('roomId');
     this.langService.langEmitter.subscribe(lang => {
       this.translateService.use(lang);
     });
-    this.dataManager = new TagCloudDataManager(wsCommentService, commentService);
     this._currentSettings = TagCloudComponent.getCurrentCloudParameters();
+    this._calcCanvas = document.createElement('canvas');
+    this._calcRenderContext = this._calcCanvas.getContext('2d');
   }
 
   private static getCurrentCloudParameters(): CloudParameters {
@@ -207,7 +218,10 @@ export class TagCloudComponent implements OnInit, AfterViewInit, OnDestroy {
       } else if (e === 'topicCloudAdministration') {
         this.dialog.open(TopicCloudAdministrationComponent, {
           minWidth: '50%',
-          maxHeight: '80%'
+          maxHeight: '80%',
+          data: {
+            user: this.user
+          }
         });
       }
     });
@@ -251,25 +265,20 @@ export class TagCloudComponent implements OnInit, AfterViewInit, OnDestroy {
 
   ngAfterViewInit() {
     document.getElementById('footer_rescale').style.display = 'none';
+    this._calcFont = window.getComputedStyle(document.getElementById('tagCloudComponent')).fontFamily;
+    this.dataManager.bindToRoom(this.roomId);
+    this.dataManager.updateDemoData(this.translateService);
+    this.setCloudParameters(TagCloudComponent.getCurrentCloudParameters(), false);
   }
 
   ngOnDestroy() {
     document.getElementById('footer_rescale').style.display = 'block';
     this.headerInterface.unsubscribe();
     this.themeSubscription.unsubscribe();
-    this.dataManager.deactivate();
-  }
-
-  initTagCloud() {
-    this.dataManager.activate(this.roomId);
-    this.dataManager.updateDemoData(this.translateService);
-    this.setCloudParameters(TagCloudComponent.getCurrentCloudParameters(), false);
-    setTimeout(() => {
-      this.redraw();
-    });
+    this.dataManager.unbindRoom();
   }
 
-  get tagCloudDataManager(): TagCloudDataManager {
+  get tagCloudDataManager(): TagCloudDataService {
     return this.dataManager;
   }
 
@@ -325,7 +334,7 @@ export class TagCloudComponent implements OnInit, AfterViewInit, OnDestroy {
           if (rotation === null || this._currentSettings.randomAngles) {
             rotation = Math.floor(Math.random() * 30 - 15);
           }
-          newElements.push(new TagComment(null, true, null, null, rotation, tag, 'TODO', tagData.weight));
+          newElements.push(new TagComment(tag, rotation, tagData.weight, tagData, newElements.length));
         }
       }
     }
@@ -339,6 +348,7 @@ export class TagCloudComponent implements OnInit, AfterViewInit, OnDestroy {
           newElements[i].position = new CustomPosition((k + 1) / (size + 1), (line + 1) / (lines + 1));
         }
       }
+      this.updateAlphabeticalPosition(newElements);
     }
     this.data = newElements;
     setTimeout(() => {
@@ -349,31 +359,23 @@ export class TagCloudComponent implements OnInit, AfterViewInit, OnDestroy {
   updateTagCloud(dataUpdated = false) {
     this.isLoading = true;
     if (this._currentSettings.sortAlphabetically && this.data.length) {
-      if (dataUpdated || !this.child.cloudDataHtmlElements || !this.child.cloudDataHtmlElements.length) {
-        this.child.reDraw();
-      }
-      const width = this.child.calculatedWidth;
-      const height = this.child.calculatedHeight;
-      this.data.forEach((e, i) => {
-        (e.position as CustomPosition).updatePosition(width, height, e.text,
-          window.getComputedStyle(this.child.cloudDataHtmlElements[i]));
-      });
+      this.updateAlphabeticalPosition(this.data);
     }
     const debounceTime = 1_000;
     const current = new Date().getTime();
     const diff = current - this.lastDebounceTime;
     if (diff >= debounceTime) {
-      this.redraw();
+      this.redraw(dataUpdated);
     } else {
       clearTimeout(this.debounceTimer);
       this.debounceTimer = setTimeout(() => {
-        this.redraw();
+        this.redraw(dataUpdated);
       }, debounceTime - diff);
     }
   }
 
   openTags(tag: CloudData): void {
-    if(this._subscriptionCommentlist !== null){
+    if (this._subscriptionCommentlist !== null) {
       return;
     }
     this._subscriptionCommentlist = this.eventService.on('commentListCreated').subscribe(() => {
@@ -384,17 +386,42 @@ export class TagCloudComponent implements OnInit, AfterViewInit, OnDestroy {
     this.router.navigate(['../'], {relativeTo: this.route});
   }
 
-  private redraw(): void {
+  private updateAlphabeticalPosition(elements: TagComment[]): void {
+    const sizes = new Array(10);
+    const fontRange = (this._currentSettings.fontSizeMax - this._currentSettings.fontSizeMin) / 10;
+    for (let i = 1; i <= 10; i++) {
+      sizes[i - 1] = (this._currentSettings.fontSizeMin + fontRange * i).toFixed(0) + '%';
+    }
+    const width = this.child.calculatedWidth;
+    const height = this.child.calculatedHeight;
+    elements.forEach((e, i) => {
+      this._calcRenderContext.font = sizes[e.tagData.adjustedWeight] + ' ' + this._calcFont;
+      (e.position as CustomPosition).updatePosition(width, height, this._calcRenderContext.measureText(e.text));
+    });
+  }
+
+  private redraw(dataUpdate: boolean): void {
     if (this.child === undefined) {
       return;
     }
     this.lastDebounceTime = new Date().getTime();
-    this.child.reDraw();
     this.isLoading = false;
+    if (!dataUpdate) {
+      this.child.reDraw();
+    }
     // This should fix the hover bug (scale was not turned off sometimes)
-    this.child.cloudDataHtmlElements.forEach(elem => {
+    if (this.dataManager.currentData === null) {
+      return;
+    }
+    this.child.cloudDataHtmlElements.forEach((elem, i) => {
+      const dataElement = this.data[i];
       elem.addEventListener('mouseleave', () => {
         elem.style.transform = elem.style.transform.replace(transformationScaleKiller, '').trim();
+        this.popup.leave();
+      });
+      elem.addEventListener('mouseenter', () => {
+        this.popup.enter(elem, dataElement.text, dataElement.tagData,
+          (this._currentSettings.hoverTime + this._currentSettings.hoverDelay) * 1_000);
       });
     });
   }
@@ -425,7 +452,8 @@ export class TagCloudComponent implements OnInit, AfterViewInit, OnDestroy {
         (this._currentSettings.fontSizeMin + fontRange * i).toFixed(0) + '%; }', rules.length);
     }
     customTagCloudStyles.sheet.insertRule('.spacyTagCloud > span:hover, .spacyTagCloud > span:hover > a { color: ' +
-      this._currentSettings.fontColor + '; }', rules.length);
+      this._currentSettings.fontColor + '; background-color: ' +
+      this._currentSettings.backgroundColor + '; }', rules.length);
     customTagCloudStyles.sheet.insertRule('.spacyTagCloudContainer { background-color: ' +
       this._currentSettings.backgroundColor + '; }', rules.length);
   }
diff --git a/src/app/models/comment.ts b/src/app/models/comment.ts
index 2c00741a2d8fcf731699596822cea5716a524ebc..c857181c08a6730ac351acfd3c5ecbce31b83d52 100644
--- a/src/app/models/comment.ts
+++ b/src/app/models/comment.ts
@@ -1,5 +1,4 @@
 import { CorrectWrong } from './correct-wrong.enum';
-import { ViewChild } from '@angular/core';
 
 export class Comment {
   id: string;
@@ -22,6 +21,8 @@ export class Comment {
   number: number;
   keywordsFromQuestioner: string[];
   keywordsFromSpacy: string[];
+  upvotes: number;
+  downvotes: number;
 
   constructor(roomId: string = '',
               creatorId: string = '',
@@ -39,7 +40,9 @@ export class Comment {
               answer: string = '',
               userNumber: number = 0,
               keywordsFromQuestioner: string[] = [],
-              keywordsFromSpacy: string[] = []) {
+              keywordsFromSpacy: string[] = [],
+              upvotes = 0,
+              downvotes = 0) {
     this.id = '';
     this.roomId = roomId;
     this.creatorId = creatorId;
@@ -59,5 +62,7 @@ export class Comment {
     this.userNumber = userNumber;
     this.keywordsFromQuestioner = keywordsFromQuestioner;
     this.keywordsFromSpacy = keywordsFromSpacy;
+    this.upvotes = upvotes;
+    this.downvotes = downvotes;
   }
 }
diff --git a/src/app/models/room.ts b/src/app/models/room.ts
index e83d37ce5b7980f6243cc482754bf1876d594211..1eaaa39ff1471e370993f4eaf2d317e8a86e953b 100644
--- a/src/app/models/room.ts
+++ b/src/app/models/room.ts
@@ -8,6 +8,7 @@ export class Room {
   abbreviation: string;
   name: string;
   description: string;
+  blacklist: string;
   closed: boolean;
   moderated: boolean;
   directSend: boolean;
@@ -20,6 +21,7 @@ export class Room {
     abbreviation: string = '',
     name: string = '',
     description: string = '',
+    blacklist: string = '[]',
     closed: boolean = false,
     moderated: boolean = true,
     directSend: boolean = true,
@@ -32,6 +34,7 @@ export class Room {
     this.abbreviation = abbreviation;
     this.name = name;
     this.description = description;
+    this.blacklist = blacklist;
     this.closed = closed;
     this.moderated = moderated;
     this.directSend = directSend;
diff --git a/src/app/services/http/comment.service.ts b/src/app/services/http/comment.service.ts
index a282312f83c9ea33d4fd7dda7c28a0db004a71f4..a5a1a0aeff10c0a40c0323305f19d6fc504c6689 100644
--- a/src/app/services/http/comment.service.ts
+++ b/src/app/services/http/comment.service.ts
@@ -72,7 +72,7 @@ export class CommentService extends BaseHttpService {
   getComment(commentId: string): Observable<Comment> {
     const connectionUrl = `${this.apiUrl.base}${this.apiUrl.comment}/${commentId}`;
     return this.http.get<Comment>(connectionUrl, httpOptions).pipe(
-      map(comment => this.parseUserNumber(comment)),
+      map(comment => this.parseComment(comment)),
       tap(_ => ''),
       catchError(this.handleError<Comment>('getComment'))
     );
@@ -87,9 +87,9 @@ export class CommentService extends BaseHttpService {
         keywordsFromSpacy: JSON.stringify(comment.keywordsFromSpacy),
         keywordsFromQuestioner: JSON.stringify(comment.keywordsFromQuestioner)
       }, httpOptions).pipe(
-        tap(_ => ''),
-        catchError(this.handleError<Comment>('addComment'))
-      );
+      tap(_ => ''),
+      catchError(this.handleError<Comment>('addComment'))
+    );
   }
 
   deleteComment(commentId: string): Observable<Comment> {
@@ -110,16 +110,7 @@ export class CommentService extends BaseHttpService {
       properties: { roomId: roomId, ack: true },
       externalFilters: {}
     }, httpOptions).pipe(
-      map(commentList => commentList.map(comment => {
-          const newComment = this.parseUserNumber(comment);
-          newComment.keywordsFromQuestioner =
-            // @ts-ignore
-            newComment.keywordsFromQuestioner ? JSON.parse(newComment.keywordsFromQuestioner as string) : null;
-          newComment.keywordsFromSpacy =
-            // @ts-ignore
-            newComment.keywordsFromSpacy ? JSON.parse(newComment.keywordsFromSpacy as string) : null;
-          return newComment;
-        })),
+      map(commentList => commentList.map(comment => this.parseComment(comment))),
       tap(_ => ''),
       catchError(this.handleError<Comment[]>('getComments', []))
     );
@@ -132,7 +123,7 @@ export class CommentService extends BaseHttpService {
       externalFilters: {}
     }, httpOptions).pipe(
       map(commentList => {
-        return commentList.map(comment => this.parseUserNumber(comment));
+        return commentList.map(comment => this.parseComment(comment));
       }),
       tap(_ => ''),
       catchError(this.handleError<Comment[]>('getComments', []))
@@ -146,7 +137,7 @@ export class CommentService extends BaseHttpService {
       externalFilters: {}
     }, httpOptions).pipe(
       map(commentList => {
-        return commentList.map(comment => this.parseUserNumber(comment));
+        return commentList.map(comment => this.parseComment(comment));
       }),
       tap(_ => ''),
       catchError(this.handleError<Comment[]>('getComments', []))
@@ -233,8 +224,11 @@ export class CommentService extends BaseHttpService {
   }
 
 
-  parseUserNumber(comment: Comment): Comment {
+  parseComment(comment: Comment): Comment {
     comment.userNumber = this.hashCode(comment.creatorId);
+    // make list out of string "array"
+    comment.keywordsFromQuestioner = comment.keywordsFromQuestioner ? JSON.parse(comment.keywordsFromQuestioner as unknown as string) : null;
+    comment.keywordsFromSpacy = comment.keywordsFromSpacy ? JSON.parse(comment.keywordsFromSpacy as unknown as string) : null;
     return comment;
   }
 
diff --git a/src/app/services/util/custom-icon.service.ts b/src/app/services/util/custom-icon.service.ts
index 4183d9e8f9ece9f6d5f258a063255af2f2c6c416..b66cc7777ed1d8d6b944e7880fbfe438c5181dff 100644
--- a/src/app/services/util/custom-icon.service.ts
+++ b/src/app/services/util/custom-icon.service.ts
@@ -9,6 +9,7 @@ export class CustomIconService {
     'beamer',
     'meeting_room',
     'comment_tag',
+    'hashtag',
     'qrcode'
   ];
 
diff --git a/src/app/services/util/tag-cloud-data.service.spec.ts b/src/app/services/util/tag-cloud-data.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ee5ff3e99990ea480d89a1d867eb3f7d1236aa89
--- /dev/null
+++ b/src/app/services/util/tag-cloud-data.service.spec.ts
@@ -0,0 +1,16 @@
+/*import { TestBed } from '@angular/core/testing';
+
+import { TagCloudDataService } from './tag-cloud-data.service';
+
+describe('TagCloudDataService', () => {
+  let service: TagCloudDataService;
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+    service = TestBed.inject(TagCloudDataService);
+  });
+
+  it('should be created', () => {
+    expect(service).toBeTruthy();
+  });
+});*/
diff --git a/src/app/components/shared/tag-cloud/tag-cloud.data-manager.ts b/src/app/services/util/tag-cloud-data.service.ts
similarity index 58%
rename from src/app/components/shared/tag-cloud/tag-cloud.data-manager.ts
rename to src/app/services/util/tag-cloud-data.service.ts
index 2c870737338f2f0263899efac73ffdd491369db3..48e81f9512fb43f10f050551c026c574dc538509 100644
--- a/src/app/components/shared/tag-cloud/tag-cloud.data-manager.ts
+++ b/src/app/services/util/tag-cloud-data.service.ts
@@ -1,14 +1,25 @@
-import { Comment } from '../../../models/comment';
-import { Observable, Subject } from 'rxjs';
-import { WsCommentServiceService } from '../../../services/websockets/ws-comment-service.service';
-import { CommentService } from '../../../services/http/comment.service';
-import { CloudParameters } from './tag-cloud.interface';
+import { Injectable } from '@angular/core';
+import { TopicCloudAdminData } from '../../components/shared/_dialogs/topic-cloud-administration/TopicCloudAdminData';
+import { Observable, Subject, Subscription } from 'rxjs';
+import { WsCommentServiceService } from '../websockets/ws-comment-service.service';
+import { CommentService } from '../http/comment.service';
+import { TopicCloudAdminService } from './topic-cloud-admin.service';
+import { CommentFilterOptions } from '../../utils/filter-options';
 import { TranslateService } from '@ngx-translate/core';
+import { CloudParameters } from '../../components/shared/tag-cloud/tag-cloud.interface';
+import { Comment } from '../../models/comment';
+import { CommentFilterUtils } from '../../utils/filter-comments';
+import { Message } from '@stomp/stompjs';
 
 export interface TagCloudDataTagEntry {
   weight: number;
   adjustedWeight: number;
   cachedVoteCount: number;
+  cachedUpVotes: number;
+  cachedDownVotes: number;
+  distinctUsers: Set<number>;
+  firstTimeStamp: Date;
+  categories: Set<string>;
   comments: Comment[];
 }
 
@@ -40,8 +51,8 @@ export type TagCloudMetaDataCount = [
 ];
 
 export enum TagCloudDataSupplyType {
-  fullText,
   keywords,
+  fullText,
   keywordsAndFullText
 }
 
@@ -51,7 +62,10 @@ export enum TagCloudCalcWeightType {
   byLengthAndVotes
 }
 
-export class TagCloudDataManager {
+@Injectable({
+  providedIn: 'root'
+})
+export class TagCloudDataService {
   private _isDemoActive: boolean;
   private _isAlphabeticallySorted: boolean;
   private _dataBus: Subject<TagCloudData>;
@@ -66,9 +80,14 @@ export class TagCloudDataManager {
   private _lastMetaData: TagCloudMetaData = null;
   private readonly _currentMetaData: TagCloudMetaData;
   private _demoData: TagCloudData = null;
+  private _adminData: TopicCloudAdminData = null;
+  private _currentBlacklist: string[] = [];
+  private _subscriptionAdminData: Subscription;
+  private _subscriptionBlacklist: Subscription;
 
   constructor(private _wsCommentService: WsCommentServiceService,
-              private _commentService: CommentService) {
+              private _commentService: CommentService,
+              private _tagCloudAdmin: TopicCloudAdminService) {
     this._isDemoActive = false;
     this._isAlphabeticallySorted = false;
     this._dataBus = new Subject<TagCloudData>();
@@ -88,25 +107,32 @@ export class TagCloudDataManager {
     });
   }
 
-  activate(roomId: string): void {
-    if (this._wsCommentSubscription !== null) {
-      console.error('Tag cloud data manager was already activated!');
-      return;
-    }
+  bindToRoom(roomId: string): void {
     this._roomId = roomId;
-    this.onUpdateData();
-    //TODO Optimize for special events => better performance
-    this._wsCommentSubscription = this._wsCommentService
-      .getCommentStream(this._roomId).subscribe(e => this.onUpdateData());
+    this._subscriptionAdminData = this._tagCloudAdmin.getAdminData.subscribe(adminData => {
+      this._adminData = adminData;
+      this._calcWeightType = this._adminData.considerVotes ? TagCloudCalcWeightType.byLengthAndVotes : TagCloudCalcWeightType.byLength;
+      this._supplyType = this._adminData.keywordORfulltext as unknown as TagCloudDataSupplyType;
+      this.rebuildTagData();
+    });
+    this._subscriptionBlacklist = this._tagCloudAdmin.getBlacklist().subscribe(blacklist => {
+      this._currentBlacklist = blacklist || [];
+      this.rebuildTagData();
+    });
+    this.fetchData();
+    if (!CommentFilterOptions.readFilter().paused) {
+      this._wsCommentSubscription = this._wsCommentService
+        .getCommentStream(this._roomId).subscribe(e => this.onMessage(e));
+    }
   }
 
-  deactivate(): void {
-    if (this._wsCommentSubscription === null) {
-      console.error('Tag cloud data manager was already deactivated!');
-      return;
+  unbindRoom(): void {
+    this._subscriptionBlacklist.unsubscribe();
+    this._subscriptionAdminData.unsubscribe();
+    if (this._wsCommentSubscription !== null) {
+      this._wsCommentSubscription.unsubscribe();
+      this._wsCommentSubscription = null;
     }
-    this._wsCommentSubscription.unsubscribe();
-    this._wsCommentSubscription = null;
   }
 
   updateDemoData(translate: TranslateService): void {
@@ -115,9 +141,14 @@ export class TagCloudDataManager {
       for (let i = 10; i >= 1; i--) {
         this._demoData.set(text.replace('%d', '' + i), {
           cachedVoteCount: 0,
+          cachedUpVotes: 0,
+          cachedDownVotes: 0,
           comments: [],
           weight: i,
-          adjustedWeight: i - 1
+          adjustedWeight: i - 1,
+          categories: new Set<string>(),
+          distinctUsers: new Set<number>(),
+          firstTimeStamp: new Date()
         });
       }
     });
@@ -182,6 +213,11 @@ export class TagCloudDataManager {
     return this._isAlphabeticallySorted;
   }
 
+  blockWord(tag: string): void {
+    this._tagCloudAdmin.addWordToBlacklist(tag.toLowerCase());
+    this.rebuildTagData();
+  }
+
   updateConfig(parameters: CloudParameters): boolean {
     if (parameters.sortAlphabetically !== this._isAlphabeticallySorted) {
       this._isAlphabeticallySorted = parameters.sortAlphabetically;
@@ -223,7 +259,7 @@ export class TagCloudDataManager {
     return this._lastFetchedData;
   }
 
-  private onUpdateData(): void {
+  private fetchData(): void {
     this._commentService.getFilteredComments(this._roomId).subscribe((comments: Comment[]) => {
       this._lastFetchedComments = comments;
       if (this._isDemoActive) {
@@ -247,6 +283,9 @@ export class TagCloudDataManager {
   }
 
   private rebuildTagData() {
+    if (!this._lastFetchedComments) {
+      return;
+    }
     const currentMeta = this._isDemoActive ? this._lastMetaData : this._currentMetaData;
     const data: TagCloudData = new Map<string, TagCloudDataTagEntry>();
     const users = new Set<number>();
@@ -263,13 +302,43 @@ export class TagCloudDataManager {
         keywords = [];
       }
       for (const keyword of keywords) {
-        //TODO Check spelling & check profanity
+        const lowerCaseKeyWord = keyword.toLowerCase();
+        let profanity = false;
+        for (const word of this._currentBlacklist) {
+          if (lowerCaseKeyWord.includes(word)) {
+            profanity = true;
+            break;
+          }
+        }
+        if (profanity) {
+          continue;
+        }
         let current = data.get(keyword);
         if (current === undefined) {
-          current = {cachedVoteCount: 0, comments: [], weight: 0, adjustedWeight: 0};
+          current = {
+            cachedVoteCount: 0,
+            cachedUpVotes: 0,
+            cachedDownVotes: 0,
+            comments: [],
+            weight: 0,
+            adjustedWeight: 0,
+            distinctUsers: new Set<number>(),
+            categories: new Set<string>(),
+            firstTimeStamp: comment.timestamp
+          };
           data.set(keyword, current);
         }
         current.cachedVoteCount += comment.score;
+        current.cachedUpVotes += comment.upvotes;
+        current.cachedDownVotes += comment.downvotes;
+        current.distinctUsers.add(comment.userNumber);
+        if (comment.tag) {
+          current.categories.add(comment.tag);
+        }
+        // @ts-ignore
+        if (current.firstTimeStamp - comment.timestamp > 0) {
+          current.firstTimeStamp = comment.timestamp;
+        }
         current.comments.push(comment);
       }
       users.add(comment.userNumber);
@@ -300,4 +369,60 @@ export class TagCloudDataManager {
     }
   }
 
+  private onMessage(message: Message): void {
+    const msg = JSON.parse(message.body);
+    const payload = msg.payload;
+    switch (msg.type) {
+      case 'CommentCreated':
+        this._commentService.getComment(payload.id).subscribe(c => {
+          if (CommentFilterUtils.checkComment(c)) {
+            this._lastFetchedComments.push(c);
+            this.rebuildTagData();
+          }
+        });
+        break;
+      case 'CommentPatched':
+        for (const comment of this._lastFetchedComments) {
+          if (payload.id === comment.id) {
+            let needRebuild = false;
+            for (const [key, value] of Object.entries(payload.changes)) {
+              switch (key) {
+                case 'score':
+                  comment.score = value as number;
+                  needRebuild = true;
+                  break;
+                case 'upvotes':
+                  comment.upvotes = value as number;
+                  needRebuild = true;
+                  break;
+                case 'downvotes':
+                  comment.downvotes = value as number;
+                  needRebuild = true;
+                  break;
+                case 'ack':
+                  const isNowAck = value as boolean;
+                  if (!isNowAck) {
+                    this._lastFetchedComments = this._lastFetchedComments.filter((el) => el.id !== payload.id);
+                  }
+                  needRebuild = true;
+                  break;
+                case 'tag':
+                  comment.tag = value as string;
+                  needRebuild = true;
+                  break;
+              }
+            }
+            if (needRebuild) {
+              this.rebuildTagData();
+            }
+            break;
+          }
+        }
+        break;
+      case 'CommentDeleted':
+        this._lastFetchedComments = this._lastFetchedComments.filter((el) => el.id !== payload.id);
+        this.rebuildTagData();
+        break;
+    }
+  }
 }
diff --git a/src/app/services/util/topic-cloud-admin.service.ts b/src/app/services/util/topic-cloud-admin.service.ts
index 401b26c6e01d5f684f0b305f03fbcd0abddda2d3..6fe470befa0fb87ad3aa1aab50fb33a888f56fcb 100644
--- a/src/app/services/util/topic-cloud-admin.service.ts
+++ b/src/app/services/util/topic-cloud-admin.service.ts
@@ -1,45 +1,49 @@
-import { stringify } from '@angular/compiler/src/util';
 import { Injectable } from '@angular/core';
 import * as BadWords from 'naughty-words';
 // eslint-disable-next-line max-len
-import { TopicCloudAdminData, KeywordOrFulltext } from '../../../app/components/shared/_dialogs/topic-cloud-administration/TopicCloudAdminData';
+import { TopicCloudAdminData, KeywordOrFulltext, Labels, spacyLabels } from '../../components/shared/_dialogs/topic-cloud-administration/TopicCloudAdminData';
+import { RoomService } from './../../services/http/room.service';
+import { Room } from '../../models/room';
+import { TranslateService } from '@ngx-translate/core';
+import { NotificationService } from './notification.service';
+import { Observable, Subject } from 'rxjs';
+
 @Injectable({
   providedIn: 'root',
 })
 export class TopicCloudAdminService {
-  private badWords = [];
+  private adminData: Subject<TopicCloudAdminData>;
+  private blacklist: Subject<string[]>;
   private profanityWords = [];
-  private blacklist = []; // should be stored in backend
-  private profanityKey = 'custom-Profanity-List';
-
-  constructor() {
-    this.badWords = BadWords;
+  private readonly profanityKey = 'custom-Profanity-List';
+  private readonly adminKey = 'Topic-Cloud-Admin-Data';
+  constructor(private roomService: RoomService,
+    private translateService: TranslateService,
+    private notificationService: NotificationService) {
+    this.blacklist = new Subject<string[]>();
+    this.adminData = new Subject<TopicCloudAdminData>();
     /* put all arrays of languages together */
-    this.profanityWords = this.badWords['en']
-      .concat(this.badWords['de'])
-      .concat(this.badWords['fr'])
-      .concat(this.badWords['ar'])
-      .concat(this.badWords['ru'])
-      .concat(this.badWords['tr']);
-  }
-
-  getBlacklistWords(profanityFilter: boolean, blacklistFilter: boolean) {
-    let words = [];
-    if (profanityFilter) {
-      // TODO: send only words that are contained in keywords
-      words = words.concat(this.profanityWords).concat(this.getProfanityList());
-    }
-    if (blacklistFilter && this.blacklist.length > 0) {
-        words = words.concat(this.blacklist);
-    }
-    return words;
+    this.profanityWords = BadWords['en']
+      .concat(BadWords['de'])
+      .concat(BadWords['fr'])
+      .concat(BadWords['ar'])
+      .concat(BadWords['ru'])
+      .concat(BadWords['tr']);
+  }
+
+  get getAdminData(): Observable<TopicCloudAdminData>{
+    return this.adminData.asObservable();
   }
 
-  get getAdminData(): TopicCloudAdminData {
-    let data = JSON.parse(localStorage.getItem('Topic-Cloud-Admin-Data'));
+  get getDefaultAdminData(): TopicCloudAdminData {
+    let data = JSON.parse(localStorage.getItem(this.adminKey));
     if (!data) {
       data = {
-        blacklist: this.profanityWords,
+        blacklist: [],
+        wantedLabels: {
+          de: this.getDefaultSpacyTagsDE(),
+          en: this.getDefaultSpacyTagsEN()
+        },
         considerVotes: false,
         profanityFilter: true,
         blacklistIsActive: false,
@@ -49,13 +53,25 @@ export class TopicCloudAdminService {
     return data;
   }
 
-  setAdminData(adminData: TopicCloudAdminData){
-    localStorage.setItem('Topic-Cloud-Admin-Data', JSON.stringify(adminData));
+  setAdminData(_adminData: TopicCloudAdminData) {
+    localStorage.setItem(this.adminKey, JSON.stringify(_adminData));
+    this.getBlacklist().subscribe(list => {
+      _adminData.blacklist = this.getCustomProfanityList().concat(list).concat(this.profanityWords);
+      this.adminData.next(_adminData);
+    });
+  }
+
+  getBlacklist(): Observable<string[]> {
+    // TODO: add watcher for another moderators
+    this.getRoom().subscribe(room => {
+      this.blacklist.next(JSON.parse(room.blacklist));
+    });
+    return this.blacklist.asObservable();
   }
 
   filterProfanityWords(str: string): string {
     let questionWithProfanity = str;
-    this.profanityWords.concat(this.getProfanityList()).map((word) => {
+    this.profanityWords.concat(this.getCustomProfanityList()).map((word) => {
       questionWithProfanity = questionWithProfanity
         .toLowerCase()
         .includes(word.toLowerCase())
@@ -69,15 +85,15 @@ export class TopicCloudAdminService {
     return questionWithProfanity;
   }
 
-  getProfanityList(): string[] {
+  getCustomProfanityList(): string[] {
     const list = localStorage.getItem(this.profanityKey);
     return list ? list.split(',') : [];
   }
 
   addToProfanityList(word: string) {
     if (word !== undefined) {
-      const newList = this.getProfanityList();
-      if (newList.includes(word)){
+      const newList = this.getCustomProfanityList();
+      if (newList.includes(word)) {
         return;
       }
       newList.push(word);
@@ -86,33 +102,80 @@ export class TopicCloudAdminService {
   }
 
   removeFromProfanityList(profanityWord: string) {
-    const list = this.getProfanityList();
+    const list = this.getCustomProfanityList();
     list.map(word => {
-      if (word === profanityWord){
+      if (word === profanityWord) {
         list.splice(list.indexOf(word, 0), 1);
       }
     });
     localStorage.setItem(this.profanityKey, list.toString());
   }
 
-  removeProfanityList(){
+  removeProfanityList() {
     localStorage.removeItem(this.profanityKey);
   }
 
-  getBlacklist(): string[] {
-    return this.blacklist;
+  getRoom(): Observable<Room> {
+    return this.roomService.getRoom(localStorage.getItem('roomId'));
   }
 
-  addToBlacklistWordList(word: string) {
+  addWordToBlacklist(word: string) {
     if (word !== undefined) {
-      this.blacklist.push(word);
+      this.getRoom().subscribe(room => {
+        const newlist = JSON.parse(room.blacklist);
+        newlist.push(word);
+        this.updateBlacklist(newlist, room);
+      });
     }
   }
 
   removeWordFromBlacklist(word: string) {
-    this.blacklist.splice(this.blacklist.indexOf(word), 1);
+    if (word !== undefined) {
+      this.getRoom().subscribe(room => {
+        if (room.blacklist.length > 0){
+          const newlist = JSON.parse(room.blacklist);
+          newlist.splice(newlist.indexOf(word, 0), 1);
+          this.updateBlacklist(newlist, room);
+        }
+      });
+    }
+  }
+
+  updateBlacklist(list: string[], room: Room){
+    room.blacklist = JSON.stringify(list);
+    this.updateRoom(room);
+  }
+
+  updateRoom(updatedRoom: Room){
+    this.roomService.updateRoom(updatedRoom).subscribe(_ => {
+      this.translateService.get('topic-cloud.changes-successful').subscribe(msg => {
+        this.notificationService.show(msg);
+        /* update blacklist for subscribers */
+        this.blacklist.next(JSON.parse(updatedRoom.blacklist));
+      });
+    },
+      error => {
+        this.translateService.get('topic-cloud.changes-gone-wrong').subscribe(msg => {
+          this.notificationService.show(msg);
+        });
+    });
   }
 
+  getDefaultSpacyTagsDE(): string[] {
+    let tags: string[] = [];
+    spacyLabels.de.forEach(label => {
+      tags.push(label.tag);
+    });
+    return tags;
+  }
+
+  getDefaultSpacyTagsEN(): string[] {
+    let tags: string[] = [];
+    spacyLabels.en.forEach(label => {
+      tags.push(label.tag);
+    });
+    return tags;
+  }
 
   private replaceString(str: string, search: string, replace: string) {
     return str.split(search).join(replace);
diff --git a/src/assets/i18n/creator/de.json b/src/assets/i18n/creator/de.json
index de34755b800fb0d5195cd285df3b53f7c4c05aff..194f3250feb4f5931838c6288c274aad0d25f97e 100644
--- a/src/assets/i18n/creator/de.json
+++ b/src/assets/i18n/creator/de.json
@@ -93,7 +93,8 @@
     "select-all-hint": "Alle Stichwörter auswählen",
     "select-keyword-hint": "Dieses Stickwort auswählen",
     "edit-keyword-hint": "Stichwort editieren",
-    "editing-done-hint": "Editierung abschliessen"
+    "editing-done-hint": "Editierung abschliessen",
+    "force-language-selection": "Bitte wähle eine Sprache aus."
   },
   "comment-page": {
     "a11y-comment_delete": "Löscht diese Frage",
@@ -369,8 +370,8 @@
     "hover-time-tooltip": "Festlegen der Erscheinungszeit der Wörter",
     "hover-delay-tooltip": "Hover-Verzögerung der Wörter",
 
-    "weight-class-settings": "Gewichtsklassen Einstellungen",
-    "weight-class": "Gewichtsklasse",
+    "weight-class-settings": "Wichtigkeitsklasse Einstellungen",
+    "weight-class": "Wichtigkeitsklasse",
     "back-btn": "Zurück",
     "weight-color": "Schriftfarbe",
     "weight-number": "max. Anzahl Schlüsselwörter",
@@ -388,9 +389,8 @@
     "automatic-spelling-tooltip": "automatische Überprüfung der Rechtschreibung",
     "notation-tooltip": "Einstellung der Schreibweise: klein, groß, standard",
     "alphabetical-sorting-tooltip": "Alphabetische Sortierung",
-    "rotation-tooltip": "Einige Einträge zufällig um x Grad drehen",
     "highestWeight-tooltip": "x Tags mit der höchsten Gewichtung anzeigen",
-    "rotate-weight": "Einige Einträge dieser Gewichtsklasse zufällig um x Grad drehen",
-    "rotate-weight-tooltip": "Einige zufällig ausgewählte Einträge um diesen Winkel drehen"
+    "rotate-weight": "Einige Einträge dieser Klasse zufällig um x Grad drehen",
+    "rotate-weight-tooltip": "einige Einträge dieser Wichtigkeitsklassen zufällig um x Grad drehen"
   }
 }
diff --git a/src/assets/i18n/creator/en.json b/src/assets/i18n/creator/en.json
index 4a5d7b843e8460691dd3eb44eca9ddbcdadb5a73..55c278f278981ceb9c6057848f915e32124b8ab3 100644
--- a/src/assets/i18n/creator/en.json
+++ b/src/assets/i18n/creator/en.json
@@ -94,7 +94,8 @@
     "select-all-hint": "Select all keywords",
     "select-keyword-hint": "Select this keyword",
     "edit-keyword-hint": "Edit keyword",
-    "editing-done-hint": "Finish editing"
+    "editing-done-hint": "Finish editing",
+    "force-language-selection": "Please select a language."
   },
   "comment-page": {
     "a11y-comment_delete": "Deletes this question",
@@ -370,13 +371,13 @@
     "hover-time-tooltip": "Select hover-time",
     "hover-delay-tooltip": "Select hover-delay",
 
-    "weight-class-settings": "Weight-classes settings",
+    "weight-class-settings": "Weight-class Settings",
     "weight-class": "Weight class",
     "back-btn": "Back",
     "weight-color": "Font color",
     "weight-number": "max. number of keywords",
-    "weight-color-tooltip": "select font-color",
-    "weight-number-tooltip": "select maximal number of keywords",
+    "weight-color-tooltip": "Select font-color",
+    "weight-number-tooltip": "Select maximal number of keywords",
     "automatic-spelling": "Automatic spelling",
     "notation": "Notation:",
     "lowerCase": "Lower case",
@@ -389,9 +390,8 @@
     "automatic-spelling-tooltip": "automatic spelling check",
     "notation-tooltip": "Notation-Settings: small, large, standard",
     "alphabetical-sorting-tooltip": "Alphabetical sorting",
-    "rotation-tooltip": "rotate some entries randomly by x degrees",
+    "rotate-weight-tooltip": "Rotate some entries randomly by x degrees",
     "highestWeight-tooltip": "show x tags with the highest weight",
-    "rotate-weight": "Rotate some entries of this weight class randomly by x degrees",
-    "rotate-weight-tooltip": "Rotate some randomly selected entries by this angle"
+    "rotate-weight": "Rotate some entries of this weight class randomly by x degrees"
   }
 }
diff --git a/src/assets/i18n/home/de.json b/src/assets/i18n/home/de.json
index 0d824a82c8f071e9a85578d32e4f12cc5ae47e93..e54b40b66a45a4530e80e9aa144bb5ef949697aa 100644
--- a/src/assets/i18n/home/de.json
+++ b/src/assets/i18n/home/de.json
@@ -311,5 +311,9 @@
   },
   "qr-dialog": {
     "session": "Raum"
+  },
+  "topic-cloud": {
+    "changes-gone-wrong": "Etwas ist schief gelaufen!",
+    "changes-successful": "Änderungen gespeichert."
   }
 }
diff --git a/src/assets/i18n/home/en.json b/src/assets/i18n/home/en.json
index af3295e5a159ffdcd041c04bee2281fcc2d146d4..b886992ec32e98181f3c3a17c4d455cb38749ba4 100644
--- a/src/assets/i18n/home/en.json
+++ b/src/assets/i18n/home/en.json
@@ -315,5 +315,9 @@
   },
   "qr-dialog": {
     "session": "Key code"
+  },
+  "topic-cloud": {
+    "changes-gone-wrong": "Something went wrong!",
+    "changes-successful": "Successfully updated."
   }
 }
diff --git a/src/assets/i18n/participant/de.json b/src/assets/i18n/participant/de.json
index 38e15dac7460deeeeeb0223520ef9774bed14f5b..baee4ae21a3d403ad978fbfa9e0ab322966b7986 100644
--- a/src/assets/i18n/participant/de.json
+++ b/src/assets/i18n/participant/de.json
@@ -87,7 +87,7 @@
     "cancel": "Abbrechen",
     "delete": "Löschen"
   },
-  "spacy-dialog":{
+  "spacy-dialog": {
     "auto": "auto",
     "de": "Deutsch",
     "en": "Englisch",
@@ -98,7 +98,8 @@
     "select-all-hint": "Alle Stichwörter auswählen",
     "select-keyword-hint": "Dieses Stickwort auswählen",
     "edit-keyword-hint": "Stichwort editieren",
-    "editing-done-hint": "Editierung abschliessen"
+    "editing-done-hint": "Editierung abschliessen",
+    "force-language-selection": "Bitte wähle eine Sprache aus."
   },
   "comment-page": {
     "a11y-comment_input": "Gib deine Frage ein",
@@ -155,7 +156,6 @@
     "sure": "Bist du sicher?",
     "grammar-check": "Rechtschreibprüfung"
   },
-
   "home-page": {
     "exactly-8": "Ein Raum-Code hat genau 8 Ziffern.",
     "no-room-found": "Es wurde keine Sitzung mit diesem Raum-Code gefunden.",
@@ -218,7 +218,7 @@
     "filter-lbl": "Filter-Menü anzeigen",
     "filter-lbl-favorites": "Favorisierte Fragen filtern",
     "filter-lbl-approved": "Bejahte Fragen filtern",
-    "filter-lbl-disapproved":"Verneinte Fragen filtern",
+    "filter-lbl-disapproved": "Verneinte Fragen filtern",
     "filter-tags-lbl": "Nach Kategorien filtern",
     "user-lbl": "Benutzer-Menü öffnen",
     "slider-lbl": "Fragen-Zoom einstellen",
@@ -233,7 +233,25 @@
   "tag-cloud": {
     "config": "Wolkenansicht ändern",
     "administration": "Wolkenthemen editieren",
-    "demo-data-topic": "Thema %d"
+    "demo-data-topic": "Thema %d",
+    "overview-question-topic-tooltip": "Anzahl gestellter Fragen mit diesem Thema",
+    "overview-questioners-topic-tooltip": "Anzahl Fragensteller*innen mit diesem Thema",
+    "period-since-first-comment":"Zeitraum seit erstem Kommentar",
+    "upvote-topic": "Upvotes für dieses Thema",
+    "downvote-topic": "Downvotes für dieses Thema",
+    "blacklist-topic": "Thema zur Blacklist hinzufügen"
+  },
+  "tag-cloud-popup": {
+    "few-seconds": "wenige Sekunden",
+    "few-minutes": "wenige Minuten",
+    "some-minutes": "{{minutes}} Minuten",
+    "one-hour": "1 Stunde",
+    "some-hours": "{{hours}} Stunden",
+    "one-day": "1 Tag",
+    "some-days": "{{days}} Tage",
+    "one-week": "1 Woche",
+    "some-weeks": "{{weeks}} Wochen",
+    "some-months": "{{months}} Monate"
   },
   "topic-cloud-dialog": {
     "cancel": "Abbrechen",
@@ -263,7 +281,14 @@
     "show-blacklist": "Zeige Blackliste",
     "hide-blacklist": "Verberge Blackliste",
     "show-profanity-list": "Zeige Schimpfwortliste",
-    "hide-profanity-list": "Verberge Schimpfwortliste"
+    "hide-profanity-list": "Verberge Schimpfwortliste",
+    "keyword-delete": "Stichwort gelöscht",
+    "keyword-edit": "Stichwort umbenannt",
+    "keywords-merge": "Stichwörter zusammengefügt",
+    "changes-gone-wrong": "Etwas ist schiefgelaufen",
+    "english": "Englisch",
+    "german": "Deutsch",
+    "select-all": "Alle auswählen"
   },
   "topic-cloud-confirm-dialog": {
     "cancel": "Abbrechen",
@@ -277,24 +302,24 @@
     "read-more": "Mehr lesen",
     "read-less": "Weniger lesen"
   },
-  "tag-cloud-config":{
-    "title":"Tag-Cloud Einstellungen",
-    "general":"Allgemeine Einstellungen",
-    "overflow":"Ãœberlauf",
-    "height":"Höhe",
-    "random-angle":"Zufallswinkel",
-    "realign":"Neu ausrichten",
-    "background":"Hintergrundfarbe",
-    "word-delay":"Wortverzögerung",
-    "hover-color":"Schriftfarbe",
-    "hover-scale":"Hover Skala",
-    "hover-time":"Hover Zeit",
-    "hover-title":"Hover Einstellungen",
-    "hover-delay":"Hover Verzögerung",
-    "cancel-btn":"Abbruch",
-    "save-btn":"Speichern",
-    "font-size-min":"Schriftgröße min",
-    "font-size-max":"Schriftgröße max",
+  "tag-cloud-config": {
+    "title": "Tag-Cloud Einstellungen",
+    "general": "Allgemeine Einstellungen",
+    "overflow": "Ãœberlauf",
+    "height": "Höhe",
+    "random-angle": "Zufallswinkel",
+    "realign": "Neu ausrichten",
+    "background": "Hintergrundfarbe",
+    "word-delay": "Wortverzögerung",
+    "hover-color": "Schriftfarbe",
+    "hover-scale": "Hover Skala",
+    "hover-time": "Hover Zeit",
+    "hover-title": "Hover Einstellungen",
+    "hover-delay": "Hover Verzögerung",
+    "cancel-btn": "Abbruch",
+    "save-btn": "Speichern",
+    "font-size-min": "Schriftgröße min",
+    "font-size-max": "Schriftgröße max",
     "select-color": "Farbauswahl",
     "random-angle-tooltip": "Anordnung der Winkel zufällig generieren",
     "background-tooltip": "Auswahl der Hintergrundfarbe",
@@ -305,10 +330,10 @@
     "hover-scale-tooltip": "Skallierung der Wörter beim Erscheinen",
     "hover-time-tooltip": "Festlegen der Erscheinungszeit der Wörter",
     "hover-delay-tooltip": "Hover-Verzögerung der Wörter",
-    "extended-btn": "Gewichtsklassen",
+    "extended-btn": "Wichtigkeitsklasse",
     "back-btn": "Zurück",
-    "weight-class-settings": "Gewichtsklassen Einstellungen",
-    "weight-class": "Gewichtsklasse",
+    "weight-class-settings": "Wichtigkeitsklassen Einstellungen",
+    "weight-class": "Wichtigkeitsklasse",
     "weight-color": "Schriftfarbe",
     "weight-number": "max. Anzahl Schlüsselwörter",
     "weight-color-tooltip": "Auswahl der Schriftfarbe",
@@ -325,9 +350,8 @@
     "automatic-spelling-tooltip": "automatische Überprüfung der Rechtschreibung",
     "notation-tooltip": "Einstellung der Schreibweise: klein, groß, standard",
     "alphabetical-sorting-tooltip": "Alphabetische Sortierung",
-    "rotation-tooltip": "Einige Einträge zufällig um x Grad drehen",
     "highestWeight-tooltip": "x Tags mit der höchsten Gewichtung anzeigen",
-    "rotate-weight": "Einige Einträge dieser Gewichtsklasse zufällig um x Grad drehen",
-    "rotate-weight-tooltip": "Einige zufällig ausgewählte Einträge um diesen Winkel drehen"
+    "rotate-weight": "Einige Einträge dieser Klasse zufällig um x Grad drehen",
+    "rotate-weight-tooltip": "einige Einträge dieser Wichtigkeitsklasse zufällig um x Grad drehen"
   }
-}
+}
\ No newline at end of file
diff --git a/src/assets/i18n/participant/en.json b/src/assets/i18n/participant/en.json
index 4e481147a7f80aa5276cdb9fba94fd701642b0c0..da69a91b4d84e963b43e8fddf3245a0d70bb2305 100644
--- a/src/assets/i18n/participant/en.json
+++ b/src/assets/i18n/participant/en.json
@@ -108,7 +108,8 @@
     "select-all-hint": "Select all keywords",
     "select-keyword-hint": "Select this keyword",
     "edit-keyword-hint": "Edit keyword",
-    "editing-done-hint": "Finish editing"
+    "editing-done-hint": "Finish editing",
+    "force-language-selection": "Please select a language."
   },
   "comment-page": {
     "a11y-comment_input": "Enter your question",
@@ -238,7 +239,25 @@
   "tag-cloud": {
     "config": "Modify cloud view",
     "administration": "Edit cloud topics",
-    "demo-data-topic": "Topic %d"
+    "demo-data-topic": "Topic %d",
+    "overview-question-topic-tooltip": "Number of questions with this topic",
+    "overview-questioners-topic-tooltip": "Number of questioners with this topic",
+    "period-since-first-comment":"Period since first comment",
+    "upvote-topic": "Upvotes for this topic",
+    "downvote-topic": "Downvotes for this topic",
+    "blacklist-topic": "Add topic to blacklist"
+  },
+  "tag-cloud-popup": {
+    "few-seconds": "few seconds",
+    "few-minutes": "few minutes",
+    "some-minutes": "{{minutes}} minutes",
+    "one-hour": "1 hour",
+    "some-hours": "{{hours}} hours",
+    "one-day": "1 day",
+    "some-days": "{{days}} days",
+    "one-week": "1 week",
+    "some-weeks": "{{weeks}} weeks",
+    "some-months": "{{months}} months"
   },
   "topic-cloud-dialog":{
     "edit": "Edit",
@@ -268,7 +287,14 @@
     "show-blacklist": "Show blacklist",
     "hide-blacklist": "Hide blacklist",
     "show-profanity-list": "Show profanity list",
-    "hide-profanity-list": "Hide profanity list"
+    "hide-profanity-list": "Hide profanity list",
+    "keyword-delete": "keyword deleted",
+    "keyword-edit": "keyword renamed",
+    "keywords-merge": "keywords merged",
+    "changes-gone-wrong": "somthing has gone wrong",
+    "english": "English",
+    "german": "German",
+    "select-all": "Select all"
   },
   "topic-cloud-confirm-dialog":{
     "cancel": "Cancel",
@@ -311,13 +337,13 @@
     "hover-time-tooltip": "Select hover-time",
     "hover-delay-tooltip": "Select hover-delay",
     "extended-btn": "Weight classes",
-    "weight-class-settings": "Weight-classes settings",
+    "weight-class-settings": "Weight-class Settings",
     "weight-class": "Weight class",
     "back-btn": "Back",
     "weight-color": "Font color",
     "weight-number": "max. number of keywords",
-    "weight-color-tooltip": "select font-color",
-    "wieght-number-tooltip": "select maximal number of keywords",
+    "weight-color-tooltip": "Select font-color",
+    "weight-number-tooltip": "Select maximal number of keywords",
     "automatic-spelling": "Automatic spelling",
     "notation": "Notation:",
     "lowerCase": "Lower case",
@@ -330,9 +356,8 @@
     "automatic-spelling-tooltip": "automatic spelling check",
     "notation-tooltip": "Notation-Settings: small, large, standard",
     "alphabetical-sorting-tooltip": "Alphabetical sorting",
-    "rotation-tooltip": "rotate some entries randomly by x degrees",
+    "rotate-weight-tooltip": "Rotate some entries randomly by x degrees",
     "highestWeight-tooltip": "show x tags with the highest weight",
-    "rotate-weight": "Rotate some entries of this weight class randomly by x degrees",
-    "rotate-weight-tooltip":"Rotate some randomly selected entries by this angle"
+    "rotate-weight": "Rotate some entries of this weight class randomly by x degrees"
   }
-}
+}
\ No newline at end of file
diff --git a/src/assets/icons/hashtag.svg b/src/assets/icons/hashtag.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c309d2fc26484ac3b3a85dc928463cc13f0fd8fc
--- /dev/null
+++ b/src/assets/icons/hashtag.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
+<path d="M64.333,490h58.401l33.878-137.69h122.259L245.39,490h58.401l33.878-137.69h119.92v-48.162h-108.24l29.2-117.324h79.04
+	v-48.162H390.23L424.108,0H365.31l-33.878,138.661H208.79L242.668,0h-58.415l-33.864,138.661H32.411v48.162h106.298l-28.818,117.324
+	h-77.48v48.162h65.8L64.333,490z M197.11,186.824h122.642l-29.2,117.324H168.292L197.11,186.824z"/>
+</svg>