Create elements instead of using raw HTML

This commit is contained in:
Pavel Djundik 2018-07-11 20:22:44 +03:00 committed by Pavel Djundik
parent ebda927bb1
commit ebfc6fa724
3 changed files with 116 additions and 3 deletions

View file

@ -30,8 +30,7 @@
<span class="content">
<span
ref="text"
class="text"
v-html="$options.filters.parse(message.text, message.users)"/>
class="text"><ParsedMessage :message="message"/></span>
<LinkPreview
v-for="preview in message.previews"
@ -45,8 +44,10 @@
<script>
import Username from "./Username.vue";
import LinkPreview from "./LinkPreview.vue";
import ParsedMessage from "./ParsedMessage.vue";
import MessageTypes from "./MessageTypes";
MessageTypes.ParsedMessage = ParsedMessage;
MessageTypes.LinkPreview = LinkPreview;
MessageTypes.Username = Username;

View file

@ -0,0 +1,14 @@
<script>
const parse = require("../js/libs/handlebars/parse");
export default {
functional: true,
name: "ParsedMessage",
props: {
message: Object,
},
render(createElement, context) {
return parse(context.props.message.text, context.props.message.users, createElement);
},
};
</script>

View file

@ -62,9 +62,53 @@ function createFragment(fragment) {
return escapedText;
}
function createVueFragment(fragment, createElement) {
const classes = [];
if (fragment.bold) {
classes.push("irc-bold");
}
if (fragment.textColor !== undefined) {
classes.push("irc-fg" + fragment.textColor);
}
if (fragment.bgColor !== undefined) {
classes.push("irc-bg" + fragment.bgColor);
}
if (fragment.italic) {
classes.push("irc-italic");
}
if (fragment.underline) {
classes.push("irc-underline");
}
if (fragment.strikethrough) {
classes.push("irc-strikethrough");
}
if (fragment.monospace) {
classes.push("irc-monospace");
}
if (classes.length === 0 && !fragment.hexColor) {
return fragment.text;
}
return createElement("span", {
class: classes,
style: {
color: `#${fragment.hexColor}`,
"background-color": fragment.hexBgColor ? `#${fragment.hexBgColor}` : null,
},
}, fragment.text);
}
// Transform an IRC message potentially filled with styling control codes, URLs,
// nicknames, and channels into a string of HTML elements to display on the client.
module.exports = function parse(text, users = []) {
module.exports = function parse(text, users = [], createElement = null) {
// Extract the styling information and get the plain text version from it
const styleFragments = parseStyle(text);
const cleanText = styleFragments.map((fragment) => fragment.text).join("");
@ -84,6 +128,60 @@ module.exports = function parse(text, users = []) {
.concat(emojiParts)
.concat(nameParts);
if (createElement) {
return merge(parts, styleFragments, cleanText).map((textPart) => {
const fragments = textPart.fragments.map((fragment) => createVueFragment(fragment, createElement));
// Wrap these potentially styled fragments with links and channel buttons
if (textPart.link) {
return createElement("a", {
class: [
"inline-channel",
],
attrs: {
href: textPart.link,
target: "_blank",
rel: "noopener",
},
}, fragments);
} else if (textPart.channel) {
return createElement("span", {
class: [
"inline-channel",
],
attrs: {
role: "button",
tabindex: 0,
"data-chan": textPart.channel,
},
}, fragments);
} else if (textPart.emoji) {
return createElement("span", {
class: [
"emoji",
],
attrs: {
role: "img",
"aria-label": emojiMap[textPart.emoji] ? `Emoji: ${emojiMap[textPart.emoji]}` : null,
},
}, fragments);
} else if (textPart.nick) {
return createElement("span", {
class: [
"user",
colorClass(textPart.nick),
],
attrs: {
role: "button",
"data-name": textPart.nick,
},
}, fragments);
}
return fragments;
});
}
// Merge the styling information with the channels / URLs / nicks / text objects and
// generate HTML strings with the resulting fragments
return merge(parts, styleFragments, cleanText).map((textPart) => {