Create elements instead of using raw HTML
This commit is contained in:
parent
ebda927bb1
commit
ebfc6fa724
@ -30,8 +30,7 @@
|
|||||||
<span class="content">
|
<span class="content">
|
||||||
<span
|
<span
|
||||||
ref="text"
|
ref="text"
|
||||||
class="text"
|
class="text"><ParsedMessage :message="message"/></span>
|
||||||
v-html="$options.filters.parse(message.text, message.users)"/>
|
|
||||||
|
|
||||||
<LinkPreview
|
<LinkPreview
|
||||||
v-for="preview in message.previews"
|
v-for="preview in message.previews"
|
||||||
@ -45,8 +44,10 @@
|
|||||||
<script>
|
<script>
|
||||||
import Username from "./Username.vue";
|
import Username from "./Username.vue";
|
||||||
import LinkPreview from "./LinkPreview.vue";
|
import LinkPreview from "./LinkPreview.vue";
|
||||||
|
import ParsedMessage from "./ParsedMessage.vue";
|
||||||
import MessageTypes from "./MessageTypes";
|
import MessageTypes from "./MessageTypes";
|
||||||
|
|
||||||
|
MessageTypes.ParsedMessage = ParsedMessage;
|
||||||
MessageTypes.LinkPreview = LinkPreview;
|
MessageTypes.LinkPreview = LinkPreview;
|
||||||
MessageTypes.Username = Username;
|
MessageTypes.Username = Username;
|
||||||
|
|
||||||
|
14
client/components/ParsedMessage.vue
Normal file
14
client/components/ParsedMessage.vue
Normal 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>
|
@ -62,9 +62,53 @@ function createFragment(fragment) {
|
|||||||
return escapedText;
|
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,
|
// 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.
|
// 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
|
// Extract the styling information and get the plain text version from it
|
||||||
const styleFragments = parseStyle(text);
|
const styleFragments = parseStyle(text);
|
||||||
const cleanText = styleFragments.map((fragment) => fragment.text).join("");
|
const cleanText = styleFragments.map((fragment) => fragment.text).join("");
|
||||||
@ -84,6 +128,60 @@ module.exports = function parse(text, users = []) {
|
|||||||
.concat(emojiParts)
|
.concat(emojiParts)
|
||||||
.concat(nameParts);
|
.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
|
// Merge the styling information with the channels / URLs / nicks / text objects and
|
||||||
// generate HTML strings with the resulting fragments
|
// generate HTML strings with the resulting fragments
|
||||||
return merge(parts, styleFragments, cleanText).map((textPart) => {
|
return merge(parts, styleFragments, cleanText).map((textPart) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user