Add Gitlab::Markdown::EmojiFilter
This commit is contained in:
parent
837dc328bf
commit
4ced630fed
|
|
@ -0,0 +1,79 @@
|
|||
require 'gitlab_emoji'
|
||||
require 'html/pipeline/filter'
|
||||
require 'action_controller'
|
||||
|
||||
module Gitlab
|
||||
module Markdown
|
||||
# HTML filter that replaces :emoji: with images.
|
||||
#
|
||||
# Based on HTML::Pipeline::EmojiFilter
|
||||
#
|
||||
# Context options:
|
||||
# :asset_root
|
||||
# :asset_host
|
||||
class EmojiFilter < HTML::Pipeline::Filter
|
||||
IGNORED_ANCESTOR_TAGS = %w(pre code tt).to_set
|
||||
|
||||
def call
|
||||
doc.search('text()').each do |node|
|
||||
content = node.to_html
|
||||
next unless content.include?(':')
|
||||
next if has_ancestor?(node, IGNORED_ANCESTOR_TAGS)
|
||||
|
||||
html = emoji_image_filter(content)
|
||||
|
||||
next if html == content
|
||||
|
||||
node.replace(html)
|
||||
end
|
||||
|
||||
doc
|
||||
end
|
||||
|
||||
# Replace :emoji: with corresponding images.
|
||||
#
|
||||
# text - String text to replace :emoji: in.
|
||||
#
|
||||
# Returns a String with :emoji: replaced with images.
|
||||
def emoji_image_filter(text)
|
||||
text.gsub(emoji_pattern) do |match|
|
||||
name = $1
|
||||
"<img class='emoji' title=':#{name}:' alt=':#{name}:' src='#{emoji_url(name)}' height='20' width='20' align='absmiddle' />"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def emoji_url(name)
|
||||
emoji_path = "emoji/#{emoji_filename(name)}"
|
||||
if context[:asset_host]
|
||||
# Asset host is specified.
|
||||
url_to_image(emoji_path)
|
||||
elsif context[:asset_root]
|
||||
# Gitlab url is specified
|
||||
File.join(context[:asset_root], url_to_image(emoji_path))
|
||||
else
|
||||
# All other cases
|
||||
url_to_image(emoji_path)
|
||||
end
|
||||
end
|
||||
|
||||
def url_to_image(image)
|
||||
ActionController::Base.helpers.url_to_image(image)
|
||||
end
|
||||
|
||||
# Build a regexp that matches all valid :emoji: names.
|
||||
def self.emoji_pattern
|
||||
@emoji_pattern ||= /:(#{Emoji.emojis_names.map { |name| Regexp.escape(name) }.join('|')}):/
|
||||
end
|
||||
|
||||
def emoji_pattern
|
||||
self.class.emoji_pattern
|
||||
end
|
||||
|
||||
def emoji_filename(name)
|
||||
"#{Emoji.emoji_filename(name)}.png"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
require 'spec_helper'
|
||||
|
||||
module Gitlab::Markdown
|
||||
describe EmojiFilter do
|
||||
def filter(html, contexts = {})
|
||||
described_class.call(html, contexts)
|
||||
end
|
||||
|
||||
before do
|
||||
ActionController::Base.asset_host = 'https://foo.com'
|
||||
end
|
||||
|
||||
it 'replaces supported emoji' do
|
||||
doc = filter('<p>:heart:</p>')
|
||||
expect(doc.css('img').first.attr('src')).to eq 'https://foo.com/assets/emoji/2764.png'
|
||||
end
|
||||
|
||||
it 'ignores unsupported emoji' do
|
||||
exp = act = '<p>:foo:</p>'
|
||||
doc = filter(act)
|
||||
expect(doc.to_html).to match Regexp.escape(exp)
|
||||
end
|
||||
|
||||
it 'correctly encodes the URL' do
|
||||
doc = filter('<p>:+1:</p>')
|
||||
expect(doc.css('img').first.attr('src')).to eq 'https://foo.com/assets/emoji/1F44D.png'
|
||||
end
|
||||
|
||||
it 'matches at the start of a string' do
|
||||
doc = filter(':+1:')
|
||||
expect(doc.css('img').size).to eq 1
|
||||
end
|
||||
|
||||
it 'matches at the end of a string' do
|
||||
doc = filter('This gets a :-1:')
|
||||
expect(doc.css('img').size).to eq 1
|
||||
end
|
||||
|
||||
it 'matches with adjacent text' do
|
||||
doc = filter('+1 (:+1:)')
|
||||
expect(doc.css('img').size).to eq 1
|
||||
end
|
||||
|
||||
it 'matches multiple emoji in a row' do
|
||||
doc = filter(':see_no_evil::hear_no_evil::speak_no_evil:')
|
||||
expect(doc.css('img').size).to eq 3
|
||||
end
|
||||
|
||||
it 'has a title attribute' do
|
||||
doc = filter(':-1:')
|
||||
expect(doc.css('img').first.attr('title')).to eq ':-1:'
|
||||
end
|
||||
|
||||
it 'has an alt attribute' do
|
||||
doc = filter(':-1:')
|
||||
expect(doc.css('img').first.attr('alt')).to eq ':-1:'
|
||||
end
|
||||
|
||||
it 'has an align attribute' do
|
||||
doc = filter(':8ball:')
|
||||
expect(doc.css('img').first.attr('align')).to eq 'absmiddle'
|
||||
end
|
||||
|
||||
it 'has an emoji class' do
|
||||
doc = filter(':cat:')
|
||||
expect(doc.css('img').first.attr('class')).to eq 'emoji'
|
||||
end
|
||||
|
||||
it 'has height and width attributes' do
|
||||
doc = filter(':dog:')
|
||||
img = doc.css('img').first
|
||||
|
||||
expect(img.attr('width')).to eq '20'
|
||||
expect(img.attr('height')).to eq '20'
|
||||
end
|
||||
|
||||
it 'keeps whitespace intact' do
|
||||
doc = filter('This deserves a :+1:, big time.')
|
||||
|
||||
expect(doc.to_html).to match(/^This deserves a <img.+>, big time\.\z/)
|
||||
end
|
||||
|
||||
it 'uses a custom asset_root context' do
|
||||
root = Gitlab.config.gitlab.url + 'gitlab/root'
|
||||
|
||||
doc = filter(':smile:', asset_root: root)
|
||||
expect(doc.css('img').first.attr('src')).to start_with(root)
|
||||
end
|
||||
|
||||
it 'uses a custom asset_host context' do
|
||||
ActionController::Base.asset_host = 'https://cdn.example.com'
|
||||
|
||||
doc = filter(':frowning:', asset_host: 'https://this-is-ignored-i-guess?')
|
||||
expect(doc.css('img').first.attr('src')).to start_with('https://cdn.example.com')
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue