Sengi-Windows-MacOS-Linux/src/app/components/stream/status/databinded-text/databinded-text.component.ts

291 lines
11 KiB
TypeScript
Raw Normal View History

2018-10-28 06:02:57 +01:00
import { Component, OnInit, Input, EventEmitter, Output, Renderer2, ViewChild, ElementRef } from '@angular/core';
2020-04-18 06:45:27 +02:00
import { faAngleDown } from "@fortawesome/free-solid-svg-icons";
@Component({
selector: 'app-databinded-text',
templateUrl: './databinded-text.component.html',
styleUrls: ['./databinded-text.component.scss']
})
export class DatabindedTextComponent implements OnInit {
2020-04-18 06:45:27 +02:00
faAngleDown = faAngleDown;
private accounts: string[] = [];
2018-10-28 05:45:32 +01:00
private hashtags: string[] = [];
2020-08-29 03:18:41 +02:00
private links: string[] = [];
processedText: string;
2020-04-18 06:45:27 +02:00
isCollapsed: boolean = false;
2018-10-28 06:02:57 +01:00
@ViewChild('content') contentElement: ElementRef;
@Output() accountSelected = new EventEmitter<string>();
@Output() hashtagSelected = new EventEmitter<string>();
@Output() textSelected = new EventEmitter();
@Input() textIsSelectable: boolean = true;
2020-04-18 06:45:27 +02:00
@Input() selected: boolean;
@Input('text')
2018-10-28 05:45:32 +01:00
set text(value: string) {
2020-08-29 04:12:30 +02:00
//console.log(value);
2020-04-18 06:45:27 +02:00
let parser = new DOMParser();
var dom = parser.parseFromString(value, 'text/html')
2020-04-22 02:00:49 +02:00
this.isCollapsed = [...dom.body.textContent].length > 600;
2018-10-28 06:02:57 +01:00
this.processedText = '';
2019-05-23 08:16:36 +02:00
do {
value = value.replace('@<span class="">', '<span class="">'); //Friendica sanitarization
} while (value.includes('@<span class="">'));
2019-05-23 08:24:11 +02:00
do {
value = value.replace('class="mention" rel="nofollow noopener" target="_blank">@', 'class="mention" rel="nofollow noopener" target="_blank">'); //Misskey sanitarization
} while (value.includes('class="mention" rel="nofollow noopener" target="_blank">@'));
2020-08-29 04:42:00 +02:00
do {
value = value.replace('@<span class="h-card">', '<span class="h-card">'); //Zap sanitarization
} while (value.includes('@<span class="h-card">'));
2018-10-28 06:02:57 +01:00
let linksSections = value.split('<a ');
2018-10-28 05:45:32 +01:00
for (let section of linksSections) {
if (!section.includes('href')) {
this.processedText += section;
continue;
}
2020-04-18 23:30:32 +02:00
if (section.includes('class="mention hashtag"') || section.includes('class="hashtag"') || section.includes('target="_blank">#') || section.includes('rel="tag">')) {
2018-10-30 04:56:00 +01:00
try {
this.processHashtag(section);
}
catch (err) {
2019-05-25 23:19:07 +02:00
console.error('error processing hashtag');
console.error(value);
2018-10-30 04:56:00 +01:00
}
} else if (section.includes('class="u-url mention"') || section.includes('class="mention"') || section.includes('class="mention status-link"') || section.includes('class="h-card mention')) {
2018-10-30 04:56:00 +01:00
try {
this.processUser(section);
}
catch (err) {
2019-05-25 23:19:07 +02:00
console.error('error processing mention');
console.error(value);
2018-10-30 04:56:00 +01:00
}
2018-10-28 20:03:57 +01:00
} else {
2018-10-30 04:56:00 +01:00
try {
this.processLink(section);
//this.processedText += `<a ${section}`;
2018-10-30 04:56:00 +01:00
}
catch (err) {
2019-05-25 23:19:07 +02:00
console.error('error processing link');
console.error(value);
2018-10-30 04:56:00 +01:00
}
2018-10-28 20:03:57 +01:00
}
}
}
2018-10-28 05:45:32 +01:00
2020-04-18 06:45:27 +02:00
expand(): boolean {
this.isCollapsed = false;
return false;
}
2018-10-28 20:03:57 +01:00
private processHashtag(section: string) {
let extractedLinkAndNext = section.split('</a>');
let extractedHashtag = extractedLinkAndNext[0].split('#')[1].replace('<span>', '').replace('</span>', '');
2020-08-29 20:30:13 +02:00
let extractedUrl = extractedLinkAndNext[0].split('href="')[1].split('"')[0];
2018-10-28 05:45:32 +01:00
2018-11-01 23:38:59 +01:00
let classname = this.getClassNameForHastag(extractedHashtag);
2020-08-29 20:30:13 +02:00
this.processedText += ` <a href="${extractedUrl}" class="${classname}" title="#${extractedHashtag}" target="_blank" rel="noopener noreferrer">#${extractedHashtag}</a>`;
2018-10-28 20:03:57 +01:00
if (extractedLinkAndNext[1]) this.processedText += extractedLinkAndNext[1];
this.hashtags.push(extractedHashtag);
}
2018-10-28 05:45:32 +01:00
2018-10-28 20:03:57 +01:00
private processUser(section: string) {
let extractedAccountAndNext: string[];
let extractedAccountName: string;
2019-05-23 08:16:36 +02:00
if (section.includes('<span class="mention">')) { //Friendica
extractedAccountAndNext = section.split('</a>');
extractedAccountName = extractedAccountAndNext[0].split('<span class="mention">')[1].split('</span>')[0];
2020-08-29 20:30:13 +02:00
} else if (section.includes('>@<span class="article-type">')) { //Remote status
2020-06-07 05:12:06 +02:00
extractedAccountAndNext = section.split('</a></span>');
extractedAccountName = extractedAccountAndNext[0].split('@<span class="article-type">')[1].replace('<span>', '').replace('</span>', '');
2020-08-29 20:30:13 +02:00
} else if (section.includes('class="u-url mention" rel="nofollow noopener noreferrer" target="_blank">@') && !section.includes('target="_blank">@<')) { //Misskey
2020-08-29 04:12:30 +02:00
//console.warn('misskey');
extractedAccountAndNext = section.split('</a>');
extractedAccountName = extractedAccountAndNext[0].split('class="u-url mention" rel="nofollow noopener noreferrer" target="_blank">@')[1];
2020-08-29 20:30:13 +02:00
if (extractedAccountName.includes('@'))
2020-08-29 04:12:30 +02:00
extractedAccountName = extractedAccountName.split('@')[0];
} else if (!section.includes('@<span>')) { //GNU social
extractedAccountAndNext = section.split('</a>');
extractedAccountName = extractedAccountAndNext[0].split('>')[1];
} else {
extractedAccountAndNext = section.split('</a></span>');
extractedAccountName = extractedAccountAndNext[0].split('@<span>')[1].replace('<span>', '').replace('</span>', '');
}
2018-10-28 20:03:57 +01:00
let extractedAccountLink = extractedAccountAndNext[0].split('href="https://')[1].split('"')[0].replace(' ', '').replace('@', '').split('/');
let domain = extractedAccountLink[0];
//let username = extractedAccountLink[extractedAccountLink.length - 1];
let extractedAccount = `@${extractedAccountName}@${domain}`;
2020-08-29 20:46:38 +02:00
let extractedUrl = section.split('href="')[1].split('"')[0];
2018-10-28 20:03:57 +01:00
let classname = this.getClassNameForAccount(extractedAccount);
2020-08-29 20:46:38 +02:00
this.processedText += `<a href="${extractedUrl}" class="${classname}" title="${extractedAccount}" target="_blank" rel="noopener noreferrer">@${extractedAccountName}</a>`;
2019-05-23 08:16:36 +02:00
if (extractedAccountAndNext[1])
this.processedText += extractedAccountAndNext[1];
//GNU Social clean up
2019-05-23 08:16:36 +02:00
if (this.processedText.includes('@<a'))
this.processedText = this.processedText.replace('@<a', '<a');
2018-10-28 20:03:57 +01:00
this.accounts.push(extractedAccount);
}
private processLink(section: string) {
2020-08-29 20:30:13 +02:00
if (!section.includes('</a>')) {
2020-05-07 00:25:33 +02:00
this.processedText += section;
return;
}
2018-10-28 20:03:57 +01:00
let extractedLinkAndNext = section.split('</a>')
let extractedUrl = extractedLinkAndNext[0].split('"')[1];
let extractedName = '';
try {
extractedName = extractedLinkAndNext[0].split('<span class="ellipsis">')[1].split('</span>')[0];
2018-10-30 04:56:00 +01:00
} catch (err) {
2018-10-28 20:03:57 +01:00
try {
extractedName = extractedLinkAndNext[0].split(`<span class="">`)[1].split('</span>')[0];
2018-10-28 05:45:32 +01:00
}
2018-10-30 04:56:00 +01:00
catch (err) {
try {
extractedName = extractedLinkAndNext[0].split(' target="_blank">')[1].split('</span>')[0];
} catch (err) { // Pleroma
2019-05-25 23:19:07 +02:00
try {
extractedName = extractedLinkAndNext[0].split('</span><span>')[1].split('</span>')[0];
} catch (err) {
extractedName = extractedLinkAndNext[0].split('">')[1];
}
}
2018-10-28 20:03:57 +01:00
}
2018-10-30 04:56:00 +01:00
}
2018-10-28 20:03:57 +01:00
2020-08-29 03:18:41 +02:00
this.links.push(extractedUrl);
2018-10-28 20:03:57 +01:00
let classname = this.getClassNameForLink(extractedUrl);
2020-08-29 02:54:09 +02:00
let sanitizedLink = this.sanitizeLink(extractedUrl);
this.processedText += `<a href="${sanitizedLink}" class="${classname}" title="open link" target="_blank" rel="noopener noreferrer">${extractedName}</a>`;
2018-10-28 20:03:57 +01:00
if (extractedLinkAndNext.length > 1) this.processedText += extractedLinkAndNext[1];
}
2018-10-28 05:45:32 +01:00
constructor(private renderer: Renderer2) { }
ngOnInit() {
}
2018-10-28 05:45:32 +01:00
ngAfterViewInit() {
2018-10-28 06:02:57 +01:00
for (const hashtag of this.hashtags) {
2018-11-01 23:38:59 +01:00
let classname = this.getClassNameForHastag(hashtag);
let els = <Element[]>this.contentElement.nativeElement.querySelectorAll(`.${classname}`);
2018-10-28 06:02:57 +01:00
for (const el of els) {
this.renderer.listen(el, 'click', (event) => {
event.preventDefault();
event.stopImmediatePropagation();
2018-10-28 06:25:13 +01:00
this.selectHashtag(hashtag);
return false;
});
}
2018-10-28 06:02:57 +01:00
}
for (const account of this.accounts) {
2018-10-28 20:03:57 +01:00
let classname = this.getClassNameForAccount(account);
let els = this.contentElement.nativeElement.querySelectorAll(`.${classname}`);
2018-10-28 06:02:57 +01:00
for (const el of els) {
this.renderer.listen(el, 'click', (event) => {
event.preventDefault();
event.stopImmediatePropagation();
2018-10-28 06:25:13 +01:00
this.selectAccount(account);
return false;
});
}
2018-10-28 06:02:57 +01:00
}
2020-08-29 03:18:41 +02:00
for (const link of this.links) {
let classname = this.getClassNameForLink(link);
let els = this.contentElement.nativeElement.querySelectorAll(`.${classname}`);
2019-05-23 08:16:36 +02:00
2020-08-29 03:18:41 +02:00
let sanitizedLink = this.sanitizeLink(link);
2020-08-29 03:18:41 +02:00
for (const el of els) {
this.renderer.listen(el, 'click', (event) => {
event.preventDefault();
event.stopImmediatePropagation();
2020-08-29 03:39:55 +02:00
window.open(sanitizedLink, '_blank', 'noopener');
2020-08-29 03:18:41 +02:00
return false;
});
2020-08-29 03:39:55 +02:00
// this.renderer.listen(el, 'mouseup', (event) => {
// if (event.which === 2) {
// event.preventDefault();
// event.stopImmediatePropagation();
// window.open(sanitizedLink, '_blank', 'noopener');
// return false;
// }
// });
2020-08-29 03:18:41 +02:00
}
}
2018-10-28 05:45:32 +01:00
}
2020-05-29 00:34:51 +02:00
private sanitizeLink(link: string): string {
let res = link.replace(/&amp;/g, '&');
return res;
}
2018-11-01 23:38:59 +01:00
private getClassNameForHastag(value: string): string {
let res = value.replace(/[.,\/#?!@$%+\^&\*;:{}=\-_`~()]/g, "");
return `hashtag-${res}`;
}
2018-10-28 20:03:57 +01:00
private getClassNameForAccount(value: string): string {
2018-10-28 06:25:13 +01:00
let res = value;
2018-10-28 20:03:57 +01:00
while (res.includes('.')) res = res.replace('.', '-');
while (res.includes('@')) res = res.replace('@', '-');
return `account-${res}`;
}
private getClassNameForLink(value: string): string {
let res = value.replace(/[.,\/#?!@°$%+\'\^&\*;:{}=\-_`~()]/g, "");
2018-10-28 20:03:57 +01:00
return `link-${res}`;
2018-10-28 06:02:57 +01:00
}
private selectAccount(account: string) {
2018-10-28 05:45:32 +01:00
this.accountSelected.next(account);
}
2018-10-28 06:02:57 +01:00
private selectHashtag(hashtag: string) {
2018-10-28 05:45:32 +01:00
this.hashtagSelected.next(hashtag);
}
selectText(event) {
if (event.view.getSelection().toString().length === 0) {
this.textSelected.next();
}
2018-10-28 05:45:32 +01:00
}
}