Remove HTML version of parse()

This commit is contained in:
Pavel Djundik 2018-07-12 11:41:40 +03:00 committed by Pavel Djundik
parent d83dcc35e2
commit e3ff385ae0
18 changed files with 113 additions and 180 deletions

View file

@ -28,8 +28,9 @@
<span class="title">{{ channel.name }}</span>
<span
:title="channel.topic"
class="topic"
v-html="$options.filters.parse(channel.topic)"/>
class="topic"><ParsedMessage
v-if="channel.topic"
:text="channel.topic"/></span>
<button
class="menu"
aria-label="Open the context menu"
@ -86,6 +87,7 @@
<script>
require("intersection-observer");
const socket = require("../js/socket");
import ParsedMessage from "./ParsedMessage.vue";
import MessageList from "./MessageList.vue";
import ChatInput from "./ChatInput.vue";
import ChatUserList from "./ChatUserList.vue";
@ -96,6 +98,7 @@ import ListIgnored from "./Special/ListIgnored.vue";
export default {
name: "Chat",
components: {
ParsedMessage,
MessageList,
ChatInput,
ChatUserList,

View file

@ -28,9 +28,7 @@
</template>
</span>
<span class="content">
<span
ref="text"
class="text"><ParsedMessage :message="message"/></span>
<span class="text"><ParsedMessage :message="message"/></span>
<LinkPreview
v-for="preview in message.previews"

View file

@ -1,24 +1,24 @@
<template>
<span class="content">
<template v-if="message.self">
<i v-html="$options.filters.parse(message.text)"/>
</template>
<ParsedMessage
v-if="message.self"
:message="message"/>
<template v-else>
<Username :user="message.from"/>
is away
<i
class="away-message"
v-html="'(' + $options.filters.parse(message.text) + ')'"/>
<i class="away-message">(<ParsedMessage :message="message"/>)</i>
</template>
</span>
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
name: "MessageTypeAway",
components: {
ParsedMessage,
Username,
},
props: {

View file

@ -1,8 +1,8 @@
<template>
<span class="content">
<template v-if="message.self">
<i v-html="$options.filters.parse(message.text)"/>
</template>
<ParsedMessage
v-if="message.self"
:message="message"/>
<template v-else>
<Username :user="message.from"/>
is back

View file

@ -1,18 +1,18 @@
<template>
<span class="content">
<Username :user="message.from"/>
<span
class="ctcp-message"
v-html="$options.filters.parse(message.ctcpMessage)"/>
<span class="ctcp-message"><ParsedMessage :text="message.ctcpMessage"/></span>
</span>
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
name: "MessageTypeCTCP",
components: {
ParsedMessage,
Username,
},
props: {

View file

@ -2,18 +2,18 @@
<span class="content">
<Username :user="message.from"/>
sent a <abbr title="Client-to-client protocol">CTCP</abbr> request:
<span
class="ctcp-message"
v-html="$options.filters.parse(message.ctcpMessage)"/>
<span class="ctcp-message"><ParsedMessage :text="message.ctcpMessage"/></span>
</span>
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
name: "MessageTypeRequestCTCP",
components: {
ParsedMessage,
Username,
},
props: {

View file

@ -6,16 +6,18 @@
<Username
v-else
:user="message.target"/>
to <span v-html="$options.filters.parse(message.channel)"/>
to <ParsedMessage :text="message.channel"/>
</span>
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
name: "MessageTypeInvite",
components: {
ParsedMessage,
Username,
},
props: {

View file

@ -5,17 +5,18 @@
<Username :user="message.target"/>
<i
v-if="message.text"
class="part-reason"
v-html="'(' + $options.filters.parse(message.text) + ')'"/>
class="part-reason">(<ParsedMessage :message="message"/>)</i>
</span>
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
name: "MessageTypeKick",
components: {
ParsedMessage,
Username,
},
props: {

View file

@ -2,16 +2,18 @@
<span class="content">
<Username :user="message.from"/>
sets mode
<span v-html="$options.filters.parse(message.text)"/>
<ParsedMessage :message="message"/>
</span>
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
name: "MessageTypeMode",
components: {
ParsedMessage,
Username,
},
props: {

View file

@ -5,17 +5,18 @@
has left the channel
<i
v-if="message.text"
class="part-reason"
v-html="'(' + $options.filters.parse(message.text) + ')'"/>
class="part-reason">(<ParsedMessage :message="message"/>)</i>
</span>
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
name: "MessageTypePart",
components: {
ParsedMessage,
Username,
},
props: {

View file

@ -5,17 +5,18 @@
has quit
<i
v-if="message.text"
class="quit-reason"
v-html="'(' + $options.filters.parse(message.text) + ')'"/>
class="quit-reason">(<ParsedMessage :message="message"/>)</i>
</span>
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
name: "MessageTypeQuit",
components: {
ParsedMessage,
Username,
},
props: {

View file

@ -9,17 +9,18 @@
</template>
<span
v-if="message.text"
class="new-topic"
v-html="$options.filters.parse(message.text)"/>
class="new-topic"><ParsedMessage :message="message"/></span>
</span>
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
name: "MessageTypeTopic",
components: {
ParsedMessage,
Username,
},
props: {

View file

@ -27,7 +27,7 @@
<template v-if="message.whois.real_name">
<dt>Real name:</dt>
<dd v-html="$options.filters.parse(message.whois.real_name)"/>
<dd><ParsedMessage :text="message.whois.real_name"/></dd>
</template>
<template v-if="message.whois.registered_nick">
@ -37,7 +37,7 @@
<template v-if="message.whois.channels">
<dt>Channels:</dt>
<dd v-html="$options.filters.parse(message.whois.channels)"/>
<dd><ParsedMessage :text="message.whois.channels"/></dd>
</template>
<template v-if="message.whois.modes">
@ -67,7 +67,7 @@
<template v-if="message.whois.away">
<dt>Away:</dt>
<dd v-html="$options.filters.parse(message.whois.away)"/>
<dd><ParsedMessage :text="message.whois.away"/></dd>
</template>
<template v-if="message.whois.secure">
@ -94,11 +94,13 @@
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
import Username from "../Username.vue";
export default {
name: "MessageTypeWhois",
components: {
ParsedMessage,
Username,
},
props: {

View file

@ -5,10 +5,15 @@ export default {
name: "ParsedMessage",
functional: true,
props: {
text: String,
message: Object,
},
render(createElement, context) {
return parse(context.props.message.text, context.props.message, createElement);
if (typeof context.props.text !== "undefined") {
return parse(createElement, context.props.text);
}
return parse(createElement, context.props.message.text, context.props.message);
},
};
</script>

View file

@ -12,9 +12,7 @@
v-for="ban in channel.data"
:key="ban.hostmask">
<td class="hostmask">{{ ban.hostmask }}</td>
<td
class="banned_by"
v-html="$options.filters.parse(ban.banned_by)"/>
<td class="banned_by">{{ ban.banned_by }}</td>
<td class="banned_at">{{ ban.banned_at | localetime }}</td>
</tr>
</tbody>

View file

@ -14,21 +14,22 @@
<tr
v-for="chan in channel.data"
:key="chan.channel">
<td
class="channel"
v-html="$options.filters.parse(chan.channel)"/>
<td class="channel"><ParsedMessage :text="chan.channel"/></td>
<td class="users">{{ chan.num_users }}</td>
<td
class="topic"
v-html="$options.filters.parse(chan.topic)"/>
<td class="topic"><ParsedMessage :text="chan.topic"/></td>
</tr>
</tbody>
</table>
</template>
<script>
import ParsedMessage from "../ParsedMessage.vue";
export default {
name: "ListChannels",
components: {
ParsedMessage,
},
props: {
channel: Object,
},

View file

@ -1,6 +1,5 @@
"use strict";
const Handlebars = require("handlebars/runtime");
const parseStyle = require("./ircmessageparser/parseStyle");
const findChannels = require("./ircmessageparser/findChannels");
const findLinks = require("./ircmessageparser/findLinks");
@ -12,58 +11,7 @@ const emojiMap = require("../fullnamemap.json");
const LinkPreviewToggle = require("../../../components/LinkPreviewToggle.vue").default;
// Create an HTML `span` with styling information for a given fragment
function createFragment(fragment) {
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");
}
let attributes = classes.length ? ` class="${classes.join(" ")}"` : "";
const escapedText = Handlebars.Utils.escapeExpression(fragment.text);
if (fragment.hexColor) {
attributes += ` style="color:#${fragment.hexColor}`;
if (fragment.hexBgColor) {
attributes += `;background-color:#${fragment.hexBgColor}`;
}
attributes += '"';
}
if (attributes.length) {
return `<span${attributes}>${escapedText}</span>`;
}
return escapedText;
}
function createVueFragment(fragment, createElement) {
function createFragment(fragment, createElement) {
const classes = [];
if (fragment.bold) {
@ -109,7 +57,7 @@ function createVueFragment(fragment, createElement) {
// 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, message = null, createElement = null) {
module.exports = function parse(createElement, text, message = 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("");
@ -129,97 +77,69 @@ module.exports = function parse(text, message = null, createElement = null) {
.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) {
const preview = message && message.previews.find((p) => p.link === textPart.link);
const link = createElement("a", {
class: [
"inline-channel",
],
attrs: {
href: textPart.link,
target: "_blank",
rel: "noopener",
},
}, fragments);
if (!preview) {
return link;
}
return [link, createElement(LinkPreviewToggle, {
class: ["toggle-button", "toggle-preview"],
props: {
link: preview,
},
}, 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) => {
// Create HTML strings with styling information
const fragments = textPart.fragments.map(createFragment).join("");
const fragments = textPart.fragments.map((fragment) => createFragment(fragment, createElement));
// Wrap these potentially styled fragments with links and channel buttons
if (textPart.link) {
const escapedLink = Handlebars.Utils.escapeExpression(textPart.link);
return `<a href="${escapedLink}" target="_blank" rel="noopener">${fragments}</a>` +
`<button class="toggle-button toggle-preview" data-url="${escapedLink}" hidden></button>`;
} else if (textPart.channel) {
const escapedChannel = Handlebars.Utils.escapeExpression(textPart.channel);
return `<span class="inline-channel" role="button" tabindex="0" data-chan="${escapedChannel}">${fragments}</span>`;
} else if (textPart.emoji) {
if (!emojiMap[textPart.emoji]) {
return `<span class="emoji" role="img">${fragments}</span>`;
const preview = message && message.previews.find((p) => p.link === textPart.link);
const link = createElement("a", {
class: [
"inline-channel",
],
attrs: {
href: textPart.link,
target: "_blank",
rel: "noopener",
},
}, fragments);
if (!preview) {
return link;
}
return `<span class="emoji" role="img" aria-label="Emoji: ${emojiMap[textPart.emoji]}" title="${emojiMap[textPart.emoji]}">${fragments}</span>`;
return [link, createElement(LinkPreviewToggle, {
class: ["toggle-button", "toggle-preview"],
props: {
link: preview,
},
}, 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) {
const nick = Handlebars.Utils.escapeExpression(textPart.nick);
return `<span role="button" class="user ${colorClass(textPart.nick)}" data-name="${nick}">${fragments}</span>`;
return createElement("span", {
class: [
"user",
colorClass(textPart.nick),
],
attrs: {
"role": "button",
"data-name": textPart.nick,
},
}, fragments);
}
return fragments;
}).join("");
});
};

View file

@ -3,7 +3,6 @@
const Vue = require("vue").default;
const App = require("../components/App.vue").default;
const roundBadgeNumber = require("./libs/handlebars/roundBadgeNumber");
const parse = require("./libs/handlebars/parse");
const tz = require("./libs/handlebars/tz");
const localetime = require("./libs/handlebars/localetime");
const localedate = require("./libs/handlebars/localedate");
@ -11,7 +10,6 @@ const friendlydate = require("./libs/handlebars/friendlydate");
const friendlysize = require("./libs/handlebars/friendlysize");
const colorClass = require("./libs/handlebars/colorClass");
Vue.filter("parse", parse);
Vue.filter("tz", tz);
Vue.filter("localetime", localetime);
Vue.filter("localedate", localedate);