fchat-rising/chat/bbcode.ts

136 lines
5.6 KiB
TypeScript
Raw Normal View History

2017-09-02 01:50:31 +00:00
import Vue, {Component, CreateElement, RenderContext, VNode} from 'vue';
2019-06-08 02:26:01 +00:00
import { CoreBBCodeParser, analyzeUrlTag } from '../bbcode/core';
2017-09-02 01:50:31 +00:00
//tslint:disable-next-line:match-default-export-name
import BaseEditor from '../bbcode/Editor.vue';
2018-03-28 13:51:05 +00:00
import {BBCodeTextTag} from '../bbcode/parser';
2018-04-16 23:14:13 +00:00
import ChannelView from './ChannelTagView.vue';
2017-09-02 01:50:31 +00:00
import {characterImage} from './common';
import core from './core';
import {Character} from './interfaces';
2019-07-06 16:49:19 +00:00
import {default as UrlView} from '../bbcode/UrlTagView.vue';
2017-09-02 01:50:31 +00:00
import UserView from './user_view';
export const BBCodeView: Component = {
functional: true,
render(createElement: CreateElement, context: RenderContext): VNode {
2017-09-02 01:50:31 +00:00
/*tslint:disable:no-unsafe-any*///because we're not actually supposed to do any of this
context.data.hook = {
2018-03-28 13:51:05 +00:00
insert(node: VNode): void {
node.elm!.appendChild(core.bbCodeParser.parseEverything(
context.props.text !== undefined ? context.props.text : context.props.unsafeText));
2018-09-28 00:08:10 +00:00
if(context.props.afterInsert !== undefined) context.props.afterInsert(node.elm);
2017-09-02 01:50:31 +00:00
},
2018-03-28 13:51:05 +00:00
destroy(node: VNode): void {
const element = (<BBCodeElement>(<Element>node.elm).firstChild);
2017-09-02 01:50:31 +00:00
if(element.cleanup !== undefined) element.cleanup();
}
};
const vnode = createElement('span', context.data);
vnode.key = context.props.text;
return vnode;
//tslint:enable
}
};
export class Editor extends BaseEditor {
parser = core.bbCodeParser;
}
export type BBCodeElement = HTMLElement & {cleanup?(): void};
export default class BBCodeParser extends CoreBBCodeParser {
cleanup: Vue[] = [];
constructor() {
super();
2018-03-28 13:51:05 +00:00
this.addTag(new BBCodeTextTag('user', (parser, parent, param, content) => {
2017-09-02 01:50:31 +00:00
if(param.length > 0)
parser.warning('Unexpected parameter on user tag.');
const uregex = /^[a-zA-Z0-9_\-\s]+$/;
2018-03-28 13:51:05 +00:00
if(!uregex.test(content)) return;
2017-09-02 01:50:31 +00:00
const el = parser.createElement('span');
parent.appendChild(el);
2018-03-28 13:51:05 +00:00
const view = new UserView({el, propsData: {character: core.characters.get(content)}});
this.cleanup.push(view);
2017-09-02 01:50:31 +00:00
return el;
2018-03-28 13:51:05 +00:00
}));
this.addTag(new BBCodeTextTag('icon', (parser, parent, param, content) => {
2017-09-02 01:50:31 +00:00
if(param.length > 0)
parser.warning('Unexpected parameter on icon tag.');
const uregex = /^[a-zA-Z0-9_\-\s]+$/;
if(!uregex.test(content))
return;
const img = parser.createElement('img');
img.src = characterImage(content);
img.style.cursor = 'pointer';
img.className = 'character-avatar icon';
2017-09-02 01:50:31 +00:00
img.title = img.alt = content;
(<HTMLImageElement & {character: Character}>img).character = core.characters.get(content);
2018-03-28 13:51:05 +00:00
parent.appendChild(img);
return img;
}));
this.addTag(new BBCodeTextTag('eicon', (parser, parent, param, content) => {
2017-09-02 01:50:31 +00:00
if(param.length > 0)
parser.warning('Unexpected parameter on eicon tag.');
const uregex = /^[a-zA-Z0-9_\-\s]+$/;
if(!uregex.test(content))
return;
const extension = core.connection.isOpen && !core.state.settings.animatedEicons ? 'png' : 'gif';
2017-09-02 01:50:31 +00:00
const img = parser.createElement('img');
img.src = `https://static.f-list.net/images/eicon/${content.toLowerCase()}.${extension}`;
img.title = img.alt = content;
img.className = 'character-avatar icon';
2018-03-28 13:51:05 +00:00
parent.appendChild(img);
return img;
}));
this.addTag(new BBCodeTextTag('session', (parser, parent, param, content) => {
2019-01-03 17:38:17 +00:00
const root = parser.createElement('span');
2017-09-02 01:50:31 +00:00
const el = parser.createElement('span');
2019-01-03 17:38:17 +00:00
parent.appendChild(root);
root.appendChild(el);
2018-03-28 13:51:05 +00:00
const view = new ChannelView({el, propsData: {id: content, text: param}});
2017-09-02 01:50:31 +00:00
this.cleanup.push(view);
2019-01-03 17:38:17 +00:00
return root;
2018-03-28 13:51:05 +00:00
}));
this.addTag(new BBCodeTextTag('channel', (parser, parent, _, content) => {
2019-01-03 17:38:17 +00:00
const root = parser.createElement('span');
2017-09-02 01:50:31 +00:00
const el = parser.createElement('span');
2019-01-03 17:38:17 +00:00
parent.appendChild(root);
root.appendChild(el);
2018-03-28 13:51:05 +00:00
const view = new ChannelView({el, propsData: {id: content, text: content}});
2017-09-02 01:50:31 +00:00
this.cleanup.push(view);
2019-01-03 17:38:17 +00:00
return root;
2018-03-28 13:51:05 +00:00
}));
2019-06-08 02:26:01 +00:00
this.addTag(new BBCodeTextTag(
'url',
(parser, parent, _, content) => {
const tagData = analyzeUrlTag(parser, _, content);
const root = parser.createElement('span');
// const el = parser.createElement('span');
parent.appendChild(root);
// root.appendChild(el);
if (!tagData.success) {
root.textContent = tagData.textContent;
return;
}
const view = new UrlView({el: root, propsData: {url: tagData.url, text: tagData.textContent, domain: tagData.domain}});
this.cleanup.push(view);
return root;
}));
2017-09-02 01:50:31 +00:00
}
parseEverything(input: string): BBCodeElement {
const elm = <BBCodeElement>super.parseEverything(input);
if(this.cleanup.length > 0)
elm.cleanup = ((cleanup: Vue[]) => () => {
for(const component of cleanup) component.$destroy();
})(this.cleanup);
this.cleanup = [];
return elm;
}
}