From 0df48da0b4d81f356b7d31c477ac442aa1e2908b Mon Sep 17 00:00:00 2001
From: Martin Mechtel <mechtelm@user.hu-berlin.de>
Date: Sun, 30 Sep 2018 22:52:23 +0200
Subject: [PATCH] navigation and reload seems ok

---
 src/app/app.module.ts                         |   4 +-
 src/app/backend.service.ts                    |  48 +-
 src/app/errormsg/errormsg.component.css       |   3 +
 src/app/errormsg/errormsg.component.html      |   3 +
 src/app/errormsg/errormsg.component.spec.ts   |  25 +
 src/app/errormsg/errormsg.component.ts        |  21 +
 src/app/logindata.service.ts                  |  10 +-
 src/app/start/start.component.html            |   2 +-
 src/app/start/start.component.ts              |  32 +-
 src/app/test-controller/backend.service.ts    | 143 +-----
 .../tc-menu-buttons.component.ts              |   7 +-
 .../tc-sidenavi-button.component.css          |  18 +
 .../tc-sidenavi-button.component.html         |   8 +
 .../tc-sidenavi-button.component.spec.ts      |  25 +
 .../tc-sidenavi-button.component.ts           |  33 ++
 .../test-controller.component.html            |   5 +-
 .../test-controller.component.ts              |  67 +--
 .../test-controller/test-controller.module.ts |   4 +-
 .../test-controller.service.ts                |  73 ++-
 .../test-controller/testdata.service.spec.ts  |  15 -
 src/app/test-controller/testdata.service.ts   | 463 ------------------
 .../test-controller/unithost/unit-routing.ts  |  13 +-
 .../unithost/unithost.component.html          |   2 +-
 .../unithost/unithost.component.ts            |  29 +-
 src/environments/environment.ts               |   2 +-
 25 files changed, 351 insertions(+), 704 deletions(-)
 create mode 100644 src/app/errormsg/errormsg.component.css
 create mode 100644 src/app/errormsg/errormsg.component.html
 create mode 100644 src/app/errormsg/errormsg.component.spec.ts
 create mode 100644 src/app/errormsg/errormsg.component.ts
 create mode 100644 src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.css
 create mode 100644 src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.html
 create mode 100644 src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.spec.ts
 create mode 100644 src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.ts
 delete mode 100644 src/app/test-controller/testdata.service.spec.ts
 delete mode 100644 src/app/test-controller/testdata.service.ts

diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 8ad2a077..338dc0c0 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -16,12 +16,14 @@ import { BackendService } from './backend.service';
 import { StartComponent } from './start/start.component';
 import { LocationStrategy, HashLocationStrategy } from '@angular/common';
 import { FlexLayoutModule } from '@angular/flex-layout';
