fchat-rising/bbcode/core.ts

133 lines
5.0 KiB
TypeScript
Raw Permalink Normal View History

2018-03-28 13:51:05 +00:00
import {BBCodeCustomTag, BBCodeParser, BBCodeSimpleTag, BBCodeTextTag} from './parser';
2017-09-02 01:50:31 +00:00
2018-01-06 16:14:21 +00:00
const urlFormat = '((?:https?|ftps?|irc)://[^\\s/$.?#"\']+\\.[^\\s"]+)';
export const findUrlRegex = new RegExp(`(\\[url[=\\]]\\s*)?${urlFormat}`, 'gi');
2017-09-02 01:50:31 +00:00
export const urlRegex = new RegExp(`^${urlFormat}$`);
2019-09-17 17:14:14 +00:00
export type BBCodeElement = HTMLElement & {cleanup?(): void};
2019-06-08 02:26:01 +00:00
export function domain(url: string): string | undefined {
2017-09-02 01:50:31 +00:00
const pieces = urlRegex.exec(url);
if(pieces === null) return;
const match = pieces[1].match(/(?:(https?|ftps?|irc):)?\/\/(?:www.)?([^\/]+)/);
return match !== null ? match[2] : undefined;
}
function fixURL(url: string): string {
if(/^www\./.test(url))
url = `https://${url}`;
return url.replace(/ /g, '%20');
}
2019-07-06 16:49:19 +00:00
// tslint:disable-next-line: max-line-length
2019-06-08 02:26:01 +00:00
export function analyzeUrlTag(parser: BBCodeParser, param: string, content: string): {success: boolean, url?: string, domain?: string, textContent: string} {
let url: string | undefined, textContent: string = content;
let success = true;
if(param.length > 0) {
url = param.trim();
if(content.length === 0) textContent = param;
2020-06-20 18:43:34 +00:00
} else if(content.length > 0) {
const m = content.match(/^\[url=?](.+)\[\/url]$/i);
url = m ? m[1] : content;
} else {
2019-06-08 02:26:01 +00:00
parser.warning('url tag contains no url.');
textContent = '';
success = false;
}
if((success) && (url)) {
// This fixes problems where content based urls are marked as invalid if they contain spaces.
url = fixURL(url);
if (!urlRegex.test(url)) {
textContent = `[BAD URL] ${url}`;
success = false;
}
}
return {
success,
url,
textContent,
domain: url ? domain(url) : undefined
};
}
2017-09-02 01:50:31 +00:00
export class CoreBBCodeParser extends BBCodeParser {
/*tslint:disable-next-line:typedef*///https://github.com/palantir/tslint/issues/711
constructor(public makeLinksClickable = true) {
super();
2018-03-28 13:51:05 +00:00
this.addTag(new BBCodeSimpleTag('b', 'strong'));
this.addTag(new BBCodeSimpleTag('i', 'em'));
this.addTag(new BBCodeSimpleTag('u', 'u'));
this.addTag(new BBCodeSimpleTag('s', 'del'));
this.addTag(new BBCodeSimpleTag('noparse', 'span', [], []));
this.addTag(new BBCodeSimpleTag('sub', 'sub', [], ['b', 'i', 'u', 's', 'color']));
2019-12-07 15:22:49 +00:00
this.addTag(new BBCodeSimpleTag('big', 'span', ['bigText'], ['b', 'i', 'u', 's', 'color']));
this.addTag(new BBCodeSimpleTag('sup', 'sup', [], ['b', 'i', 'u', 's', 'color']));
2018-03-28 13:51:05 +00:00
this.addTag(new BBCodeCustomTag('color', (parser, parent, param) => {
2017-09-02 01:50:31 +00:00
const cregex = /^(red|blue|white|yellow|pink|gray|green|orange|purple|black|brown|cyan)$/;
if(!cregex.test(param)) {
parser.warning('Invalid color parameter provided.');
2018-03-28 13:51:05 +00:00
return undefined;
2017-09-02 01:50:31 +00:00
}
const el = parser.createElement('span');
2018-03-28 13:51:05 +00:00
el.className = `${param}Text`;
2017-09-02 01:50:31 +00:00
parent.appendChild(el);
return el;
2018-03-28 13:51:05 +00:00
}));
2018-03-28 13:51:05 +00:00
this.addTag(new BBCodeTextTag('url', (parser, parent, param, content) => {
2019-06-08 02:26:01 +00:00
const tagData = analyzeUrlTag(parser, param, content);
2018-03-28 13:51:05 +00:00
const element = parser.createElement('span');
2019-06-08 02:26:01 +00:00
2018-03-28 13:51:05 +00:00
parent.appendChild(element);
2017-09-02 01:50:31 +00:00
2019-06-08 02:26:01 +00:00
if (!tagData.success) {
element.textContent = tagData.textContent;
2017-09-02 01:50:31 +00:00
return;
}
const fa = parser.createElement('i');
fa.className = 'fa fa-link';
element.appendChild(fa);
2017-09-02 01:50:31 +00:00
const a = parser.createElement('a');
2019-06-08 02:26:01 +00:00
a.href = tagData.url as string;
2017-09-02 01:50:31 +00:00
a.rel = 'nofollow noreferrer noopener';
a.target = '_blank';
a.className = 'user-link';
2019-06-08 02:26:01 +00:00
a.title = tagData.url as string;
a.textContent = tagData.textContent;
2017-09-02 01:50:31 +00:00
element.appendChild(a);
const span = document.createElement('span');
2019-01-03 17:38:17 +00:00
span.className = 'link-domain bbcode-pseudo';
2019-06-08 02:26:01 +00:00
span.textContent = ` [${tagData.domain}]`;
2017-09-02 01:50:31 +00:00
element.appendChild(span);
2019-06-08 02:26:01 +00:00
2018-03-28 13:51:05 +00:00
return element;
}));
2021-03-17 23:29:56 +00:00
this.addTag(new BBCodeCustomTag('spoiler', (parser, parent) => {
const link = parser.createElement('a');
const content = parser.createElement('span');
link.href = '#';
link.onclick = (e) => {
const target = e.target as HTMLElement;
target.parentElement!.replaceChild(content, target);
return false;
};
link.appendChild(document.createTextNode('[click to show spoiler]'));
parent.appendChild(link);
return content;
}));
2017-09-02 01:50:31 +00:00
}
parseEverything(input: string): HTMLElement {
if(this.makeLinksClickable && input.length > 0)
input = input.replace(findUrlRegex, (match, tag) => tag === undefined ? `[url]${match}[/url]` : match);
2017-09-02 01:50:31 +00:00
return super.parseEverything(input);
}
}