motion-html-pipeline 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +379 -0
  3. data/lib/motion-html-pipeline.rb +14 -0
  4. data/lib/motion-html-pipeline/document_fragment.rb +27 -0
  5. data/lib/motion-html-pipeline/pipeline.rb +153 -0
  6. data/lib/motion-html-pipeline/pipeline/absolute_source_filter.rb +45 -0
  7. data/lib/motion-html-pipeline/pipeline/body_content.rb +42 -0
  8. data/lib/motion-html-pipeline/pipeline/disabled/@mention_filter.rb +140 -0
  9. data/lib/motion-html-pipeline/pipeline/disabled/autolink_filter.rb +27 -0
  10. data/lib/motion-html-pipeline/pipeline/disabled/camo_filter.rb +93 -0
  11. data/lib/motion-html-pipeline/pipeline/disabled/email_reply_filter.rb +66 -0
  12. data/lib/motion-html-pipeline/pipeline/disabled/emoji_filter.rb +125 -0
  13. data/lib/motion-html-pipeline/pipeline/disabled/markdown_filter.rb +37 -0
  14. data/lib/motion-html-pipeline/pipeline/disabled/plain_text_input_filter.rb +13 -0
  15. data/lib/motion-html-pipeline/pipeline/disabled/sanitization_filter.rb +137 -0
  16. data/lib/motion-html-pipeline/pipeline/disabled/syntax_highlight_filter.rb +44 -0
  17. data/lib/motion-html-pipeline/pipeline/disabled/toc_filter.rb +67 -0
  18. data/lib/motion-html-pipeline/pipeline/filter.rb +163 -0
  19. data/lib/motion-html-pipeline/pipeline/https_filter.rb +27 -0
  20. data/lib/motion-html-pipeline/pipeline/image_filter.rb +17 -0
  21. data/lib/motion-html-pipeline/pipeline/image_max_width_filter.rb +37 -0
  22. data/lib/motion-html-pipeline/pipeline/text_filter.rb +14 -0
  23. data/lib/motion-html-pipeline/pipeline/version.rb +5 -0
  24. data/spec/motion-html-pipeline/_helpers/mock_instumentation_service.rb +19 -0
  25. data/spec/motion-html-pipeline/pipeline/absolute_source_filter_spec.rb +47 -0
  26. data/spec/motion-html-pipeline/pipeline/disabled/auto_link_filter_spec.rb +33 -0
  27. data/spec/motion-html-pipeline/pipeline/disabled/camo_filter_spec.rb +75 -0
  28. data/spec/motion-html-pipeline/pipeline/disabled/email_reply_filter_spec.rb +64 -0
  29. data/spec/motion-html-pipeline/pipeline/disabled/emoji_filter_spec.rb +92 -0
  30. data/spec/motion-html-pipeline/pipeline/disabled/markdown_filter_spec.rb +112 -0
  31. data/spec/motion-html-pipeline/pipeline/disabled/plain_text_input_filter_spec.rb +20 -0
  32. data/spec/motion-html-pipeline/pipeline/disabled/sanitization_filter_spec.rb +164 -0
  33. data/spec/motion-html-pipeline/pipeline/disabled/syntax_highlighting_filter_spec.rb +59 -0
  34. data/spec/motion-html-pipeline/pipeline/disabled/toc_filter_spec.rb +137 -0
  35. data/spec/motion-html-pipeline/pipeline/https_filter_spec.rb +52 -0
  36. data/spec/motion-html-pipeline/pipeline/image_filter_spec.rb +37 -0
  37. data/spec/motion-html-pipeline/pipeline/image_max_width_filter_spec.rb +57 -0
  38. data/spec/motion-html-pipeline/pipeline_spec.rb +80 -0
  39. data/spec/spec_helper.rb +48 -0
  40. metadata +147 -0
