module Jekyll::Filters

def array_to_sentence_string(array, connector = "and")

Returns the formatted String.

# => "apples, oranges, and grapes"
array_to_sentence_string(["apples", "oranges", "grapes"])

Examples

connector - Word used to connect the last 2 items in the array
array - The Array of Strings to join.

word "and" for the last one.
Join an array of things into a string by separating with commas and the
def array_to_sentence_string(array, connector = "and")
  case array.length
  when 0
    ""
  when 1
    array[0].to_s
  when 2
    "#{array[0]} #{connector} #{array[1]}"
  else
    "#{array[0...-1].join(", ")}, #{connector} #{array[-1]}"
  end
end

def as_liquid(item)

def as_liquid(item)
  case item
  when Hash
    item.each_with_object({}) { |(k, v), result| result[as_liquid(k)] = as_liquid(v) }
  when Array
    item.map { |i| as_liquid(i) }
  else
    if item.respond_to?(:to_liquid)
      liquidated = item.to_liquid
      # prevent infinite recursion for simple types (which return `self`)
      if liquidated == item
        item
      else
        as_liquid(liquidated)
      end
    else
      item
    end
  end
end

def cgi_escape(input)

Returns the escaped String.

# => "foo%2Cbar%3Bbaz%3F"
cgi_escape('foo,bar;baz?')

Examples

input - The String to escape.

with appropriate %XX replacements.
CGI escape a string for use in a URL. Replaces any special characters
def cgi_escape(input)
  CGI.escape(input)
end

def compare_property_vs_target(property, target)


`where` filter helper
def compare_property_vs_target(property, target)
  case target
  when NilClass
    return true if property.nil?
  when Liquid::Expression::MethodLiteral # `empty` or `blank`
    target = target.to_s
    return true if property == target || Array(property).join == target
  else
    target = target.to_s
    if property.is_a? String
      return true if property == target
    else
      Array(property).each do |prop|
        return true if prop.to_s == target
      end
    end
  end
  false
end

def find(input, property, value)

rubocop:disable Metrics/CyclomaticComplexity

Returns the found object or nil

their `#inspect` string object.
Cannot be an instance of Array nor Hash since calling #to_s on them returns
value - the desired value.
property - the property within each object to search by.
input - the object array.

with the given value or returns nil otherwise.
Search an array of objects and returns the first object that has the queried attribute
def find(input, property, value)
  return input if !property || value.is_a?(Array) || value.is_a?(Hash)
  return input unless input.respond_to?(:find)
  input    = input.values if input.is_a?(Hash)
  input_id = input.hash
  # implement a hash based on method parameters to cache the end-result for given parameters.
  @find_filter_cache ||= {}
  @find_filter_cache[input_id] ||= {}
  @find_filter_cache[input_id][property] ||= {}
  # stash or retrieve results to return
  # Since `enum.find` can return nil or false, we use a placeholder string "<__NO MATCH__>"
  #   to validate caching.
  result = @find_filter_cache[input_id][property][value] ||= input.find do |object|
    compare_property_vs_target(item_property(object, property), value)
  end || "<__NO MATCH__>"
  return nil if result == "<__NO MATCH__>"
  result
end

def find_exp(input, variable, expression)

Returns the found object or nil

expression - a Liquid comparison expression passed in as a string
variable - the variable to assign each item to in the expression
input - the object array

the expression evaluates to true, or returns nil otherwise.
Searches an array of objects against an expression and returns the first object for which
def find_exp(input, variable, expression)
  return input unless input.respond_to?(:find)
  input = input.values if input.is_a?(Hash)
  condition = parse_condition(expression)
  @context.stack do
    input.find do |object|
      @context[variable] = object
      condition.evaluate(@context)
    end
  end
end

def inspect(input)

Returns a String representation of the object.

input - The Object to be converted

Convert an object into its String representation for debugging
def inspect(input)
  xml_escape(input.inspect)
end

def item_property(item, property)

def item_property(item, property)
  @item_property_cache ||= @context.registers[:site].filter_cache[:item_property] ||= {}
  @item_property_cache[property] ||= {}
  @item_property_cache[property][item] ||= begin
    property = property.to_s
    property = if item.respond_to?(:to_liquid)
                 read_liquid_attribute(item.to_liquid, property)
               elsif item.respond_to?(:data)
                 item.data[property]
               else
                 item[property]
               end
    parse_sort_input(property)
  end
end

def jsonify(input)

Returns the converted json string

input - The Array or Hash to be converted

