mirror of
https://github.com/discourse/discourse.git
synced 2025-05-22 21:11:10 +08:00
Revert "FIX: Better emoji escaping for topic title"
This reverts commit 35426b5ad6b00c9047b9b7ba17d42d4f30bdb488.
This commit is contained in:
@ -2,7 +2,6 @@
|
|||||||
//= require ./pretty-text/guid
|
//= require ./pretty-text/guid
|
||||||
//= require ./pretty-text/censored-words
|
//= require ./pretty-text/censored-words
|
||||||
//= require ./pretty-text/emoji/data
|
//= require ./pretty-text/emoji/data
|
||||||
//= require ./pretty-text/emoji/version
|
|
||||||
//= require ./pretty-text/emoji
|
//= require ./pretty-text/emoji
|
||||||
//= require ./pretty-text/engines/discourse-markdown-it
|
//= require ./pretty-text/engines/discourse-markdown-it
|
||||||
//= require xss.min
|
//= require xss.min
|
||||||
|
@ -1,175 +0,0 @@
|
|||||||
import {
|
|
||||||
emojis,
|
|
||||||
aliases,
|
|
||||||
searchAliases,
|
|
||||||
translations,
|
|
||||||
tonableEmojis,
|
|
||||||
replacements
|
|
||||||
} from "pretty-text/emoji/data";
|
|
||||||
import { IMAGE_VERSION } from "pretty-text/emoji/version";
|
|
||||||
|
|
||||||
const extendedEmoji = {};
|
|
||||||
|
|
||||||
export function registerEmoji(code, url) {
|
|
||||||
code = code.toLowerCase();
|
|
||||||
extendedEmoji[code] = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function extendedEmojiList() {
|
|
||||||
return extendedEmoji;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emojiHash = {};
|
|
||||||
|
|
||||||
// add all default emojis
|
|
||||||
emojis.forEach(code => (emojiHash[code] = true));
|
|
||||||
|
|
||||||
// and their aliases
|
|
||||||
const aliasHash = {};
|
|
||||||
Object.keys(aliases).forEach(name => {
|
|
||||||
aliases[name].forEach(alias => (aliasHash[alias] = name));
|
|
||||||
});
|
|
||||||
|
|
||||||
export function performEmojiUnescape(string, opts) {
|
|
||||||
if (!string) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.replace(/[\u1000-\uFFFF]+|\B:[^\s:]+(?::t\d)?:?\B/g, m => {
|
|
||||||
const isEmoticon = !!translations[m];
|
|
||||||
const isUnicodeEmoticon = !!replacements[m];
|
|
||||||
const emojiVal = isEmoticon
|
|
||||||
? translations[m]
|
|
||||||
: isUnicodeEmoticon
|
|
||||||
? replacements[m]
|
|
||||||
: m.slice(1, m.length - 1);
|
|
||||||
const hasEndingColon = m.lastIndexOf(":") === m.length - 1;
|
|
||||||
const url = buildEmojiUrl(emojiVal, opts);
|
|
||||||
const classes = isCustomEmoji(emojiVal, opts)
|
|
||||||
? "emoji emoji-custom"
|
|
||||||
: "emoji";
|
|
||||||
|
|
||||||
return url && (isEmoticon || hasEndingColon || isUnicodeEmoticon)
|
|
||||||
? `<img src='${url}' ${
|
|
||||||
opts.skipTitle ? "" : `title='${emojiVal}'`
|
|
||||||
} alt='${emojiVal}' class='${classes}'>`
|
|
||||||
: m;
|
|
||||||
});
|
|
||||||
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function performEmojiEscape(string) {
|
|
||||||
if (!string) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.replace(/[\u1000-\uFFFF]+|\B:[^\s:]+(?::t\d)?:?\B/g, m => {
|
|
||||||
if (!!translations[m]) {
|
|
||||||
return ":" + translations[m] + ":";
|
|
||||||
} else if (!!replacements[m]) {
|
|
||||||
return ":" + replacements[m] + ":";
|
|
||||||
} else {
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isCustomEmoji(code, opts) {
|
|
||||||
code = code.toLowerCase();
|
|
||||||
if (extendedEmoji.hasOwnProperty(code)) return true;
|
|
||||||
if (opts && opts.customEmoji && opts.customEmoji.hasOwnProperty(code))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function buildEmojiUrl(code, opts) {
|
|
||||||
let url;
|
|
||||||
code = String(code).toLowerCase();
|
|
||||||
if (extendedEmoji.hasOwnProperty(code)) {
|
|
||||||
url = extendedEmoji[code];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (opts && opts.customEmoji && opts.customEmoji[code]) {
|
|
||||||
url = opts.customEmoji[code];
|
|
||||||
}
|
|
||||||
|
|
||||||
const noToneMatch = code.match(/([^:]+):?/);
|
|
||||||
if (
|
|
||||||
noToneMatch &&
|
|
||||||
!url &&
|
|
||||||
(emojiHash.hasOwnProperty(noToneMatch[1]) ||
|
|
||||||
aliasHash.hasOwnProperty(noToneMatch[1]))
|
|
||||||
) {
|
|
||||||
url = opts.getURL(
|
|
||||||
`/images/emoji/${opts.emojiSet}/${code.replace(/:t/, "/")}.png`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (url) {
|
|
||||||
url = url + "?v=" + IMAGE_VERSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function emojiExists(code) {
|
|
||||||
code = code.toLowerCase();
|
|
||||||
return !!(
|
|
||||||
extendedEmoji.hasOwnProperty(code) ||
|
|
||||||
emojiHash.hasOwnProperty(code) ||
|
|
||||||
aliasHash.hasOwnProperty(code)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let toSearch;
|
|
||||||
export function emojiSearch(term, options) {
|
|
||||||
const maxResults = (options && options["maxResults"]) || -1;
|
|
||||||
if (maxResults === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
toSearch =
|
|
||||||
toSearch ||
|
|
||||||
_.union(_.keys(emojiHash), _.keys(extendedEmoji), _.keys(aliasHash)).sort();
|
|
||||||
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
function addResult(t) {
|
|
||||||
const val = aliasHash[t] || t;
|
|
||||||
if (results.indexOf(val) === -1) {
|
|
||||||
results.push(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if term matches from beginning
|
|
||||||
for (let i = 0; i < toSearch.length; i++) {
|
|
||||||
const item = toSearch[i];
|
|
||||||
if (item.indexOf(term) === 0) addResult(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (searchAliases[term]) {
|
|
||||||
results.push.apply(results, searchAliases[term]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < toSearch.length; i++) {
|
|
||||||
const item = toSearch[i];
|
|
||||||
if (item.indexOf(term) > 0) addResult(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxResults === -1) {
|
|
||||||
return results;
|
|
||||||
} else {
|
|
||||||
return results.slice(0, maxResults);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isSkinTonableEmoji(term) {
|
|
||||||
const match = _.compact(term.split(":"))[0];
|
|
||||||
if (match) {
|
|
||||||
return tonableEmojis.indexOf(match) !== -1;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
127
app/assets/javascripts/pretty-text/emoji.js.es6.erb
Normal file
127
app/assets/javascripts/pretty-text/emoji.js.es6.erb
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import { emojis, aliases, searchAliases, translations, tonableEmojis } from 'pretty-text/emoji/data';
|
||||||
|
|
||||||
|
// bump up this number to expire all emojis
|
||||||
|
export const IMAGE_VERSION = "<%= Emoji::EMOJI_VERSION %>";
|
||||||
|
|
||||||
|
const extendedEmoji = {};
|
||||||
|
|
||||||
|
export function registerEmoji(code, url) {
|
||||||
|
code = code.toLowerCase();
|
||||||
|
extendedEmoji[code] = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extendedEmojiList() {
|
||||||
|
return extendedEmoji;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emojiHash = {};
|
||||||
|
|
||||||
|
// add all default emojis
|
||||||
|
emojis.forEach(code => emojiHash[code] = true);
|
||||||
|
|
||||||
|
// and their aliases
|
||||||
|
const aliasHash = {};
|
||||||
|
Object.keys(aliases).forEach(name => {
|
||||||
|
aliases[name].forEach(alias => aliasHash[alias] = name);
|
||||||
|
});
|
||||||
|
|
||||||
|
export function performEmojiUnescape(string, opts) {
|
||||||
|
if (!string) { return; }
|
||||||
|
|
||||||
|
// this can be further improved by supporting matches of emoticons that don't begin with a colon
|
||||||
|
if (string.indexOf(":") >= 0) {
|
||||||
|
return string.replace(/\B:[^\s:]+(?::t\d)?:?\B/g, m => {
|
||||||
|
const isEmoticon = !!translations[m];
|
||||||
|
const emojiVal = isEmoticon ? translations[m] : m.slice(1, m.length - 1);
|
||||||
|
const hasEndingColon = m.lastIndexOf(":") === m.length - 1;
|
||||||
|
const url = buildEmojiUrl(emojiVal, opts);
|
||||||
|
const classes = isCustomEmoji(emojiVal, opts) ? "emoji emoji-custom" : "emoji";
|
||||||
|
|
||||||
|
return url && (isEmoticon || hasEndingColon) ?
|
||||||
|
`<img src='${url}' ${opts.skipTitle ? '' : `title='${emojiVal}'`} alt='${emojiVal}' class='${classes}'>` : m;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isCustomEmoji(code, opts) {
|
||||||
|
code = code.toLowerCase();
|
||||||
|
if (extendedEmoji.hasOwnProperty(code)) return true;
|
||||||
|
if (opts && opts.customEmoji && opts.customEmoji.hasOwnProperty(code)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildEmojiUrl(code, opts) {
|
||||||
|
let url;
|
||||||
|
code = String(code).toLowerCase();
|
||||||
|
if (extendedEmoji.hasOwnProperty(code)) {
|
||||||
|
url = extendedEmoji[code];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts && opts.customEmoji && opts.customEmoji[code]) {
|
||||||
|
url = opts.customEmoji[code];
|
||||||
|
}
|
||||||
|
|
||||||
|
const noToneMatch = code.match(/([^:]+):?/);
|
||||||
|
if (noToneMatch && !url && (emojiHash.hasOwnProperty(noToneMatch[1]) || aliasHash.hasOwnProperty(noToneMatch[1]))) {
|
||||||
|
url = opts.getURL(`/images/emoji/${opts.emojiSet}/${code.replace(/:t/, '/')}.png`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url) {
|
||||||
|
url = url + "?v=" + IMAGE_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function emojiExists(code) {
|
||||||
|
code = code.toLowerCase();
|
||||||
|
return !!(extendedEmoji.hasOwnProperty(code) || emojiHash.hasOwnProperty(code) || aliasHash.hasOwnProperty(code));
|
||||||
|
};
|
||||||
|
|
||||||
|
let toSearch;
|
||||||
|
export function emojiSearch(term, options) {
|
||||||
|
const maxResults = (options && options["maxResults"]) || -1;
|
||||||
|
if (maxResults === 0) { return []; }
|
||||||
|
|
||||||
|
toSearch = toSearch || _.union(_.keys(emojiHash), _.keys(extendedEmoji), _.keys(aliasHash)).sort();
|
||||||
|
|
||||||
|
const results = [];
|
||||||
|
|
||||||
|
function addResult(t) {
|
||||||
|
const val = aliasHash[t] || t;
|
||||||
|
if (results.indexOf(val) === -1) {
|
||||||
|
results.push(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if term matches from beginning
|
||||||
|
for (let i=0; i<toSearch.length; i++) {
|
||||||
|
const item = toSearch[i];
|
||||||
|
if (item.indexOf(term) === 0) addResult(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchAliases[term]) {
|
||||||
|
results.push.apply(results, searchAliases[term]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i=0; i<toSearch.length; i++) {
|
||||||
|
const item = toSearch[i];
|
||||||
|
if (item.indexOf(term) > 0) addResult(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxResults === -1) {
|
||||||
|
return results;
|
||||||
|
} else {
|
||||||
|
return results.slice(0, maxResults);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export function isSkinTonableEmoji(term) {
|
||||||
|
const match = _.compact(term.split(":"))[0];
|
||||||
|
if (match) {
|
||||||
|
return tonableEmojis.indexOf(match) !== -1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
@ -3,4 +3,3 @@ export const tonableEmojis = <%= Emoji.tonable_emojis.flatten.inspect %>;
|
|||||||
export const aliases = <%= Emoji.aliases.inspect.gsub("=>", ":") %>;
|
export const aliases = <%= Emoji.aliases.inspect.gsub("=>", ":") %>;
|
||||||
export const searchAliases = <%= Emoji.search_aliases.inspect.gsub("=>", ":") %>;
|
export const searchAliases = <%= Emoji.search_aliases.inspect.gsub("=>", ":") %>;
|
||||||
export const translations = <%= Emoji.translations.inspect.gsub("=>", ":") %>;
|
export const translations = <%= Emoji.translations.inspect.gsub("=>", ":") %>;
|
||||||
export const replacements = <%= Emoji.unicode_replacements_json %>;
|
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
// bump up this number to expire all emojis
|
|
||||||
export const IMAGE_VERSION = "<%= Emoji::EMOJI_VERSION %>";
|
|
@ -157,7 +157,13 @@ class Emoji
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.unicode_unescape(string)
|
def self.unicode_unescape(string)
|
||||||
PrettyText.escape_emoji(string)
|
string.each_char.map do |c|
|
||||||
|
if str = unicode_replacements[c]
|
||||||
|
":#{str}:"
|
||||||
|
else
|
||||||
|
c
|
||||||
|
end
|
||||||
|
end.join
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.gsub_emoji_to_unicode(str)
|
def self.gsub_emoji_to_unicode(str)
|
||||||
|
@ -227,7 +227,7 @@ module PrettyText
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.unescape_emoji(title)
|
def self.unescape_emoji(title)
|
||||||
return title unless SiteSetting.enable_emoji? && title
|
return title unless SiteSetting.enable_emoji?
|
||||||
|
|
||||||
set = SiteSetting.emoji_set.inspect
|
set = SiteSetting.emoji_set.inspect
|
||||||
custom = Emoji.custom.map { |e| [e.name, e.url] }.to_h.to_json
|
custom = Emoji.custom.map { |e| [e.name, e.url] }.to_h.to_json
|
||||||
@ -239,16 +239,6 @@ module PrettyText
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.escape_emoji(title)
|
|
||||||
return unless title
|
|
||||||
|
|
||||||
protect do
|
|
||||||
v8.eval(<<~JS)
|
|
||||||
__performEmojiEscape(#{title.inspect});
|
|
||||||
JS
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.cook(text, opts = {})
|
def self.cook(text, opts = {})
|
||||||
options = opts.dup
|
options = opts.dup
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
__PrettyText = require("pretty-text/pretty-text").default;
|
__PrettyText = require("pretty-text/pretty-text").default;
|
||||||
__buildOptions = require("pretty-text/pretty-text").buildOptions;
|
__buildOptions = require("pretty-text/pretty-text").buildOptions;
|
||||||
__performEmojiUnescape = require("pretty-text/emoji").performEmojiUnescape;
|
__performEmojiUnescape = require("pretty-text/emoji").performEmojiUnescape;
|
||||||
__performEmojiEscape = require("pretty-text/emoji").performEmojiEscape;
|
|
||||||
|
|
||||||
__utils = require("discourse/lib/utilities");
|
__utils = require("discourse/lib/utilities");
|
||||||
|
|
||||||
|
@ -279,7 +279,6 @@ describe Topic do
|
|||||||
let(:topic_image) { build_topic_with_title("Topic with <img src='something'> image in its title") }
|
let(:topic_image) { build_topic_with_title("Topic with <img src='something'> image in its title") }
|
||||||
let(:topic_script) { build_topic_with_title("Topic with <script>alert('title')</script> script in its title") }
|
let(:topic_script) { build_topic_with_title("Topic with <script>alert('title')</script> script in its title") }
|
||||||
let(:topic_emoji) { build_topic_with_title("I 💖 candy alot") }
|
let(:topic_emoji) { build_topic_with_title("I 💖 candy alot") }
|
||||||
let(:topic_modifier_emoji) { build_topic_with_title("I 👨🌾 candy alot") }
|
|
||||||
|
|
||||||
it "escapes script contents" do
|
it "escapes script contents" do
|
||||||
expect(topic_script.fancy_title).to eq("Topic with <script>alert(‘title’)</script> script in its title")
|
expect(topic_script.fancy_title).to eq("Topic with <script>alert(‘title’)</script> script in its title")
|
||||||
@ -289,10 +288,6 @@ describe Topic do
|
|||||||
expect(topic_emoji.fancy_title).to eq("I :sparkling_heart: candy alot")
|
expect(topic_emoji.fancy_title).to eq("I :sparkling_heart: candy alot")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "keeps combined emojis" do
|
|
||||||
expect(topic_modifier_emoji.fancy_title).to eq("I :man_farmer: candy alot")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "escapes bold contents" do
|
it "escapes bold contents" do
|
||||||
expect(topic_bold.fancy_title).to eq("Topic with <b>bold</b> text in its title")
|
expect(topic_bold.fancy_title).to eq("Topic with <b>bold</b> text in its title")
|
||||||
end
|
end
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { acceptance } from "helpers/qunit-helpers";
|
import { acceptance } from "helpers/qunit-helpers";
|
||||||
import { IMAGE_VERSION as v } from "pretty-text/emoji/version";
|
import { IMAGE_VERSION as v } from "pretty-text/emoji";
|
||||||
import { resetCache } from "discourse/components/emoji-picker";
|
import { resetCache } from "discourse/components/emoji-picker";
|
||||||
|
|
||||||
acceptance("EmojiPicker", {
|
acceptance("EmojiPicker", {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { IMAGE_VERSION as v } from "pretty-text/emoji/version";
|
import { IMAGE_VERSION as v } from "pretty-text/emoji";
|
||||||
import { acceptance } from "helpers/qunit-helpers";
|
import { acceptance } from "helpers/qunit-helpers";
|
||||||
|
|
||||||
acceptance("Emoji", { loggedIn: true });
|
acceptance("Emoji", { loggedIn: true });
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { acceptance } from "helpers/qunit-helpers";
|
import { acceptance } from "helpers/qunit-helpers";
|
||||||
import { IMAGE_VERSION as v } from "pretty-text/emoji/version";
|
import { IMAGE_VERSION as v } from "pretty-text/emoji";
|
||||||
|
|
||||||
acceptance("Topic", {
|
acceptance("Topic", {
|
||||||
loggedIn: true,
|
loggedIn: true,
|
||||||
@ -121,23 +121,6 @@ QUnit.test("Updating the topic title with emojis", async assert => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test("Updating the topic title with unicode emojis", async assert => {
|
|
||||||
await visit("/t/internationalization-localization/280");
|
|
||||||
await click("#topic-title .d-icon-pencil-alt");
|
|
||||||
|
|
||||||
await fillIn("#edit-title", "emojis title 👨🌾");
|
|
||||||
|
|
||||||
await click("#topic-title .submit-edit");
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
find(".fancy-title")
|
|
||||||
.html()
|
|
||||||
.trim(),
|
|
||||||
`emojis title <img src="/images/emoji/emoji_one/man_farmer.png?v=${v}" title="man_farmer" alt="man_farmer" class="emoji">`,
|
|
||||||
"it displays the new title with escaped unicode emojis"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
acceptance("Topic featured links", {
|
acceptance("Topic featured links", {
|
||||||
loggedIn: true,
|
loggedIn: true,
|
||||||
settings: {
|
settings: {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import { emojiSearch } from "pretty-text/emoji";
|
import { emojiSearch, IMAGE_VERSION as v } from "pretty-text/emoji";
|
||||||
import { IMAGE_VERSION as v } from "pretty-text/emoji/version";
|
|
||||||
import { emojiUnescape } from "discourse/lib/text";
|
import { emojiUnescape } from "discourse/lib/text";
|
||||||
|
|
||||||
QUnit.module("lib:emoji");
|
QUnit.module("lib:emoji");
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Quote from "discourse/lib/quote";
|
import Quote from "discourse/lib/quote";
|
||||||
import Post from "discourse/models/post";
|
import Post from "discourse/models/post";
|
||||||
import { default as PrettyText, buildOptions } from "pretty-text/pretty-text";
|
import { default as PrettyText, buildOptions } from "pretty-text/pretty-text";
|
||||||
import { IMAGE_VERSION as v } from "pretty-text/emoji/version";
|
import { IMAGE_VERSION as v } from "pretty-text/emoji";
|
||||||
import { INLINE_ONEBOX_LOADING_CSS_CLASS } from "pretty-text/inline-oneboxer";
|
import { INLINE_ONEBOX_LOADING_CSS_CLASS } from "pretty-text/inline-oneboxer";
|
||||||
|
|
||||||
QUnit.module("lib:pretty-text");
|
QUnit.module("lib:pretty-text");
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { IMAGE_VERSION as v } from "pretty-text/emoji/version";
|
import { IMAGE_VERSION as v } from "pretty-text/emoji";
|
||||||
|
|
||||||
QUnit.module("model:topic");
|
QUnit.module("model:topic");
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user