import { performEmojiUnescape } from "pretty-text/emoji"; const rule = { tag: "quote", before(state, tagInfo) { const attrs = tagInfo.attrs; let options = state.md.options.discourse; let quoteInfo = attrs["_default"]; let username, postNumber, topicId, avatarImg, primaryGroupName, full, displayName; if (quoteInfo) { let split = quoteInfo.split(/\,\s*/); username = split[0]; let i; for (i = 1; i < split.length; i++) { if (split[i].indexOf("post:") === 0) { postNumber = parseInt(split[i].slice(5), 10); continue; } if (split[i].indexOf("topic:") === 0) { topicId = parseInt(split[i].slice(6), 10); continue; } if (/full:\s*true/.test(split[i])) { full = true; continue; } // if we have the additional attribute of username: because we are prioritizing full name // then assign the name to be the displayName if (split[i].indexOf("username:") === 0) { // return users name by selecting all values from the first index to the post // this protects us from when a user has a `,` in their name displayName = split.slice(0, split.indexOf(`post:${postNumber}`)); // preserve `,` in a users name if they exist if (displayName.length > 1) { displayName = displayName.join(", "); } // strip key of 'username:' and return username username = split[i].slice(9); continue; } } } if (options.lookupAvatarByPostNumber) { // client-side, we can retrieve the avatar from the post avatarImg = options.lookupAvatarByPostNumber(postNumber, topicId); } else if (options.lookupAvatar) { // server-side, we need to lookup the avatar from the username avatarImg = options.lookupAvatar(username); } if (options.lookupPrimaryUserGroupByPostNumber) { // client-side, we can retrieve the primary user group from the post primaryGroupName = options.lookupPrimaryUserGroupByPostNumber( postNumber, topicId ); } else if (options.lookupPrimaryUserGroup) { // server-side, we need to lookup the primary user group from the username primaryGroupName = options.lookupPrimaryUserGroup(username); } if (options.formatUsername) { displayName = displayName || options.formatUsername(username); } else { displayName = displayName || username; } let token = state.push("bbcode_open", "aside", 1); token.attrs = []; if (primaryGroupName && primaryGroupName.length !== 0) { token.attrs.push(["class", `quote group-${primaryGroupName}`]); } else { token.attrs.push(["class", "quote no-group"]); } if (username) { token.attrs.push(["data-username", username]); } if (postNumber) { token.attrs.push(["data-post", postNumber]); } if (topicId) { token.attrs.push(["data-topic", topicId]); } if (full) { token.attrs.push(["data-full", "true"]); } if (username) { let forOtherTopic = options.topicId && topicId !== options.topicId; let offTopicQuote = postNumber && options.getTopicInfo && (forOtherTopic || options.forceQuoteLink); // on topic quote token = state.push("quote_header_open", "div", 1); token.attrs = [["class", "title"]]; token = state.push("quote_controls_open", "div", 1); token.attrs = [["class", "quote-controls"]]; state.push("quote_controls_close", "div", -1); if (avatarImg) { token = state.push("html_inline", "", 0); token.content = avatarImg; } if (offTopicQuote) { const topicInfo = options.getTopicInfo(topicId); if (topicInfo) { let href = topicInfo.href; if (postNumber > 0) { href += "/" + postNumber; } let title = topicInfo.title; if (options.enableEmoji) { title = performEmojiUnescape(topicInfo.title, { getURL: options.getURL, emojiSet: options.emojiSet, emojiCDNUrl: options.emojiCDNUrl, enableEmojiShortcuts: options.enableEmojiShortcuts, inlineEmoji: options.inlineEmoji, lazy: true, }); } token = state.push("link_open", "a", 1); token.attrs = [["href", href]]; token.block = false; token = state.push("html_inline", "", 0); token.content = title; token = state.push("link_close", "a", -1); token.block = false; } } else { token = state.push("text", "", 0); token.content = ` ${displayName}:`; } state.push("quote_header_close", "div", -1); } state.push("bbcode_open", "blockquote", 1); }, after(state) { state.push("bbcode_close", "blockquote", -1); state.push("bbcode_close", "aside", -1); }, }; export function setup(helper) { helper.registerOptions((opts, siteSettings) => { opts.enableEmoji = siteSettings.enable_emoji; opts.emojiSet = siteSettings.emoji_set; opts.emojiCDNUrl = siteSettings.external_emoji_url; opts.enableEmojiShortcuts = siteSettings.enable_emoji_shortcuts; opts.inlineEmoji = siteSettings.enable_inline_emoji_translation; }); helper.registerPlugin((md) => { md.block.bbcode.ruler.push("quotes", rule); }); helper.allowList(["img[class=avatar]", "img[loading=lazy]"]); helper.allowList({ custom(tag, name, value) { if (tag === "aside" && name === "class") { return ( value === "quote no-group" || !!/^quote group\-(.+)$/.exec(value) ); } }, }); }