diff --git a/src/app/components/shared/_dialogs/create-comment/create-comment.component.html b/src/app/components/shared/_dialogs/create-comment/create-comment.component.html index eb4b8d88af2d0af963d0e30a5ac2b763dfb28580..1c94986def026458b15144bc5e4b223bcaac346f 100644 --- a/src/app/components/shared/_dialogs/create-comment/create-comment.component.html +++ b/src/app/components/shared/_dialogs/create-comment/create-comment.component.html @@ -46,6 +46,8 @@ <mat-form-field style="width:100%;"> <input [disabled]="true" matInput> <div [contentEditable]="true" + (paste)="clearHTML($event); + maxLength(commentBody)" [spellcheck]="false" spellcheck="false" (focus)="eventService.makeFocusOnInputTrue()" @@ -70,6 +72,9 @@ {{inputText.length}} / {{user.role === 3 ? 1000 : 500}} </span> </mat-hint> + <span *ngIf="!this.hasSpellcheckConfidence"> + <p>{{ 'spacy-dialog.force-language-selection' | translate }}</p> + </span> </mat-form-field> </ars-row> </mat-tab> @@ -92,7 +97,12 @@ </ars-row> <ars-row ars-flex-box> <ars-col> - <button class="mat-flat-button spell-button" (click)="grammarCheck(commentBody)">{{ 'comment-page.grammar-check' | translate}}</button> + <button mat-button class="spell-button" (click)="grammarCheck(commentBody)"> + {{ 'comment-page.grammar-check' | translate}} + <mat-icon *ngIf="isSpellchecking" style="margin: 0;"> + <mat-spinner diameter="20"></mat-spinner> + </mat-icon> + </button> </ars-col> <ars-col> <app-dialog-action-buttons diff --git a/src/app/components/shared/_dialogs/create-comment/create-comment.component.scss b/src/app/components/shared/_dialogs/create-comment/create-comment.component.scss index 41d5b82dbc9498fee7f9f9c119a673d3708c554a..d7ea6685b22ad2d3dc4a1996b1d2e7335a0be616 100644 --- a/src/app/components/shared/_dialogs/create-comment/create-comment.component.scss +++ b/src/app/components/shared/_dialogs/create-comment/create-comment.component.scss @@ -123,3 +123,12 @@ mat-hint { .mat-option { color: var(--on-surface); } + +::ng-deep .mat-tab-body-content { + max-width: 540px !important; + overflow-x: hidden !important; +} + +::ng-deep .mat-spinner circle { + stroke: var(--on-primary); +} diff --git a/src/app/components/shared/_dialogs/create-comment/create-comment.component.ts b/src/app/components/shared/_dialogs/create-comment/create-comment.component.ts index cb679c21af613760a779abe073e0d26a80fa9718..8191bdb32c6625bf4a489cc8f837df27293d9885 100644 --- a/src/app/components/shared/_dialogs/create-comment/create-comment.component.ts +++ b/src/app/components/shared/_dialogs/create-comment/create-comment.component.ts @@ -31,6 +31,9 @@ export class CreateCommentComponent implements OnInit, OnDestroy { bodyForm = new FormControl('', [Validators.required]); + isSpellchecking = false; + hasSpellcheckConfidence = true; + @ViewChild('commentBody', { static: true }) commentBody: HTMLDivElement; constructor( @@ -69,6 +72,11 @@ export class CreateCommentComponent implements OnInit, OnDestroy { onNoClick(): void { this.dialogRef.close(); } + clearHTML(e){ + e.preventDefault(); + const text = e.clipboardData.getData('text'); + document.getElementById('answer-input').innerText += text.replace(/<[^>]*>?/gm, ''); + } checkInputData(body: string): boolean { body = body.trim(); @@ -97,8 +105,9 @@ export class CreateCommentComponent implements OnInit, OnDestroy { this.checkSpellings(this.inputText).subscribe((res) => { const words: string[] = this.inputText.trim().split(' '); const errorQuotient = (res.matches.length * 100) / words.length; + const hasSpellcheckConfidence = this.checkLanguageConfidence(res); - if (errorQuotient <= 20) { + if (hasSpellcheckConfidence && errorQuotient <= 20) { let commentBodyChecked = this.inputText; const commentLang = this.languagetoolService.mapLanguageToSpacyModel(res.language.code); @@ -148,19 +157,29 @@ export class CreateCommentComponent implements OnInit, OnDestroy { } maxLength(commentBody: HTMLDivElement): void { - this.inputText = commentBody.innerText; if (this.user.role === 3 && commentBody.innerText.length > 1000) { commentBody.innerText = commentBody.innerText.slice(0, 1000); } else if (this.user.role !== 3 && commentBody.innerText.length > 500) { commentBody.innerText = commentBody.innerText.slice(0, 500); } this.body = commentBody.innerText; + if(this.body.length === 1 && this.body.charCodeAt(this.body.length - 1) === 10){ + commentBody.innerHTML = commentBody.innerHTML.replace('<br>',''); + } + this.inputText = commentBody.innerText; } grammarCheck(commentBody: HTMLDivElement): void { const wrongWords: string[] = []; commentBody.innerHTML = this.inputText; + this.isSpellchecking = true; + this.hasSpellcheckConfidence = true; this.checkSpellings(commentBody.innerText).subscribe((wordsCheck) => { + if(!this.checkLanguageConfidence(wordsCheck)) { + this.hasSpellcheckConfidence = false; + return; + } + if (wordsCheck.matches.length > 0) { wordsCheck.matches.forEach(grammarError => { const wrongWord = commentBody.innerText.slice(grammarError.offset, grammarError.offset + grammarError.length); @@ -192,13 +211,13 @@ export class CreateCommentComponent implements OnInit, OnDestroy { const replacement = '<div class="markUp" data-id="'+i+'" style="position: relative; display: inline-block; border-bottom: 1px dotted black">' + - ' <span data-id="' + i + '" style="text-decoration: underline wavy red; cursor: pointer;">' + - wrongWord + - ' </span>' + - // eslint-disable-next-line max-len - ' <div class="dropdownBlock" style="display: none; width: 160px; background-color: white; border-style: solid; border-color: var(--primary); color: #fff; text-align: center; border-radius: 6px; padding: 5px 0; position: absolute; z-index: 1000; bottom: 100%; left: 50%; margin-left: -80px;">' + - suggestionsHTML + - ' </div>' + + '<span data-id="' + i + '" style="text-decoration: underline wavy red; cursor: pointer;">' + + wrongWord + + '</span>' + + // eslint-disable-next-line max-len + '<div class="dropdownBlock" style="display: none; width: 160px; background-color: white; border-style: solid; border-color: var(--primary); color: #fff; text-align: center; border-radius: 6px; padding: 5px 0; position: absolute; z-index: 1000; bottom: 100%;">' + + suggestionsHTML + + '</div>' + '</div>'; commentBody.innerHTML = commentBody.innerHTML.substr(0, res.matches[i].offset) + @@ -207,13 +226,26 @@ export class CreateCommentComponent implements OnInit, OnDestroy { } setTimeout(() => { - Array.from(document.getElementsByClassName('markUp')).forEach(marked => { - marked.addEventListener('click', () => { - ((marked as HTMLElement).lastChild as HTMLElement).style.display = 'block'; + Array.from(document.getElementsByClassName('markUp')).forEach(markup => { + markup.addEventListener('click', () => { + ((markup as HTMLElement).lastChild as HTMLElement).style.display = 'block'; + const rectdiv = (document.getElementById('answer-input')).getBoundingClientRect(); + const rectmarkup = markup.getBoundingClientRect(); + let offset; + if (rectmarkup.x + rectmarkup.width / 2 > rectdiv.right - 80) { + offset = rectdiv.right - rectmarkup.x - rectmarkup.width; + ((markup as HTMLElement).lastChild as HTMLElement).style.right = -offset + 'px'; + } else if (rectmarkup.x + rectmarkup.width / 2 < rectdiv.left + 80) { + offset = rectmarkup.x - rectdiv.left; + ((markup as HTMLElement).lastChild as HTMLElement).style.left = -offset + 'px'; + } else { + ((markup as HTMLElement).lastChild as HTMLElement).style.left = '50%'; + ((markup as HTMLElement).lastChild as HTMLElement).style.marginLeft = '-80px'; + } setTimeout(() => { - Array.from(document.getElementsByClassName('suggestions')).forEach(e => { - e.addEventListener('click', () => { - e.parentElement.parentElement.outerHTML = e.innerHTML; + Array.from(document.getElementsByClassName('suggestions')).forEach(suggestion => { + suggestion.addEventListener('click', () => { + suggestion.parentElement.parentElement.outerHTML = suggestion.innerHTML; this.inputText = commentBody.innerText; }); }); @@ -223,6 +255,12 @@ export class CreateCommentComponent implements OnInit, OnDestroy { }, 500); }); } + }, () => {}, () => { + this.isSpellchecking = false; }); } + + checkLanguageConfidence(wordsCheck: any) { + return this.selectedLang === 'auto' ? wordsCheck.language.detectedLanguage.confidence >= 0.5 : true; + } } diff --git a/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.html b/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.html index 3a2cab8045ff60eeb87392acaf297dbc199f95ce..66a7e1f1efe14fc99d2c6a26ae55b71ad4ab80e2 100644 --- a/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.html +++ b/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.html @@ -55,8 +55,8 @@ </mat-list-item> </mat-list> </ars-row> - <span *ngIf="keywords.length<=0"> - <p>{{ 'spacy-dialog.empty-nouns' | translate}}</p> + <span *ngIf="keywords.length <= 0 && !this.isLoading"> + <p>{{ 'spacy-dialog.empty-nouns' | translate }}</p> </span> </div> diff --git a/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.ts b/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.ts index 150aa70e3fb88fa7e2c3c0a09afcf6b4f90a6650..4a69313c912f7eafc40aff1070177819db8c91c6 100644 --- a/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.ts +++ b/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.ts @@ -25,6 +25,7 @@ export class SpacyDialogComponent implements OnInit, AfterContentInit { commentBodyChecked: string; keywords: Keyword[] = []; keywordsOriginal: Keyword[] = []; + isLoading = false; constructor( protected langService: LanguageService, @@ -68,6 +69,9 @@ export class SpacyDialogComponent implements OnInit, AfterContentInit { } else { regex = new RegExp('(?!au|de|la|le|en|un)[A-ZÀ-Ÿ]{2,}', 'gi'); } + + this.isLoading = true; + // N at first pos = all Nouns(NN de/en) including singular(NN, NNP en), plural (NNPS, NNS en), proper Noun(NNE, NE de) this.spacyService.getKeywords(this.commentBodyChecked, model) .subscribe(words => { @@ -89,6 +93,8 @@ export class SpacyDialogComponent implements OnInit, AfterContentInit { }, () => { this.keywords = []; this.keywordsOriginal = []; + }, () => { + this.isLoading = false; }); } diff --git a/src/assets/i18n/creator/de.json b/src/assets/i18n/creator/de.json index de34755b800fb0d5195cd285df3b53f7c4c05aff..40d73a8bd86f3d172007a49d052c3e3e871928cf 100644 --- a/src/assets/i18n/creator/de.json +++ b/src/assets/i18n/creator/de.json @@ -93,7 +93,8 @@ "select-all-hint": "Alle Stichwörter auswählen", "select-keyword-hint": "Dieses Stickwort auswählen", "edit-keyword-hint": "Stichwort editieren", - "editing-done-hint": "Editierung abschliessen" + "editing-done-hint": "Editierung abschliessen", + "force-language-selection": "Bitte wähle eine Sprache aus." }, "comment-page": { "a11y-comment_delete": "Löscht diese Frage", diff --git a/src/assets/i18n/creator/en.json b/src/assets/i18n/creator/en.json index 4a5d7b843e8460691dd3eb44eca9ddbcdadb5a73..415d722fd1074c15b0bad29a264e693e863f98d4 100644 --- a/src/assets/i18n/creator/en.json +++ b/src/assets/i18n/creator/en.json @@ -94,7 +94,8 @@ "select-all-hint": "Select all keywords", "select-keyword-hint": "Select this keyword", "edit-keyword-hint": "Edit keyword", - "editing-done-hint": "Finish editing" + "editing-done-hint": "Finish editing", + "force-language-selection": "Please select a language." }, "comment-page": { "a11y-comment_delete": "Deletes this question", diff --git a/src/assets/i18n/participant/de.json b/src/assets/i18n/participant/de.json index b431fe33f527cac0d72a4f28f87d4e344aad5461..03c4cc6b9ce78607093280ee61078a350f791717 100644 --- a/src/assets/i18n/participant/de.json +++ b/src/assets/i18n/participant/de.json @@ -98,7 +98,8 @@ "select-all-hint": "Alle Stichwörter auswählen", "select-keyword-hint": "Dieses Stickwort auswählen", "edit-keyword-hint": "Stichwort editieren", - "editing-done-hint": "Editierung abschliessen" + "editing-done-hint": "Editierung abschliessen", + "force-language-selection": "Bitte wähle eine Sprache aus." }, "comment-page": { "a11y-comment_input": "Gib deine Frage ein", @@ -354,4 +355,4 @@ "rotate-weight": "Einige Einträge dieser Gewichtsklasse zufällig um x Grad drehen", "rotate-weight-tooltip": "Einige zufällig ausgewählte Einträge um diesen Winkel drehen" } -} +} \ No newline at end of file diff --git a/src/assets/i18n/participant/en.json b/src/assets/i18n/participant/en.json index 9821382bbad0a6ff070a20aa026309e4a3913983..a26da8716bd3aae9609f63b6210c9a2984594325 100644 --- a/src/assets/i18n/participant/en.json +++ b/src/assets/i18n/participant/en.json @@ -108,7 +108,8 @@ "select-all-hint": "Select all keywords", "select-keyword-hint": "Select this keyword", "edit-keyword-hint": "Edit keyword", - "editing-done-hint": "Finish editing" + "editing-done-hint": "Finish editing", + "force-language-selection": "Please select a language." }, "comment-page": { "a11y-comment_input": "Enter your question", @@ -360,4 +361,4 @@ "rotate-weight": "Rotate some entries of this weight class randomly by x degrees", "rotate-weight-tooltip":"Rotate some randomly selected entries by this angle" } -} +} \ No newline at end of file