export class JSONEditingService implements IJSONEditingService {

	public _serviceBrand: any;

	private queue: Queue<void>;

	constructor(
		@IFileService private readonly fileService: IFileService,
		@ITextModelService private readonly textModelResolverService: ITextModelService,
		@ITextFileService private readonly textFileService: ITextFileService
	) {
		this.queue = new Queue<void>();
	}

	write(resource: URI, value: IJSONValue, save: boolean): Promise<void> {
		return Promise.resolve(this.queue.queue(() => this.doWriteConfiguration(resource, value, save))); // queue up writes to prevent race conditions
	}

	private doWriteConfiguration(resource: URI, value: IJSONValue, save: boolean): Promise<void> {
		return this.resolveAndValidate(resource, save)
			.then(reference => this.writeToBuffer(reference.object.textEditorModel, value)
				.then(() => reference.dispose()));
	}

	private async writeToBuffer(model: ITextModel, value: IJSONValue): Promise<any> {
		const edit = this.getEdits(model, value)[0];
		if (this.applyEditsToBuffer(edit, model)) {
			return this.textFileService.save(model.uri);
		}
	}

	private applyEditsToBuffer(edit: Edit, model: ITextModel): boolean {
		const startPosition = model.getPositionAt(edit.offset);
		const endPosition = model.getPositionAt(edit.offset + edit.length);
		const range = new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column);
		let currentText = model.getValueInRange(range);
		if (edit.content !== currentText) {
			const editOperation = currentText ? EditOperation.replace(range, edit.content) : EditOperation.insert(startPosition, edit.content);
			model.pushEditOperations([new Selection(startPosition.lineNumber, startPosition.column, startPosition.lineNumber, startPosition.column)], [editOperation], () => []);
			return true;
		}
		return false;
	}

	private getEdits(model: ITextModel, configurationValue: IJSONValue): Edit[] {
		const { tabSize, insertSpaces } = model.getOptions();
		const eol = model.getEOL();
		const { key, value } = configurationValue;

		// Without key, the entire settings file is being replaced, so we just use JSON.stringify
		if (!key) {
			const content = JSON.stringify(value, null, insertSpaces ? strings.repeat(' ', tabSize) : '\t');
			return [{
				content,
				length: content.length,
				offset: 0
			}];
		}

		return setProperty(model.getValue(), [key], value, { tabSize, insertSpaces, eol });
	}

	private async resolveModelReference(resource: URI): Promise<IReference<IResolvedTextEditorModel>> {
		const exists = await this.fileService.exists(resource);
		if (!exists) {
			await this.textFileService.write(resource, '{}');
		}
		return this.textModelResolverService.createModelReference(resource);
	}

	private hasParseErrors(model: ITextModel): boolean {
		const parseErrors: json.ParseError[] = [];
		json.parse(model.getValue(), parseErrors);
		return parseErrors.length > 0;
	}

	private resolveAndValidate(resource: URI, checkDirty: boolean): Promise<IReference<IResolvedTextEditorModel>> {
		return this.resolveModelReference(resource)
			.then(reference => {
				const model = reference.object.textEditorModel;

				if (this.hasParseErrors(model)) {
					return this.reject<IReference<IResolvedTextEditorModel>>(JSONEditingErrorCode.ERROR_INVALID_FILE);
				}

				// Target cannot be dirty if not writing into buffer
				if (checkDirty && this.textFileService.isDirty(resource)) {
					return this.reject<IReference<IResolvedTextEditorModel>>(JSONEditingErrorCode.ERROR_FILE_DIRTY);
				}
				return reference;
			});
	}

	private reject<T>(code: JSONEditingErrorCode): Promise<T> {
		const message = this.toErrorMessage(code);
		return Promise.reject(new JSONEditingError(message, code));
	}

	private toErrorMessage(error: JSONEditingErrorCode): string {
		switch (error) {
			// User issues
			case JSONEditingErrorCode.ERROR_INVALID_FILE: {
				return nls.localize('errorInvalidFile', "Unable to write into the file. Please open the file to correct errors/warnings in the file and try again.");
			}
			case JSONEditingErrorCode.ERROR_FILE_DIRTY: {
				return nls.localize('errorFileDirty', "Unable to write into the file because the file is dirty. Please save the file and try again.");
			}
		}
	}
}


write(resource: URI, value: IJSONValue, save: boolean): Promise<void> {
		return Promise.resolve(this.queue.queue(() => this.doWriteConfiguration(resource, value, save))); // queue up writes to prevent race conditions
	}


private doWriteConfiguration(resource: URI, value: IJSONValue, save: boolean): Promise<void> {
		return this.resolveAndValidate(resource, save)
			.then(reference => this.writeToBuffer(reference.object.textEditorModel, value)
				.then(() => reference.dispose()));
	}


private async writeToBuffer(model: ITextModel, value: IJSONValue): Promise<any> {
		const edit = this.getEdits(model, value)[0];
		if (this.applyEditsToBuffer(edit, model)) {
			return this.textFileService.save(model.uri);
		}
	}


private applyEditsToBuffer(edit: Edit, model: ITextModel): boolean {
		const startPosition = model.getPositionAt(edit.offset);
		const endPosition = model.getPositionAt(edit.offset + edit.length);
		const range = new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column);
		let currentText = model.getValueInRange(range);
		if (edit.content !== currentText) {
			const editOperation = currentText ? EditOperation.replace(range, edit.content) : EditOperation.insert(startPosition, edit.content);
			model.pushEditOperations([new Selection(startPosition.lineNumber, startPosition.column, startPosition.lineNumber, startPosition.column)], [editOperation], () => []);
			return true;
		}
		return false;
	}


private getEdits(model: ITextModel, configurationValue: IJSONValue): Edit[] {
		const { tabSize, insertSpaces } = model.getOptions();
		const eol = model.getEOL();
		const { key, value } = configurationValue;

		// Without key, the entire settings file is being replaced, so we just use JSON.stringify
		if (!key) {
			const content = JSON.stringify(value, null, insertSpaces ? strings.repeat(' ', tabSize) : '\t');
			return [{
				content,
				length: content.length,
				offset: 0
			}];
		}

		return setProperty(model.getValue(), [key], value, { tabSize, insertSpaces, eol });
	}


private async resolveModelReference(resource: URI): Promise<IReference<IResolvedTextEditorModel>> {
		const exists = await this.fileService.exists(resource);
		if (!exists) {
			await this.textFileService.write(resource, '{}');
		}
		return this.textModelResolverService.createModelReference(resource);
	}


private hasParseErrors(model: ITextModel): boolean {
		const parseErrors: json.ParseError[] = [];
		json.parse(model.getValue(), parseErrors);
		return parseErrors.length > 0;
	}


private resolveAndValidate(resource: URI, checkDirty: boolean): Promise<IReference<IResolvedTextEditorModel>> {
		return this.resolveModelReference(resource)
			.then(reference => {
				const model = reference.object.textEditorModel;

				if (this.hasParseErrors(model)) {
					return this.reject<IReference<IResolvedTextEditorModel>>(JSONEditingErrorCode.ERROR_INVALID_FILE);
				}

				// Target cannot be dirty if not writing into buffer
				if (checkDirty && this.textFileService.isDirty(resource)) {
					return this.reject<IReference<IResolvedTextEditorModel>>(JSONEditingErrorCode.ERROR_FILE_DIRTY);
				}
				return reference;
			});
	}


private reject<T>(code: JSONEditingErrorCode): Promise<T> {
		const message = this.toErrorMessage(code);
		return Promise.reject(new JSONEditingError(message, code));
	}


private toErrorMessage(error: JSONEditingErrorCode): string {
		switch (error) {
			// User issues
			case JSONEditingErrorCode.ERROR_INVALID_FILE: {
				return nls.localize('errorInvalidFile', "Unable to write into the file. Please open the file to correct errors/warnings in the file and try again.");
			}
			case JSONEditingErrorCode.ERROR_FILE_DIRTY: {
				return nls.localize('errorFileDirty', "Unable to write into the file because the file is dirty. Please save the file and try again.");
			}
		}
	}


function getTextMatchesUnderCursor(
	lineText: string,
	columnNumber: number,
	regex: RegExp,
): string[] | undefined {
	for (const match of lineText.matchAll(regex)) {
		const { index } = match;

		if (index === undefined) {
			continue;
		}

		if (index === -1) {
			continue;
		}

		if (_inRange(columnNumber, index, index + match[0].length)) {
			return Array.from(match);
		}
	}
}


(
	line: vscode.TextLine,
	position: vscode.Position,
): Token | undefined => {
	for (const { type, pattern } of tokenTypePatternMap) {
		let matches = getTextMatchesUnderCursor(
			line.text,
			position.character,
			new RegExp(pattern),
		);

		if (matches !== undefined) {
			return {
				matches,
				type,
			};
		}
	}
}


async (
	glob: string,
): Promise<vscode.Location[] | undefined> => {
	const files = await vscode.workspace.findFiles(glob);

	if (files.length === 0) {
		return;
	}

	return files.map(
		(uri) => new vscode.Location(uri, new vscode.Position(0, 0)),
	);
}


(uri) => new vscode.Location(uri, new vscode.Position(0, 0))


async (
	glob: string,
	search: string,
): Promise<vscode.Location[] | undefined> => {
	const files = await vscode.workspace.findFiles(glob);

	if (files.length === 0) {
		return;
	}

	const lines = await ripgrep({
		search,
		paths: files.map((uri) => uri.fsPath),
		args: ['--vimgrep'],
	});

	return lines.map((s) => {
		const [filepath, lineNumber, columnNumber] = s.split(':');

		return new vscode.Location(
			vscode.Uri.from({
				path: filepath,
				scheme: 'file',
			}),

			new vscode.Position(
				Number(lineNumber) - 1,
				Number(columnNumber) - 1,
			),
		);
	});
}


(uri) => uri.fsPath


(s) => {
		const [filepath, lineNumber, columnNumber] = s.split(':');

		return new vscode.Location(
			vscode.Uri.from({
				path: filepath,
				scheme: 'file',
			}),

			new vscode.Position(
				Number(lineNumber) - 1,
				Number(columnNumber) - 1,
			),
		);
	}


export class DefinitionProviderImpl implements vscode.DefinitionProvider {
	async provideDefinition(
		document: vscode.TextDocument,
		position: vscode.Position,
	): Promise<vscode.Definition | undefined> {
		if (!isGoToDefinitionEnabled()) {
			return;
		}

		const token = getToken(document.lineAt(position), position);

		if (!token) {
			return;
		}

		switch (token.type) {
			case 'className':
				return getFileLocations(
					`**/${token.matches[1]}.{function,macro}`,
				);
			case 'methodInvocation':
				const [, className, methodName] = token.matches;

				return getMethodLocations(
					`**/${className}.{function,macro}`,
					`(macro|function) ${methodName} `,
				);
			case 'pathFileName':
				return getFileLocations(`**/${token.matches[1]}.path`);
			case 'pathLocator':
				const [, fileName, locatorName] = token.matches;

				return getMethodLocations(
					`**/${fileName}.path`,
					`<td>${locatorName}`,
				);
		}
	}
}


interface Token {
	matches: string[];
	type: TokenType;
}


type TokenType =
	| 'className'
	| 'methodInvocation'
	| 'pathFileName'
	| 'pathLocator';


async provideDefinition(
		document: vscode.TextDocument,
		position: vscode.Position,
	): Promise<vscode.Definition | undefined> {
		if (!isGoToDefinitionEnabled()) {
			return;
		}

		const token = getToken(document.lineAt(position), position);

		if (!token) {
			return;
		}

		switch (token.type) {
			case 'className':
				return getFileLocations(
					`**/${token.matches[1]}.{function,macro}`,
				);
			case 'methodInvocation':
				const [, className, methodName] = token.matches;

				return getMethodLocations(
					`**/${className}.{function,macro}`,
					`(macro|function) ${methodName} `,
				);
			case 'pathFileName':
				return getFileLocations(`**/${token.matches[1]}.path`);
			case 'pathLocator':
				const [, fileName, locatorName] = token.matches;

				return getMethodLocations(
					`**/${fileName}.path`,
					`<td>${locatorName}`,
				);
		}
	}


