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 650b336c170d919f0fc40c7f3b981af24c1028b3..33773a73d890456af91f0b9d5d8ed73fcba2c751 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 @@ -48,7 +48,7 @@ </mat-form-field> </div> </div> - <mat-tab-group> + <mat-tab-group (selectedTabChange)="tempEditView = commentBody.innerText"> <mat-tab label="{{ 'comment-page.write-comment' | translate }}"> <ars-row [height]="12"></ars-row> <ars-row> @@ -84,7 +84,7 @@ </mat-hint> <mat-hint align="end"> <span aria-hidden="true"> - {{commentBody.innerHTML.length}} / {{user.role === 3 ? 1000 : 500}} + {{commentBody.innerText.length}} / {{user.role === 3 ? 1000 : 500}} </span> </mat-hint> <span *ngIf="!grammarChecker.hasSpellcheckConfidence"> @@ -106,7 +106,7 @@ emoji lineNumbers lineHighlight - [data]="commentBody.innerText"></markdown> + [data]="tempEditView"></markdown> </ars-row> </mat-tab> </mat-tab-group> 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 e7e23677a371ac7e3f58d358929640395ea9a694..b282c5bef015098eae6c0afbd0c495dc958234a6 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 @@ -25,6 +25,7 @@ export class CreateCommentComponent implements OnInit { selectedTag: string; isSendingToSpacy = false; grammarChecker: GrammarChecker; + tempEditView: string; constructor( private notification: NotificationService, diff --git a/src/app/components/shared/comment-answer/comment-answer.component.html b/src/app/components/shared/comment-answer/comment-answer.component.html index aab408107457b62f553a0155fd7848ac560fcb98..edae65f082f0f55c345302ce3e0615ea2da0c612 100644 --- a/src/app/components/shared/comment-answer/comment-answer.component.html +++ b/src/app/components/shared/comment-answer/comment-answer.component.html @@ -47,7 +47,7 @@ </button> </div> <div *ngIf="!isStudent && (edit || !answer)"> - <mat-tab-group [dynamicHeight]="false"> + <mat-tab-group [dynamicHeight]="false" (selectedTabChange)="tempEditView = commentBody.innerText"> <mat-tab label="{{'comment-page.your-answer' | translate}}"> <mat-divider></mat-divider> <mat-form-field class="input-block"> @@ -63,7 +63,7 @@ #commentBody aria-labelledby="ask-question-description" autofocus - (input)="grammarChecker.maxLength(commentBody, 2000); answer = commentBody.innerText" + (input)="grammarChecker.maxLength(commentBody, 2000);" id="answer-input"> </div> <mat-hint align="start"> @@ -73,15 +73,15 @@ </mat-hint> <mat-hint align="end"> <span aria-hidden="true"> - {{ answer ? answer.length : 0 }} / 2000 + {{ commentBody.innerText ? commentBody.innerText.length : 0 }} / 2000 </span> </mat-hint> </mat-form-field> </mat-tab> <mat-tab label="{{'session.preview' | translate}}" - [disabled]="!answer"> + [disabled]="!commentBody.innerText"> <markdown class="images" katex emoji lineNumbers lineHighlight - [data]="answer"></markdown> + [data]="tempEditView"></markdown> </mat-tab> </mat-tab-group> <ars-row ars-flex-box class="spellcheck"> @@ -100,7 +100,7 @@ </ars-col> <ars-col style="display: flex; flex-direction: row;"> <button mat-raised-button - *ngIf="answer" + *ngIf="answer || commentBody.innerText" class="delete" style="display: inline-block" (click)="openDeleteAnswerDialog()"> diff --git a/src/app/components/shared/comment-answer/comment-answer.component.ts b/src/app/components/shared/comment-answer/comment-answer.component.ts index 291f7d7da1f101c9bdee6d9a488a5bd09efb39d3..14c8144cc208f67ac064627505e84decd4685388 100644 --- a/src/app/components/shared/comment-answer/comment-answer.component.ts +++ b/src/app/components/shared/comment-answer/comment-answer.component.ts @@ -30,6 +30,7 @@ export class CommentAnswerComponent implements OnInit { edit = false; grammarChecker: GrammarChecker; + tempEditView: string; @ViewChild('commentBody') commentBody: ElementRef<HTMLDivElement>; diff --git a/src/app/utils/create-comment-keywords.ts b/src/app/utils/create-comment-keywords.ts index 27f6c888eb5dea7bf0c4716749e241742c4602b0..ab65318b45f072f678cf7b0f8394a19ec9649ded 100644 --- a/src/app/utils/create-comment-keywords.ts +++ b/src/app/utils/create-comment-keywords.ts @@ -24,15 +24,17 @@ export class CreateCommentKeywords { ); } - static cleaningFunction(text: string): string { + static cleaningFunction(text: string, removeAsciiNamedEmojis = false): string { // eslint-disable-next-line max-len const regexEmoji = new RegExp('\uD918\uDD28|\ufe0f|\u200D|\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]', 'g'); const regexKatex = new RegExp('\\$[^$\\n ]+\\$|\\$\\$[^$\\n ]+\\$\\$', 'g'); const regexMarkdown = new RegExp('(?:__|[*#])|\\[(.+?)]\\((.+?)\\)', 'g'); const regexBlank = new RegExp('[\\s]{2,}', 'gu'); + const regexAsciiNamedEmojis = new RegExp(':([a-z0-9_]+):', 'g'); text = text.replace(regexKatex, ''); text = text.replace(regexEmoji, ''); text = text.replace(regexMarkdown, ''); + text = text.replace(regexAsciiNamedEmojis, removeAsciiNamedEmojis ? '' : '$1'); text = text.replace(regexBlank, ' '); return text; } diff --git a/src/app/utils/grammar-checker.ts b/src/app/utils/grammar-checker.ts index 2524e73a92c2ce9e627cef2574f747b58b3aeb2f..598d1835794831629e6556eb1cb62193cd33e2ec 100644 --- a/src/app/utils/grammar-checker.ts +++ b/src/app/utils/grammar-checker.ts @@ -15,8 +15,9 @@ export class GrammarChecker { onDocumentClick(e) { const container = document.getElementsByClassName('dropdownBlock'); Array.prototype.forEach.call(container, (elem) => { - if (!elem.contains(e.target) && (!(e.target as Node).parentElement.classList.contains('markUp') - || (e.target as HTMLElement).dataset.id !== ((elem as Node).parentElement as HTMLElement).dataset.id)) { + const hasMarkup = (e.target as Node).parentElement ? (e.target as Node).parentElement.classList.contains('markUp') : false; + if (!elem.contains(e.target) && (!hasMarkup || + (e.target as HTMLElement).dataset.id !== (elem as Node).parentElement.dataset.id)) { (elem as HTMLElement).style.display = 'none'; } }); @@ -48,10 +49,14 @@ export class GrammarChecker { } grammarCheck(commentBody: HTMLDivElement): void { + this.onDocumentClick({ + target: document + }); const wrongWords: string[] = []; this.isSpellchecking = true; this.hasSpellcheckConfidence = true; - const text = CreateCommentKeywords.cleaningFunction(commentBody.innerText); + const unfilteredText = commentBody.innerText; + const text = CreateCommentKeywords.cleaningFunction(commentBody.innerText, true); this.checkSpellings(text).subscribe((wordsCheck) => { if (!this.checkLanguageConfidence(wordsCheck)) { this.hasSpellcheckConfidence = false; @@ -70,84 +75,88 @@ export class GrammarChecker { } document.getElementById('langSelect').innerHTML = this.newLang; } - if (wordsCheck.matches.length > 0) { - wordsCheck.matches.forEach(grammarError => { - const wrongWord = commentBody.innerText.slice(grammarError.offset, grammarError.offset + grammarError.length); - wrongWords.push(wrongWord); - }); - - let html = ''; - let lastFound = text.length; - this.checkSpellings(text).subscribe((res) => { - for (let i = res.matches.length - 1; i >= 0; i--) { - const end = res.matches[i].offset + res.matches[i].length; - const start = res.matches[i].offset; - const wrongWord = text.slice(start, end); - - if (wrongWords.includes(wrongWord)) { - const suggestions: any[] = res.matches[i].replacements; - let displayOptions = 3; - let suggestionsHTML = ''; - - if (!suggestions.length) { - suggestionsHTML = '<span style="color: black; display: block; text-align: center;">' + res.matches[i].message + '</span>'; - } + if (wordsCheck.matches.length <= 0) { + return; + } + wordsCheck.matches.forEach(grammarError => { + const wrongWord = text.slice(grammarError.offset, grammarError.offset + grammarError.length); + wrongWords.push(wrongWord); + }); + + let html = ''; + let lastFound = unfilteredText.length; + this.checkSpellings(unfilteredText).subscribe((res) => { + for (let i = res.matches.length - 1; i >= 0; i--) { + const end = res.matches[i].offset + res.matches[i].length; + const start = res.matches[i].offset; + const wrongWord = unfilteredText.slice(start, end); + + if (!wrongWords.includes(wrongWord)) { + continue; + } - if (suggestions.length < displayOptions) { - displayOptions = suggestions.length; - } + const suggestions: any[] = res.matches[i].replacements; + let displayOptions = 3; + let suggestionsHTML = ''; - for (let j = 0; j < displayOptions; j++) { - // eslint-disable-next-line max-len - suggestionsHTML += '<span class="suggestions"' + ' style="color: black; display: block; text-align: center; cursor: pointer;">' + suggestions[j].value + '</span>'; - } + if (!suggestions.length) { + suggestionsHTML = '<span style="color: black; display: block; text-align: center;">' + res.matches[i].message + '</span>'; + } + + if (suggestions.length < displayOptions) { + displayOptions = suggestions.length; + } - 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%;">' + - suggestionsHTML + - '</div>' + - '</div>'; - - html = replacement + text.slice(end, lastFound) + html; - lastFound = res.matches[i].offset; - } + for (let j = 0; j < displayOptions; j++) { + // eslint-disable-next-line max-len + suggestionsHTML += '<span class="suggestions"' + ' style="color: black; display: block; text-align: center; cursor: pointer;">' + suggestions[j].value + '</span>'; } - commentBody.innerHTML = text.slice(0, lastFound) + html; - - setTimeout(() => { - 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(suggestion => { - suggestion.addEventListener('click', () => { - suggestion.parentElement.parentElement.outerHTML = suggestion.innerHTML; - }); + + 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%;">' + + suggestionsHTML + + '</div>' + + '</div>'; + + html = replacement + unfilteredText.slice(end, lastFound) + html; + lastFound = res.matches[i].offset; + } + commentBody.innerHTML = unfilteredText.slice(0, lastFound) + html; + + setTimeout(() => { + Array.from(document.getElementsByClassName('markUp')).forEach((markup: HTMLElement) => { + markup.addEventListener('click', () => { + const lastChild = markup.lastChild as HTMLElement; + lastChild.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; + lastChild.style.right = -offset + 'px'; + } else if (rectmarkup.x + rectmarkup.width / 2 < rectdiv.left + 80) { + offset = rectmarkup.x - rectdiv.left; + lastChild.style.left = -offset + 'px'; + } else { + lastChild.style.left = '50%'; + lastChild.style.marginLeft = '-80px'; + } + setTimeout(() => { + Array.from(document.getElementsByClassName('suggestions')).forEach(suggestion => { + suggestion.addEventListener('click', () => { + suggestion.parentElement.parentElement.outerHTML = suggestion.innerHTML; }); - }, 500); - }); + }); + }, 500); }); - }, 500); - }); - } + }); + }, 500); + }); }, () => '', () => { this.isSpellchecking = false; });