Convert the input into json string
def jsonify(input)
  as_liquid(input).to_json
end

def markdownify(input)

Returns the HTML formatted String.

input - The Markdown String to convert.

Convert a Markdown string into HTML output.
def markdownify(input)
  @context.registers[:site].find_converter_instance(
    Jekyll::Converters::Markdown
  ).convert(input.to_s)
end

def normalize_whitespace(input)

Returns the formatted String

input - The String on which to operate.

Replace any whitespace in the input string with a single space
def normalize_whitespace(input)
  input.to_s.gsub(%r!\s+!, " ").tap(&:strip!)
end

def number_of_words(input, mode = nil)

Returns the Integer word count.

input - The String on which to operate.

Count the number of words in the input string.
def number_of_words(input, mode = nil)
  cjk_charset = '\p{Han}\p{Katakana}\p{Hiragana}\p{Hangul}'
  cjk_regex = %r![#{cjk_charset}]!o
  word_regex = %r![^#{cjk_charset}\s]+!o
  case mode
  when "cjk"
    input.scan(cjk_regex).length + input.scan(word_regex).length
  when "auto"
    cjk_count = input.scan(cjk_regex).length
    cjk_count.zero? ? input.split.length : cjk_count + input.scan(word_regex).length
  else
    input.split.length
  end
end

def parse_binary_comparison(parser)

Returns an instance of Liquid::Condition

- parser: an instance of Liquid::Parser

Liquid operators `and` or `or`
the parsed expression based on whether the expression consists of binary operations with
Generate a Liquid::Condition object from a Liquid::Parser object additionally processing
def parse_binary_comparison(parser)
  condition = parse_comparison(parser)
  first_condition = condition
  while (binary_operator = parser.id?("and") || parser.id?("or"))
    child_condition = parse_comparison(parser)
    condition.send(binary_operator, child_condition)
    condition = child_condition
  end
  first_condition
end

def parse_comparison(parser)

Returns an instance of Liquid::Condition

- parser: an instance of Liquid::Parser

expression involves a "comparison" operator (e.g. <, ==, >, !=, etc)
Generates a Liquid::Condition object from a Liquid::Parser object based on whether the parsed
def parse_comparison(parser)
  left_operand = Liquid::Expression.parse(parser.expression)
  operator     = parser.consume?(:comparison)
  # No comparison-operator detected. Initialize a Liquid::Condition using only left operand
  return Liquid::Condition.new(left_operand) unless operator
  # Parse what remained after extracting the left operand and the `:comparison` operator
  # and initialize a Liquid::Condition object using the operands and the comparison-operator
  Liquid::Condition.new(left_operand, operator, Liquid::Expression.parse(parser.expression))
end

def parse_condition(exp)

Parse a string to a Liquid Condition
def parse_condition(exp)
  parser    = Liquid::Parser.new(exp)
  condition = parse_binary_comparison(parser)
  parser.consume(:end_of_string)
  condition
end

def parse_sort_input(property)

return numeric values as numbers for proper sorting
def parse_sort_input(property)
  stringified = property.to_s
  return property.to_i if INTEGER_LIKE.match?(stringified)
  return property.to_f if FLOAT_LIKE.match?(stringified)
  property
end

def pop(array, num = 1)

def pop(array, num = 1)
  return array unless array.is_a?(Array)
  num = Liquid::Utils.to_integer(num)
  new_ary = array.dup
  new_ary.pop(num)
  new_ary
end

def push(array, input)

def push(array, input)
  return array unless array.is_a?(Array)
  new_ary = array.dup
  new_ary.push(input)
  new_ary
end

def read_liquid_attribute(liquid_data, property)

def read_liquid_attribute(liquid_data, property)
  return liquid_data[property] unless property.include?(".")
  property.split(".").reduce(liquid_data) do |data, key|
    data.respond_to?(:[]) && data[key]
  end
end

def sample(input, num = 1)

def sample(input, num = 1)
  return input unless input.respond_to?(:sample)
  num = Liquid::Utils.to_integer(num) rescue 1
  if num == 1
    input.sample
  else
    input.sample(num)
  end
end

def sassify(input)

Returns the CSS formatted String.

input - The Sass String to convert.

Convert a Sass string into CSS output.
def sassify(input)
  @context.registers[:site].find_converter_instance(
    Jekyll::Converters::Sass
  ).convert(input)
end

def scssify(input)

Returns the CSS formatted String.

input - The Scss String to convert.

Convert a Scss string into CSS output.
def scssify(input)
  @context.registers[:site].find_converter_instance(
    Jekyll::Converters::Scss
  ).convert(input)
end

def shift(array, num = 1)

def shift(array, num = 1)
  return array unless array.is_a?(Array)
  num = Liquid::Utils.to_integer(num)
  new_ary = array.dup
  new_ary.shift(num)
  new_ary
end

def slugify(input, mode = nil)

See Utils.slugify for more detail.
Returns the given filename or title as a lowercase URL String.

mode - how string is slugified
input - The filename or title to slugify.

Slugify a filename or title.
def slugify(input, mode = nil)
  Utils.slugify(input, :mode => mode)
end

def smartify(input)

Returns the smart-quotified String.

input - The String to convert.

Convert quotes into smart quotes.
def smartify(input)
  @context.registers[:site].find_converter_instance(
    Jekyll::Converters::SmartyPants
  ).convert(input.to_s)
end

def sort(input, property = nil, nils = "first")

Returns the filtered array of objects

nils ('first' | 'last') - nils appear before or after non-nil values
property - property within each object to filter by
input - the object array

Sort an array of objects
def sort(input, property = nil, nils = "first")
  raise ArgumentError, "Cannot sort a null object." if input.nil?
  if property.nil?
    input.sort
  else
    case nils
    when "first"
      order = - 1
    when "last"
      order = + 1
    else
      raise ArgumentError, "Invalid nils order: " \
                           "'#{nils}' is not a valid nils order. It must be 'first' or 'last'."
    end
    sort_input(input, property, order)
  end
end

def sort_input(input, property, order)

We also utilize the Schwartzian transform to make this more efficient.
which item doesn't have the property.
If the property doesn't exist, return the sort order respective of
Sort the input Enumerable by the given property.
def sort_input(input, property, order)
  input.map { |item| [item_property(item, property), item] }
    .sort! do |a_info, b_info|
      a_property = a_info.first
      b_property = b_info.first
      if !a_property.nil? && b_property.nil?
        - order
      elsif a_property.nil? && !b_property.nil?
        + order
      else
        a_property <=> b_property || a_property.to_s <=> b_property.to_s
      end
    end
    .map!(&:last)
end

def to_integer(input)

Returns the integer value

input - the object string

Convert the input into integer
def to_integer(input)
  return 1 if input == true
  return 0 if input == false
  input.to_i
end

def unshift(array, input)

def unshift(array, input)
  return array unless array.is_a?(Array)
  new_ary = array.dup
  new_ary.unshift(input)
  new_ary
end

def uri_escape(input)

Returns the escaped String.

# => "foo,%20bar%20%5Cbaz?"
uri_escape('foo, bar \\baz?')

Examples

input - The String to escape.

URI escape a string.
def uri_escape(input)
  Addressable::URI.normalize_component(input)
end

def where(input, property, value)

Returns the filtered array of objects

their `#inspect` string object.
Cannot be an instance of Array nor Hash since calling #to_s on them returns
value - the desired value.
property - the property within each object to filter by.
input - the object array.

Filter an array of objects
def where(input, property, value)
  return input if !property || value.is_a?(Array) || value.is_a?(Hash)
  return input unless input.respond_to?(:select)
  input    = input.values if input.is_a?(Hash)
  input_id = input.hash
  # implement a hash based on method parameters to cache the end-result
  # for given parameters.
  @where_filter_cache ||= {}
  @where_filter_cache[input_id] ||= {}
  @where_filter_cache[input_id][property] ||= {}
  # stash or retrieve results to return
  @where_filter_cache[input_id][property][value] ||= input.select do |object|
    compare_property_vs_target(item_property(object, property), value)
  end.to_a
end

def where_exp(input, variable, expression)

Returns the filtered array of objects

expression - a Liquid comparison expression passed in as a string
variable - the variable to assign each item to in the expression
input - the object array

Filters an array of objects against an expression
def where_exp(input, variable, expression)
  return input unless input.respond_to?(:select)
  input = input.values if input.is_a?(Hash) # FIXME
  condition = parse_condition(expression)
  @context.stack do
    input.select do |object|
      @context[variable] = object
      condition.evaluate(@context)
    end
  end || []
end

def xml_escape(input)

Returns the escaped String.

# => "foo "bar" <baz>"
xml_escape('foo "bar" ')

Examples

input - The String to escape.

appropriate HTML entity replacements.
XML escape a string for use. Replaces any special characters with
def xml_escape(input)
  input.to_s.encode(:xml => :attr).gsub(%r!\A"|"\Z!, "")
end