class ProfileUserControler {
  async handle(request: Request, response: Response){
    const user_id  = request.user_id

    const service = new ProfileUserService()
    const result = await service.execite(user_id)

    return response.json(result)
  }
}


async handle(request: Request, response: Response){
    const user_id  = request.user_id

    const service = new ProfileUserService()
    const result = await service.execite(user_id)

    return response.json(result)
  }


() => {
  it('Should be category', () => {
    expect(getFacetType([])).toBe('category')
  })
  it('Should be range', () => {
    expect(getFacetType({})).toBe('range')
  })
  it('Should be text', () => {
    expect(getFacetType('')).toBe('text')
  })
}


() => {
    expect(getFacetType([])).toBe('category')
  }


() => {
    expect(getFacetType({})).toBe('range')
  }


() => {
    expect(getFacetType('')).toBe('text')
  }


() => {
  const state = {
    q: '',
    filters: {
      text: ['text'],
      category1: [['category1']],
      category2: [['category2']],
      range: [{ from: 1, to: 2 }]
    }
  };

  const query = {
    q: '',
    filters: [{
      name: 'text',
      type: 'text',
      values: [{ value: 'text' }]
    }, {
      name: 'category1',
      type: 'category',
      values: [{ value: 'category1' }]
    }, {
      name: 'category2',
      type: 'category',
      values: [{ value: 'category2' }]
    }, {
      name: 'range',
      type: 'range',
      values: [{ from: 1, to: 2 }]
    }]
  };

  it('Should convert state to query', () => {
    expect(stateToQuery(fromJS(state)).toJS()).toEqual(query)
  })

  it('Should convert query to state', () => {
    expect(queryToState(fromJS({ q: true, filters: {} }), fromJS(query)).toJS()).toEqual(state)
  })
}


() => {
    expect(stateToQuery(fromJS(state)).toJS()).toEqual(query)
  }


() => {
    expect(queryToState(fromJS({ q: true, filters: {} }), fromJS(query)).toJS()).toEqual(state)
  }


@NgModule({
  imports: [
    ThemeModule,
    AngularEchartsModule,
  ],
  declarations: [
    DashboardComponent,
    StatusCardComponent,
    TemperatureDraggerComponent,
    ContactsComponent,
    RoomSelectorComponent,
    TemperatureComponent,
    RoomsComponent,
    TeamComponent,
    KittenComponent,
    SecurityCamerasComponent,
    ElectricityComponent,
    ElectricityChartComponent,
    WeatherComponent,
    PlayerComponent,
    SolarComponent,
    TrafficComponent,
    TrafficChartComponent,
  ],
  providers: [
    ApiService,
  ],
})
export class DashboardModule { }


function prepareGuidedTour(guidedTour: GuidedTour) {
            // Prepare GuidedTourService and GuidedTourComponent
            spyOn(guidedTourService, 'init').and.returnValue(of());
            spyOn(guidedTourService, 'getLastSeenTourStepIndex').and.returnValue(0);
            spyOn<any>(guidedTourService, 'checkSelectorValidity').and.returnValue(true);
            spyOn<any>(guidedTourService, 'checkTourState').and.returnValue(true);
            spyOn<any>(guidedTourService, 'updateGuidedTourSettings').and.returnValue(of());
            spyOn<any>(guidedTourService, 'enableTour').and.callFake(() => {
                guidedTourService['availableTourForComponent'] = guidedTour;
                guidedTourService.currentTour = guidedTour;
            });
            spyOn<any>(guidedTourComponent, 'subscribeToDotChanges').and.callFake(() => {});
        }


async function startCourseOverviewTour(guidedTour: GuidedTour) {
            guidedTourComponent.ngAfterViewInit();

            await guidedTourComponentFixture.ngZone!.run(() => {
                router.navigateByUrl('/courses');
            });

            // Start course overview tour
            expect(guidedTourComponentFixture.debugElement.query(By.css('.tour-step'))).to.not.exist;
            guidedTourService['enableTour'](guidedTour, true);
            guidedTourService['startTour']();
            guidedTourComponentFixture.detectChanges();
            expect(guidedTourComponentFixture.debugElement.query(By.css('.tour-step'))).to.exist;
            expect(guidedTourService.isOnFirstStep).to.be.true;
            expect(guidedTourService.currentTourStepDisplay).to.equal(1);
            expect(guidedTourService.currentTourStepCount).to.equal(2);
        }


function resetCurrentTour(): void {
                guidedTourService['currentCourse'] = null;
                guidedTourService['currentExercise'] = null;
                guidedTourService.currentTour = completedTour;
                guidedTourService.resetTour();
            }


function currentCourseAndExerciseNull(): void {
                expect(guidedTourService.currentTour).to.be.null;
                expect(guidedTourService['currentCourse']).to.be.null;
                expect(guidedTourService['currentExercise']).to.be.null;
            }


function prepareParticipation(exercise: Exercise, studentParticipation: StudentParticipation, httpResponse: HttpResponse<StudentParticipation>) {
                    exercise.course = course1;
                    exercise.studentParticipations = [studentParticipation];
                    findParticipationStub.reset();
                    deleteParticipationStub.reset();
                    deleteGuidedTourSettingStub.reset();
                    navigationStub.reset();
                    findParticipationStub.returns(Observable.of(httpResponse));
                    deleteParticipationStub.returns(Observable.of(null));
                    deleteGuidedTourSettingStub.returns(Observable.of(null));
                }


() => {
        let service: GuidedTourService;
        let httpMock: HttpTestingController;
        const expected = new GuidedTourSetting('guided_tour_key', 1, GuidedTourState.STARTED);

        beforeEach(() => {
            TestBed.configureTestingModule({
                imports: [ArtemisTestModule, ArtemisSharedModule, HttpClientTestingModule],
                providers: [
                    { provide: DeviceDetectorService },
                    { provide: LocalStorageService, useClass: MockSyncStorage },
                    { provide: SessionStorageService, useClass: MockSyncStorage },
                ],
            })
                .overrideModule(ArtemisTestModule, { set: { declarations: [], exports: [] } })
                .compileComponents();

            service = TestBed.inject(GuidedTourService);
            httpMock = TestBed.inject(HttpTestingController);
        });

        afterEach(() => {
            httpMock.verify();
        });

        it('should call the correct update URL and return the right JSON object', () => {
            service.guidedTourSettings = [];
            service['updateGuidedTourSettings']('guided_tour_key', 1, GuidedTourState.STARTED).subscribe();
            const req = httpMock.expectOne({ method: 'PUT' });
            const resourceUrl = SERVER_API_URL + 'api/guided-tour-settings';
            expect(req.request.url).equal(`${resourceUrl}`);
            expect(service.guidedTourSettings).to.eql([expected]);
        });

        it('should call the correct delete URL', () => {
            service.guidedTourSettings = [new GuidedTourSetting('guided_tour_key', 1, GuidedTourState.STARTED)];
            service['deleteGuidedTourSetting']('guided_tour_key').subscribe();
            const req = httpMock.expectOne({ method: 'DELETE' });
            const resourceUrl = SERVER_API_URL + 'api/guided-tour-settings';
            expect(req.request.url).equal(`${resourceUrl}/guided_tour_key`);
            expect(service.guidedTourSettings).to.eql([]);
        });
    }


() => {
            TestBed.configureTestingModule({
                imports: [ArtemisTestModule, ArtemisSharedModule, HttpClientTestingModule],
                providers: [
                    { provide: DeviceDetectorService },
                    { provide: LocalStorageService, useClass: MockSyncStorage },
                    { provide: SessionStorageService, useClass: MockSyncStorage },
                ],
            })
                .overrideModule(ArtemisTestModule, { set: { declarations: [], exports: [] } })
                .compileComponents();

            service = TestBed.inject(GuidedTourService);
            httpMock = TestBed.inject(HttpTestingController);
        }


() => {
            httpMock.verify();
        }


() => {
            service.guidedTourSettings = [];
            service['updateGuidedTourSettings']('guided_tour_key', 1, GuidedTourState.STARTED).subscribe();
            const req = httpMock.expectOne({ method: 'PUT' });
            const resourceUrl = SERVER_API_URL + 'api/guided-tour-settings';
            expect(req.request.url).equal(`${resourceUrl}`);
            expect(service.guidedTourSettings).to.eql([expected]);
        }


() => {
            service.guidedTourSettings = [new GuidedTourSetting('guided_tour_key', 1, GuidedTourState.STARTED)];
            service['deleteGuidedTourSetting']('guided_tour_key').subscribe();
            const req = httpMock.expectOne({ method: 'DELETE' });
            const resourceUrl = SERVER_API_URL + 'api/guided-tour-settings';
            expect(req.request.url).equal(`${resourceUrl}/guided_tour_key`);
            expect(service.guidedTourSettings).to.eql([]);
        }


