From 6e5cd67faba15402b9222ffe3ce925186d37754b Mon Sep 17 00:00:00 2001 From: JuanitoFatas Date: Tue, 28 Jun 2016 15:59:23 +0800 Subject: [PATCH 1/3] Make Emoji img attributes configurable --- lib/html/pipeline/emoji_filter.rb | 37 ++++++++++++++++++++++++- test/html/pipeline/emoji_filter_test.rb | 22 +++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/lib/html/pipeline/emoji_filter.rb b/lib/html/pipeline/emoji_filter.rb index 7a6b88db..40d23152 100644 --- a/lib/html/pipeline/emoji_filter.rb +++ b/lib/html/pipeline/emoji_filter.rb @@ -71,7 +71,42 @@ def asset_path(name) # Build an emoji image tag def emoji_image_tag(name) - ":#{name}:" + "" + end + + def img_html_attrs(name) + img_attrs(name).map { |attr, value| %(#{attr}="#{value}") }.join(" ") + end + + def img_attrs(name) + user_overrides = customized_attrs(name).select { |k, v| !v.nil? } + excluded_keys = customized_attrs(name).select { |k, v| v.nil? }.keys + result = default_img_attrs(name).merge!(user_overrides) + result.except(*excluded_keys) + end + + def default_img_attrs(name) + { + "class" => "emoji", + "title" => ":#{name}:", + "alt" => ":#{name}:", + "src" => "#{emoji_url(name)}", + "height" => "20", + "width" => "20", + "align" => "absmiddle", + } + end + + def customized_attrs(name) + return {} unless context[:img_attrs] + + @_custom_img_attributes ||= begin + custom_img_attributes = context[:img_attrs] + + custom_img_attributes.each do |key, value| + custom_img_attributes[key] = value.call(name) if value.respond_to?(:call) + end + end end def emoji_url(name) diff --git a/test/html/pipeline/emoji_filter_test.rb b/test/html/pipeline/emoji_filter_test.rb index 990faad1..510dfaf5 100644 --- a/test/html/pipeline/emoji_filter_test.rb +++ b/test/html/pipeline/emoji_filter_test.rb @@ -62,4 +62,26 @@ def test_not_emojify_in_custom_multiple_tags_foo_and_bar doc = filter.call assert_equal body, doc.to_html end + + def test_img_tag_attributes + body = ":shipit:" + filter = EmojiFilter.new(body, {:asset_root => 'https://foo.com'}) + doc = filter.call + assert_equal %(:shipit:), doc.to_html + end + + def test_img_custom_tag_attributes + body = ":shipit:" + filter = EmojiFilter.new(body, {:asset_root => 'https://foo.com', img_attrs: Hash("draggable"=> false, "height" => nil, "width" => nil, "align" => nil)}) + doc = filter.call + assert_equal %(:shipit:), doc.to_html + end + + def test_img_attr_accept_proclike_object + remove_colons = ->(name) { name.gsub(":", "") } + body = ":shipit:" + filter = EmojiFilter.new(body, {:asset_root => 'https://foo.com', img_attrs: Hash("title" => remove_colons)}) + doc = filter.call + assert_equal %(:shipit:), doc.to_html + end end From 1b3e5e4341963ac4cd7488cce0c569c1e6dd52cf Mon Sep 17 00:00:00 2001 From: JuanitoFatas Date: Thu, 30 Jun 2016 01:01:11 +0800 Subject: [PATCH 2/3] Add docs and remove indirections --- lib/html/pipeline/emoji_filter.rb | 39 ++++++++++++++----------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/lib/html/pipeline/emoji_filter.rb b/lib/html/pipeline/emoji_filter.rb index 40d23152..2c848e03 100644 --- a/lib/html/pipeline/emoji_filter.rb +++ b/lib/html/pipeline/emoji_filter.rb @@ -14,6 +14,7 @@ class Pipeline # :asset_root (required) - base url to link to emoji sprite # :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. # :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). + # :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. class EmojiFilter < Filter DEFAULT_IGNORED_ANCESTOR_TAGS = %w(pre code tt).freeze @@ -71,40 +72,34 @@ def asset_path(name) # Build an emoji image tag def emoji_image_tag(name) - "" - end - - def img_html_attrs(name) - img_attrs(name).map { |attr, value| %(#{attr}="#{value}") }.join(" ") - end + html_attrs = + default_img_attrs(name). + merge!(customized_img_attrs(name)). + except(*customized_img_attrs(name).select { |_, v| v.nil? }.keys). + map { |attr, value| %(#{attr}="#{value}") }.join(" ".freeze) - def img_attrs(name) - user_overrides = customized_attrs(name).select { |k, v| !v.nil? } - excluded_keys = customized_attrs(name).select { |k, v| v.nil? }.keys - result = default_img_attrs(name).merge!(user_overrides) - result.except(*excluded_keys) + "" end + # Default attributes for img tag def default_img_attrs(name) { - "class" => "emoji", + "class" => "emoji".freeze, "title" => ":#{name}:", "alt" => ":#{name}:", "src" => "#{emoji_url(name)}", - "height" => "20", - "width" => "20", - "align" => "absmiddle", + "height" => "20".freeze, + "width" => "20".freeze, + "align" => "absmiddle".freeze, } end - def customized_attrs(name) - return {} unless context[:img_attrs] - - @_custom_img_attributes ||= begin - custom_img_attributes = context[:img_attrs] + def customized_img_attrs(name) + @_customized_img_attrs ||= begin + return {} unless context[:img_attrs] - custom_img_attributes.each do |key, value| - custom_img_attributes[key] = value.call(name) if value.respond_to?(:call) + context[:img_attrs].each do |key, value| + context[:img_attrs][key] = value.call(name) if value.respond_to?(:call) end end end From f1c16183c32c2bd34a22239cafb62cbeaecb7523 Mon Sep 17 00:00:00 2001 From: JuanitoFatas Date: Thu, 30 Jun 2016 15:42:46 +0800 Subject: [PATCH 3/3] Put all logics into the emoji_image_tag method 1. User can specify a hash `context[:img_attrs]` to change the `img` tag attributes, e.g. `{ "draggable" => false }` 2. The hash key can be either `String` / `Symbol` (indifferent access) `{ "draggable" => false }` / `{ draggable: false }` => `` 3. The hash value can be either anything / proc-like object Proc-like object with default argument `name`: Given name is `:shipit:` `{ title: ->(name) { |n| n.gsub(":", "") } }` => `` So you can do any customisations with the attribute. 4. The hash value nil means clear the attribute of img tag For example, to clear the default `height`, `width`, and `align` attributes, pass `{ height: nil, width: nil, align: nil }` to `context[:img_attrs]`. 5. Refine tests with consistent styles --- lib/html/pipeline/emoji_filter.rb | 17 ++++------------- test/html/pipeline/emoji_filter_test.rb | 19 +++++++++++++------ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/lib/html/pipeline/emoji_filter.rb b/lib/html/pipeline/emoji_filter.rb index 2c848e03..5be5f0b1 100644 --- a/lib/html/pipeline/emoji_filter.rb +++ b/lib/html/pipeline/emoji_filter.rb @@ -72,11 +72,12 @@ def asset_path(name) # Build an emoji image tag def emoji_image_tag(name) + require "active_support/core_ext/hash/indifferent_access" html_attrs = default_img_attrs(name). - merge!(customized_img_attrs(name)). - except(*customized_img_attrs(name).select { |_, v| v.nil? }.keys). - map { |attr, value| %(#{attr}="#{value}") }.join(" ".freeze) + merge!((context[:img_attrs] || {}).with_indifferent_access). + map { |attr, value| !value.nil? && %(#{attr}="#{value.try(:call, name) || value}") }. + reject(&:blank?).join(" ".freeze) "" end @@ -94,16 +95,6 @@ def default_img_attrs(name) } end - def customized_img_attrs(name) - @_customized_img_attrs ||= begin - return {} unless context[:img_attrs] - - context[:img_attrs].each do |key, value| - context[:img_attrs][key] = value.call(name) if value.respond_to?(:call) - end - end - end - def emoji_url(name) File.join(asset_root, asset_path(name)) end diff --git a/test/html/pipeline/emoji_filter_test.rb b/test/html/pipeline/emoji_filter_test.rb index 510dfaf5..f9c7d94c 100644 --- a/test/html/pipeline/emoji_filter_test.rb +++ b/test/html/pipeline/emoji_filter_test.rb @@ -65,23 +65,30 @@ def test_not_emojify_in_custom_multiple_tags_foo_and_bar def test_img_tag_attributes body = ":shipit:" - filter = EmojiFilter.new(body, {:asset_root => 'https://foo.com'}) + filter = EmojiFilter.new(body, {:asset_root => "https://foo.com"}) doc = filter.call assert_equal %(:shipit:), doc.to_html end - def test_img_custom_tag_attributes + def test_img_tag_attributes_can_be_customized body = ":shipit:" - filter = EmojiFilter.new(body, {:asset_root => 'https://foo.com', img_attrs: Hash("draggable"=> false, "height" => nil, "width" => nil, "align" => nil)}) + filter = EmojiFilter.new(body, {:asset_root => "https://foo.com", img_attrs: Hash("draggable"=> "false", "height" => nil, "width" => nil, "align" => nil)}) doc = filter.call - assert_equal %(:shipit:), doc.to_html + assert_equal %(:shipit:), doc.to_html end - def test_img_attr_accept_proclike_object + def test_img_attrs_value_can_accept_proclike_object remove_colons = ->(name) { name.gsub(":", "") } body = ":shipit:" - filter = EmojiFilter.new(body, {:asset_root => 'https://foo.com', img_attrs: Hash("title" => remove_colons)}) + filter = EmojiFilter.new(body, {:asset_root => "https://foo.com", img_attrs: Hash("title" => remove_colons)}) doc = filter.call assert_equal %(:shipit:), doc.to_html end + + def test_img_attrs_can_accept_symbolized_keys + body = ":shipit:" + filter = EmojiFilter.new(body, {:asset_root => "https://foo.com", img_attrs: Hash(draggable: false, height: nil, width: nil, align: nil)}) + doc = filter.call + assert_equal %(:shipit:), doc.to_html + end end