+import { ErrormsgComponent } from './errormsg/errormsg.component';
 
 @NgModule({
   declarations: [
     AppComponent,
     StartComponent,
-    AboutComponent
+    AboutComponent,
+    ErrormsgComponent
   ],
   imports: [
     BrowserModule,
diff --git a/src/app/backend.service.ts b/src/app/backend.service.ts
index 5d8d69c3..8ac0b3e6 100644
--- a/src/app/backend.service.ts
+++ b/src/app/backend.service.ts
@@ -24,7 +24,7 @@ export class BackendService {
     return this.http
       .post<string>(this.serverUrl + 'testlogin.php', {n: name, p: password}, httpOptions)
         .pipe(
-          catchError(this.handleError)
+          catchError(ErrorHandler.handle)
         );
   }
 
@@ -39,7 +39,7 @@ export class BackendService {
     return this.http
       .post<LoginData>(this.serverUrl + 'getLoginDataByLoginToken.php', {lt: logintoken}, httpOptions)
         .pipe(
-          catchError(this.handleError)
+          catchError(ErrorHandler.handle)
         );
   }
 
@@ -53,7 +53,7 @@ export class BackendService {
     return this.http
       .post<LoginData>(this.serverUrl + 'getLoginDataByPersonToken.php', {pt: persontoken}, httpOptions)
         .pipe(
-          catchError(this.handleError)
+          catchError(ErrorHandler.handle)
         );
   }
 
@@ -68,7 +68,7 @@ export class BackendService {
       .post<BookletStatus>(this.serverUrl + 'getBookletStatusByNameAndPersonToken.php', {
         pt: persontoken, b: bookletname}, httpOptions)
         .pipe(
-          catchError(this.handleError)
+          catchError(ErrorHandler.handle)
         );
   }
 
@@ -84,7 +84,7 @@ export class BackendService {
       .post<BookletStatus>(this.serverUrl + 'getBookletStatusByNameAndLoginToken.php', {
         lt: logintoken, b: bookletid, c: code, bl: bookletlabel}, httpOptions)
         .pipe(
-          catchError(this.handleError)
+          catchError(ErrorHandler.handle)
         );
   }
 
@@ -99,7 +99,7 @@ export class BackendService {
       .post<BookletStatus>(this.serverUrl + 'getBookletStatusByDbId.php', {
         pt: persontoken, b: bookletid}, httpOptions)
         .pipe(
-          catchError(this.handleError)
+          catchError(ErrorHandler.handle)
         );
   }
 
@@ -114,7 +114,7 @@ export class BackendService {
       .post<PersonTokenAndBookletId>(this.serverUrl +
             'startBookletByLoginToken.php', {lt: logintoken, c: code, b: bookletFilename}, httpOptions)
         .pipe(
-          catchError(this.handleError)
+          catchError(ErrorHandler.handle)
         );
   }
 
@@ -129,7 +129,7 @@ export class BackendService {
     return this.http
       .post<number>(this.serverUrl + 'startBookletByPersonToken.php', {pt: persontoken, b: bookletFilename}, httpOptions)
         .pipe(
-          catchError(this.handleError)
+          catchError(ErrorHandler.handle)
         );
   }
 
@@ -143,23 +143,25 @@ export class BackendService {
     return this.http
       .post<boolean>(this.serverUrl + 'endBooklet.php', {pt: persontoken, b: bookletDbId}, httpOptions)
         .pipe(
-          catchError(this.handleError)
+          catchError(ErrorHandler.handle)
         );
   }
 
   // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+}
 
-  private handleError(errorObj: HttpErrorResponse): Observable<ServerError> {
-    const myreturn = new ServerError(errorObj.status, 'Fehler bei Datenübertragung');
-
-    if (errorObj.status === 401) {
-      myreturn.label = 'Fehler: Zugriff verweigert - bitte (neu) anmelden!';
-    } else if (errorObj.status === 503) {
-      myreturn.label = 'Fehler: Server meldet Datenbankproblem.';
-    } else if (errorObj.error instanceof ErrorEvent) {
-      myreturn.label = 'Fehler: ' + (<ErrorEvent>errorObj.error).message;
+export class ErrorHandler {
+  public static handle(errorObj: HttpErrorResponse): Observable<ServerError> {
+    let myreturn: ServerError = null;
+    if (errorObj.error instanceof ErrorEvent) {
+      myreturn = new ServerError(500, 'Verbindungsproblem', (<ErrorEvent>errorObj.error).message);
     } else {
-      myreturn.label = 'Fehler: ' + errorObj.message;
+      myreturn = new ServerError(errorObj.status, 'Verbindungsproblem', errorObj.message);
+      if (errorObj.status === 401) {
+        myreturn.labelNice = 'Zugriff verweigert - bitte (neu) anmelden!';
+      } else if (errorObj.status === 503) {
+        myreturn.labelNice = 'Achtung: Server meldet Datenbankproblem.';
+      }
     }
 
     return of(myreturn);
@@ -172,10 +174,12 @@ export class BackendService {
 // class instead of interface to be able to use instanceof to check type
 export class ServerError {
   public code: number;
-  public label: string;
-  constructor(code: number, label: string) {
+  public labelNice: string;
+  public labelSystem: string;
+  constructor(code: number, labelNice: string, labelSystem) {
     this.code = code;
-    this.label = label;
+    this.labelNice = labelNice;
+    this.labelSystem = labelSystem;
   }
 }
 
diff --git a/src/app/errormsg/errormsg.component.css b/src/app/errormsg/errormsg.component.css
new file mode 100644
index 00000000..361c51c8
--- /dev/null
+++ b/src/app/errormsg/errormsg.component.css
@@ -0,0 +1,3 @@
+div {
+  color: brown;
+}
diff --git a/src/app/errormsg/errormsg.component.html b/src/app/errormsg/errormsg.component.html
new file mode 100644
index 00000000..8181a966
--- /dev/null
+++ b/src/app/errormsg/errormsg.component.html
@@ -0,0 +1,3 @@
+<div *ngIf="errorMsg !== null" [matTooltip]="errorMsg.labelSystem">
+  {{ errorMsg.labelNice }}
+</div>
diff --git a/src/app/errormsg/errormsg.component.spec.ts b/src/app/errormsg/errormsg.component.spec.ts
new file mode 100644
index 00000000..671f6575
--- /dev/null
+++ b/src/app/errormsg/errormsg.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ErrormsgComponent } from './errormsg.component';
+
+describe('ErrormsgComponent', () => {
+  let component: ErrormsgComponent;
+  let fixture: ComponentFixture<ErrormsgComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ ErrormsgComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ErrormsgComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/errormsg/errormsg.component.ts b/src/app/errormsg/errormsg.component.ts
new file mode 100644
index 00000000..a12d8f15
--- /dev/null
+++ b/src/app/errormsg/errormsg.component.ts
@@ -0,0 +1,21 @@
+import { ServerError } from './../backend.service';
+import { LogindataService } from './../logindata.service';
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+  selector: 'app-errormsg',
+  templateUrl: './errormsg.component.html',
+  styleUrls: ['./errormsg.component.css']
+})
+export class ErrormsgComponent implements OnInit {
+  private errorMsg: ServerError = null;
+
+  constructor(
+    private lds: LogindataService
+  ) { }
+
+  ngOnInit() {
+    this.lds.globalErrorMsg$.subscribe(m => this.errorMsg = m);
+  }
+
+}
diff --git a/src/app/logindata.service.ts b/src/app/logindata.service.ts
index 9f611044..3503d254 100644
--- a/src/app/logindata.service.ts
+++ b/src/app/logindata.service.ts
@@ -24,7 +24,7 @@ export class LogindataService {
   public groupName$ = new BehaviorSubject<string>('');
   public personCode$ = new BehaviorSubject<string>('');
   public bookletLabel$ = new BehaviorSubject<string>('');
-  public globalErrorMsg$ = new BehaviorSubject<string>('');
+  public globalErrorMsg$ = new BehaviorSubject<ServerError>(null);
   public bookletsByCode$ = new BehaviorSubject<BookletDataListByCode>(null);
   public bookletData$ = new BehaviorSubject<BookletData[]>([]);
   public loginToken$ = new BehaviorSubject<string>('');
@@ -102,12 +102,12 @@ export class LogindataService {
         this.bs.getLoginDataByPersonToken(pt).subscribe(loginDataUntyped => {
           if (loginDataUntyped instanceof ServerError) {
             const e = loginDataUntyped as ServerError;
-            this.globalErrorMsg$.next(e.code.toString() + ': ' + e.label);
+            this.globalErrorMsg$.next(e);
             this.bookletDbId$.next(0);
             this.personToken$.next('');
           } else {
             const loginData = loginDataUntyped as LoginData;
-            this.globalErrorMsg$.next('');
+            this.globalErrorMsg$.next(null);
             this.groupName$.next(loginData.groupname);
             this.workspaceName$.next(loginData.workspaceName);
             this.personCode$.next(loginData.code);
@@ -120,11 +120,11 @@ export class LogindataService {
             this.bs.getBookletStatusByDbId(pt, b).subscribe(bookletStatusUntyped => {
               if (bookletStatusUntyped instanceof ServerError) {
                 const e = bookletStatusUntyped as ServerError;
-                this.globalErrorMsg$.next(e.code.toString() + ': ' + e.label);
+                this.globalErrorMsg$.next(e);
                 this.bookletDbId$.next(0);
               } else {
                 const bookletStatus = bookletStatusUntyped as BookletStatus;
-                this.globalErrorMsg$.next('');
+                this.globalErrorMsg$.next(null);
                 if (bookletStatus.canStart) {
                   this.bookletLabel$.next(bookletStatus.label);
                 } else {
diff --git a/src/app/start/start.component.html b/src/app/start/start.component.html
index d0c796d2..ab8cf40e 100644
--- a/src/app/start/start.component.html
+++ b/src/app/start/start.component.html
@@ -95,7 +95,7 @@
       <button mat-raised-button color="foreground" *ngIf="validCodes.length > 1" (click)="changeCode()">Personen-Code ändern</button>
       <p *ngIf="!showLoginForm || validCodes.length > 1">&nbsp;</p>
       <button mat-raised-button color="foreground" [routerLink]="['/about']">Impressum/Datenschutz</button>
-      <p> {{ errorMsg }}</p>
+      <app-errormsg></app-errormsg>
     </div>
   </div>
 </div>
diff --git a/src/app/start/start.component.ts b/src/app/start/start.component.ts
index 5ebfe065..f3c58e4d 100644
--- a/src/app/start/start.component.ts
+++ b/src/app/start/start.component.ts
@@ -27,7 +27,6 @@ export class StartComponent implements OnInit {
 
   private testtakerloginform: FormGroup;
   private codeinputform: FormGroup;
-  private errorMsg = '';
   private testEndButtonText = 'Test beenden';
 
   // ??
@@ -79,8 +78,6 @@ export class StartComponent implements OnInit {
       }
     });
 
-    this.lds.globalErrorMsg$.subscribe(m => this.errorMsg = m);
-
     this.testtakerloginform = this.fb.group({
       testname: this.fb.control(this.lds.loginName$.getValue(), [Validators.required, Validators.minLength(3)]),
       testpw: this.fb.control('', [Validators.required, Validators.minLength(3)])
@@ -106,14 +103,14 @@ export class StartComponent implements OnInit {
       loginTokenUntyped => {
         if (loginTokenUntyped instanceof ServerError) {
           const e = loginTokenUntyped as ServerError;
-          this.lds.globalErrorMsg$.next(e.code.toString() + ': ' + e.label);
+          this.lds.globalErrorMsg$.next(e);
           // no change in other data
         } else {
           this.validCodes = [];
           this.bookletlist = [];
           this.lds.personToken$.next('');
           this.lds.personCode$.next('');
-          this.lds.globalErrorMsg$.next('');
+          this.lds.globalErrorMsg$.next(null);
           this.lds.workspaceName$.next('');
           this.lds.bookletsByCode$.next(null);
           this.lds.bookletData$.next([]);
@@ -129,10 +126,11 @@ export class StartComponent implements OnInit {
             loginDataUntyped => {
               if (loginDataUntyped instanceof ServerError) {
                 const e = loginDataUntyped as ServerError;
-                this.lds.globalErrorMsg$.next(e.code.toString() + ': ' + e.label);
+                this.lds.globalErrorMsg$.next(e);
                 this.lds.loginToken$.next('');
               } else {
                 const loginData = loginDataUntyped as LoginData;
+                this.lds.globalErrorMsg$.next(null);
                 this.lds.personToken$.next('');
                 this.lds.bookletsByCode$.next(loginData.booklets);
                 this.lds.bookletData$.next([]);
@@ -226,12 +224,14 @@ export class StartComponent implements OnInit {
           bookletIdUntyped => {
             if (bookletIdUntyped instanceof ServerError) {
               const e = bookletIdUntyped as ServerError;
-              this.lds.globalErrorMsg$.next(e.code.toString() + ': ' + e.label);
+              this.lds.globalErrorMsg$.next(e);
             } else {
               const bookletId = bookletIdUntyped as number;
+              this.lds.globalErrorMsg$.next(null);
               if (bookletId > 0) {
                 this.lds.bookletDbId$.next(bookletId);
                 this.lds.bookletLabel$.next(b.label);
+                this.lds.globalErrorMsg$.next(null);
                 // ************************************************
 
                 // by setting bookletDbId$ the test-controller will load the booklet
@@ -239,7 +239,7 @@ export class StartComponent implements OnInit {
 
                 // ************************************************
               } else {
-                this.lds.globalErrorMsg$.next('ungültige Anmeldung');
+                this.lds.globalErrorMsg$.next(new ServerError(401, 'ungültige Anmeldung', 'start.component'));
               }
             }
           }
@@ -249,12 +249,13 @@ export class StartComponent implements OnInit {
           startDataUntyped => {
             if (startDataUntyped instanceof ServerError) {
               const e = startDataUntyped as ServerError;
-              this.lds.globalErrorMsg$.next(e.code.toString() + ': ' + e.label);
+              this.lds.globalErrorMsg$.next(e);
             } else {
               const startData = startDataUntyped as PersonTokenAndBookletId;
 
               if (startData.b > 0) {
                 this.lds.personToken$.next(startData.pt);
+                this.lds.globalErrorMsg$.next(null);
                 this.lds.bookletLabel$.next(b.label);
                 this.lds.bookletDbId$.next(startData.b); // as last to trigger auth with success!
                 // ************************************************
@@ -264,14 +265,14 @@ export class StartComponent implements OnInit {
 
                 // ************************************************
               } else {
-                this.lds.globalErrorMsg$.next('ungültige Anmeldung');
+                this.lds.globalErrorMsg$.next(new ServerError(401, 'ungültige Anmeldung', 'start.component'));
               }
             }
           }
         );
-          }
+      }
     } else {
-      this.lds.globalErrorMsg$.next('ungültige Anmeldung');
+      this.lds.globalErrorMsg$.next(new ServerError(401, 'ungültige Anmeldung', 'start.component'));
     }
   }
 
@@ -298,9 +299,10 @@ export class StartComponent implements OnInit {
         finOkUntyped => {
           if (finOkUntyped instanceof ServerError) {
             const e = finOkUntyped as ServerError;
-            this.lds.globalErrorMsg$.next(e.code.toString() + ': ' + e.label);
+            this.lds.globalErrorMsg$.next(e);
           } else {
             const finOK = finOkUntyped as boolean;
+            this.lds.globalErrorMsg$.next(null);
             if (finOK) {
               this.showLoginForm = false;
               this.showCodeForm = false;
@@ -354,7 +356,7 @@ export class StartButtonData {
     bs.getBookletStatusByNameAndLoginToken(loginToken, code, this.id, this.label).subscribe(respDataUntyped => {
       if (respDataUntyped instanceof ServerError) {
         const e = respDataUntyped as ServerError;
-        this.statustxt = e.code.toString() + ': ' + e.label;
+        this.statustxt = e.code.toString() + ': ' + e.labelNice;
       } else {
         const respData = respDataUntyped as BookletStatus;
         this.statustxt = respData.statusLabel;
@@ -369,7 +371,7 @@ export class StartButtonData {
     bs.getBookletStatusByNameAndPersonToken(personToken, this.id).subscribe(respDataUntyped => {
       if (respDataUntyped instanceof ServerError) {
         const e = respDataUntyped as ServerError;
-        this.statustxt = e.code.toString() + ': ' + e.label;
+        this.statustxt = e.code.toString() + ': ' + e.labelNice;
       } else {
         const respData = respDataUntyped as BookletStatus;
         this.statustxt = respData.statusLabel;
diff --git a/src/app/test-controller/backend.service.ts b/src/app/test-controller/backend.service.ts
index 0bf91faf..02373ae3 100644
--- a/src/app/test-controller/backend.service.ts
+++ b/src/app/test-controller/backend.service.ts
@@ -1,3 +1,4 @@
+import { ServerError, ErrorHandler } from './../backend.service';
 import { Injectable, Inject } from '@angular/core';
 import { HttpClient, HttpParams, HttpHeaders, HttpEvent, HttpErrorResponse } from '@angular/common/http';
 import { ResponseContentType } from '@angular/http';
@@ -11,36 +12,12 @@ import { Authorisation } from '../logindata.service';
 })
 export class BackendService {
 
-  private lastBookletState = '';
-  private lastUnitResponses = '';
-  private restorePoints: {[unitname: string]: string} = {};
-  private itemplayers: {[filename: string]: string} = {};
-
   constructor(
     @Inject('SERVER_URL') private serverUrl: string,
     private http: HttpClient) {
       this.serverUrl = this.serverUrl + 'php_tc/';
     }
 
-  private normaliseFileName(fn: string, ext: string): string {
-    fn = fn.toUpperCase();
-    ext = ext.toUpperCase();
-    if (ext.slice(0, 1) !== '.') {
-      ext = '.' + ext;
-    }
-
-    if (fn.slice(-(ext.length)) === ext) {
-      return fn;
-    } else {
-      return fn + ext;
-    }
-  }
-
-  // 7777777777777777777777777777777777777777777777777777777777777777777777
-  getUnitRestorePoint(unitname: string): string {
-    return this.restorePoints[unitname];
-  }
-
   // 7777777777777777777777777777777777777777777777777777777777777777777777
   saveUnitReview(auth: Authorisation, unit: string, priority: number,
     categories: string, entry: string): Observable<boolean | ServerError> {
@@ -53,7 +30,7 @@ export class BackendService {
     .post<boolean>(this.serverUrl + 'addUnitReview.php', {au: auth.toAuthString(), u: unit,
         p: priority, c: categories, e: entry}, httpOptions)
       .pipe(
-        catchError(this.handleError)
+        catchError(ErrorHandler.handle)
       );
   }
 
@@ -68,7 +45,7 @@ export class BackendService {
     .post<boolean>(this.serverUrl + 'addBookletReview.php', {au: auth.toAuthString(),
         p: priority, c: categories, e: entry}, httpOptions)
       .pipe(
-        catchError(this.handleError)
+        catchError(ErrorHandler.handle)
       );
   }
 
@@ -82,7 +59,7 @@ export class BackendService {
     return this.http
       .post<BookletData>(this.serverUrl + 'getBookletData.php', {au: auth.toAuthString()}, httpOptions)
         .pipe(
-          catchError(this.handleError)
+          catchError(ErrorHandler.handle)
         );
   }
 
@@ -97,70 +74,27 @@ export class BackendService {
     return this.http
     .post<UnitData>(this.serverUrl + 'getUnitData.php', {au: auth.toAuthString(), u: unitid}, httpOptions)
       .pipe(
-        catchError(this.handleError)
+        catchError(ErrorHandler.handle)
       );
   }
 
-  // 7777777777777777777777777777777777777777777777777777777777777777777777
-  loadItemplayerOk(auth: Authorisation, unitDefinitionType: string): Observable<boolean> {
-    unitDefinitionType = this.normaliseFileName(unitDefinitionType, 'html');
-    if (this.itemplayers.hasOwnProperty(unitDefinitionType)) {
-      return of(true);
-    } else {
-      // to avoid multiple calls before returning:
-      this.itemplayers[unitDefinitionType] = null;
-      return this.getUnitResourceTxt(auth, unitDefinitionType)
-          .pipe(
-            switchMap(myData => {
-              if (myData instanceof ServerError) {
-                return of(false);
-              } else {
-                const itemplayerData = myData as string;
-                if (itemplayerData.length > 0) {
-                  this.itemplayers[unitDefinitionType] = itemplayerData;
-                  return of(true);
-                } else {
-                  return of(false);
-                }
-              }
-            }));
-    }
-  }
-
-
-  // 7777777777777777777777777777777777777777777777777777777777777777777777
-  getItemplayer(unitDefinitionType: string): string {
-    unitDefinitionType = this.normaliseFileName(unitDefinitionType, 'html');
-    if ((unitDefinitionType.length > 0) && this.itemplayers.hasOwnProperty(unitDefinitionType)) {
-      return this.itemplayers[unitDefinitionType];
-    } else {
-      return '';
-    }
-  }
-
-  // 7777777777777777777777777777777777777777777777777777777777777777777777
-  isItemplayerReady(unitDefinitionType: string): boolean {
-    unitDefinitionType = this.normaliseFileName(unitDefinitionType, 'html');
-    return (unitDefinitionType.length > 0) && this.itemplayers.hasOwnProperty(unitDefinitionType);
-  }
-
   // 888888888888888888888888888888888888888888888888888888888888888888
-  setBookletStatus(sessiontoken: string, state: {}): Observable<string | ServerError> {
+  setBookletStatus(auth: Authorisation, state: {}): Observable<string | ServerError> {
     const httpOptions = {
       headers: new HttpHeaders({
         'Content-Type':  'application/json'
       })
     };
-    if ((sessiontoken + JSON.stringify(state)) === this.lastBookletState) {
-      return new Observable(null);
-    } else {
-      this.lastBookletState = sessiontoken + JSON.stringify(state);
+    // if ((sessiontoken + JSON.stringify(state)) === this.lastBookletState) {
+    //   return new Observable(null);
+    // } else {
+    //   this.lastBookletState = sessiontoken + JSON.stringify(state);
       return this.http
-      .post<string>(this.serverUrl + 'setBookletStatus.php', {st: sessiontoken, state: state}, httpOptions)
+      .post<string>(this.serverUrl + 'setBookletStatus.php', {au: auth.toAuthString(), state: state}, httpOptions)
         .pipe(
-          catchError(this.handleError)
+          catchError(ErrorHandler.handle)
         );
-    }
+    // }
   }
 
   // 888888888888888888888888888888888888888888888888888888888888888888
@@ -183,7 +117,7 @@ export class BackendService {
           }
           return window.btoa(str64);
         }),
-        catchError(this.handleError)
+        catchError(ErrorHandler.handle)
     );
   }
 
@@ -199,7 +133,7 @@ export class BackendService {
       return this.http
       .post<string>(this.serverUrl + 'getUnitResource64.php', {st: sessiontoken, r: resId}, myHttpOptions)
         .pipe(
-          catchError(this.handleError)
+          catchError(ErrorHandler.handle)
         );
   }
 
@@ -215,7 +149,7 @@ export class BackendService {
       return this.http
       .post<string>(this.serverUrl + 'getUnitResourceTxt.php', {au: auth.toAuthString(), r: resId}, myHttpOptions)
         .pipe(
-          catchError(this.handleError)
+          catchError(ErrorHandler.handle)
         );
   }
 
@@ -230,7 +164,7 @@ export class BackendService {
     return this.http
     .post<boolean>(this.serverUrl + 'setUnitResponse.php', {au: auth.toAuthString(), u: unit, d: JSON.stringify(unitdata)}, httpOptions)
       .pipe(
-        catchError(this.handleError)
+        catchError(ErrorHandler.handle)
       );
   }
 
@@ -241,11 +175,10 @@ export class BackendService {
         'Content-Type':  'application/json'
       })
     };
-    this.restorePoints[unit] = unitdata;
     return this.http
     .post<boolean>(this.serverUrl + 'setUnitRestorePoint.php', {au: auth.toAuthString(), u: unit, d: JSON.stringify(unitdata)}, httpOptions)
       .pipe(
-        catchError(this.handleError)
+        catchError(ErrorHandler.handle)
       );
   }
 
@@ -259,46 +192,13 @@ export class BackendService {
     return this.http
     .post<boolean>(this.serverUrl + 'setUnitLog.php', {au: auth.toAuthString(), u: unit, d: unitdata}, httpOptions)
       .pipe(
-        catchError(this.handleError)
+        catchError(ErrorHandler.handle)
       );
   }
-
-  // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
-  private handleError(errorObj: HttpErrorResponse): Observable<ServerError> {
-    const myreturn = new ServerError(errorObj.status, 'Fehler bei Datenübertragung');
-
-    if (errorObj.status === 401) {
-      myreturn.label = 'Fehler: Zugriff verweigert - bitte (neu) anmelden!';
-    } else if (errorObj.status === 503) {
-      myreturn.label = 'Fehler: Server meldet Datenbankproblem.';
-    } else if (errorObj.error instanceof ErrorEvent) {
-      myreturn.label = 'Fehler: ' + (<ErrorEvent>errorObj.error).message;
-    } else {
-      myreturn.label = 'Fehler: ' + errorObj.message;
-    }
-
-    return of(myreturn);
-  }
-
-  private handleErrorSimple(errorObj: HttpErrorResponse): Observable<boolean> {
-    const myreturn = new ServerError(errorObj.status, 'Fehler bei Datenübertragung');
-
-    return of(false);
-  }
 }
 
 // #############################################################################################
 
-// class instead of interface to be able to use instanceof to check type
-export class ServerError {
-  public code: number;
-  public label: string;
-  constructor(code: number, label: string) {
-    this.code = code;
-    this.label = label;
-  }
-}
-
 export interface BookletData {
   xml: string;
   locked: boolean;
@@ -310,8 +210,3 @@ export interface UnitData {
   restorepoint: string;
   status: {};
 }
-
-export interface ServerError {
-  code: number;
-  label: string;
-}
diff --git a/src/app/test-controller/tc-menu-buttons/tc-menu-buttons.component.ts b/src/app/test-controller/tc-menu-buttons/tc-menu-buttons.component.ts
index 7867786a..f5d5f536 100644
--- a/src/app/test-controller/tc-menu-buttons/tc-menu-buttons.component.ts
+++ b/src/app/test-controller/tc-menu-buttons/tc-menu-buttons.component.ts
@@ -1,5 +1,6 @@
 import { FormGroup } from '@angular/forms';
-import { BackendService, ServerError } from './../backend.service';
+import { BackendService } from './../backend.service';
+import { ServerError } from './../../backend.service';
 import { MatDialog, MatSnackBar } from '@angular/material';
 import { Component, OnInit } from '@angular/core';
 import { TestControllerService } from '../test-controller.service';
@@ -64,7 +65,7 @@ export class TcMenuButtonsComponent implements OnInit {
               ).subscribe(myData => {
                 if (myData instanceof ServerError) {
                   const e = myData as ServerError;
-                  this.snackBar.open('Konnte Kommentar nicht speichern (' + e.code.toString() + ': ' + e.label, '', {duration: 3000});
+                  this.snackBar.open('Konnte Kommentar nicht speichern (' + e.code.toString() + ': ' + e.labelNice, '', {duration: 3000});
                 } else {
                   const ok = myData as boolean;
                   if (ok) {
@@ -83,7 +84,7 @@ export class TcMenuButtonsComponent implements OnInit {
             ).subscribe(myData => {
               if (myData instanceof ServerError) {
                 const e = myData as ServerError;
-                this.snackBar.open('Konnte Kommentar nicht speichern (' + e.code.toString() + ': ' + e.label, '', {duration: 3000});
+                this.snackBar.open('Konnte Kommentar nicht speichern (' + e.code.toString() + ': ' + e.labelNice, '', {duration: 3000});
               } else {
                 const ok = myData as boolean;
                 if (ok) {
diff --git a/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.css b/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.css
new file mode 100644
index 00000000..d6d4d8e8
--- /dev/null
+++ b/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.css
@@ -0,0 +1,18 @@
+button {
+  margin: 0px 10px 0px 10px;
+}
+
+div.active-bar, div.not-active-bar {
+  position: relative;
+  top: 24px;
+  width: 100%;
+  height: 10px;
+}
+
+div.active-bar {
+  background-color: yellow;
+}
+
+div.not-active-bar {
+  background-color: transparent;
+}
diff --git a/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.html b/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.html
new file mode 100644
index 00000000..5f6e8e5c
--- /dev/null
+++ b/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.html
@@ -0,0 +1,8 @@
+<div>
+  <div class="active-bar" *ngIf="isActive"></div>
+  <div class="not-active-bar" *ngIf="!isActive"></div>
+  <button mat-raised-button (click)="sideNaviButtonClick()"
+  [disabled]="unitData.locked" >
+  {{ unitData.sequenceId + 1 }}
+  </button>
+</div>
diff --git a/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.spec.ts b/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.spec.ts
new file mode 100644
index 00000000..49cf33c1
--- /dev/null
+++ b/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TcSidenaviButtonComponent } from './tc-sidenavi-button.component';
+
+describe('TcSidenaviButtonComponent', () => {
+  let component: TcSidenaviButtonComponent;
+  let fixture: ComponentFixture<TcSidenaviButtonComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ TcSidenaviButtonComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(TcSidenaviButtonComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.ts b/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.ts
new file mode 100644
index 00000000..4078858f
--- /dev/null
+++ b/src/app/test-controller/tc-sidenavi-button/tc-sidenavi-button.component.ts
@@ -0,0 +1,33 @@
+import { UnitDef, TestControllerService } from './../test-controller.service';
+import { Component, OnInit, Input } from '@angular/core';
+
+@Component({
+  selector: 'tc-sidenavi-button',
+  templateUrl: './tc-sidenavi-button.component.html',
+  styleUrls: ['./tc-sidenavi-button.component.css']
+})
+export class TcSidenaviButtonComponent implements OnInit {
+  @Input() unitData: UnitDef = null;
+  isActive = false;
+  constructor(
+    private tcs: TestControllerService
+  ) {
+    this.tcs.currentUnitPos$.subscribe(up => {
+      if (this.unitData !== null) {
+        this.isActive = up === this.unitData.sequenceId;
+      }
+    });
+  }
+
+  ngOnInit() {
+    if (this.unitData !== null) {
+      this.isActive = this.tcs.currentUnitPos$.getValue() === this.unitData.sequenceId;
+    }
+  }
+
+  sideNaviButtonClick() {
+    if (this.unitData !== null) {
+      this.tcs.goToUnitByPosition(this.unitData.sequenceId);
+    }
+  }
+}
diff --git a/src/app/test-controller/test-controller.component.html b/src/app/test-controller/test-controller.component.html
index 4350cc88..b5dab95e 100644
--- a/src/app/test-controller/test-controller.component.html
+++ b/src/app/test-controller/test-controller.component.html
@@ -5,10 +5,7 @@
 <div class="page-body" fxLayout="row" iqbResizeIFrameChild>
   <div class="tcSidenav" name="sideNav" fxFlex="110px" fxLayout="column" fxLayoutAlign="start center" fxLayoutGap="5px">
     <p>Aufgaben</p>
-    <button mat-raised-button (click)="sideNaviButtonClick(u.sequenceId)"
-      [disabled]="u.locked" *ngFor="let u of allUnits">
-      {{ u.sequenceId + 1 }}
-    </button>
+    <tc-sidenavi-button *ngFor="let u of allUnits" [unitData]="u"></tc-sidenavi-button>
   </div>
   <div fxFlex>
     <router-outlet></router-outlet>
diff --git a/src/app/test-controller/test-controller.component.ts b/src/app/test-controller/test-controller.component.ts
index 02470afa..bf38963f 100644
--- a/src/app/test-controller/test-controller.component.ts
+++ b/src/app/test-controller/test-controller.component.ts
@@ -1,4 +1,6 @@
-import { BackendService, ServerError, BookletData } from './backend.service';
+import { ServerError } from './../backend.service';
+import { BackendService, BookletData } from './backend.service';
+
 import { LogindataService } from './../logindata.service';
 import { TestControllerService, UnitDef, BookletDef } from './test-controller.service';
 import { Component, OnInit } from '@angular/core';
@@ -10,9 +12,9 @@ import { Component, OnInit } from '@angular/core';
 export class TestControllerComponent implements OnInit {
   private showUnitComponent = false;
   private allUnits: UnitDef[] = [];
-  private errorMsg = '';
   private statusMsg = '';
   private dataLoading = false;
+  private myLastAuthString = ''; // to avoid double load
 
   constructor (
     private tcs: TestControllerService,
@@ -27,44 +29,49 @@ export class TestControllerComponent implements OnInit {
       }
       this.updateStatus();
     });
-    this.lds.globalErrorMsg$.subscribe(m => this.errorMsg = m);
     this.tcs.currentUnitPos$.subscribe(u => this.updateStatus());
   }
 
   ngOnInit() {
-    this.loadBooklet();
+    this.loadBooklet('init');
     this.lds.authorisation$.subscribe(authori => {
-      this.loadBooklet();
+      this.loadBooklet('subsc');
     });
   }
 
-  private loadBooklet() {
+  private loadBooklet(s: string) {
     const auth = this.lds.authorisation$.getValue();
     if (auth == null) {
-      console.log('load booklet (null)');
       this.resetBookletData();
+      this.myLastAuthString = '';
     } else {
-      console.log('load booklet');
-      this.dataLoading = true;
-      this.bs.getBookletData(auth).subscribe(myData => {
-        if (myData instanceof ServerError) {
-          const e = myData as ServerError;
-          this.lds.globalErrorMsg$.next(e.code.toString() + ': ' + e.label);
-          this.tcs.booklet$.next(null);
-          this.tcs.currentUnitPos$.next(-1);
-        } else {
-          this.lds.globalErrorMsg$.next('');
-          const myBookletData = myData as BookletData;
-          const myBookletDef = new BookletDef(myBookletData);
-          myBookletDef.loadUnits(this.bs, auth).subscribe(okList => {
-            this.dataLoading = false;
-            this.tcs.booklet$.next(myBookletDef);
-            this.tcs.showNaviButtons$.next(myBookletDef.unlockedUnitCount() > 1);
+      if (this.myLastAuthString === auth.toAuthString()) {
+        console.log('not doubling');
+      } else {
+        this.myLastAuthString = auth.toAuthString();
+
+        console.log('load booklet ' + auth.toAuthString() + ' ' + s);
+        this.dataLoading = true;
+        this.bs.getBookletData(auth).subscribe(myData => {
+          if (myData instanceof ServerError) {
+            const e = myData as ServerError;
+            this.lds.globalErrorMsg$.next(e);
+            this.tcs.booklet$.next(null);
             this.tcs.currentUnitPos$.next(-1);
-            this.tcs.goToUnitByPosition(myBookletData.u);
-          });
-        }
-      });
+          } else {
+            this.lds.globalErrorMsg$.next(null);
+            const myBookletData = myData as BookletData;
+            const myBookletDef = new BookletDef(myBookletData);
+            myBookletDef.loadUnits(this.bs, this.tcs, auth).subscribe(okList => {
+              this.dataLoading = false;
+              this.tcs.booklet$.next(myBookletDef);
+              this.tcs.showNaviButtons$.next(myBookletDef.unlockedUnitCount() > 1);
+              this.tcs.currentUnitPos$.next(-1);
+              this.tcs.goToUnitByPosition(myBookletData.u);
+            });
+          }
+        });
+      }
     }
   }
 
@@ -86,7 +93,7 @@ export class TestControllerComponent implements OnInit {
     if (cu >= 0) {
       this.statusMsg = '';
     } else {
-      this.tcs.pageTitle$.next('IQB-Testcenter');
+      // this.tcs.pageTitle$.next('IQB-Testcenter');
 
       if (this.allUnits.length === 0) {
         this.statusMsg = 'Es stehen keine Informationen über ein gewähltes Testheft zur Verfügung.';
@@ -107,8 +114,4 @@ export class TestControllerComponent implements OnInit {
     }
     this.showUnitComponent = this.statusMsg.length === 0;
   }
-
-  sideNaviButtonClick(targetId: number) {
-    this.tcs.goToUnitByPosition(targetId);
-  }
 }
diff --git a/src/app/test-controller/test-controller.module.ts b/src/app/test-controller/test-controller.module.ts
index fd1b2604..e71c7cc9 100644
--- a/src/app/test-controller/test-controller.module.ts
+++ b/src/app/test-controller/test-controller.module.ts
@@ -15,6 +15,7 @@ import { TcNaviButtonsComponent } from './tc-navi-buttons/tc-navi-buttons.compon
 import { FlexLayoutModule } from '@angular/flex-layout';
 import { ReviewDialogComponent } from './tc-menu-buttons/review-dialog.component';
 import { ReactiveFormsModule } from '../../../node_modules/@angular/forms';
+import { TcSidenaviButtonComponent } from './tc-sidenavi-button/tc-sidenavi-button.component';
 
 
 @NgModule({
@@ -41,7 +42,8 @@ import { ReactiveFormsModule } from '../../../node_modules/@angular/forms';
     ResizeIFrameChildDirective,
     TcMenuButtonsComponent,
     TcNaviButtonsComponent,
-    ReviewDialogComponent
+    ReviewDialogComponent,
+    TcSidenaviButtonComponent
   ],
   entryComponents: [
     ReviewDialogComponent
diff --git a/src/app/test-controller/test-controller.service.ts b/src/app/test-controller/test-controller.service.ts
index 07a39b37..7e522244 100644
--- a/src/app/test-controller/test-controller.service.ts
+++ b/src/app/test-controller/test-controller.service.ts
@@ -3,7 +3,8 @@ import { LogindataService, Authorisation } from './../logindata.service';
 import { BehaviorSubject, of, Observable, forkJoin, merge } from 'rxjs';
 import { Injectable } from '@angular/core';
 import { debounceTime, bufferTime, switchMap, map } from 'rxjs/operators';
-import { BackendService, BookletData, ServerError, UnitData } from './backend.service';
+import { BackendService, BookletData, UnitData } from './backend.service';
+import { ServerError} from './../backend.service';
 
 @Injectable({
   providedIn: 'root'
@@ -27,6 +28,10 @@ export class TestControllerService {
   public itemplayerPageRequest$ = new BehaviorSubject<string>('');
 
   // ))))))))))))))))))))))))))))))))))))))))))))))))
+  // buffering itemplayers
+
+  private itemplayers: {[filename: string]: string} = {};
+
 
 
   // public unitname$ = new BehaviorSubject<string>('-');
@@ -84,6 +89,7 @@ export class TestControllerService {
     });
   }
 
+  // 66666666666666666666666666666666666666666666666666666666666666666666666666
   getUnitForPlayer(unitId): UnitDef {
     const myBooklet = this.booklet$.getValue();
     if (myBooklet === null) {
@@ -98,12 +104,65 @@ export class TestControllerService {
     return null;
   }
 
+  // 7777777777777777777777777777777777777777777777777777777777777777777777
+  loadItemplayerOk(auth: Authorisation, unitDefinitionType: string): Observable<boolean> {
+    unitDefinitionType = this.normaliseFileName(unitDefinitionType, 'html');
+    if (this.itemplayers.hasOwnProperty(unitDefinitionType)) {
+      return of(true);
+    } else {
+      // to avoid multiple calls before returning:
+      this.itemplayers[unitDefinitionType] = null;
+      return this.bs.getUnitResourceTxt(auth, unitDefinitionType)
+          .pipe(
+            switchMap(myData => {
+              if (myData instanceof ServerError) {
+                return of(false);
+              } else {
+                const itemplayerData = myData as string;
+                if (itemplayerData.length > 0) {
+                  this.itemplayers[unitDefinitionType] = itemplayerData;
+                  return of(true);
+                } else {
+                  return of(false);
+                }
+              }
+            }));
+    }
+  }
+
+  // uppercase and add extension if not part
+  private normaliseFileName(fn: string, ext: string): string {
+    fn = fn.toUpperCase();
+    ext = ext.toUpperCase();
+    if (ext.slice(0, 1) !== '.') {
+      ext = '.' + ext;
+    }
+
+    if (fn.slice(-(ext.length)) === ext) {
+      return fn;
+    } else {
+      return fn + ext;
+    }
+  }
+
+  // 7777777777777777777777777777777777777777777777777777777777777777777777
+  getItemplayer(unitDefinitionType: string): string {
+    unitDefinitionType = this.normaliseFileName(unitDefinitionType, 'html');
+    if ((unitDefinitionType.length > 0) && this.itemplayers.hasOwnProperty(unitDefinitionType)) {
+      return this.itemplayers[unitDefinitionType];
+    } else {
+      return '';
+    }
+  }
+
+
   goToUnitByPosition(pos: number) {
     const myBooklet = this.booklet$.getValue();
     if (myBooklet !== null) {
       const unitCount = myBooklet.units.length;
       if ((pos >= 0 ) && (pos < unitCount)) {
         this.setCurrentUnit(pos);
+        this.bs.setBookletStatus(this.lds.authorisation$.getValue(), {u: pos}).subscribe();
         this.router.navigateByUrl('/t/u/' + pos.toString());
       }
     }
@@ -168,10 +227,10 @@ export class BookletDef {
     }
   }
 
-  loadUnits(bs: BackendService, auth: Authorisation): Observable<boolean[]> {
+  loadUnits(bs: BackendService, tcs: TestControllerService, auth: Authorisation): Observable<boolean[]> {
     const myUnitLoadings = [];
     for (let i = 0; i < this.units.length; i++) {
-      myUnitLoadings.push(this.units[i].loadOk(bs, auth));
+      myUnitLoadings.push(this.units[i].loadOk(bs, tcs, auth));
     }
     return forkJoin(myUnitLoadings);
   }
@@ -259,13 +318,13 @@ export class UnitDef {
     return myResources;
   }
 
-  loadOk(bs: BackendService, auth: Authorisation): Observable<boolean> {
+  loadOk(bs: BackendService, tcs: TestControllerService, auth: Authorisation): Observable<boolean> {
     return bs.getUnitData(auth, this.id)
       .pipe(
         switchMap(myData => {
           if (myData instanceof ServerError) {
             const e = myData as ServerError;
-            this.label = e.code.toString() + ': ' + e.label;
+            this.label = e.code.toString() + ': ' + e.labelNice;
             return of(false);
           } else {
             const myUnitData = myData as UnitData;
@@ -283,8 +342,8 @@ export class UnitDef {
                 this.unitDefinition = defElement.textContent;
                 this.unitDefinitionType = defElement.getAttribute('type');
 
-                return bs.loadItemplayerOk(auth, this.unitDefinitionType).pipe(
-                  map(ok => this.locked = !ok));
+                return tcs.loadItemplayerOk(auth, this.unitDefinitionType).pipe(
+                  switchMap(ok => of(this.locked = !ok)));
                 // ________________________
                 // const resourcesElements = oDOM.documentElement.getElementsByTagName('Resources');
                 // if (resourcesElements.length > 0) {
diff --git a/src/app/test-controller/testdata.service.spec.ts b/src/app/test-controller/testdata.service.spec.ts
deleted file mode 100644
index 64bb3050..00000000
--- a/src/app/test-controller/testdata.service.spec.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { TestBed, inject } from '@angular/core/testing';
-
-import { TestdataService } from './testdata.service';
-
-describe('TestdataService', () => {
-  beforeEach(() => {
-    TestBed.configureTestingModule({
-      providers: [TestdataService]
-    });
-  });
-
-  it('should be created', inject([TestdataService], (service: TestdataService) => {
-    expect(service).toBeTruthy();
-  }));
-});
diff --git a/src/app/test-controller/testdata.service.ts b/src/app/test-controller/testdata.service.ts
deleted file mode 100644
index 7497fc52..00000000
--- a/src/app/test-controller/testdata.service.ts
+++ /dev/null
@@ -1,463 +0,0 @@
-import { BehaviorSubject ,  Observable, Subject } from 'rxjs';
-import { BackendService, BookletData, UnitData, ServerError } from './backend.service';
-import { Injectable, Component, Input, Output, EventEmitter, Pipe } from '@angular/core';
-import { Element } from '@angular/compiler';
-import { mergeAll, switchMap, map } from 'rxjs/operators';
-import { Router } from '@angular/router';
-
-@Injectable()
-export class TestdataService {
-  public isSession$ = new BehaviorSubject<boolean>(false);
-  public statusmessage$ = new BehaviorSubject<string>('Bitte warten!');
-  public bookletname$ = new BehaviorSubject<string>('-');
-  public unitname$ = new BehaviorSubject<string>('-');
-  public pendingItemDefinition$ = new BehaviorSubject<string>('');
-  public pendingItemRestorePoint$ = new BehaviorSubject<string>('');
-  public pendingItemResources$ = new BehaviorSubject<{[resourceID: string]: string}>(null);
-
-  public postMessage$ = new Subject<MessageEvent>();
-
-  public restorePoint$ = new BehaviorSubject<string>('');
-  public response$ = new BehaviorSubject<string>('');
-  public log$ = new BehaviorSubject<string>('');
-  private itemplayers = {};
-  private unitRestorePoints = {};
-
-
-
-  // ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
-  constructor(
-    private bs: BackendService,
-    private router: Router
-  ) {
-/*    this._sessionToken = localStorage.getItem('st');
-    if (this._sessionToken === null) {
-      this._sessionToken = '';
-    }
-    if (this._sessionToken === '') {
-      this.isSession$.next(false);
-    } else {
-      this.isSession$.next(true);
-    }
-
-    this.currentUnitId$.subscribe(myUnitId => {
-      this.navPrevEnabled$.next(myUnitId > 0);
-      this.navNextEnabled$.next((myUnitId >= 0) && (myUnitId < this.allUnits.length - 1));
-    });
-
-    this.postMessage$.subscribe(postData => {
-      const msgData = postData.data;
-      const msgType = msgData['type'];
-      if ((msgType !== undefined) || (msgType !== null)) {
-        if (msgType.substr(0, 7) === 'OpenCBA') {
-          // .........................................
-          const targetWindow = postData.source;
-          if (msgType == 'OpenCBA.newData') {
-            const responseData = playerData['newResponses'];
-            if ((responseData !== undefined) || (responseData !== null)) {
-              this.response$.next(responseData);
-              // this.bs.setUnitResponses(this.sessionToken, this.unitname$.getValue(), responseData)
-              // .subscribe();
-            }
-
-            const restoreData = playerData['newRestorePoint'];
-            if ((restoreData !== undefined) || (restoreData !== null)) {
-              this.restorePoint$.next(restoreData);
-              // this.bs.setUnitRestorePoint(this.sessionToken, this.unitname$.getValue(), restoreData)
-              // .subscribe();
-            }
-
-            const logData = playerData['newLogEntry'];
-            if ((logData !== undefined) || (logData !== null)) {
-              this.log$.next(logData);
-              // this.bs.setUnitLog(this.sessionToken, this.unitname$.getValue(), logData)
-              // .subscribe();
-            }
-          }
-
-    })*/
-/*    this.isSession$.subscribe(isSession => {
-      if (isSession) {
-
-      } else {
-        this.updateBookletData('?', [], '', '');
-      }
-    }); */
-  }
-/*
-  // -----------------------------------------------------------------
-  public addItemPlayer(playerid: string, player: string) {
-    this.itemplayers[playerid] = player;
-  }
-
-  // -----------------------------------------------------------------
-  public getItemPlayer(playerid: string) {
-    if (this.itemplayers.hasOwnProperty(playerid)) {
-      return this.itemplayers[playerid];
-    } else {
-      return '';
-    }
-  }
-
-  // -----------------------------------------------------------------
-  public addUnitRestorePoint(unitId: string, restPoint: string) {
-    this.unitRestorePoints[unitId] = restPoint;
-  }
-
-  // -----------------------------------------------------------------
-  public getUnitRestorePoint(unitId: string) {
-    if (this.unitRestorePoints.hasOwnProperty(unitId)) {
-      return this.unitRestorePoints[unitId];
-    } else {
-      return '';
-    }
-  }
-
-  getUnitAt (unitId: any): Observable<UnitDef | ServerError> {
-    const unitIdNumber = Number(unitId);
-    if (Number.isNaN(unitId) || (unitId < 0)) {
-      return new Observable(observer => {
-        observer.next(null);
-        observer.complete();
-      });
-    } else {
-      if (this.allUnits.length === 0) {
-
-        // first call at the beginning of test -> get booklet
-        return this.bs.getSessionData(this.sessionToken)
-          .pipe(
-            map((bdata: SessionData) => {
-
-            let myBookletName = '';
-
-            // Create Unit-List
-            const oParser = new DOMParser();
-            const oDOM = oParser.parseFromString(bdata.xml, 'text/xml');
-            if (oDOM.documentElement.nodeName === 'Booklet') {
-              // ________________________
-              const metadataElements = oDOM.documentElement.getElementsByTagName('Metadata');
-              if (metadataElements.length > 0) {
-                const metadataElement = metadataElements[0];
-                const NameElement = metadataElement.getElementsByTagName('Name')[0];
-                myBookletName = NameElement.textContent;
-              }
-
-              // ________________________
-              const unitsElements = oDOM.documentElement.getElementsByTagName('Units');
-              if (unitsElements.length > 0) {
-                const unitsElement = unitsElements[0];
-                const unitList = unitsElement.getElementsByTagName('Unit');
-                for (let i = 0; i < unitList.length; i++) {
-                  this.allUnits[i] = new UnitDef(unitList[i].getAttribute('name'), unitList[i].getAttribute('title'));
-                  this.allUnits[i].sequenceId = i;
-                }
-              }
-            }
-            return this.allUnits[unitId];
-          }));
-        } else {
-            // this.updateBookletData(myBookletName, myUnits, 'Bitte warten', bdata.status);
-        // }, (err: ServerError) => {
-            // this.updateBookletData('?', [], err.label, '');
-          return new Observable(observer => {
-            observer.next(this.allUnits[unitId]);
-            observer.complete();
-          });
-      }
-    }
-  }
-
-  // switchMap because current requests have to cancelled if new fetchUnitData-call arrives
-  fetchUnitData (myUnit: UnitDef): Observable<UnitDef> {
-    if (myUnit === null) {
-      return new Observable(observer => {
-        observer.next(null);
-        observer.complete();
-      });
-    } else {
-
-      return this.bs.getUnit(this.sessionToken, myUnit.name)
-        .pipe(
-          switchMap((udata: UnitData) => {
-            myUnit.restorePoint = udata.restorepoint;
-
-            const oParser = new DOMParser();
-            const oDOM = oParser.parseFromString(udata.xml, 'text/xml');
-            if (oDOM.documentElement.nodeName === 'Unit') {
-              // ________________________
-              const dataElements = oDOM.documentElement.getElementsByTagName('Data');
-              if (dataElements.length > 0) {
-                const dataElement = dataElements[0];
-                myUnit.dataForItemplayer = dataElement.textContent;
-              }
-
-              // ________________________
-              const resourcesElements = oDOM.documentElement.getElementsByTagName('Resources');
-              if (resourcesElements.length > 0) {
-                let ResourceFetchPromises: Promise<number>[];
-                ResourceFetchPromises = [];
-
-                // resources ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-                const resourcesElement = resourcesElements[0];
-                const rList = resourcesElement.getElementsByTagName('Resource');
-                for (let i = 0; i < rList.length; i++) {
-                  const myResource = new ResourceData(rList[i].textContent, rList[i].getAttribute('id'));
-                  myResource.type = rList[i].getAttribute('type');
-                  myUnit.resources.push(myResource);
-
-                  // prepare promise for each resource loading
-                  if (myResource.type === 'itemplayer_html') {
-                    ResourceFetchPromises.push(new Promise((resolve, reject) => {
-                      this.bs.getUnitResourceTxt(this.sessionToken, myResource.filename).subscribe(
-                        (fileAsTxt: string) => {
-                          myResource.dataString = fileAsTxt;
-                          resolve(myResource.dataString.length);
-                        }
-                      );
-                    }));
-                  } else {
-                    ResourceFetchPromises.push(new Promise((resolve, reject) => {
-                      this.bs.getUnitResource64(this.sessionToken, myResource.filename).subscribe(
-                        (fileAsBase64: string) => {
-                          myResource.dataString = fileAsBase64;
-                          resolve(myResource.dataString.length);
-                        }
-                      );
-                    }));
-                  }
-                }
-
-                // run all promises (i. e. resource loading requests)
-                return Promise.all(ResourceFetchPromises)
-                  .then(promisesReturnValues => {
-                    this.bs.setBookletStatus(this.sessionToken, {u: myUnit.sequenceId})
-                      .subscribe();
-                    return myUnit;
-                  });
-
-
-              } else {
-                return this.bs.setBookletStatus(this.sessionToken, {u: myUnit.sequenceId})
-                .pipe(
-                  map(d => myUnit)
-                );
-              }
-            } else {
-              return null;
-            }
-          }));
-        }
-  }
-
-  // + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-  getUnitId(target: string): number {
-    let myUnitId = this.currentUnitId$.getValue();
-
-    if (this.allUnits.length > 0) {
-      switch (target) {
-        case 'next':
-          if (myUnitId < this.allUnits.length - 1) {
-            myUnitId = myUnitId + 1;
-          }
-          break;
-
-        case 'prev':
-          if (myUnitId > 0) {
-            myUnitId = myUnitId - 1;
-          }
-          break;
-
-        case 'first':
-          myUnitId = 0;
-          break;
-
-        case 'last':
-          myUnitId = this.allUnits.length - 1;
-          break;
-
-        default:
-          myUnitId = -1;
-          break;
-      }
-    } else {
-      myUnitId = 0;
-    }
-    return myUnitId;
-  }
-
-  // + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-  public gotoPrevUnit() {
-    this.gotoUnit(this.getUnitId('prev'));
-  }
-
-  // + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-  public gotoNextUnit() {
-    this.gotoUnit(this.getUnitId('next'));
-  }
-
-  // + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-  public gotoFirstUnit(lastUnit?: number) {
-    if (lastUnit == null) {
-      this.gotoUnit(this.getUnitId('first'));
-    } else {
-      this.gotoUnit(lastUnit);
-    }
-  }
-
-  // + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-  // + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-  private gotoUnit(newUnitId) {
-    this.router.navigateByUrl('/t/u/' + newUnitId, { skipLocationChange: false });
-  }
-
-  // /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
-  processMessagePost(postData: MessageEvent) {
-    const msgData = postData.data;
-    const msgType = msgData['type'];
-    if ((msgType !== undefined) || (msgType !== null)) {
-      if (msgType.substr(0, 7) === 'OpenCBA') {
-        this.postMessage$.next(postData);
-      }
-    }
-  }
-
-  // app.component.ngOnInit sets a listener on 'message'-event.
-  processMessagePostX(postData: MessageEvent) {
-    const msgData = postData.data;
-    const msgType = msgData['type'];
-    if ((msgType !== undefined) || (msgType !== null)) {
-      if (msgType.substr(0, 7) === 'OpenCBA') {
-        // .........................................
-        const targetWindow = postData.source;
-        switch (msgType) {
-
-          // // // // // // //
-          case 'OpenCBA.stateChanged':
-            if (msgData['newState'] === 'readyToInitialize') {
-              let hasData = false;
-              const initParams = {};
-
-              const pendingSpec = this.pendingItemDefinition$.getValue();
-              if ((pendingSpec !== null) || (pendingSpec.length > 0)) {
-                initParams['itemSpecification'] = pendingSpec;
-                hasData = true;
-                this.pendingItemDefinition$.next(null);
-              }
-              const pendingRes = this.pendingItemResources$.getValue();
-              if ((pendingRes !== null) || (pendingRes !== {})) {
-                initParams['itemResources'] = pendingRes;
-                hasData = true;
-                this.pendingItemResources$.next({});
-              }
-              const pendingRP = this.pendingItemRestorePoint$.getValue();
-              if ((pendingRP !== null) || (pendingRP.length > 0)) {
-                initParams['restorePoint'] = pendingRP;
-                hasData = true;
-                this.pendingItemRestorePoint$.next(null);
-              }
-
-              if (hasData) {
-                targetWindow.postMessage({
-                  type: 'OpenCBA.initItemPlayer',
-                  initParameters: initParams
-                }, '*');
-              }
-            }
-            break;
-
-          // // // // // // //
-          case 'OpenCBA.newData':
-            const responseData = msgData['newResponses'];
-            if ((responseData !== undefined) || (responseData !== null)) {
-              this.bs.setUnitResponses(this.sessionToken, this.unitname$.getValue(), responseData)
-              .subscribe();
-            }
-
-            const restoreData = msgData['newRestorePoint'];
-            if ((restoreData !== undefined) || (restoreData !== null)) {
-              this.bs.setUnitRestorePoint(this.sessionToken, this.unitname$.getValue(), restoreData)
-              .subscribe();
-            }
-
-            const logData = msgData['newLogEntry'];
-            if ((restoreData !== undefined) || (restoreData !== null)) {
-              this.bs.setUnitLog(this.sessionToken, this.unitname$.getValue(), logData)
-              .subscribe();
-            }
-
-            break;
-
-          // // // // // // //
-          default:
-            console.log('processMessagePost unknown message type: ' + msgType);
-            break;
-        }
-
-
-        // .........................................
-      }
-    }
-  }
-  // /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
-
-
-  updateSessionToken(newToken: string) {
-    this._sessionToken = newToken;
-    if ((newToken !== null) && (newToken.length > 0)) {
-      localStorage.setItem('st', newToken);
-      this.isSession$.next(true);
-    } else {
-      localStorage.removeItem('st');
-      this.isSession$.next(false);
-    }
-  }
-
-  updateBookletData(bookletname: string, units: UnitDef[], message: string, status: {}) {
-    this.allUnits = units;
-    this.bookletname$.next(bookletname);
-    this.statusmessage$.next(message);
-
-    if ((status === null) || (status['u'] === undefined)) {
-      this.gotoFirstUnit();
-    } else {
-      this.gotoUnit(status['u']);
-    }
-  }
-
-  updatePageTitle(newTitle: string) {
-    this.pageTitle$.next(newTitle);
-  }
-
-  updateUnitId(newUnitId: number) {
-    if ((newUnitId >= 0) && (newUnitId < this.allUnits.length)) {
-      this.currentUnitId$.next(newUnitId);
-    } else {
-      this.currentUnitId$.next(-1);
-    }
-  }*/
-}
-
-
-
-// .....................................................................
-export class Testlet {
-  private _testlets: Testlet[];
-  private _units: string;
-  constructor() {
-
-  }
-
-}
-
-// .....................................................................
-export interface NavigationPoint {
-  title: string;
-  unitId: number;
-  path: string;
-}
-
-// .....................................................................
-export class ResourceStore {
-
-}
-
diff --git a/src/app/test-controller/unithost/unit-routing.ts b/src/app/test-controller/unithost/unit-routing.ts
index 2cf373e7..f172dac6 100644
--- a/src/app/test-controller/unithost/unit-routing.ts
+++ b/src/app/test-controller/unithost/unit-routing.ts
@@ -1,6 +1,6 @@
 import { UnitDef, TestControllerService } from './../test-controller.service';
 import { switchMap, map } from 'rxjs/operators';
-import { BackendService, ServerError } from './../backend.service';
+import { BackendService } from './../backend.service';
 import { UnithostComponent } from './unithost.component';
 import { Injectable, Component } from '@angular/core';
 import { CanActivate, CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot, Resolve } from '@angular/router';
@@ -28,8 +28,8 @@ export class UnitActivateGuard implements CanActivate {
       const newUnit = currentBooklet.getUnitAt(targetUnitSequenceId);
       if (newUnit.locked) {
         console.log('unit is locked');
-      } else if (!this.bs.isItemplayerReady(newUnit.unitDefinitionType)) {
-        console.log('itemplayer for unit not available');
+      // } else if (!this.bs.isItemplayerReady(newUnit.unitDefinitionType)) {
+      //   console.log('itemplayer for unit not available');
       } else {
         this.tcs.setCurrentUnit(targetUnitSequenceId);
       }
@@ -39,6 +39,13 @@ export class UnitActivateGuard implements CanActivate {
   }
 }
 
+  // // 7777777777777777777777777777777777777777777777777777777777777777777777
+  // isItemplayerReady(unitDefinitionType: string): boolean {
+  //   unitDefinitionType = this.normaliseFileName(unitDefinitionType, 'html');
+  //   return (unitDefinitionType.length > 0) && this.itemplayers.hasOwnProperty(unitDefinitionType);
+  // }
+
+
 @Injectable()
 export class UnitDeactivateGuard implements CanDeactivate<UnithostComponent> {
   constructor(
diff --git a/src/app/test-controller/unithost/unithost.component.html b/src/app/test-controller/unithost/unithost.component.html
index 1a69f66a..8fdfecb6 100644
--- a/src/app/test-controller/unithost/unithost.component.html
+++ b/src/app/test-controller/unithost/unithost.component.html
@@ -1,3 +1,3 @@
-<div id="iFrameHost" class="ityyemplayer-body">
+<div id="iFrameHost">
 
 </div>
diff --git a/src/app/test-controller/unithost/unithost.component.ts b/src/app/test-controller/unithost/unithost.component.ts
index 346cf44b..5c6d9960 100644
--- a/src/app/test-controller/unithost/unithost.component.ts
+++ b/src/app/test-controller/unithost/unithost.component.ts
@@ -1,7 +1,9 @@
-import { debounceTime, bufferTime } from 'rxjs/operators';
+import { Authorisation } from './../../logindata.service';
+import { debounceTime, bufferTime, switchMap } from 'rxjs/operators';
 import { UnitDef, TestControllerService } from './../test-controller.service';
-import { Subscriber, Subscription, BehaviorSubject } from 'rxjs';
-import { BackendService, ServerError } from './../backend.service';
+import { Subscriber, Subscription, BehaviorSubject, Observable, of } from 'rxjs';
+import { BackendService } from './../backend.service';
+import { ServerError } from './../../backend.service';
 import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute } from '@angular/router';
 import { OnDestroy } from '@angular/core/src/metadata/lifecycle_hooks';
@@ -36,6 +38,11 @@ export class UnithostComponent implements OnInit, OnDestroy {
   public response$ = new BehaviorSubject<string>('');
   public log$ = new BehaviorSubject<string>('');
 
+  // buffering restorePoints
+  private lastBookletState = '';
+  private lastUnitResponses = '';
+  private restorePoints: {[unitname: string]: string} = {};
+
   constructor(
     private tcs: TestControllerService,
     private bs: BackendService,
@@ -143,6 +150,7 @@ export class UnithostComponent implements OnInit, OnDestroy {
         }
       }
 
+      this.restorePoints[this.myUnitName] = data;
       this.bs.setUnitRestorePoint(this.lds.authorisation$.getValue(), this.myUnitName, data)
         .subscribe(d => {
           if (d === false) {
@@ -205,7 +213,15 @@ export class UnithostComponent implements OnInit, OnDestroy {
       this.loadItemplayer();
     });
 
-    // this.tcs.currentUnitPos$.subscribe(cu => this.loadItemplayer());
+    this.lds.authorisation$.subscribe(auth => {
+      this.restorePoints = {};
+    });
+
+    this.tcs.currentUnitPos$.subscribe(up => {
+      if (up >= 0) {
+        this.loadItemplayer();
+      }
+    });
   }
 
   loadItemplayer() {
@@ -214,6 +230,7 @@ export class UnithostComponent implements OnInit, OnDestroy {
     }
     const currentUnitId = this.tcs.currentUnitPos$.getValue();
     const booklet = this.tcs.booklet$.getValue();
+    console.log('load Itemplayer 1st');
     if ((currentUnitId >= 0) && (this.myUnitNumber === currentUnitId) && (booklet !== null)) {
       console.log('load Itemplayer - currentUnitId: ' + currentUnitId);
       const currentUnit = booklet.getUnitAt(currentUnitId);
@@ -221,14 +238,14 @@ export class UnithostComponent implements OnInit, OnDestroy {
       this.myUnitName = currentUnit.id;
 
       this.iFrameItemplayer = <HTMLIFrameElement>document.createElement('iframe');
-      this.iFrameItemplayer.setAttribute('srcdoc', this.bs.getItemplayer(currentUnit.unitDefinitionType));
+      this.iFrameItemplayer.setAttribute('srcdoc', this.tcs.getItemplayer(currentUnit.unitDefinitionType));
       this.iFrameItemplayer.setAttribute('sandbox', 'allow-forms allow-scripts allow-same-origin');
       this.iFrameItemplayer.setAttribute('class', 'unitHost');
       const sideNavElement = document.getElementsByName('sideNav')[0];
       this.iFrameItemplayer.setAttribute('height', String(sideNavElement.clientHeight - 5));
 
       this.pendingUnitDefinition$.next(currentUnit.unitDefinition);
-      const restorePoint = this.bs.getUnitRestorePoint(this.myUnitName);
+      const restorePoint = this.restorePoints[this.myUnitName];
 
       if ((restorePoint === null) || (restorePoint === undefined)) {
         this.pendingRestorePoint$.next(currentUnit.restorePoint);
diff --git a/src/environments/environment.ts b/src/environments/environment.ts
index b5d68c88..f3e2e760 100644
--- a/src/environments/environment.ts
+++ b/src/environments/environment.ts
@@ -4,7 +4,7 @@
 
 export const environment = {
   production: false,
-  testcenterUrl: 'https://mdr2.iqb.hu-berlin.de/',
+  testcenterUrl: 'https://ocba.iqb.hu-berlin.de/',
   appName: 'IQB-Testcenter',
   appPublisher: 'IQB - Institut zur Qualitätsentwicklung im Bildungswesen',
   appVersion: '0 (dev)'
-- 
GitLab