# frozen_string_literal: true
require "jekyll"
require "negarmoji"
require "html/pipeline"
require "html/pipeline/negarmoji-pipeline"
module Jekyll
class Emoji
# Extend negarmoji emoji class.
# Default configuration.
DEFAULT_HOST_URL = "https://cdn.jsdelivr.net/gh/azadeh-afzar/OpenMoji-Jekyll-Plugin@latest"
DEFAULT_ASSET_PATH = "/images/color/svg"
DEFAULT_DIR = "/emoji"
DEFAULT_EXTENSION = "svg"
DEFAULT_IMG_ATTRS = { "class" => "emoji",
"height" => "20",
"width" => "20",
"align" => nil, }
# Regex values.
FILE_NAME = "/:file_name"
BODY_START_TAG = "
\s*!m.freeze
class << self
def emojify(doc)
return unless
doc.output =~ HTML::Pipeline::NegarMojiHtmlPipeline::NegarehEmojiFilter
.emoji_pattern
doc.output = if doc.output.include? BODY_START_TAG
replace_document_body(doc)
else
src_root = emoji_src_root(doc.site.config)
asset_path = emoji_asset_path(doc.site.config)
file_extension = emoji_extension(doc.site.config)
img_attrs = emoji_attributes(doc.site.config)
filter_with_emoji(src_root, asset_path, file_extension,
img_attrs).call(doc.output)[:output].to_s
end
end
# Public: Create or fetch the filter for the given {{src_root}} asset root.
#
# :src_root - the asset root URL (e.g. https://cdn.jsdelivr.net/gh/azadeh-afzar/OpenMoji-Jekyll-Plugin@latest)
# :asset_path - the asset sub-path of src (e.g. "/images/color/svg")
# [default = "emoji"].
#
# :extension - the extension of emoji image files, [default = svg ].
#
# :img_attrs - the custom css properties for emoji
tag.
#
# examples of _config.yml:
# 1. user provided all URLs:
# emoji:
# src: https://example.com/asset
# asset: /images/png
# emoji files will serve from https://example.com/asset/images/png
#
# 2. user provided just src:
# emoji:
# src: https://example.com/asset
# emoji files will serve from https://example.com/emoji
#
# 3. user provided nothing:
# emoji files will serve from https://cdn.jsdelivr.net/gh/azadeh-afzar/OpenMoji-Jekyll-Plugin@latest/images/color/svg
#
# Returns an HTML::Pipeline instance for the given asset root.
def filter_with_emoji(src_root, asset_path, file_extension, img_attrs)
filters[src_root] ||= HTML::Pipeline.new([HTML::Pipeline::
NegarMojiHtmlPipeline::NegarehEmojiFilter],
:asset_root => src_root,
:asset_path => asset_path,
:extension => file_extension,
:img_attrs => img_attrs)
end
# Public: Filters hash where the key is the asset root source.
# Effectively a cache.
def filters
@filters ||= {}
end
# Public: Calculate the asset root source for the given config.
# The custom emoji asset root can be defined in the config as
# emoji.src, and must be a valid URL (i.e. it must include a
# protocol and valid domain).
#
# config - the hash-like configuration of the document's site.
#
# Returns a full URL to use as the asset root URL. Defaults to the root
# URL for assets provided by an ASSET_HOST_URL environment variable,
# otherwise the root URL for emoji assets at https://cdn.jsdelivr.net/gh/azadeh-afzar/OpenMoji-Jekyll-Plugin@latest.
def emoji_src_root(config = {})
if config.key?("emoji") && config["emoji"].key?("src")
config["emoji"]["src"]
else
default_asset_root
end
end
# Public: Calculate the asset root source for the given config.
# The custom emoji asset root can be defined in the config as
# emoji.asset.
#
# If emoji.asset isn't defined, its value will explicitly set to "emoji".
#
# config - the hash-like configuration of the document's site.
#
# Returns a string to use as the asset path. Defaults to the ASSET_PATH.
def emoji_asset_path(config = {})
if config.key?("emoji") && config["emoji"].key?("src")
if config["emoji"].key?("asset")
config["emoji"]["asset"].chomp("/") + FILE_NAME.to_s
else
"#{DEFAULT_DIR}#{FILE_NAME}"
end
else
"#{DEFAULT_ASSET_PATH}#{FILE_NAME}"
end
end
# Public: return extension for emoji files.
# The custom emoji extension can be defined in the config as
# emoji.extension.
#
# If emoji.extension isn't defined, its value will explicitly set to "svg".
#
# config - the hash-like configuration of the document's site.
#
# Returns a string to use as the extension. Defaults to the DEFAULT_EXTENSION.
def emoji_extension(config = {})
if config.key?("emoji") && config["emoji"].key?("extension")
config["emoji"]["extension"]
else
DEFAULT_EXTENSION.to_s
end
end
# Public: return emoji
tag attributes.
# The custom emoji css attributes can be defined with emoji.img_attrs.
#
# for example:
#
# emoji:
# img_attrs:
# class: "openmoji"
# height: 30
#
# this will override default emoji css attributes.
#
# If emoji.img_attrs isn't defined, its value will explicitly set to
# DEFAULT_IMG_ATTRS.
#
# config - the hash-like configuration of the document's site
#
# Returns a hash to use as the attributes. Defaults to the DEFAULT_IMG_ATTRS.
def emoji_attributes(config = {})
if config.key?("emoji") && config["emoji"].key?("img_attrs")
# merge default values with custom values and then
# convert hash keys to symbols.
DEFAULT_IMG_ATTRS.merge!(config["emoji"]["img_attrs"])
.map { |key, value| [key.to_sym, value] }.to_h
else
# convert hash keys to symbols.
DEFAULT_IMG_ATTRS.map { |key, value| [key.to_sym, value] }.to_h
end
end
# Public: Defines the conditions for a document to be emojiable.
#
# doc - the Jekyll::Document or Jekyll::Page
#
# Returns true if the doc is written & is HTML.
def emojiable?(doc)
(doc.is_a?(Jekyll::Page) || doc.write?) &&
doc.output_ext == ".html" || (doc.permalink&.end_with?("/"))
end
private
def default_asset_root
if !ENV["ASSET_HOST_URL"].to_s.empty?
# Ensure that any trailing "/" is trimmed
asset_host_url = ENV["ASSET_HOST_URL"].chomp("/")
asset_host_url.to_s
else
DEFAULT_HOST_URL.to_s
end
end
def replace_document_body(doc)
src_root = emoji_src_root(doc.site.config)
asset_path = emoji_asset_path(doc.site.config)
file_extension = emoji_extension(doc.site.config)
img_attrs = emoji_attributes(doc.site.config)
head, opener, tail = doc.output.partition(OPENING_BODY_TAG_REGEX)
body_content, *rest = tail.partition("")
processed_markup = filter_with_emoji(src_root, asset_path, file_extension,
img_attrs).call(body_content)[:output]
.to_s
String.new(head) << opener << processed_markup << rest.join
end
end
end
end
Jekyll::Hooks.register [:pages, :documents], :post_render do |doc|
Jekyll::Emoji.emojify(doc) if Jekyll::Emoji.emojiable?(doc)
end