@@ -0,0 +1,45 @@
1
+ module MotionHTMLPipeline
2
+ class Pipeline
3
+ class AbsoluteSourceFilter < Filter
4
+ # HTML Filter for replacing relative and root relative image URLs with
5
+ # fully qualified URLs
6
+ #
7
+ # This is useful if an image is root relative but should really be going
8
+ # through a cdn, or if the content for the page assumes the host is known
9
+ # i.e. scraped webpages and some RSS feeds.
10
+ #
11
+ # Context options:
12
+ # :image_base_url - Base URL for image host for root relative src.
13
+ # :image_subpage_url - For relative src.
14
+ #
15
+ # This filter does not write additional information to the context.
16
+ # This filter would need to be run before CamoFilter.
17
+ def call
18
+ doc.css('img').each do |element|
19
+ next if element['src'].nil? || element['src'].empty?
20
+ src = element['src'].strip
21
+ next if src.start_with? 'http'
22
+ base = if src.start_with? '/'
23
+ image_base_url
24
+ else
25
+ image_subpage_url
26
+ end
27
+
28
+ base = NSURL.URLWithString(base)
29
+ element['src'] = NSURL.URLWithString(src, relativeToURL: base).absoluteString
30
+ end
31
+ doc
32
+ end
33
+
34
+ # Private: the base url you want to use
35
+ def image_base_url
36
+ context[:image_base_url] || raise("Missing context :image_base_url for #{self.class.name}")
37
+ end
38
+
39
+ # Private: the relative url you want to use
40
+ def image_subpage_url
41
+ context[:image_subpage_url] || raise("Missing context :image_subpage_url for #{self.class.name}")
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,42 @@
1
+ module MotionHTMLPipeline
2
+ class Pipeline
3
+ # Public: Runs a String of content through an HTML processing pipeline,
4
+ # providing easy access to a generated DocumentFragment.
5
+ class BodyContent
6
+ attr_reader :result
7
+
8
+ # Public: Initialize a BodyContent.
9
+ #
10
+ # body - A String body.
11
+ # context - A Hash of context options for the filters.
12
+ # pipeline - A MotionHTMLPipeline::Pipeline object with one or more Filters.
13
+ def initialize(body, context, pipeline)
14
+ @body = body
15
+ @context = context
16
+ @pipeline = pipeline
17
+ end
18
+
19
+ # Public: Gets the memoized result of the body content as it passed through
20
+ # the Pipeline.
21
+ #
22
+ # Returns a Hash, or something similar as defined by @pipeline.result_class.
23
+ def result
24
+ @result ||= @pipeline.call @body, @context
25
+ end
26
+
27
+ # Public: Gets the updated body from the Pipeline result.
28
+ #
29
+ # Returns a String or DocumentFragment.
30
+ def output
31
+ @output ||= result[:output]
32
+ end
33
+
34
+ # Public: Parses the output into a DocumentFragment.
35
+ #
36
+ # Returns a DocumentFragment.
37
+ def document
38
+ @document ||= MotionHTMLPipeline::Pipeline.parse output
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,140 @@
1
+ # TODO Requires Set (or something similar)
2
+ #------------------------------------------------------------------------------
3
+ # require 'set'
4
+ #
5
+ # module MotionHTMLPipeline
6
+ # class Pipeline
7
+ # # HTML filter that replaces @user mentions with links. Mentions within <pre>,
8
+ # # <code>, and <a> elements are ignored. Mentions that reference users that do
9
+ # # not exist are ignored.
10
+ # #
11
+ # # Context options:
12
+ # # :base_url - Used to construct links to user profile pages for each
13
+ # # mention.
14
+ # # :info_url - Used to link to "more info" when someone mentions @mention
15
+ # # or @mentioned.
16
+ # # :username_pattern - Used to provide a custom regular expression to
17
+ # # identify usernames
18
+ # #
19
+ # class MentionFilter < Filter
20
+ # # Public: Find user @mentions in text. See
21
+ # # MentionFilter#mention_link_filter.
22
+ # #
23
+ # # MentionFilter.mentioned_logins_in(text) do |match, login, is_mentioned|
24
+ # # "<a href=...>#{login}</a>"
25
+ # # end
26
+ # #
27
+ # # text - String text to search.
28
+ # #
29
+ # # Yields the String match, the String login name, and a Boolean determining
30
+ # # if the match = "@mention[ed]". The yield's return replaces the match in
31
+ # # the original text.
32
+ # #
33
+ # # Returns a String replaced with the return of the block.
34
+ # def self.mentioned_logins_in(text, username_pattern = UsernamePattern)
35
+ # text.gsub MentionPatterns[username_pattern] do |match|
36
+ # login = Regexp.last_match(1)
37
+ # yield match, login, MentionLogins.include?(login.downcase)
38
+ # end
39
+ # end
40
+ #
41
+ # # Hash that contains all of the mention patterns used by the pipeline
42
+ # MentionPatterns = Hash.new do |hash, key|
43
+ # hash[key] = /
44
+ # (?:^|\W) # beginning of string or non-word char
45
+ # @((?>#{key})) # @username
46
+ # (?!\/) # without a trailing slash
47
+ # (?=
48
+ # \.+[ \t\W]| # dots followed by space or non-word character
49
+ # \.+$| # dots at end of line
50
+ # [^0-9a-zA-Z_.]| # non-word character except dot
51
+ # $ # end of line
52
+ # )
53
+ # /ix
54
+ # end
55
+ #
56
+ # # Default pattern used to extract usernames from text. The value can be
57
+ # # overriden by providing the username_pattern variable in the context.
58
+ # UsernamePattern = /[a-z0-9][a-z0-9-]*/
59
+ #
60
+ # # List of username logins that, when mentioned, link to the blog post
61
+ # # about @mentions instead of triggering a real mention.
62
+ # MentionLogins = %w[
63
+ # mention
64
+ # mentions
65
+ # mentioned
66
+ # mentioning
67
+ # ].freeze
68
+ #
69
+ # # Don't look for mentions in text nodes that are children of these elements
70
+ # IGNORE_PARENTS = %w(pre code a style script).to_set
71
+ #
72
+ # def call
73
+ # result[:mentioned_usernames] ||= []
74
+ #
75
+ # doc.search('.//text()').each do |node|
76
+ # content = node.to_html
77
+ # next unless content.include?('@')
78
+ # next if has_ancestor?(node, IGNORE_PARENTS)
79
+ # html = mention_link_filter(content, base_url, info_url, username_pattern)
80
+ # next if html == content
81
+ # node.replace(html)
82
+ # end
83
+ # doc
84
+ # end
85
+ #
86
+ # # The URL to provide when someone @mentions a "mention" name, such
87
+ # # as @mention or @mentioned, that will give them more info on mentions.
88
+ # def info_url
89
+ # context[:info_url] || nil
90
+ # end
91
+ #
92
+ # def username_pattern
93
+ # context[:username_pattern] || UsernamePattern
94
+ # end
95
+ #
96
+ # # Replace user @mentions in text with links to the mentioned user's
97
+ # # profile page.
98
+ # #
99
+ # # text - String text to replace @mention usernames in.
100
+ # # base_url - The base URL used to construct user profile URLs.
101
+ # # info_url - The "more info" URL used to link to more info on @mentions.
102
+ # # If nil we don't link @mention or @mentioned.
103
+ # # username_pattern - Regular expression used to identify usernames in
104
+ # # text
105
+ # #
106
+ # # Returns a string with @mentions replaced with links. All links have a
107
+ # # 'user-mention' class name attached for styling.
108
+ # def mention_link_filter(text, _base_url = '/', info_url = nil, username_pattern = UsernamePattern)
109
+ # self.class.mentioned_logins_in(text, username_pattern) do |match, login, is_mentioned|
110
+ # link =
111
+ # if is_mentioned
112
+ # link_to_mention_info(login, info_url)
113
+ # else
114
+ # link_to_mentioned_user(login)
115
+ # end
116
+ #
117
+ # link ? match.sub("@#{login}", link) : match
118
+ # end
119
+ # end
120
+ #
121
+ # def link_to_mention_info(text, info_url = nil)
122
+ # return "@#{text}" if info_url.nil?
123
+ # "<a href='#{info_url}' class='user-mention'>" \
124
+ # "@#{text}" \
125
+ # '</a>'
126
+ # end
127
+ #
128
+ # def link_to_mentioned_user(login)
129
+ # result[:mentioned_usernames] |= [login]
130
+ #
131
+ # url = base_url.dup
132
+ # url << '/' unless url =~ /[\/~]\z/
133
+ #
134
+ # "<a href='#{url << login}' class='user-mention'>" \
135
+ # "@#{login}" \
136
+ # '</a>'
137
+ # end
138
+ # end
139
+ # end
140
+ # end
@@ -0,0 +1,27 @@
1
+ # TODO Requires Rinku gem (or something similar)
2
+ #------------------------------------------------------------------------------
3
+ # module MotionHTMLPipeline
4
+ # class Pipeline
5
+ # # HTML Filter for auto_linking urls in HTML.
6
+ # #
7
+ # # Context options:
8
+ # # :autolink - boolean whether to autolink urls
9
+ # # :link_attr - HTML attributes for the link that will be generated
10
+ # # :skip_tags - HTML tags inside which autolinking will be skipped.
11
+ # # See Rinku.skip_tags
12
+ # # :flags - additional Rinku flags. See https://github.com/vmg/rinku
13
+ # #
14
+ # # This filter does not write additional information to the context.
15
+ # class AutolinkFilter < Filter
16
+ # def call
17
+ # return html if context[:autolink] == false
18
+ #
19
+ # skip_tags = context[:skip_tags]
20
+ # flags = 0
21
+ # flags |= context[:flags] if context[:flags]
22
+ #
23
+ # Rinku.auto_link(html, :urls, context[:link_attr], skip_tags, flags)
24
+ # end
25
+ # end
26
+ # end
27
+ # end
@@ -0,0 +1,93 @@
1
+ # require 'openssl'
2
+ # require 'uri'
3
+ #
4
+ # module MotionHTMLPipeline
5
+ # class Pipeline
6
+ # # HTML Filter for replacing http image URLs with camo versions. See:
7
+ # #
8
+ # # https://github.com/atmos/camo
9
+ # #
10
+ # # All images provided in user content should be run through this
11
+ # # filter so that http image sources do not cause mixed-content warnings
12
+ # # in browser clients.
13
+ # #
14
+ # # Context options:
15
+ # # :asset_proxy (required) - Base URL for constructed asset proxy URLs.
16
+ # # :asset_proxy_secret_key (required) - The shared secret used to encode URLs.
17
+ # # :asset_proxy_whitelist - Array of host Strings or Regexps to skip
18
+ # # src rewriting.
19
+ # #
20
+ # # This filter does not write additional information to the context.
21
+ # class CamoFilter < Filter
22
+ # # Hijacks images in the markup provided, replacing them with URLs that
23
+ # # go through the github asset proxy.
24
+ # def call
25
+ # return doc unless asset_proxy_enabled?
26
+ #
27
+ # doc.search('img').each do |element|
28
+ # original_src = element['src']
29
+ # next unless original_src
30
+ #
31
+ # begin
32
+ # uri = URI.parse(original_src)
33
+ # rescue Exception
34
+ # next
35
+ # end
36
+ #
37
+ # next if uri.host.nil?
38
+ # next if asset_host_whitelisted?(uri.host)
39
+ #
40
+ # element['src'] = asset_proxy_url(original_src)
41
+ # element['data-canonical-src'] = original_src
42
+ # end
43
+ # doc
44
+ # end
45
+ #
46
+ # # Implementation of validate hook.
47
+ # # Errors should raise exceptions or use an existing validator.
48
+ # def validate
49
+ # needs :asset_proxy, :asset_proxy_secret_key
50
+ # end
51
+ #
52
+ # # The camouflaged URL for a given image URL.
53
+ # def asset_proxy_url(url)
54
+ # "#{asset_proxy_host}/#{asset_url_hash(url)}/#{hexencode(url)}"
55
+ # end
56
+ #
57
+ # # Private: calculate the HMAC digest for a image source URL.
58
+ # def asset_url_hash(url)
59
+ # OpenSSL::HMAC.hexdigest('sha1', asset_proxy_secret_key, url)
60
+ # end
61
+ #
62
+ # # Private: Return true if asset proxy filter should be enabled
63
+ # def asset_proxy_enabled?
64
+ # !context[:disable_asset_proxy]
65
+ # end
66
+ #
67
+ # # Private: the host to use for generated asset proxied URLs.
68
+ # def asset_proxy_host
69
+ # context[:asset_proxy]
70
+ # end
71
+ #
72
+ # def asset_proxy_secret_key
73
+ # context[:asset_proxy_secret_key]
74
+ # end
75
+ #
76
+ # def asset_proxy_whitelist
77
+ # context[:asset_proxy_whitelist] || []
78
+ # end
79
+ #
80
+ # def asset_host_whitelisted?(host)
81
+ # asset_proxy_whitelist.any? do |test|
82
+ # test.is_a?(String) ? host == test : test.match(host)
83
+ # end
84
+ # end
85
+ #
86
+ # # Private: helper to hexencode a string. Each byte ends up encoded into
87
+ # # two characters, zero padded value in the range [0-9a-f].
88
+ # def hexencode(str)
89
+ # str.unpack('H*').first
90
+ # end
91
+ # end
92
+ # end
93
+ # end
@@ -0,0 +1,66 @@
1
+ # MotionHTMLPipeline::Pipeline.require_dependency('escape_utils', 'EmailReplyFilter')
2
+ # MotionHTMLPipeline::Pipeline.require_dependency('email_reply_parser', 'EmailReplyFilter')
3
+ #
4
+ # module MotionHTMLPipeline
5
+ # class Pipeline
6
+ # # HTML Filter that converts email reply text into an HTML DocumentFragment.
7
+ # # It must be used as the first filter in a pipeline.
8
+ # #
9
+ # # Context options:
10
+ # # None
11
+ # #
12
+ # # This filter does not write any additional information to the context hash.
13
+ # class EmailReplyFilter < TextFilter
14
+ # include EscapeUtils
15
+ #
16
+ # EMAIL_HIDDEN_HEADER = %(<span class="email-hidden-toggle"><a href="#">&hellip;</a></span><div class="email-hidden-reply" style="display:none">).freeze
17
+ # EMAIL_QUOTED_HEADER = %(<div class="email-quoted-reply">).freeze
18
+ # EMAIL_SIGNATURE_HEADER = %(<div class="email-signature-reply">).freeze
19
+ # EMAIL_FRAGMENT_HEADER = %(<div class="email-fragment">).freeze
20
+ # EMAIL_HEADER_END = '</div>'.freeze
21
+ # EMAIL_REGEX = /[^@\s.][^@\s]*@\[?[a-z0-9.-]+\]?/
22
+ # HIDDEN_EMAIL_PATTERN = '***@***.***'.freeze
23
+ #
24
+ # # Scans an email body to determine which bits are quoted and which should
25
+ # # be hidden. EmailReplyParser is used to split the comment into an Array
26
+ # # of quoted or unquoted Blocks. Now, we loop through them and attempt to
27
+ # # add <div> tags around them so we can hide the hidden blocks, and style
28
+ # # the quoted blocks differently. Since multiple blocks may be hidden, be
29
+ # # sure to keep the "email-hidden-reply" <div>s around "email-quoted-reply"
30
+ # # <div> tags. Call this on each comment of a visible thread in the order
31
+ # # that they are displayed. Note: all comments are processed so we can
32
+ # # maintain a Set of SHAs of paragraphs. Only plaintext comments skip the
33
+ # # markdown step.
34
+ # #
35
+ # # Returns the email comment HTML as a String
36
+ # def call
37
+ # found_hidden = nil
38
+ # paragraphs = EmailReplyParser.read(text.dup).fragments.map do |fragment|
39
+ # pieces = [escape_html(fragment.to_s.strip).gsub(/^\s*(>|&gt;)/, '')]
40
+ # if fragment.quoted?
41
+ # if context[:hide_quoted_email_addresses]
42
+ # pieces.map! do |piece|
43
+ # piece.gsub(EMAIL_REGEX, HIDDEN_EMAIL_PATTERN)
44
+ # end
45
+ # end
46
+ # pieces.unshift EMAIL_QUOTED_HEADER
47
+ # pieces << EMAIL_HEADER_END
48
+ # elsif fragment.signature?
49
+ # pieces.unshift EMAIL_SIGNATURE_HEADER
50
+ # pieces << EMAIL_HEADER_END
51
+ # else
52
+ # pieces.unshift EMAIL_FRAGMENT_HEADER
53
+ # pieces << EMAIL_HEADER_END
54
+ # end
55
+ # if fragment.hidden? && !found_hidden
56
+ # found_hidden = true
57
+ # pieces.unshift EMAIL_HIDDEN_HEADER
58
+ # end
59
+ # pieces.join
60
+ # end
61
+ # paragraphs << EMAIL_HEADER_END if found_hidden
62
+ # paragraphs.join("\n")
63
+ # end
64
+ # end
65
+ # end
66
+ # end
@@ -0,0 +1,125 @@
1
+ # require 'cgi'
2
+ # MotionHTMLPipeline::Pipeline.require_dependency('gemoji', 'EmojiFilter')
3
+ #
4
+ # module MotionHTMLPipeline
5
+ # class Pipeline
6
+ # # HTML filter that replaces :emoji: with images.
7
+ # #
8
+ # # Context:
9
+ # # :asset_root (required) - base url to link to emoji sprite
10
+ # # :asset_path (optional) - url path to link to emoji sprite. :file_name can be used as a placeholder for the sprite file name. If no asset_path is set "emoji/:file_name" is used.
11
+ # # :ignored_ancestor_tags (optional) - Tags to stop the emojification. Node has matched ancestor HTML tags will not be emojified. Default to pre, code, and tt tags. Extra tags please pass in the form of array, e.g., %w(blockquote summary).
12
+ # # :img_attrs (optional) - Attributes for generated img tag. E.g. Pass { "draggble" => true, "height" => nil } to set draggable attribute to "true" and clear height attribute of generated img tag.
13
+ # class EmojiFilter < Filter
14
+ # DEFAULT_IGNORED_ANCESTOR_TAGS = %w[pre code tt].freeze
15
+ #
16
+ # def call
17
+ # doc.search('.//text()').each do |node|
18
+ # content = node.text
19
+ # next unless content.include?(':')
20
+ # next if has_ancestor?(node, ignored_ancestor_tags)
21
+ # html = emoji_image_filter(content)
22
+ # next if html == content
23
+ # node.replace(html)
24
+ # end
25
+ # doc
26
+ # end
27
+ #
28
+ # # Implementation of validate hook.
29
+ # # Errors should raise exceptions or use an existing validator.
30
+ # def validate
31
+ # needs :asset_root
32
+ # end
33
+ #
34
+ # # Replace :emoji: with corresponding images.
35
+ # #
36
+ # # text - String text to replace :emoji: in.
37
+ # #
38
+ # # Returns a String with :emoji: replaced with images.
39
+ # def emoji_image_filter(text)
40
+ # text.gsub(emoji_pattern) do |_match|
41
+ # emoji_image_tag(Regexp.last_match(1))
42
+ # end
43
+ # end
44
+ #
45
+ # # The base url to link emoji sprites
46
+ # #
47
+ # # Raises ArgumentError if context option has not been provided.
48
+ # # Returns the context's asset_root.
49
+ # def asset_root
50
+ # context[:asset_root]
51
+ # end
52
+ #
53
+ # # The url path to link emoji sprites
54
+ # #
55
+ # # :file_name can be used in the asset_path as a placeholder for the sprite file name. If no asset_path is set in the context "emoji/:file_name" is used.
56
+ # # Returns the context's asset_path or the default path if no context asset_path is given.
57
+ # def asset_path(name)
58
+ # if context[:asset_path]
59
+ # context[:asset_path].gsub(':file_name', emoji_filename(name))
60
+ # else
61
+ # File.join('emoji', emoji_filename(name))
62
+ # end
63
+ # end
64
+ #
65
+ # private
66
+ #
67
+ # # Build an emoji image tag
68
+ # def emoji_image_tag(name)
69
+ # require 'active_support/core_ext/hash/indifferent_access'
70
+ # html_attrs =
71
+ # default_img_attrs(name)
72
+ # .merge!((context[:img_attrs] || {}).with_indifferent_access)
73
+ # .map { |attr, value| !value.nil? && %(#{attr}="#{value.respond_to?(:call) && value.call(name) || value}") }
74
+ # .reject(&:blank?).join(' '.freeze)
75
+ #
76
+ # "<img #{html_attrs}>"
77
+ # end
78
+ #
79
+ # # Default attributes for img tag
80
+ # def default_img_attrs(name)
81
+ # {
82
+ # 'class' => 'emoji'.freeze,
83
+ # 'title' => ":#{name}:",
84
+ # 'alt' => ":#{name}:",
85
+ # 'src' => emoji_url(name).to_s,
86
+ # 'height' => '20'.freeze,
87
+ # 'width' => '20'.freeze,
88
+ # 'align' => 'absmiddle'.freeze
89
+ # }
90
+ # end
91
+ #
92
+ # def emoji_url(name)
93
+ # File.join(asset_root, asset_path(name))
94
+ # end
95
+ #
96
+ # # Build a regexp that matches all valid :emoji: names.
97
+ # def self.emoji_pattern
98
+ # @emoji_pattern ||= /:(#{emoji_names.map { |name| Regexp.escape(name) }.join('|')}):/
99
+ # end
100
+ #
101
+ # def emoji_pattern
102
+ # self.class.emoji_pattern
103
+ # end
104
+ #
105
+ # def self.emoji_names
106
+ # Emoji.all.map(&:aliases).flatten.sort
107
+ # end
108
+ #
109
+ # def emoji_filename(name)
110
+ # Emoji.find_by_alias(name).image_filename
111
+ # end
112
+ #
113
+ # # Return ancestor tags to stop the emojification.
114
+ # #
115
+ # # @return [Array<String>] Ancestor tags.
116
+ # def ignored_ancestor_tags
117
+ # if context[:ignored_ancestor_tags]
118
+ # DEFAULT_IGNORED_ANCESTOR_TAGS | context[:ignored_ancestor_tags]
119
+ # else
120
+ # DEFAULT_IGNORED_ANCESTOR_TAGS
121
+ # end
122
+ # end
123
+ # end
124
+ # end
125
+ # end