() => {
            TestBed.configureTestingModule({
                imports: [
                    ArtemisTestModule,
                    ArtemisSharedModule,
                    RouterTestingModule.withRoutes([
                        {
                            path: 'courses',
                            component: NavbarComponent,
                        },
                    ]),
                ],
                declarations: [NavbarComponent, GuidedTourComponent],
                providers: [
                    { provide: LocalStorageService, useClass: MockSyncStorage },
                    { provide: SessionStorageService, useClass: MockSyncStorage },
                    { provide: CookieService, useClass: MockCookieService },
                    { provide: AccountService, useClass: MockAccountService },
                    { provide: DeviceDetectorService },
                    { provide: TranslateService, useClass: MockTranslateService },
                ],
            })
                .overrideModule(ArtemisTestModule, { set: { declarations: [], exports: [] } })
                .overrideTemplate(NavbarComponent, '

')
                .compileComponents()
                .then(() => {
                    guidedTourComponentFixture = TestBed.createComponent(GuidedTourComponent);
                    guidedTourComponent = guidedTourComponentFixture.componentInstance;

                    const navBarComponentFixture = TestBed.createComponent(NavbarComponent);
                    // eslint-disable-next-line @typescript-eslint/no-unused-vars
                    const navBarComponent = navBarComponentFixture.componentInstance;

                    router = TestBed.inject(Router);
                    guidedTourService = TestBed.inject(GuidedTourService);
                    participationService = TestBed.inject(ParticipationService);
                    courseService = TestBed.inject(CourseManagementService);

                    findParticipationStub = stub(participationService, 'findParticipation');
                    deleteParticipationStub = stub(participationService, 'deleteForGuidedTour');
                    // @ts-ignore
                    deleteGuidedTourSettingStub = stub(guidedTourService, 'deleteGuidedTourSetting');
                    navigationStub = stub(router, 'navigateByUrl');
                });
        }


() => {
                    guidedTourComponentFixture = TestBed.createComponent(GuidedTourComponent);
                    guidedTourComponent = guidedTourComponentFixture.componentInstance;

                    const navBarComponentFixture = TestBed.createComponent(NavbarComponent);
                    // eslint-disable-next-line @typescript-eslint/no-unused-vars
                    const navBarComponent = navBarComponentFixture.componentInstance;

                    router = TestBed.inject(Router);
                    guidedTourService = TestBed.inject(GuidedTourService);
                    participationService = TestBed.inject(ParticipationService);
                    courseService = TestBed.inject(CourseManagementService);

                    findParticipationStub = stub(participationService, 'findParticipation');
                    deleteParticipationStub = stub(participationService, 'deleteForGuidedTour');
                    // @ts-ignore
                    deleteGuidedTourSettingStub = stub(guidedTourService, 'deleteGuidedTourSetting');
                    navigationStub = stub(router, 'navigateByUrl');
                }


() => {
                guidedTourService['availableTourForComponent'] = guidedTour;
                guidedTourService.currentTour = guidedTour;
            }


() => {
                router.navigateByUrl('/courses');
            }


() => {
            beforeEach(async () => {
                prepareGuidedTour(tour);
                await startCourseOverviewTour(tour);
            });

            it('should start and finish the course overview guided tour', async () => {
                // Navigate to next step
                const nextButton = guidedTourComponentFixture.debugElement.query(By.css('.next-button'));
                expect(nextButton).to.exist;
                nextButton.nativeElement.click();
                expect(guidedTourService.isOnLastStep).to.be.true;

                // Finish guided tour
                nextButton.nativeElement.click();
                guidedTourComponentFixture.detectChanges();
                expect(guidedTourComponentFixture.debugElement.query(By.css('.tour-step'))).to.not.exist;
            });

            it('should start and skip the tour', () => {
                const skipButton = guidedTourComponentFixture.debugElement.query(By.css('.close'));
                expect(skipButton).to.exist;
                skipButton.nativeElement.click();
                guidedTourComponentFixture.detectChanges();
                expect(guidedTourComponentFixture.debugElement.query(By.css('.tour-step'))).to.not.exist;
            });

            it('should prevent backdrop from advancing', () => {
                const backdrop = guidedTourComponentFixture.debugElement.queryAll(By.css('.guided-tour-overlay'));
                expect(backdrop).to.exist;
                expect(backdrop.length).to.equal(4);
                backdrop.forEach((overlay) => {
                    overlay.nativeElement.click();
                });
                guidedTourComponentFixture.detectChanges();
                expect(guidedTourService.isOnFirstStep).to.be.true;
            });
        }


async () => {
                prepareGuidedTour(tour);
                await startCourseOverviewTour(tour);
            }


async () => {
                // Navigate to next step
                const nextButton = guidedTourComponentFixture.debugElement.query(By.css('.next-button'));
                expect(nextButton).to.exist;
                nextButton.nativeElement.click();
                expect(guidedTourService.isOnLastStep).to.be.true;

                // Finish guided tour
                nextButton.nativeElement.click();
                guidedTourComponentFixture.detectChanges();
                expect(guidedTourComponentFixture.debugElement.query(By.css('.tour-step'))).to.not.exist;
            }


() => {
                const skipButton = guidedTourComponentFixture.debugElement.query(By.css('.close'));
                expect(skipButton).to.exist;
                skipButton.nativeElement.click();
                guidedTourComponentFixture.detectChanges();
                expect(guidedTourComponentFixture.debugElement.query(By.css('.tour-step'))).to.not.exist;
            }


() => {
                const backdrop = guidedTourComponentFixture.debugElement.queryAll(By.css('.guided-tour-overlay'));
                expect(backdrop).to.exist;
                expect(backdrop.length).to.equal(4);
                backdrop.forEach((overlay) => {
                    overlay.nativeElement.click();
                });
                guidedTourComponentFixture.detectChanges();
                expect(guidedTourService.isOnFirstStep).to.be.true;
            }


(overlay) => {
                    overlay.nativeElement.click();
                }


() => {
            beforeEach(async () => {
                prepareGuidedTour(tourWithUserInteraction);
                await startCourseOverviewTour(tourWithUserInteraction);
            });

            it('should disable the next button', () => {
                guidedTourComponentFixture.detectChanges();
                const nextButton = guidedTourComponentFixture.debugElement.nativeElement.querySelector('.next-button').disabled;
                expect(nextButton).to.exist;
            });
        }


async () => {
                prepareGuidedTour(tourWithUserInteraction);
                await startCourseOverviewTour(tourWithUserInteraction);
            }


() => {
                guidedTourComponentFixture.detectChanges();
                const nextButton = guidedTourComponentFixture.debugElement.nativeElement.querySelector('.next-button').disabled;
                expect(nextButton).to.exist;
            }


async () => {
                guidedTourService.guidedTourMapping = guidedTourMapping;
                prepareGuidedTour(tourWithCourseAndExercise);
                resetCurrentTour();
            }


() => {
                spyOn(courseService, 'findWithExercises').and.returnValue(of({ body: course1 } as HttpResponse<any>));
                const courses = [course1];

                // enable tour for matching course title
                guidedTourService.enableTourForCourseOverview(courses, tourWithCourseAndExercise, true);
                expect(guidedTourService.currentTour).to.equal(tourWithCourseAndExercise);
                expect(guidedTourService['currentCourse']).to.equal(course1);
                expect(guidedTourService['currentExercise']).to.equal(exercise1);
                resetCurrentTour();

                const tourWithoutExerciseMapping = { courseShortName: 'tutorial', tours: { tour_with_course_and_exercise: '' } } as GuidedTourMapping;
                guidedTourService.guidedTourMapping = tourWithoutExerciseMapping;

                // enable tour for matching course title
                guidedTourService.enableTourForCourseOverview(courses, tourWithCourseAndExercise, true);
                expect(guidedTourService.currentTour).to.equal(tourWithCourseAndExercise);
                expect(guidedTourService['currentCourse']).to.equal(course1);
                expect(guidedTourService['currentExercise']).to.be.null;
                resetCurrentTour();
            }


() => {
                const courses = [course2];
                // disable tour for not matching titles
                guidedTourService.enableTourForCourseOverview(courses, tourWithCourseAndExercise, true);
                currentCourseAndExerciseNull();
            }


() => {
                // disable tour for exercises without courses
                guidedTourService.currentTour = null;
                guidedTourService.enableTourForExercise(exercise1, tourWithCourseAndExercise, true);
                currentCourseAndExerciseNull();
                resetCurrentTour();

                // disable tour for not matching course and exercise identifiers
                exercise2.course = course2;
                guidedTourService.enableTourForExercise(exercise2, tourWithCourseAndExercise, true);
                currentCourseAndExerciseNull();
                resetCurrentTour();

                // disable tour for not matching course identifier
                exercise3.course = course2;
                guidedTourService.enableTourForExercise(exercise3, tourWithCourseAndExercise, true);
                currentCourseAndExerciseNull();
                resetCurrentTour();

                // enable tour for matching course and exercise identifiers
                exercise1.course = course1;
                guidedTourService.enableTourForExercise(exercise1, tourWithCourseAndExercise, true);
                expect(guidedTourService.currentTour).to.equal(tourWithCourseAndExercise);
                expect(guidedTourService['currentCourse']).to.equal(course1);
                expect(guidedTourService['currentExercise']).to.equal(exercise1);
            }


() => {
                guidedTourService.currentTour = null;

                // enable tour for matching course / exercise short name
                guidedTourService.enableTourForCourseExerciseComponent(course1, tourWithCourseAndExercise, true);
                expect(guidedTourService.currentTour).to.equal(tourWithCourseAndExercise);

                course1.exercises.forEach((exercise) => {
                    exercise.course = course1;
                    if (exercise === exercise1) {
                        expect(guidedTourService['isGuidedTourAvailableForExercise'](exercise)).to.be.true;
                    } else {
                        expect(guidedTourService['isGuidedTourAvailableForExercise'](exercise)).to.be.false;
                    }
                });

                // disable tour for not matching course without exercise
                guidedTourService.currentTour = null;
                guidedTourService.enableTourForCourseExerciseComponent(course2, tourWithCourseAndExercise, true);
                expect(guidedTourService.currentTour).to.be.null;

                // disable tour for not matching course but matching exercise identifier
                guidedTourService.currentTour = null;
                course2.exercises = [exercise3];
                guidedTourService.enableTourForCourseExerciseComponent(course2, tourWithCourseAndExercise, true);
                expect(guidedTourService.currentTour).to.be.null;
            }


(exercise) => {
                    exercise.course = course1;
                    if (exercise === exercise1) {
                        expect(guidedTourService['isGuidedTourAvailableForExercise'](exercise)).to.be.true;
                    } else {
                        expect(guidedTourService['isGuidedTourAvailableForExercise'](exercise)).to.be.false;
                    }
                }


() => {
                const studentParticipation1 = { id: 1, student: { id: 1 }, exercise: exercise1, initializationState: InitializationState.INITIALIZED } as StudentParticipation;
                const studentParticipation2 = { id: 2, student: { id: 1 }, exercise: exercise3, initializationState: InitializationState.INITIALIZED } as StudentParticipation;
                const httpResponse1 = { body: studentParticipation1 } as HttpResponse<StudentParticipation>;
                const httpResponse2 = { body: studentParticipation2 } as HttpResponse<StudentParticipation>;
                const exercise4 = { id: 4, title: 'git', type: ExerciseType.MODELING } as Exercise;

                function prepareParticipation(exercise: Exercise, studentParticipation: StudentParticipation, httpResponse: HttpResponse<StudentParticipation>) {
                    exercise.course = course1;
                    exercise.studentParticipations = [studentParticipation];
                    findParticipationStub.reset();
                    deleteParticipationStub.reset();
                    deleteGuidedTourSettingStub.reset();
                    navigationStub.reset();
                    findParticipationStub.returns(Observable.of(httpResponse));
                    deleteParticipationStub.returns(Observable.of(null));
                    deleteGuidedTourSettingStub.returns(Observable.of(null));
                }

                it('should find and delete the student participation for exercise', () => {
                    course1.exercises.push(exercise4);

                    prepareParticipation(exercise1, studentParticipation1, httpResponse1);
                    guidedTourService.enableTourForExercise(exercise1, tourWithCourseAndExercise, true);
                    guidedTourService.restartTour();
                    expect(findParticipationStub).to.have.been.calledOnceWithExactly(1);
                    expect(deleteParticipationStub).to.have.been.calledOnceWithExactly(1, { deleteBuildPlan: true, deleteRepository: true });
                    expect(deleteGuidedTourSettingStub).to.have.been.calledOnceWith('tour_with_course_and_exercise');
                    expect(navigationStub).to.have.been.calledOnceWith('/courses/1/exercises');

                    prepareParticipation(exercise4, studentParticipation2, httpResponse2);
                    guidedTourService.enableTourForExercise(exercise4, tourWithCourseAndExercise, true);
                    guidedTourService.restartTour();
                    expect(findParticipationStub).to.have.been.calledOnceWithExactly(4);
                    expect(deleteParticipationStub).to.have.been.calledOnceWithExactly(2, { deleteBuildPlan: false, deleteRepository: false });
                    expect(deleteGuidedTourSettingStub).to.have.been.calledOnceWith('tour_with_course_and_exercise');
                    expect(navigationStub).to.have.been.calledOnceWith('/courses/1/exercises');

                    const index = course1.exercises.findIndex((exercise) => (exercise.id = exercise4.id));
                    course1.exercises.splice(index, 1);
                });
            }


() => {
                    course1.exercises.push(exercise4);

                    prepareParticipation(exercise1, studentParticipation1, httpResponse1);
                    guidedTourService.enableTourForExercise(exercise1, tourWithCourseAndExercise, true);
                    guidedTourService.restartTour();
                    expect(findParticipationStub).to.have.been.calledOnceWithExactly(1);
                    expect(deleteParticipationStub).to.have.been.calledOnceWithExactly(1, { deleteBuildPlan: true, deleteRepository: true });
                    expect(deleteGuidedTourSettingStub).to.have.been.calledOnceWith('tour_with_course_and_exercise');
                    expect(navigationStub).to.have.been.calledOnceWith('/courses/1/exercises');

                    prepareParticipation(exercise4, studentParticipation2, httpResponse2);
                    guidedTourService.enableTourForExercise(exercise4, tourWithCourseAndExercise, true);
                    guidedTourService.restartTour();
                    expect(findParticipationStub).to.have.been.calledOnceWithExactly(4);
                    expect(deleteParticipationStub).to.have.been.calledOnceWithExactly(2, { deleteBuildPlan: false, deleteRepository: false });
                    expect(deleteGuidedTourSettingStub).to.have.been.calledOnceWith('tour_with_course_and_exercise');
                    expect(navigationStub).to.have.been.calledOnceWith('/courses/1/exercises');

                    const index = course1.exercises.findIndex((exercise) => (exercise.id = exercise4.id));
                    course1.exercises.splice(index, 1);
                }


(exercise) => (exercise.id = exercise4.id)


() => {
            it('should enable the next step if the results are correct', inject(
                [],
                fakeAsync(() => {
                    const enableNextStep = spyOn<any>(guidedTourService, 'enableNextStepClick').and.returnValue(of());
                    guidedTourService.currentTour = tourWithModelingTask;
                    guidedTourService.updateModelingResult(personUML.name, true);
                    tick(0);
                    expect(enableNextStep.calls.count()).to.equal(1);
                }),
            ));
        }


() => {
                    const enableNextStep = spyOn<any>(guidedTourService, 'enableNextStepClick').and.returnValue(of());
                    guidedTourService.currentTour = tourWithModelingTask;
                    guidedTourService.updateModelingResult(personUML.name, true);
                    tick(0);
                    expect(enableNextStep.calls.count()).to.equal(1);
                }


(i) => ({
  key: `${i + 1}`,
  label: `Item ${i + 1}`,
})


({ canvasElement }: { canvasElement: HTMLCanvasElement }) => {
  const canvas = within(canvasElement);
  const thirdItem = canvas.getByText('Item 3', {
    selector: 'button[data-testid="list-item-3"] p',
  });

  userEvent.click(thirdItem);
}


() => (
  <Body noMargin


({
  label,
  showMoreInfo,
  showLoading,
  showClose,
  onBack,
  onClose,
}: DefaultChildrenProps & Partial<ChildrenRenderProps>) => {
  const { setSidePanel } = useSidePanel();

  if (showLoading) {
    return <Body noMargin>{`Loading the details of ${label}...`}</Body>;
  }


(renderProps) => (
                <DefaultChildren
                  label={label}


(props: SidePanelHookProps) => {
  const [selectedItem, setSelectedItem] = useState<string>(null);
  const { setSidePanel } = useSidePanel();

  return (
    <ListItemGroup
      items={items.map((item) => ({
        ...item,
        'variant': 'navigation',
        'selected': item.key === selectedItem,
        'onClick': () => {
          setSidePanel({
            ...props,
            children: <DefaultChildren label={item.label} showMoreInfo />,
            onClose: () => setSelectedItem(null),
          });
          setSelectedItem(item.key);
        }


(item) => ({
        ...item,
        'variant': 'navigation',
        'selected': item.key === selectedItem,
        'onClick': () => {
          setSidePanel({
            ...props,
            children: <DefaultChildren label={item.label} showMoreInfo />,
            onClose: () => setSelectedItem(null),
          })


() => {
          setSidePanel({
            ...props,
            children: <DefaultChildren label={item.label} showMoreInfo />,
            onClose: () => setSelectedItem(null),
          }


() => setSelectedItem(null)


(props: SidePanelHookProps): JSX.Element => (
  <div style={


(props: SidePanelHookProps): JSX.Element => {
  const [isSideNavigationOpen, setSideNavigationOpen] = useState(false);
  const hamburger = {
    activeLabel: 'Close side navigation',
    inactiveLabel: 'Open side navigation',
    isActive: isSideNavigationOpen,
    onClick: () => setSideNavigationOpen((prev) => !prev),
  };
  return (
    <ModalProvider>
      <TopNavigation {...topNavigationProps} hamburger={hamburger} />
      <div style={{ display: 'flex' }}>
        <SideNavigation
          {...sideNavigationProps}
          isOpen={isSideNavigationOpen}
          onClose={()


() => setSideNavigationOpen((prev) => !prev)


(props: SidePanelHookProps) => {
  const [selectedItem, setSelectedItem] = useState<string>(null);
  const { setSidePanel, updateSidePanel, removeSidePanel } = useSidePanel();

  return (
    <ListItemGroup
      items={items.map((item) => ({
        ...item,
        'variant': 'navigation',
        'selected': item.key === selectedItem,
        'onClick': () => {
          if (selectedItem === item.key) {
            setSelectedItem(null);
            removeSidePanel();
          } else {
            setSidePanel({
              ...props,
              children: ({ onClose }) => (
                <DefaultChildren
                  label={item.label}
                  showLoading
                  onClose={onClose}
                />
              ),
              onClose: () => setSelectedItem(null),
            });
            setSelectedItem(item.key);
            setTimeout(() => {
              updateSidePanel({
                children: ({ onClose }) => (
                  <DefaultChildren
                    label={item.label}
                    showMoreInfo
                    showClose
                    onClose={onClose}
                  />
                ),
              });
            }


(item) => ({
        ...item,
        'variant': 'navigation',
        'selected': item.key === selectedItem,
        'onClick': () => {
          if (selectedItem === item.key) {
            setSelectedItem(null);
            removeSidePanel();
          } else {
            setSidePanel({
              ...props,
              children: ({ onClose }) => (
                <DefaultChildren
                  label={item.label}
                  showLoading
                  onClose={onClose}
                />
              ),
              onClose: () => setSelectedItem(null),
            })


() => {
          if (selectedItem === item.key) {
            setSelectedItem(null);
            removeSidePanel();
          } else {
            setSidePanel({
              ...props,
              children: ({ onClose }) => (
                <DefaultChildren
                  label={item.label}
                  showLoading
                  onClose={onClose}
                />
              ),
              onClose: () => setSelectedItem(null),
            }


({ onClose }) => (
                <DefaultChildren
                  label={item.label}


() => {
              updateSidePanel({
                children: ({ onClose }) => (
                  <DefaultChildren
                    label={item.label}
                    showMoreInfo
                    showClose
                    onClose={onClose}
                  />
                ),
              }


({ onClose }) => (
                  <DefaultChildren
                    label={item.label}


type DefaultChildrenProps = {
  label: string;
  showMoreInfo?: boolean;
  showLoading?: boolean;
  showClose?: boolean;
};


spacing('mega')


spacing({ bottom: 'mega' })


spacing({ left: 'mega', bottom: 'mega' })


setSelectedItem(item


/**
 * A {@link PointerEvent} for button presses/releases, containing helpers for
 * checking whether it was the left/primary button, right/secondary button or
 * middle/tertiary button. Always take the button ID into account when handling
 * this event as you get a pair of {@link PointerPress} and
 * {@link PointerRelease} events per button ID.
 *
 * Has a focus type decided by the child classes and does not need focus.
 *
 * @category Event
 */
export declare abstract class PointerButtonEvent extends PointerEvent {
    /**
     * The ID of the button affected.
     *
     * 0: left/primary button.
     * 1: right/secondary button.
     * 2: middle/tertiary button.
     * etc...
     */
    readonly button: number;
    /** Create a new PointerButtonEvent. */
    constructor(x: number, y: number, button: number, shift: boolean, ctrl: boolean, alt: boolean, target?: Widget | null, focusType?: FocusType | null);
    /** Is the button affected the left/primary button? */
    get isLeft(): boolean;
    /** Alias for {@link isLeft} */
    get isPrimary(): boolean;
    /** Is the button affected the right/secondary button? */
    get isRight(): boolean;
    /** Alias for {@link isRight} */
    get isSecondary(): boolean;
    /** Is the button affected the middle/tertiary button? */
    get isMiddle(): boolean;
    /** Alias for {@link isMiddle} */
    get isTertiary(): boolean;
}


(d: any) => {
        return {
          tid: d.tid,
          title: d.title,
          description: d.title,
          category: d.category, // d.category.name
          count: d.viewcount,
          timeinfo: d.timestamp,
          user: d.user,
          upvotes: d.upvotes,
          downvotes: d.downvotes,
          tags: d.tags,
          postcount: d.postcount,
          slug: d.slug,

        }
      }


/* tslint:enable */
@Component({
  selector: 'ws-widget-home-discuss-component',
  templateUrl: './card-home-discuss.component.html',
  styleUrls: ['./card-home-discuss.component.scss'],
  // providers: [DiscussService],
})
export class CardHomeDiscussComponent extends WidgetBaseComponent implements OnInit, NsWidgetResolver.IWidgetData<any> {
  @Input() widgetData: any
  discuss!: NSDiscuss.IDiscuss
  @HostBinding('id')
  public id = 'h-d-component'
  constructor(
    // private discussService: DiscussService,
    private router: Router
  ) {
    super()
  }

  discussionList = Array()
  // starColor: StarRatingColor = StarRatingColor.accent
  // starColorP: StarRatingColor = StarRatingColor.primary
  // starColorW: StarRatingColor = StarRatingColor.warn
  ngOnInit(): void {
    if (this.widgetData && this.widgetData.content) {
      this.discuss = ([this.widgetData.content] || []).map((d: any) => {
        return {
          tid: d.tid,
          title: d.title,
          description: d.title,
          category: d.category, // d.category.name
          count: d.viewcount,
          timeinfo: d.timestamp,
          user: d.user,
          upvotes: d.upvotes,
          downvotes: d.downvotes,
          tags: d.tags,
          postcount: d.postcount,
          slug: d.slug,

        }
      })[0]
    }
  }

  getUserFullName(user: any) {
    if (user && user.username) {
      return `${user.username.trim()}`
    }
    return ''
  }

  fillPopular() {
    // this.discussService.fetchRecentD().subscribe((response: any) => {
    //   this.discuss = _.get(response, 'topics')
    //   // console.log(this.discuss)
    // })
  }

  getDiscussion(discuss: any) {
    // tslint:disable-next-line:max-line-length
    this.router.navigate([`/app/discussion-forum/topic/${_.trim(_.get(discuss, 'slug'))}`], { queryParams: { page: 'home' }, queryParamsHandling: 'merge' })
  }

  public getBgColor(tagTitle: any) {
    const bgColor = this.stringToColor(tagTitle.toLowerCase())
    const color = this.getContrast()
    return { color, 'background-color': bgColor }
  }

  stringToColor(title: any) {
    let hash = 0
    // tslint:disable-next-line: no-bitwise
    for (const element of title) {
      // tslint:disable-next-line: no-bitwise
      hash = title.charCodeAt(element) + ((hash << 5) - hash)
    }
    const hue = Math.abs(hash % 360)
    // tslint:disable-next-line: prefer-template
    const colour = 'hsl(' + hue + ',100%,30%)'
    return colour
  }

  getContrast() {
    return 'rgba(255, 255, 255, 80%)'
  }
}


// starColor: StarRatingColor = StarRatingColor.accent
// starColorP: StarRatingColor = StarRatingColor.primary
// starColorW: StarRatingColor = StarRatingColor.warn
ngOnInit(): void {
    if (this.widgetData && this.widgetData.content) {
      this.discuss = ([this.widgetData.content] || []).map((d: any) => {
        return {
          tid: d.tid,
          title: d.title,
          description: d.title,
          category: d.category, // d.category.name
          count: d.viewcount,
          timeinfo: d.timestamp,
          user: d.user,
          upvotes: d.upvotes,
          downvotes: d.downvotes,
          tags: d.tags,
          postcount: d.postcount,
          slug: d.slug,

        }
      })[0]
    }
  }


getUserFullName(user: any) {
    if (user && user.username) {
      return `${user.username.trim()}`
    }
    return ''
  }


fillPopular() {
    // this.discussService.fetchRecentD().subscribe((response: any) => {
    //   this.discuss = _.get(response, 'topics')
    //   // console.log(this.discuss)
    // })
  }


getDiscussion(discuss: any) {
    // tslint:disable-next-line:max-line-length
    this.router.navigate([`/app/discussion-forum/topic/${_.trim(_.get(discuss, 'slug'))}`], { queryParams: { page: 'home' }, queryParamsHandling: 'merge' })
  }


public getBgColor(tagTitle: any) {
    const bgColor = this.stringToColor(tagTitle.toLowerCase())
    const color = this.getContrast()
    return { color, 'background-color': bgColor }
  }


stringToColor(title: any) {
    let hash = 0
    // tslint:disable-next-line: no-bitwise
    for (const element of title) {
      // tslint:disable-next-line: no-bitwise
      hash = title.charCodeAt(element) + ((hash << 5) - hash)
    }
    const hue = Math.abs(hash % 360)
    // tslint:disable-next-line: prefer-template
    const colour = 'hsl(' + hue + ',100%,30%)'
    return colour
  }


getContrast() {
    return 'rgba(255, 255, 255, 80%)'
  }


@Component({
  selector: 'demo-emoji-counter-detach-invisible',
  templateUrl: './emoji-counter.component.html',
  styleUrls: ['./emoji-counter.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class EmojiCounterDetachInvisibleComponent extends DetachInvisibleDirective {

  @Input() public readonly name: string = 'Emoji Counter Detach Invisible Component';

  @Input() public readonly count: number = 0;

}


@NgModule({
  declarations: [
    ProfileComponent
  ],
  imports: [
    CommonModule,
    FormsModule,
    NgbModule,
    SharedModule,
    profileRouting
  ]
})
export class ProfileModule {}


(argv: argvType) => {
  for (let command of commands) {
    const { done } = <CommandTerminatedType>command(argv);

    if (done) break;
  }
}


@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    AboutComponent,
    ToastComponent,
    SubmitComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    ReactiveFormsModule,
    HttpModule,
    routing,
    SelectModule,
    Ng2UploaderModule
  ],
  providers: [
    DataService,
    ToastComponent
  ],
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  bootstrap: [AppComponent]
})

export class AppModule { }


() => {
  it('should build', () => {
    expect(new KonamiListener()).toBeTruthy();
  });

  describe('keyboard', () => {
    let element: HTMLElement;

    let press = function(key) {
      let event = new KeyboardEvent('keydown', { key });
      document.dispatchEvent(event);
    }

    beforeEach(async () => {
      element = await render({
        components: [KonamiListener],
        html: '<konami-listener></konami-listener>'
      });
    });

    it('should recognize ArrowUp', () => {
      // press('ArrowUp');
      expect(element).toBeTruthy();
    });
  })

  // describe('rendering', () => {
  //   let element;
  //   beforeEach(async () => {
  //     element = await render({
  //       components: [KonamiListener],
  //       html: '<konami-listener></konami-listener>'
  //     });
  //   });

  //   it('should work without parameters', () => {
  //     expect(element..trim()).toEqual('Hello, World! I\'m');
  //   });

  //   it('should work with a first name', async () => {
  //     element.first = 'Peter';
  //     await flush(element);
  //     expect(element.textContent.trim()).toEqual('Hello, World! I\'m Peter');
  //   });

  //   it('should work with a last name', async () => {
  //     element.last = 'Parker';
  //     await flush(element);
  //     expect(element.textContent.trim()).toEqual('Hello, World! I\'m  Parker');
  //   });

  //   it('should work with both a first and a last name', async () => {
  //     element.first = 'Peter'
  //     element.last = 'Parker';
  //     await flush(element);
  //     expect(element.textContent.trim()).toEqual('Hello, World! I\'m Peter Parker');
  //   });
  // });
}


() => {
    expect(new KonamiListener()).toBeTruthy();
  }


() => {
    let element: HTMLElement;

    let press = function(key) {
      let event = new KeyboardEvent('keydown', { key });
      document.dispatchEvent(event);
    }

    beforeEach(async () => {
      element = await render({
        components: [KonamiListener],
        html: '<konami-listener></konami-listener>'
      });
    });

    it('should recognize ArrowUp', () => {
      // press('ArrowUp');
      expect(element).toBeTruthy();
    });
  }


async () => {
      element = await render({
        components: [KonamiListener],
        html: '<konami-listener></konami-listener>'
      });
    }


() => {
      // press('ArrowUp');
      expect(element).toBeTruthy();
    }


export default class PostgresEntityDatabaseAdapterProvider
  implements IEntityDatabaseAdapterProvider
{
  getDatabaseAdapter<TFields>(
    entityConfiguration: EntityConfiguration<TFields>
  ): EntityDatabaseAdapter<TFields> {
    return new PostgresEntityDatabaseAdapter(entityConfiguration);
  }
}


getDatabaseAdapter<TFields>(
    entityConfiguration: EntityConfiguration<TFields>
  ): EntityDatabaseAdapter<TFields> {
    return new PostgresEntityDatabaseAdapter(entityConfiguration);
  }


/**
 * A 2D ray that can be cast into the scene to do collision detection
 */
export class Ray {
  public pos: Vector;
  public dir: Vector;

  /**
   * @param pos The starting position for the ray
   * @param dir The vector indicating the direction of the ray
   */
  constructor(pos: Vector, dir: Vector) {
    this.pos = pos;
    this.dir = dir.normalize();
  }

  /**
   * Tests a whether this ray intersects with a line segment. Returns a number greater than or equal to 0 on success.
   * This number indicates the mathematical intersection time.
   * @param line  The line to test
   */
  public intersect(line: Line): number {
    var numerator = line.begin.sub(this.pos);

    // Test is line and ray are parallel and non intersecting
    if (this.dir.cross(line.getSlope()) === 0 && numerator.cross(this.dir) !== 0) {
      return -1;
    }

    // Lines are parallel
    var divisor = this.dir.cross(line.getSlope());
    if (divisor === 0) {
      return -1;
    }

    var t = numerator.cross(line.getSlope()) / divisor;

    if (t >= 0) {
      var u = numerator.cross(this.dir) / divisor / line.getLength();
      if (u >= 0 && u <= 1) {
        return t;
      }
    }
    return -1;
  }

  /**
   * Returns the point of intersection given the intersection time
   */
  public getPoint(time: number): Vector {
    return this.pos.add(this.dir.scale(time));
  }
}


/**
 * A 2D line segment
 */
export class Line {
  /**
   * @param begin  The starting point of the line segment
   * @param end  The ending point of the line segment
   */
  constructor(public begin: Vector, public end: Vector) {}

  /**
   * Gets the raw slope (m) of the line. Will return (+/-)Infinity for vertical lines.
   */
  public get slope() {
    return (this.end.y - this.begin.y) / (this.end.x - this.begin.x);
  }

  /**
   * Gets the Y-intercept (b) of the line. Will return (+/-)Infinity if there is no intercept.
   */
  public get intercept() {
    return this.begin.y - this.slope * this.begin.x;
  }

  /**
   * Gets the normal of the line
   */
  public normal(): Vector {
    return this.end.sub(this.begin).normal();
  }

  /**
   * Returns the slope of the line in the form of a vector
   */
  public getSlope(): Vector {
    var begin = this.begin;
    var end = this.end;
    var distance = begin.distance(end);
    return end.sub(begin).scale(1 / distance);
  }

  /**
   * Returns the length of the line segment in pixels
   */
  public getLength(): number {
    var begin = this.begin;
    var end = this.end;
    var distance = begin.distance(end);
    return distance;
  }

  /**
   * Find the perpendicular distance from the line to a point
   * https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
   * @param point
   */
  public distanceToPoint(point: Vector) {
    let x0 = point.x;
    let y0 = point.y;

    let l = this.getLength();

    let dy = this.end.y - this.begin.y;
    let dx = this.end.x - this.begin.x;
    let distance = Math.abs(dy * x0 - dx * y0 + this.end.x * this.begin.y - this.end.y * this.begin.x) / l;
    return distance;
  }

  /**
   * Finds a point on the line given only an X or a Y value. Given an X value, the function returns
   * a new point with the calculated Y value and vice-versa.
   *
   * @param x The known X value of the target point
   * @param y The known Y value of the target point
   * @returns A new point with the other calculated axis value
   */
  public findPoint(x: number = null, y: number = null): Vector {
    var m = this.slope;
    var b = this.intercept;

    if (x !== null) {
      return new Vector(x, m * x + b);
    } else if (y !== null) {
      return new Vector((y - b) / m, y);
    } else {
      throw new Error('You must provide an X or a Y value');
    }
  }

  /**
   * Whether or not the given point lies on this line. This method is precise by default
   * meaning the point must lie exactly on the line. Adjust threshold to
   * loosen the strictness of the check for floating-point calculations.
   */
  public hasPoint(x: number, y: number, threshold?: number): boolean;

  /**
   * Whether or not the given point lies on this line. This method is precise by default
   * meaning the point must lie exactly on the line. Adjust threshold to
   * loosen the strictness of the check for floating-point calculations.
   */
  public hasPoint(v: Vector, threshold?: number): boolean;

  /**
   * @see http://stackoverflow.com/a/11908158/109458
   */
  public hasPoint(): boolean {
    var currPoint: Vector;
    var threshold = 0;

    if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
      currPoint = new Vector(arguments[0], arguments[1]);
      threshold = arguments[2] || 0;
    } else if (arguments[0] instanceof Vector) {
      currPoint = arguments[0];
      threshold = arguments[1] || 0;
    } else {
      throw 'Could not determine the arguments for Vector.hasPoint';
    }

    var dxc = currPoint.x - this.begin.x;
    var dyc = currPoint.y - this.begin.y;

    var dx1 = this.end.x - this.begin.x;
    var dy1 = this.end.y - this.begin.y;

    var cross = dxc * dy1 - dyc * dx1;

    // check whether point lines on the line
    if (Math.abs(cross) > threshold) {
      return false;
    }

    // check whether point lies in-between start and end
    if (Math.abs(dx1) >= Math.abs(dy1)) {
      return dx1 > 0 ? this.begin.x <= currPoint.x && currPoint.x <= this.end.x : this.end.x <= currPoint.x && currPoint.x <= this.begin.x;
    } else {
      return dy1 > 0 ? this.begin.y <= currPoint.y && currPoint.y <= this.end.y : this.end.y <= currPoint.y && currPoint.y <= this.begin.y;
    }
  }
}


/**
 * A 1 dimensional projection on an axis, used to test overlaps
 */
export class Projection {
  constructor(public min: number, public max: number) {}
  public overlaps(projection: Projection): boolean {
    return this.max > projection.min && projection.max > this.min;
  }

  public getOverlap(projection: Projection): number {
    if (this.overlaps(projection)) {
      if (this.max > projection.max) {
        return projection.max - this.min;
      } else {
        return this.max - projection.min;
      }
    }
    return 0;
  }
}


export class GlobalCoordinates {
  public static fromPagePosition(x: number, y: number, engine: Engine): GlobalCoordinates;
  public static fromPagePosition(pos: Vector, engine: Engine): GlobalCoordinates;
  public static fromPagePosition(xOrPos: number | Vector, yOrEngine: number | Engine, engineOrUndefined?: Engine): GlobalCoordinates {
    var pageX: number;
    var pageY: number;
    var pagePos: Vector;
    var engine: Engine;

    if (arguments.length === 3) {
      pageX = <number>xOrPos;
      pageY = <number>yOrEngine;
      pagePos = new Vector(pageX, pageY);
      engine = engineOrUndefined;
    } else {
      pagePos = <Vector>xOrPos;
      pageX = pagePos.x;
      pageY = pagePos.y;
      engine = <Engine>yOrEngine;
    }

    var screenX: number = pageX - Util.getPosition(engine.canvas).x;
    var screenY: number = pageY - Util.getPosition(engine.canvas).y;
    var screenPos = new Vector(screenX, screenY);
    var worldPos = engine.screenToWorldCoordinates(screenPos);

    return new GlobalCoordinates(worldPos, pagePos, screenPos);
  }

  constructor(public worldPos: Vector, public pagePos: Vector, public screenPos: Vector) {}
}


/**
   * Returns a vector of unit length in the direction of the specified angle in Radians.
   * @param angle The angle to generate the vector
   */
public static fromAngle(angle: number) {
    return new Vector(Math.cos(angle), Math.sin(angle));
  }


/**
   * Checks if vector is not null, undefined, or if any of its components are NaN or Infinity.
   */
public static isValid(vec: Vector) {
    if (vec === null || vec === undefined) {
      return false;
    }
    if (isNaN(vec.x) || isNaN(vec.y)) {
      return false;
    }

    if (vec.x === Infinity || vec.y === Infinity || vec.x === -Infinity || vec.y === -Infinity) {
      return false;
    }

    return true;
  }


/**
   * Calculates distance between two Vectors
   * @param vec1
   * @param vec2
   */
public static distance(vec1: Vector, vec2: Vector) {
    return Math.sqrt(Math.pow(vec1.x - vec2.x, 2) + Math.pow(vec1.y - vec2.y, 2));
  }


/**
   * Sets the x and y components at once
   */
public setTo(x: number, y: number) {
    this.x = x;
    this.y = y;
  }


/**
   * Compares this point against another and tests for equality
   * @param point  The other point to compare to
   */
public equals(vector: Vector, tolerance: number = 0.001): boolean {
    return Math.abs(this.x - vector.x) <= tolerance && Math.abs(this.y - vector.y) <= tolerance;
  }


/**
   * The distance to another vector. If no other Vector is specified, this will return the [[magnitude]].
   * @param v  The other vector. Leave blank to use origin vector.
   */
public distance(v?: Vector): number {
    if (!v) {
      v = Vector.Zero;
    }
    return Math.sqrt(Math.pow(this.x - v.x, 2) + Math.pow(this.y - v.y, 2));
  }


/**
   * The magnitude (size) of the Vector
   */
public magnitude(): number {
    return this.distance();
  }


/**
   * Normalizes a vector to have a magnitude of 1.
   */
public normalize(): Vector {
    var d = this.distance();
    if (d > 0) {
      return new Vector(this.x / d, this.y / d);
    } else {
      return new Vector(0, 1);
    }
  }


/**
   * Returns the average (midpoint) between the current point and the specified
   */
public average(vec: Vector): Vector {
    return this.add(vec).scale(0.5);
  }


/**
   * Scales a vector's by a factor of size
   * @param size  The factor to scale the magnitude by
   */
public scale(size: number): Vector {
    return new Vector(this.x * size, this.y * size);
  }


/**
   * Adds one vector to another
   * @param v The vector to add
   */
public add(v: Vector): Vector {
    return new Vector(this.x + v.x, this.y + v.y);
  }


/**
   * Subtracts a vector from another, if you subract vector `B.sub(A)` the resulting vector points from A -> B
   * @param v The vector to subtract
   */
public sub(v: Vector): Vector {
    return new Vector(this.x - v.x, this.y - v.y);
  }


/**
   * Adds one vector to this one modifying the original
   * @param v The vector to add
   */
public addEqual(v: Vector): Vector {
    this.x += v.x;
    this.y += v.y;
    return this;
  }


/**
   * Subtracts a vector from this one modifying the original
   * @parallel v The vector to subtract
   */
public subEqual(v: Vector): Vector {
    this.x -= v.x;
    this.y -= v.y;
    return this;
  }


/**
   * Scales this vector by a factor of size and modifies the original
   */
public scaleEqual(size: number): Vector {
    this.x *= size;
    this.y *= size;
    return this;
  }


/**
   * Performs a dot product with another vector
   * @param v  The vector to dot
   */
public dot(v: Vector): number {
    return this.x * v.x + this.y * v.y;
  }


/**
   * Performs a 2D cross product with scalar. 2D cross products with a scalar return a vector.
   * @param v  The scalar to cross
   */
public cross(v: number): Vector;


/**
   * Performs a 2D cross product with another vector. 2D cross products return a scalar value not a vector.
   * @param v  The vector to cross
   */
public cross(v: Vector): number;


public cross(v: any): any {
    if (v instanceof Vector) {
      return this.x * v.y - this.y * v.x;
    } else if (typeof v === 'number') {
      return new Vector(v * this.y, -v * this.x);
    }
  }


/**
   * Returns the perpendicular vector to this one
   */
public perpendicular(): Vector {
    return new Vector(this.y, -this.x);
  }


/**
   * Returns the normal vector to this one, same as the perpendicular of length 1
   */
public normal(): Vector {
    return this.perpendicular().normalize();
  }


/**
   * Negate the current vector
   */
public negate(): Vector {
    return this.scale(-1);
  }


/**
   * Returns the angle of this vector.
   */
public toAngle(): number {
    return Math.atan2(this.y, this.x);
  }


/**
   * Rotates the current vector around a point by a certain number of
   * degrees in radians
   */
public rotate(angle: number, anchor?: Vector): Vector {
    if (!anchor) {
      anchor = new Vector(0, 0);
    }
    var sinAngle = Math.sin(angle);
    var cosAngle = Math.cos(angle);
    var x = cosAngle * (this.x - anchor.x) - sinAngle * (this.y - anchor.y) + anchor.x;
    var y = sinAngle * (this.x - anchor.x) + cosAngle * (this.y - anchor.y) + anchor.y;
    return new Vector(x, y);
  }


/**
   * Creates new vector that has the same values as the previous.
   */
public clone(): Vector {
    return new Vector(this.x, this.y);
  }


/**
   * Returns a string repesentation of the vector.
   */
public toString(): string {
    return `(${this.x}, ${this.y})`;
  }


/**
   * Tests a whether this ray intersects with a line segment. Returns a number greater than or equal to 0 on success.
   * This number indicates the mathematical intersection time.
   * @param line  The line to test
   */
public intersect(line: Line): number {
    var numerator = line.begin.sub(this.pos);

    // Test is line and ray are parallel and non intersecting
    if (this.dir.cross(line.getSlope()) === 0 && numerator.cross(this.dir) !== 0) {
      return -1;
    }

    // Lines are parallel
    var divisor = this.dir.cross(line.getSlope());
    if (divisor === 0) {
      return -1;
    }

    var t = numerator.cross(line.getSlope()) / divisor;

    if (t >= 0) {
      var u = numerator.cross(this.dir) / divisor / line.getLength();
      if (u >= 0 && u <= 1) {
        return t;
      }
    }
    return -1;
  }


/**
   * Returns the point of intersection given the intersection time
   */
public getPoint(time: number): Vector {
    return this.pos.add(this.dir.scale(time));
  }


/**
   * Gets the normal of the line
   */
public normal(): Vector {
    return this.end.sub(this.begin).normal();
  }


/**
   * Returns the slope of the line in the form of a vector
   */
public getSlope(): Vector {
    var begin = this.begin;
    var end = this.end;
    var distance = begin.distance(end);
    return end.sub(begin).scale(1 / distance);
  }


/**
   * Returns the length of the line segment in pixels
   */
public getLength(): number {
    var begin = this.begin;
    var end = this.end;
    var distance = begin.distance(end);
    return distance;
  }


/**
   * Find the perpendicular distance from the line to a point
   * https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
   * @param point
   */
public distanceToPoint(point: Vector) {
    let x0 = point.x;
    let y0 = point.y;

    let l = this.getLength();

    let dy = this.end.y - this.begin.y;
    let dx = this.end.x - this.begin.x;
    let distance = Math.abs(dy * x0 - dx * y0 + this.end.x * this.begin.y - this.end.y * this.begin.x) / l;
    return distance;
  }


/**
   * Finds a point on the line given only an X or a Y value. Given an X value, the function returns
   * a new point with the calculated Y value and vice-versa.
   *
   * @param x The known X value of the target point
   * @param y The known Y value of the target point
   * @returns A new point with the other calculated axis value
   */
public findPoint(x: number = null, y: number = null): Vector {
    var m = this.slope;
    var b = this.intercept;

    if (x !== null) {
      return new Vector(x, m * x + b);
    } else if (y !== null) {
      return new Vector((y - b) / m, y);
    } else {
      throw new Error('You must provide an X or a Y value');
    }
  }


/**
   * Whether or not the given point lies on this line. This method is precise by default
   * meaning the point must lie exactly on the line. Adjust threshold to
   * loosen the strictness of the check for floating-point calculations.
   */
public hasPoint(x: number, y: number, threshold?: number): boolean;


/**
   * Whether or not the given point lies on this line. This method is precise by default
   * meaning the point must lie exactly on the line. Adjust threshold to
   * loosen the strictness of the check for floating-point calculations.
   */
public hasPoint(v: Vector, threshold?: number): boolean;


/**
   * @see http://stackoverflow.com/a/11908158/109458
   */
public hasPoint(): boolean {
    var currPoint: Vector;
    var threshold = 0;

    if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
      currPoint = new Vector(arguments[0], arguments[1]);
      threshold = arguments[2] || 0;
    } else if (arguments[0] instanceof Vector) {
      currPoint = arguments[0];
      threshold = arguments[1] || 0;
    } else {
      throw 'Could not determine the arguments for Vector.hasPoint';
    }

    var dxc = currPoint.x - this.begin.x;
    var dyc = currPoint.y - this.begin.y;

    var dx1 = this.end.x - this.begin.x;
    var dy1 = this.end.y - this.begin.y;

    var cross = dxc * dy1 - dyc * dx1;

    // check whether point lines on the line
    if (Math.abs(cross) > threshold) {
      return false;
    }

    // check whether point lies in-between start and end
    if (Math.abs(dx1) >= Math.abs(dy1)) {
      return dx1 > 0 ? this.begin.x <= currPoint.x && currPoint.x <= this.end.x : this.end.x <= currPoint.x && currPoint.x <= this.begin.x;
    } else {
      return dy1 > 0 ? this.begin.y <= currPoint.y && currPoint.y <= this.end.y : this.end.y <= currPoint.y && currPoint.y <= this.begin.y;
    }
  }


public overlaps(projection: Projection): boolean {
    return this.max > projection.min && projection.max > this.min;
  }


public getOverlap(projection: Projection): number {
    if (this.overlaps(projection)) {
      if (this.max > projection.max) {
        return projection.max - this.min;
      } else {
        return this.max - projection.min;
      }
    }
    return 0;
  }


public static fromPagePosition(x: number, y: number, engine: Engine): GlobalCoordinates;


public static fromPagePosition(pos: Vector, engine: Engine): GlobalCoordinates;


public static fromPagePosition(xOrPos: number | Vector, yOrEngine: number | Engine, engineOrUndefined?: Engine): GlobalCoordinates {
    var pageX: number;
    var pageY: number;
    var pagePos: Vector;
    var engine: Engine;

    if (arguments.length === 3) {
      pageX = <number>xOrPos;
      pageY = <number>yOrEngine;
      pagePos = new Vector(pageX, pageY);
      engine = engineOrUndefined;
    } else {
      pagePos = <Vector>xOrPos;
      pageX = pagePos.x;
      pageY = pagePos.y;
      engine = <Engine>yOrEngine;
    }

    var screenX: number = pageX - Util.getPosition(engine.canvas).x;
    var screenY: number = pageY - Util.getPosition(engine.canvas).y;
    var screenPos = new Vector(screenX, screenY);
    var worldPos = engine.screenToWorldCoordinates(screenPos);

    return new GlobalCoordinates(worldPos, pagePos, screenPos);
  }


/**
 * A set of methods for managing API keys.
 */
export class ApiKeyService {
  constructor(session?: Session);
  /**
   * Retrieves the list of API keys for the specified service account.
   */
  list(request: ListApiKeysRequest): Promise<ListApiKeysResponse>;

  /**
   * Returns the specified API key.
   *
   * To get the list of available API keys, make a [List] request.
   */
  get(request: GetApiKeyRequest): Promise<ApiKey>;

  /**
   * Creates an API key for the specified service account.
   */
  create(request: CreateApiKeyRequest): Promise<CreateApiKeyResponse>;

  /**
   * Deletes the specified API key.
   */
  delete(request: DeleteApiKeyRequest): Promise<protobuf.Empty>;
}


/**
 * A set of methods for managing IAM tokens.
 */
export class IamTokenService {
  constructor(session?: Session);
  /**
   * Creates an IAM token for the specified identity.
   */
  create(request: CreateIamTokenRequest): Promise<CreateIamTokenResponse>;
}


/**
 * A set of methods for managing Key resources.
 */
export class KeyService {
  constructor(session?: Session);
  /**
   * Returns the specified Key resource.
   *
   * To get the list of available Key resources, make a [List] request.
   */
  get(request: GetKeyRequest): Promise<Key>;

  /**
   * Retrieves the list of Key resources for the specified service account.
   */
  list(request: ListKeysRequest): Promise<ListKeysResponse>;

  /**
   * Creates a key pair for the specified service account.
   */
  create(request: CreateKeyRequest): Promise<CreateKeyResponse>;

  /**
   * Deletes the specified key pair.
   */
  delete(request: DeleteKeyRequest): Promise<protobuf.Empty>;
}


/**
 * A set of methods for managing Role resources.
 */
export class RoleService {
  constructor(session?: Session);
  /**
   * Returns the specified Role resource.
   *
   * To get the list of available Role resources, use a [List] request.
   */
  get(request: GetRoleRequest): Promise<Role>;

  /**
   * Retrieves the list of Role resources.
   */
  list(request: ListRolesRequest): Promise<ListRolesResponse>;
}


/**
 * A set of methods for managing ServiceAccount resources.
 */
export class ServiceAccountService {
  constructor(session?: Session);
  /**
   * Returns the specified ServiceAccount resource.
   *
   * To get the list of available ServiceAccount resources, use a [List] request.
   */
  get(request: GetServiceAccountRequest): Promise<ServiceAccount>;

  /**
   * Retrieves the list of ServiceAccount resources in the specified folder.
   */
  list(request: ListServiceAccountsRequest): Promise<ListServiceAccountsResponse>;

  /**
   * Creates a service account in the specified folder.
   */
  create(request: CreateServiceAccountRequest): Promise<operation.Operation>;

  /**
   * Updates the specified service account.
   */
  update(request: UpdateServiceAccountRequest): Promise<operation.Operation>;

  /**
   * Deletes the specified service account.
   */
  delete(request: DeleteServiceAccountRequest): Promise<operation.Operation>;

  /**
   * Lists access bindings for the specified service account.
   */
  listAccessBindings(request: access.ListAccessBindingsRequest): Promise<access.ListAccessBindingsResponse>;

  /**
   * Sets access bindings for the service account.
   */
  setAccessBindings(request: access.SetAccessBindingsRequest): Promise<operation.Operation>;

  /**
   * Updates access bindings for the specified service account.
   */
  updateAccessBindings(request: access.UpdateAccessBindingsRequest): Promise<operation.Operation>;

  /**
   * Lists operations for the specified service account.
   */
  listOperations(request: ListServiceAccountOperationsRequest): Promise<ListServiceAccountOperationsResponse>;
}


/**
 * A set of methods for managing user accounts. Currently applicable only for [Yandex.Passport accounts](/docs/iam/concepts/#passport).
 */
export class UserAccountService {
  constructor(session?: Session);
  /**
   * Returns the specified UserAccount resource.
   */
  get(request: GetUserAccountRequest): Promise<UserAccount>;
}


/**
 * A set of methods for managing YandexPassportUserAccount resources.
 */
export class YandexPassportUserAccountService {
  constructor(session?: Session);
  /**
   * Returns the specified YandexPassportUserAccount resource.
   */
  getByLogin(request: GetUserAccountByLoginRequest): Promise<UserAccount>;
}


/**
 * An ApiKey resource.
 */
export interface ApiKey {
  /**
   * ID of the API Key.
   */
  id?: string;

  /**
   * ID of the service account that the API key belongs to.
   */
  serviceAccountId?: string;

  /**
   * Creation timestamp.
   */
  createdAt?: protobuf.Timestamp;

  /**
   * Description of the API key. 0-256 characters long.
   */
  description?: string;
}


export interface GetApiKeyRequest {
  /**
   * ID of the API key to return.
   * To get the API key ID, use a [ApiKeyService.List] request.
   */
  apiKeyId: string;
}


export interface ListApiKeysRequest {
  /**
   * ID of the service account to list API keys for.
   * To get the service account ID, use a [yandex.cloud.iam.v1.ServiceAccountService.List] request.
   * If not specified, it defaults to the subject that made the request.
   */
  serviceAccountId?: string;

  /**
   * The maximum number of results per page to return. If the number of available
   * results is larger than [page_size],
   * the service returns a [ListApiKeysResponse.next_page_token]
   * that can be used to get the next page of results in subsequent list requests.
   * Default value: 100.
   */
  pageSize?: Long;

  /**
   * Page token. To get the next page of results, set [page_token]
   * to the [ListApiKeysResponse.next_page_token]
   * returned by a previous list request.
   */
  pageToken?: string;
}


export interface ListApiKeysResponse {
  /**
   * List of API keys.
   */
  apiKeys?: ApiKey[];

  /**
   * This token allows you to get the next page of results for list requests. If the number of results
   * is larger than [ListApiKeysRequest.page_size], use
   * the [next_page_token] as the value
   * for the [ListApiKeysRequest.page_token] query parameter
   * in the next list request. Each subsequent list request will have its own
   * [next_page_token] to continue paging through the results.
   */
  nextPageToken?: string;
}


export interface CreateApiKeyRequest {
  /**
   * ID of the service account to create an API key for.
   * To get the service account ID, use a [yandex.cloud.iam.v1.ServiceAccountService.List] request.
   * If not specified, it defaults to the subject that made the request.
   */
  serviceAccountId?: string;

  /**
   * Description of the API key.
   */
  description?: string;
}


export interface CreateApiKeyResponse {
  /**
   * ApiKey resource.
   */
  apiKey?: ApiKey;

  /**
   * Secret part of the API key. This secret key you may use in the requests for authentication.
   */
  secret?: string;
}


export interface DeleteApiKeyRequest {
  /**
   * ID of the API key to delete.
   * To get the API key ID, use a [ApiKeyService.List] request.
   */
  apiKeyId: string;
}


export interface CreateIamTokenRequest {
  /**
   * OAuth token for a Yandex.Passport account.
   * For more information, see [OAuth token](/docs/iam/concepts/authorization/oauth-token).
   */
  yandexPassportOauthToken?: string;

  /**
   * JSON Web Token (JWT) for a service account.
   * For more information, see [Get IAM token for a service account](/docs/iam/operations/iam-token/create-for-sa).
   */
  jwt?: string;
}


export interface CreateIamTokenResponse {
  /**
   * IAM token for the specified identity.
   *
   * You should pass the token in the `Authorization` header for any further API requests.
   * For example, `Authorization: Bearer [iam_token]`.
   */
  iamToken?: string;

  /**
   * IAM token expiration time.
   */
  expiresAt?: protobuf.Timestamp;
}


/**
 * A Key resource. For more information, see [Authorized keys](/docs/iam/concepts/authorization/key).
 */
export interface Key {
  /**
   * ID of the Key resource.
   */
  id?: string;

  /**
   * ID of the user account that the Key resource belongs to.
   */
  userAccountId?: string;

  /**
   * ID of the service account that the Key resource belongs to.
   */
  serviceAccountId?: string;

  /**
   * Creation timestamp.
   */
  createdAt?: protobuf.Timestamp;

  /**
   * Description of the Key resource. 0-256 characters long.
   */
  description?: string;

  /**
   * An algorithm used to generate a key pair of the Key resource.
   */
  keyAlgorithm?: Key.Algorithm;

  /**
   * A public key of the Key resource.
   */
  publicKey?: string;
}


export interface GetKeyRequest {
  /**
   * ID of the Key resource to return.
   * To get the ID use a [KeyService.List] request.
   */
  keyId: string;

  /**
   * Output format of the key.
   */
  format?: KeyFormat;
}


export interface ListKeysRequest {
  /**
   * Output format of the key.
   */
  format?: KeyFormat;

  /**
   * ID of the service account to list key pairs for.
   * To get the service account ID, use a [yandex.cloud.iam.v1.ServiceAccountService.List] request.
   * If not specified, it defaults to the subject that made the request.
   */
  serviceAccountId?: string;

  /**
   * The maximum number of results per page to return. If the number of available
   * results is larger than [page_size],
   * the service returns a [ListKeysResponse.next_page_token]
   * that can be used to get the next page of results in subsequent list requests.
   * Default value: 100.
   */
  pageSize?: Long;

  /**
   * Page token. To get the next page of results, set [page_token] to the
   * [ListKeysResponse.next_page_token] returned by a previous list request.
   */
  pageToken?: string;
}


export interface ListKeysResponse {
  /**
   * List of Key resources.
   */
  keys?: Key[];

  /**
   * This token allows you to get the next page of results for list requests. If the number of results
   * is larger than [ListKeysRequest.page_size], use
   * the [next_page_token] as the value
   * for the [ListKeysRequest.page_token] query parameter
   * in the next list request. Each subsequent list request will have its own
   * [next_page_token] to continue paging through the results.
   */
  nextPageToken?: string;
}


export interface CreateKeyRequest {
  /**
   * ID of the service account to create a key pair for.
   * To get the service account ID, use a [yandex.cloud.iam.v1.ServiceAccountService.List] request.
   * If not specified, it defaults to the subject that made the request.
   */
  serviceAccountId?: string;

  /**
   * Description of the key pair.
   */
  description?: string;

  /**
   * Output format of the key.
   */
  format?: KeyFormat;

  /**
   * An algorithm used to generate a key pair of the Key resource.
   */
  keyAlgorithm?: Key.Algorithm;
}


export interface CreateKeyResponse {
  /**
   * Key resource.
   */
  key?: Key;

  /**
   * A private key of the Key resource.
   * This key must be stored securely.
   */
  privateKey?: string;
}


export interface DeleteKeyRequest {
  /**
   * ID of the key to delete.
   * To get key ID use a [KeyService.List] request.
   */
  keyId: string;
}


/**
 * A Role resource. For more information, see [Roles](/docs/iam/concepts/access-control/roles).
 */
export interface Role {
  /**
   * ID of the role.
   */
  id?: string;

  /**
   * Description of the role. 0-256 characters long.
   */
  description?: string;
}


export interface GetRoleRequest {
  /**
   * ID of the Role resource to return.
   * To get the role ID, use a [RoleService.List] request.
   */
  roleId: string;
}


export interface ListRolesRequest {
  /**
   * The maximum number of results per page to return. If the number of available
   * results is larger than [page_size],
   * the service returns a [ListRolesResponse.next_page_token]
   * that can be used to get the next page of results in subsequent list requests.
   * Default value: 100.
   */
  pageSize?: Long;

  /**
   * Page token. To get the next page of results, set [page_token]
   * to the [ListRolesResponse.next_page_token]
   * returned by a previous list request.
   */
  pageToken?: string;

  /**
   * A filter expression that filters resources listed in the response.
   */
  filter?: string;
}


export interface ListRolesResponse {
  /**
   * List of Role resources.
   */
  roles?: Role[];

  /**
   * This token allows you to get the next page of results for list requests. If the number of results
   * is larger than [ListRolesRequest.page_size], use
   * the [next_page_token] as the value
   * for the [ListRolesRequest.page_token] query parameter
   * in the next list request. Each subsequent list request will have its own
   * [next_page_token] to continue paging through the results.
   */
  nextPageToken?: string;
}


/**
 * A ServiceAccount resource. For more information, see [Service accounts](/docs/iam/concepts/users/service-accounts).
 */
export interface ServiceAccount {
  /**
   * ID of the service account.
   */
  id?: string;

  /**
   * ID of the folder that the service account belongs to.
   */
  folderId?: string;

  /**
   * Creation timestamp.
   */
  createdAt?: protobuf.Timestamp;

  /**
   * Name of the service account.
   * The name is unique within the cloud. 3-63 characters long.
   */
  name?: string;

  /**
   * Description of the service account. 0-256 characters long.
   */
  description?: string;
}


export interface GetServiceAccountRequest {
  /**
   * ID of the ServiceAccount resource to return.
   * To get the service account ID, use a [ServiceAccountService.List] request.
   */
  serviceAccountId: string;
}


export interface ListServiceAccountsRequest {
  /**
   * ID of the folder to list service accounts in.
   * To get the folder ID, use a [yandex.cloud.resourcemanager.v1.FolderService.List] request.
   */
  folderId: string;

  /**
   * The maximum number of results per page to return. If the number of available
   * results is larger than [page_size],
   * the service returns a [ListServiceAccountsResponse.next_page_token]
   * that can be used to get the next page of results in subsequent list requests.
   * Default value: 100
   */
  pageSize?: Long;

  /**
   * Page token. To get the next page of results, set [page_token]
   * to the [ListServiceAccountsResponse.next_page_token]
   * returned by a previous list request.
   */
  pageToken?: string;

  /**
   * A filter expression that filters resources listed in the response.
   * The expression must specify:
   * 1. The field name. Currently you can use filtering only on the [ServiceAccount.name] field.
   * 2. An operator. Can be either `=` or `!=` for single values, `IN` or `NOT IN` for lists of values.
   * 3. The value. Must be 3-63 characters long and match the regular expression `^[a-z][-a-z0-9]{1,61}[a-z0-9]$`.
   */
  filter?: string;
}


export interface ListServiceAccountsResponse {
  /**
   * List of ServiceAccount resources.
   */
  serviceAccounts?: ServiceAccount[];

  /**
   * This token allows you to get the next page of results for list requests. If the number of results
   * is larger than [ListServiceAccountsRequest.page_size], use
   * the [next_page_token] as the value
   * for the [ListServiceAccountsRequest.page_token] query parameter
   * in the next list request. Each subsequent list request will have its own
   * [next_page_token] to continue paging through the results.
   */
  nextPageToken?: string;
}


export interface CreateServiceAccountRequest {
  /**
   * ID of the folder to create a service account in.
   * To get the folder ID, use a [yandex.cloud.resourcemanager.v1.FolderService.List] request.
   */
  folderId: string;

  /**
   * Name of the service account.
   * The name must be unique within the cloud.
   */
  name: string;

  /**
   * Description of the service account.
   */
  description?: string;
}


export interface CreateServiceAccountMetadata {
  /**
   * ID of the service account that is being created.
   */
  serviceAccountId?: string;
}


export interface UpdateServiceAccountRequest {
  /**
   * ID of the ServiceAccount resource to update.
   * To get the service account ID, use a [ServiceAccountService.List] request.
   */
  serviceAccountId: string;

  /**
   * Field mask that specifies which fields of the ServiceAccount resource are going to be updated.
   */
  updateMask?: protobuf.FieldMask;

  /**
   * Name of the service account.
   * The name must be unique within the cloud.
   */
  name: string;

  /**
   * Description of the service account.
   */
  description?: string;
}


export interface UpdateServiceAccountMetadata {
  /**
   * ID of the ServiceAccount resource that is being updated.
   */
  serviceAccountId?: string;
}


export interface DeleteServiceAccountRequest {
  /**
   * ID of the service account to delete.
   * To get the service account ID, use a [ServiceAccountService.List] request.
   */
  serviceAccountId: string;
}


export interface DeleteServiceAccountMetadata {
  /**
   * ID of the service account that is being deleted.
   */
  serviceAccountId?: string;
}


export interface ListServiceAccountOperationsRequest {
  /**
   * ID of the ServiceAccount resource to list operations for.
   */
  serviceAccountId: string;

  /**
   * The maximum number of results per page to return. If the number of available
   * results is larger than [page_size], the service returns a [ListServiceAccountOperationsResponse.next_page_token]
   * that can be used to get the next page of results in subsequent list requests.
   * Default value: 100.
   */
  pageSize?: Long;

  /**
   * Page token. To get the next page of results, set [page_token]
   * to the [ListServiceAccountOperationsResponse.next_page_token]
   * returned by a previous list request.
   */
  pageToken?: string;
}


export interface ListServiceAccountOperationsResponse {
  /**
   * List of operations for the specified service account.
   */
  operations?: operation.Operation[];

  /**
   * This token allows you to get the next page of results for list requests. If the number of results
   * is larger than [ListServiceAccountOperationsRequest.page_size], use the [next_page_token] as the value
   * for the [ListServiceAccountOperationsRequest.page_token] query parameter in the next list request.
   * Each subsequent list request will have its own [next_page_token] to continue paging through the results.
   */
  nextPageToken?: string;
}


/**
 * Currently represents only [Yandex.Passport account](/docs/iam/concepts/#passport).
 */
export interface UserAccount {
  /**
   * ID of the user account.
   */
  id?: string;

  /**
   * A YandexPassportUserAccount resource.
   */
  yandexPassportUserAccount?: YandexPassportUserAccount;
}


/**
 * A YandexPassportUserAccount resource. For more information, see [Yandex.Passport account](/docs/iam/concepts/#passport).
 */
export interface YandexPassportUserAccount {
  /**
   * Login of the Yandex.Passport user account.
   */
  login?: string;

  /**
   * Default email of the Yandex.Passport user account.
   */
  defaultEmail?: string;
}


export interface GetUserAccountRequest {
  /**
   * ID of the UserAccount resource to return.
   */
  userAccountId: string;
}


export interface GetUserAccountByLoginRequest {
  /**
   * Login of the YandexPassportUserAccount resource to return.
   */
  login: string;
}


export enum Algorithm {
    ALGORITHM_UNSPECIFIED = 0,

    /**
     * RSA with a 2048-bit key size. Default value.
     */
    RSA_2048 = 1,

    /**
     * RSA with a 4096-bit key size.
     */
    RSA_4096 = 2
  }


export enum KeyFormat {
  /**
   * Privacy-Enhanced Mail (PEM) format. Default value.
   */
  PEM_FILE = 0
}


/**
   * Retrieves the list of API keys for the specified service account.
   */
list(request: ListApiKeysRequest): Promise<ListApiKeysResponse>;


/**
   * Returns the specified API key.
   *
   * To get the list of available API keys, make a [List] request.
   */
get(request: GetApiKeyRequest): Promise<ApiKey>;


/**
   * Creates an API key for the specified service account.
   */
create(request: CreateApiKeyRequest): Promise<CreateApiKeyResponse>;


/**
   * Deletes the specified API key.
   */
delete(request: DeleteApiKeyRequest): Promise<protobuf.Empty>;


/**
   * Creates an IAM token for the specified identity.
   */
create(request: CreateIamTokenRequest): Promise<CreateIamTokenResponse>;


/**
   * Returns the specified Key resource.
   *
   * To get the list of available Key resources, make a [List] request.
   */
get(request: GetKeyRequest): Promise<Key>;


/**
   * Retrieves the list of Key resources for the specified service account.
   */
list(request: ListKeysRequest): Promise<ListKeysResponse>;


/**
   * Creates a key pair for the specified service account.
   */
create(request: CreateKeyRequest): Promise<CreateKeyResponse>;


/**
   * Deletes the specified key pair.
   */
delete(request: DeleteKeyRequest): Promise<protobuf.Empty>;


/**
   * Returns the specified Role resource.
   *
   * To get the list of available Role resources, use a [List] request.
   */
get(request: GetRoleRequest): Promise<Role>;


/**
   * Retrieves the list of Role resources.
   */
list(request: ListRolesRequest): Promise<ListRolesResponse>;


/**
   * Returns the specified ServiceAccount resource.
   *
   * To get the list of available ServiceAccount resources, use a [List] request.
   */
get(request: GetServiceAccountRequest): Promise<ServiceAccount>;


/**
   * Retrieves the list of ServiceAccount resources in the specified folder.
   */
list(request: ListServiceAccountsRequest): Promise<ListServiceAccountsResponse>;


/**
   * Creates a service account in the specified folder.
   */
create(request: CreateServiceAccountRequest): Promise<operation.Operation>;


/**
   * Updates the specified service account.
   */
update(request: UpdateServiceAccountRequest): Promise<operation.Operation>;


/**
   * Deletes the specified service account.
   */
delete(request: DeleteServiceAccountRequest): Promise<operation.Operation>;


/**
   * Lists access bindings for the specified service account.
   */
listAccessBindings(request: access.ListAccessBindingsRequest): Promise<access.ListAccessBindingsResponse>;


/**
   * Sets access bindings for the service account.
   */
setAccessBindings(request: access.SetAccessBindingsRequest): Promise<operation.Operation>;


/**
   * Updates access bindings for the specified service account.
   */
updateAccessBindings(request: access.UpdateAccessBindingsRequest): Promise<operation.Operation>;


/**
   * Lists operations for the specified service account.
   */
listOperations(request: ListServiceAccountOperationsRequest): Promise<ListServiceAccountOperationsResponse>;


/**
   * Returns the specified UserAccount resource.
   */
get(request: GetUserAccountRequest): Promise<UserAccount>;


/**
   * Returns the specified YandexPassportUserAccount resource.
   */
getByLogin(request: GetUserAccountByLoginRequest): Promise<UserAccount>;


export class ContratistaModel  {
    id: number;
    user_id:number;
    plan_id:number;
	descripcion:string;
	ultima_ubicacion:string;
}


(theme: any, id: string) => {
  return {
    application: (null as unknown) as PIXI.Application,
    viewport: (null as unknown) as Viewport,
    viewportChildContainer: (null as unknown) as PIXI.Container,
    childrenMap: new Map<string, PIXI.DisplayObject>(),
    children: [] as PIXI.DisplayObject[],
    loader: PIXI.Loader,
    canvasReady: false,
    applicationLoaded: false,
    mouseState: MouseState.MOVE,
    selectedObjects: [] as GraphObject[],
    aliasCanvasObjects: new Set() as Set<any>,
    triggerUpdate: 1,
    selectedMachine: (null as unknown) as string,
    selectedRecipe: (null as unknown) as string,
    selectedEdge: (null as unknown) as string,
    openModals: 0,
    sourceNodeId: '',
    externalInteractionManager: new ExternalInteractionManager(
      new EventEmitter(),
      theme,
      id
    ),
    snapToGrid: false,
    autoShuffleEdge: false,
    signedIn: false,
    lastSelectedSave: {
      name: 'My Awesome Design',
      description: 'Lizard Doggo Approved',
    },
    lastUsedSave: {},
  };
}


(canvasId: string) => {
  pixiJsStore.update(triggerCanvasUpdateFunction(canvasId));
}


(canvasId: string) => (t: any) => {
  const s = t[canvasId];
  s.triggerUpdate = s.triggerUpdate + 1;
  // We limit the number of updates to 100
  s.triggerUpdate = s.triggerUpdate % 100;
}


(t: any) => {
  const s = t[canvasId];
  s.triggerUpdate = s.triggerUpdate + 1;
  // We limit the number of updates to 100
  s.triggerUpdate = s.triggerUpdate % 100;
}