| require "addressable/uri" |
| require "json" |
| require "date" |
| require "liquid" |
| |
| require_all "jekyll/filters" |
| |
| module Jekyll |
| module Filters |
| include URLFilters |
| include GroupingFilters |
| |
| # Convert a Markdown string into HTML output. |
| # |
| # input - The Markdown String to convert. |
| # |
| # Returns the HTML formatted String. |
| def markdownify(input) |
| site = @context.registers[:site] |
| converter = site.find_converter_instance(Jekyll::Converters::Markdown) |
| converter.convert(input.to_s) |
| end |
| |
| # Convert quotes into smart quotes. |
| # |
| # input - The String to convert. |
| # |
| # Returns the smart-quotified String. |
| def smartify(input) |
| site = @context.registers[:site] |
| converter = site.find_converter_instance(Jekyll::Converters::SmartyPants) |
| converter.convert(input.to_s) |
| end |
| |
| # Convert a Sass string into CSS output. |
| # |
| # input - The Sass String to convert. |
| # |
| # Returns the CSS formatted String. |
| def sassify(input) |
| site = @context.registers[:site] |
| converter = site.find_converter_instance(Jekyll::Converters::Sass) |
| converter.convert(input) |
| end |
| |
| # Convert a Scss string into CSS output. |
| # |
| # input - The Scss String to convert. |
| # |
| # Returns the CSS formatted String. |
| def scssify(input) |
| site = @context.registers[:site] |
| converter = site.find_converter_instance(Jekyll::Converters::Scss) |
| converter.convert(input) |
| end |
| |
| # Slugify a filename or title. |
| # |
| # input - The filename or title to slugify. |
| # mode - how string is slugified |
| # |
| # Returns the given filename or title as a lowercase URL String. |
| # See Utils.slugify for more detail. |
| def slugify(input, mode = nil) |
| Utils.slugify(input, :mode => mode) |
| end |
| |
| # Format a date in short format e.g. "27 Jan 2011". |
| # |
| # date - the Time to format. |
| # |
| # Returns the formatting String. |
| def date_to_string(date) |
| time(date).strftime("%d %b %Y") |
| end |
| |
| # Format a date in long format e.g. "27 January 2011". |
| # |
| # date - The Time to format. |
| # |
| # Returns the formatted String. |
| def date_to_long_string(date) |
| return date if date.to_s.empty? |
| time(date).strftime("%d %B %Y") |
| end |
| |
| # Format a date for use in XML. |
| # |
| # date - The Time to format. |
| # |
| # Examples |
| # |
| # date_to_xmlschema(Time.now) |
| # # => "2011-04-24T20:34:46+08:00" |
| # |
| # Returns the formatted String. |
| def date_to_xmlschema(date) |
| return date if date.to_s.empty? |
| time(date).xmlschema |
| end |
| |
| # Format a date according to RFC-822 |
| # |
| # date - The Time to format. |
| # |
| # Examples |
| # |
| # date_to_rfc822(Time.now) |
| # # => "Sun, 24 Apr 2011 12:34:46 +0000" |
| # |
| # Returns the formatted String. |
| def date_to_rfc822(date) |
| return date if date.to_s.empty? |
| time(date).rfc822 |
| end |
| |
| # XML escape a string for use. Replaces any special characters with |
| # appropriate HTML entity replacements. |
| # |
| # input - The String to escape. |
| # |
| # Examples |
| # |
| # xml_escape('foo "bar" <baz>') |
| # # => "foo "bar" <baz>" |
| # |
| # Returns the escaped String. |
| def xml_escape(input) |
| input.to_s.encode(:xml => :attr).gsub(%r!\A"|"\Z!, "") |
| end |
| |
| # CGI escape a string for use in a URL. Replaces any special characters |
| # with appropriate %XX replacements. |
| # |
| # input - The String to escape. |
| # |
| # Examples |
| # |
| # cgi_escape('foo,bar;baz?') |
| # # => "foo%2Cbar%3Bbaz%3F" |
| # |
| # Returns the escaped String. |
| def cgi_escape(input) |
| CGI.escape(input) |
| end |
| |
| # URI escape a string. |
| # |
| # input - The String to escape. |
| # |
| # Examples |
| # |
| # uri_escape('foo, bar \\baz?') |
| # # => "foo,%20bar%20%5Cbaz?" |
| # |
| # Returns the escaped String. |
| def uri_escape(input) |
| Addressable::URI.normalize_component(input) |
| end |
| |
| # Replace any whitespace in the input string with a single space |
| # |
| # input - The String on which to operate. |
| # |
| # Returns the formatted String |
| def normalize_whitespace(input) |
| input.to_s.gsub(%r!\s+!, " ").strip |
| end |
| |
| # Count the number of words in the input string. |
| # |
| # input - The String on which to operate. |
| # |
| # Returns the Integer word count. |
| def number_of_words(input) |
| input.split.length |
| end |
| |
| # Join an array of things into a string by separating with commas and the |
| # word "and" for the last one. |
| # |
| # array - The Array of Strings to join. |
| # connector - Word used to connect the last 2 items in the array |
| # |
| # Examples |
| # |
| # array_to_sentence_string(["apples", "oranges", "grapes"]) |
| # # => "apples, oranges, and grapes" |
| # |
| # Returns the formatted String. |
| 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 |
| |
| # Convert the input into json string |
| # |
| # input - The Array or Hash to be converted |
| # |
| # Returns the converted json string |
| def jsonify(input) |
| as_liquid(input).to_json |
| end |
| |
| # Filter an array of objects |
| # |
| # input - the object array |
| # property - property within each object to filter by |
| # value - desired value |
| # |
| # Returns the filtered array of objects |
| def where(input, property, value) |
| return input unless input.respond_to?(:select) |
| input = input.values if input.is_a?(Hash) |
| input.select do |object| |
| Array(item_property(object, property)).map(&:to_s).include?(value.to_s) |
| end || [] |
| end |
| |
| # Filters an array of objects against an expression |
| # |
| # input - the object array |
| # variable - the variable to assign each item to in the expression |
| # expression - a Liquid comparison expression passed in as a string |
| # |
| # Returns the filtered array of objects |
| 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 |
| |
| # Convert the input into integer |
| # |
| # input - the object string |
| # |
| # Returns the integer value |
| def to_integer(input) |
| return 1 if input == true |
| return 0 if input == false |
| input.to_i |
| end |
| |
| # Sort an array of objects |
| # |
| # input - the object array |
| # property - property within each object to filter by |
| # nils ('first' | 'last') - nils appear before or after non-nil values |
| # |
| # Returns the filtered array of objects |
| def sort(input, property = nil, nils = "first") |
| if input.nil? |
| raise ArgumentError, "Cannot sort a null object." |
| end |
| if property.nil? |
| input.sort |
| else |
| if nils == "first" |
| order = - 1 |
| elsif nils == "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 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) |
| return array unless array.is_a?(Array) |
| new_ary = array.dup |
| new_ary.push(input) |
| new_ary |
| end |
| |
| 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 unshift(array, input) |
| return array unless array.is_a?(Array) |
| new_ary = array.dup |
| new_ary.unshift(input) |
| new_ary |
| end |
| |
| 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 |
| |
| # Convert an object into its String representation for debugging |
| # |
| # input - The Object to be converted |
| # |
| # Returns a String representation of the object. |
| def inspect(input) |
| xml_escape(input.inspect) |
| end |
| |
| private |
| def sort_input(input, property, order) |
| input.sort do |apple, orange| |
| apple_property = item_property(apple, property) |
| orange_property = item_property(orange, property) |
| |
| if !apple_property.nil? && orange_property.nil? |
| - order |
| elsif apple_property.nil? && !orange_property.nil? |
| + order |
| else |
| apple_property <=> orange_property |
| end |
| end |
| end |
| |
| private |
| def time(input) |
| date = Liquid::Utils.to_date(input) |
| unless date.respond_to?(:to_time) |
| raise Errors::InvalidDateError, |
| "Invalid Date: '#{input.inspect}' is not a valid datetime." |
| end |
| date.to_time.dup.localtime |
| end |
| |
| private |
| def item_property(item, property) |
| if item.respond_to?(:to_liquid) |
| property.to_s.split(".").reduce(item.to_liquid) do |subvalue, attribute| |
| subvalue[attribute] |
| end |
| elsif item.respond_to?(:data) |
| item.data[property.to_s] |
| else |
| item[property.to_s] |
| end |
| end |
| |
| private |
| def as_liquid(item) |
| case item |
| when Hash |
| pairs = item.map { |k, v| as_liquid([k, v]) } |
| Hash[pairs] |
| 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 |
| |
| # Parse a string to a Liquid Condition |
| private |
| def parse_condition(exp) |
| parser = Liquid::Parser.new(exp) |
| left_expr = parser.expression |
| operator = parser.consume?(:comparison) |
| condition = |
| if operator |
| Liquid::Condition.new(Liquid::Expression.parse(left_expr), |
| operator, |
| Liquid::Expression.parse(parser.expression)) |
| else |
| Liquid::Condition.new(Liquid::Expression.parse(left_expr)) |
| end |
| parser.consume(:end_of_string) |
| |
| condition |
| end |
| |
| end |
| end |
| |
| Liquid::Template.register_filter( |
| Jekyll::Filters |
| ) |