jekyll 4.2.0 → 4.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +350 -347
  3. data/LICENSE +21 -21
  4. data/README.markdown +86 -86
  5. data/exe/jekyll +57 -57
  6. data/lib/blank_template/_config.yml +3 -3
  7. data/lib/blank_template/_layouts/default.html +12 -12
  8. data/lib/blank_template/_sass/main.scss +9 -9
  9. data/lib/blank_template/assets/css/main.scss +4 -4
  10. data/lib/blank_template/index.md +8 -8
  11. data/lib/jekyll/cache.rb +190 -190
  12. data/lib/jekyll/cleaner.rb +111 -111
  13. data/lib/jekyll/collection.rb +309 -309
  14. data/lib/jekyll/command.rb +105 -105
  15. data/lib/jekyll/commands/build.rb +93 -93
  16. data/lib/jekyll/commands/clean.rb +45 -45
  17. data/lib/jekyll/commands/doctor.rb +177 -177
  18. data/lib/jekyll/commands/help.rb +34 -34
  19. data/lib/jekyll/commands/new.rb +169 -169
  20. data/lib/jekyll/commands/new_theme.rb +40 -40
  21. data/lib/jekyll/commands/serve/live_reload_reactor.rb +122 -122
  22. data/lib/jekyll/commands/serve/livereload_assets/livereload.js +1183 -1183
  23. data/lib/jekyll/commands/serve/servlet.rb +202 -202
  24. data/lib/jekyll/commands/serve/websockets.rb +81 -81
  25. data/lib/jekyll/commands/serve.rb +362 -365
  26. data/lib/jekyll/configuration.rb +313 -313
  27. data/lib/jekyll/converter.rb +54 -54
  28. data/lib/jekyll/converters/identity.rb +41 -41
  29. data/lib/jekyll/converters/markdown/kramdown_parser.rb +199 -199
  30. data/lib/jekyll/converters/markdown.rb +113 -113
  31. data/lib/jekyll/converters/smartypants.rb +70 -70
  32. data/lib/jekyll/convertible.rb +257 -260
  33. data/lib/jekyll/deprecator.rb +50 -50
  34. data/lib/jekyll/document.rb +544 -544
  35. data/lib/jekyll/drops/collection_drop.rb +20 -20
  36. data/lib/jekyll/drops/document_drop.rb +70 -70
  37. data/lib/jekyll/drops/drop.rb +293 -293
  38. data/lib/jekyll/drops/excerpt_drop.rb +19 -19
  39. data/lib/jekyll/drops/jekyll_drop.rb +32 -32
  40. data/lib/jekyll/drops/site_drop.rb +66 -66
  41. data/lib/jekyll/drops/static_file_drop.rb +14 -14
  42. data/lib/jekyll/drops/unified_payload_drop.rb +26 -26
  43. data/lib/jekyll/drops/url_drop.rb +140 -140
  44. data/lib/jekyll/entry_filter.rb +121 -121
  45. data/lib/jekyll/errors.rb +20 -20
  46. data/lib/jekyll/excerpt.rb +201 -201
  47. data/lib/jekyll/external.rb +79 -79
  48. data/lib/jekyll/filters/date_filters.rb +110 -110
  49. data/lib/jekyll/filters/grouping_filters.rb +64 -64
  50. data/lib/jekyll/filters/url_filters.rb +98 -98
  51. data/lib/jekyll/filters.rb +535 -535
  52. data/lib/jekyll/frontmatter_defaults.rb +240 -240
  53. data/lib/jekyll/generator.rb +5 -5
  54. data/lib/jekyll/hooks.rb +107 -107
  55. data/lib/jekyll/inclusion.rb +32 -32
  56. data/lib/jekyll/layout.rb +67 -67
  57. data/lib/jekyll/liquid_extensions.rb +22 -22
  58. data/lib/jekyll/liquid_renderer/file.rb +77 -77
  59. data/lib/jekyll/liquid_renderer/table.rb +55 -55
  60. data/lib/jekyll/liquid_renderer.rb +80 -80
  61. data/lib/jekyll/log_adapter.rb +151 -151
  62. data/lib/jekyll/mime.types +866 -866
  63. data/lib/jekyll/page.rb +217 -217
  64. data/lib/jekyll/page_excerpt.rb +25 -25
  65. data/lib/jekyll/page_without_a_file.rb +14 -14
  66. data/lib/jekyll/path_manager.rb +74 -74
  67. data/lib/jekyll/plugin.rb +92 -92
  68. data/lib/jekyll/plugin_manager.rb +115 -115
  69. data/lib/jekyll/profiler.rb +58 -58
  70. data/lib/jekyll/publisher.rb +23 -23
  71. data/lib/jekyll/reader.rb +192 -192
  72. data/lib/jekyll/readers/collection_reader.rb +23 -23
  73. data/lib/jekyll/readers/data_reader.rb +79 -79
  74. data/lib/jekyll/readers/layout_reader.rb +62 -62
  75. data/lib/jekyll/readers/page_reader.rb +25 -25
  76. data/lib/jekyll/readers/post_reader.rb +85 -85
  77. data/lib/jekyll/readers/static_file_reader.rb +25 -25
  78. data/lib/jekyll/readers/theme_assets_reader.rb +52 -52
  79. data/lib/jekyll/regenerator.rb +195 -195
  80. data/lib/jekyll/related_posts.rb +52 -52
  81. data/lib/jekyll/renderer.rb +265 -265
  82. data/lib/jekyll/site.rb +551 -551
  83. data/lib/jekyll/static_file.rb +208 -208
  84. data/lib/jekyll/stevenson.rb +60 -60
  85. data/lib/jekyll/tags/highlight.rb +110 -110
  86. data/lib/jekyll/tags/include.rb +275 -270
  87. data/lib/jekyll/tags/link.rb +42 -42
  88. data/lib/jekyll/tags/post_url.rb +106 -106
  89. data/lib/jekyll/theme.rb +86 -86
  90. data/lib/jekyll/theme_builder.rb +121 -121
  91. data/lib/jekyll/url.rb +167 -167
  92. data/lib/jekyll/utils/ansi.rb +57 -57
  93. data/lib/jekyll/utils/exec.rb +26 -26
  94. data/lib/jekyll/utils/internet.rb +37 -37
  95. data/lib/jekyll/utils/platforms.rb +67 -67
  96. data/lib/jekyll/utils/thread_event.rb +31 -31
  97. data/lib/jekyll/utils/win_tz.rb +75 -75
  98. data/lib/jekyll/utils.rb +367 -367
  99. data/lib/jekyll/version.rb +5 -5
  100. data/lib/jekyll.rb +195 -195
  101. data/lib/site_template/.gitignore +5 -5
  102. data/lib/site_template/404.html +25 -25
  103. data/lib/site_template/_config.yml +55 -55
  104. data/lib/site_template/_posts/0000-00-00-welcome-to-jekyll.markdown.erb +29 -29
  105. data/lib/site_template/about.markdown +18 -18
  106. data/lib/site_template/index.markdown +6 -6
  107. data/lib/theme_template/CODE_OF_CONDUCT.md.erb +74 -74
  108. data/lib/theme_template/Gemfile +4 -4
  109. data/lib/theme_template/LICENSE.txt.erb +21 -21
  110. data/lib/theme_template/README.md.erb +52 -52
  111. data/lib/theme_template/_layouts/default.html +1 -1
  112. data/lib/theme_template/_layouts/page.html +5 -5
  113. data/lib/theme_template/_layouts/post.html +5 -5
  114. data/lib/theme_template/example/_config.yml.erb +1 -1
  115. data/lib/theme_template/example/_post.md +12 -12
  116. data/lib/theme_template/example/index.html +14 -14
  117. data/lib/theme_template/example/style.scss +7 -7
  118. data/lib/theme_template/gitignore.erb +6 -6
  119. data/lib/theme_template/theme.gemspec.erb +16 -16
  120. data/rubocop/jekyll/assert_equal_literal_actual.rb +149 -149
  121. data/rubocop/jekyll/no_p_allowed.rb +23 -23
  122. data/rubocop/jekyll/no_puts_allowed.rb +23 -23
  123. data/rubocop/jekyll.rb +5 -5
  124. metadata +3 -3
@@ -1,110 +1,110 @@
1
- # frozen_string_literal: true
2
-
3
- module Jekyll
4
- module Filters
5
- module DateFilters
6
- # Format a date in short format e.g. "27 Jan 2011".
7
- # Ordinal format is also supported, in both the UK
8
- # (e.g. "27th Jan 2011") and US ("e.g. Jan 27th, 2011") formats.
9
- # UK format is the default.
10
- #
11
- # date - the Time to format.
12
- # type - if "ordinal" the returned String will be in ordinal format
13
- # style - if "US" the returned String will be in US format.
14
- # Otherwise it will be in UK format.
15
- #
16
- # Returns the formatting String.
17
- def date_to_string(date, type = nil, style = nil)
18
- stringify_date(date, "%b", type, style)
19
- end
20
-
21
- # Format a date in long format e.g. "27 January 2011".
22
- # Ordinal format is also supported, in both the UK
23
- # (e.g. "27th January 2011") and US ("e.g. January 27th, 2011") formats.
24
- # UK format is the default.
25
- #
26
- # date - the Time to format.
27
- # type - if "ordinal" the returned String will be in ordinal format
28
- # style - if "US" the returned String will be in US format.
29
- # Otherwise it will be in UK format.
30
- #
31
- # Returns the formatted String.
32
- def date_to_long_string(date, type = nil, style = nil)
33
- stringify_date(date, "%B", type, style)
34
- end
35
-
36
- # Format a date for use in XML.
37
- #
38
- # date - The Time to format.
39
- #
40
- # Examples
41
- #
42
- # date_to_xmlschema(Time.now)
43
- # # => "2011-04-24T20:34:46+08:00"
44
- #
45
- # Returns the formatted String.
46
- def date_to_xmlschema(date)
47
- return date if date.to_s.empty?
48
-
49
- time(date).xmlschema
50
- end
51
-
52
- # Format a date according to RFC-822
53
- #
54
- # date - The Time to format.
55
- #
56
- # Examples
57
- #
58
- # date_to_rfc822(Time.now)
59
- # # => "Sun, 24 Apr 2011 12:34:46 +0000"
60
- #
61
- # Returns the formatted String.
62
- def date_to_rfc822(date)
63
- return date if date.to_s.empty?
64
-
65
- time(date).rfc822
66
- end
67
-
68
- private
69
-
70
- # month_type: Notations that evaluate to 'Month' via `Time#strftime` ("%b", "%B")
71
- # type: nil (default) or "ordinal"
72
- # style: nil (default) or "US"
73
- #
74
- # Returns a stringified date or the empty input.
75
- def stringify_date(date, month_type, type = nil, style = nil)
76
- return date if date.to_s.empty?
77
-
78
- time = time(date)
79
- if type == "ordinal"
80
- day = time.day
81
- ordinal_day = "#{day}#{ordinal(day)}"
82
- return time.strftime("#{month_type} #{ordinal_day}, %Y") if style == "US"
83
-
84
- return time.strftime("#{ordinal_day} #{month_type} %Y")
85
- end
86
- time.strftime("%d #{month_type} %Y")
87
- end
88
-
89
- def ordinal(number)
90
- return "th" if (11..13).cover?(number)
91
-
92
- case number % 10
93
- when 1 then "st"
94
- when 2 then "nd"
95
- when 3 then "rd"
96
- else "th"
97
- end
98
- end
99
-
100
- def time(input)
101
- date = Liquid::Utils.to_date(input)
102
- unless date.respond_to?(:to_time)
103
- raise Errors::InvalidDateError,
104
- "Invalid Date: '#{input.inspect}' is not a valid datetime."
105
- end
106
- date.to_time.dup.localtime
107
- end
108
- end
109
- end
110
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Filters
5
+ module DateFilters
6
+ # Format a date in short format e.g. "27 Jan 2011".
7
+ # Ordinal format is also supported, in both the UK
8
+ # (e.g. "27th Jan 2011") and US ("e.g. Jan 27th, 2011") formats.
9
+ # UK format is the default.
10
+ #
11
+ # date - the Time to format.
12
+ # type - if "ordinal" the returned String will be in ordinal format
13
+ # style - if "US" the returned String will be in US format.
14
+ # Otherwise it will be in UK format.
15
+ #
16
+ # Returns the formatting String.
17
+ def date_to_string(date, type = nil, style = nil)
18
+ stringify_date(date, "%b", type, style)
19
+ end
20
+
21
+ # Format a date in long format e.g. "27 January 2011".
22
+ # Ordinal format is also supported, in both the UK
23
+ # (e.g. "27th January 2011") and US ("e.g. January 27th, 2011") formats.
24
+ # UK format is the default.
25
+ #
26
+ # date - the Time to format.
27
+ # type - if "ordinal" the returned String will be in ordinal format
28
+ # style - if "US" the returned String will be in US format.
29
+ # Otherwise it will be in UK format.
30
+ #
31
+ # Returns the formatted String.
32
+ def date_to_long_string(date, type = nil, style = nil)
33
+ stringify_date(date, "%B", type, style)
34
+ end
35
+
36
+ # Format a date for use in XML.
37
+ #
38
+ # date - The Time to format.
39
+ #
40
+ # Examples
41
+ #
42
+ # date_to_xmlschema(Time.now)
43
+ # # => "2011-04-24T20:34:46+08:00"
44
+ #
45
+ # Returns the formatted String.
46
+ def date_to_xmlschema(date)
47
+ return date if date.to_s.empty?
48
+
49
+ time(date).xmlschema
50
+ end
51
+
52
+ # Format a date according to RFC-822
53
+ #
54
+ # date - The Time to format.
55
+ #
56
+ # Examples
57
+ #
58
+ # date_to_rfc822(Time.now)
59
+ # # => "Sun, 24 Apr 2011 12:34:46 +0000"
60
+ #
61
+ # Returns the formatted String.
62
+ def date_to_rfc822(date)
63
+ return date if date.to_s.empty?
64
+
65
+ time(date).rfc822
66
+ end
67
+
68
+ private
69
+
70
+ # month_type: Notations that evaluate to 'Month' via `Time#strftime` ("%b", "%B")
71
+ # type: nil (default) or "ordinal"
72
+ # style: nil (default) or "US"
73
+ #
74
+ # Returns a stringified date or the empty input.
75
+ def stringify_date(date, month_type, type = nil, style = nil)
76
+ return date if date.to_s.empty?
77
+
78
+ time = time(date)
79
+ if type == "ordinal"
80
+ day = time.day
81
+ ordinal_day = "#{day}#{ordinal(day)}"
82
+ return time.strftime("#{month_type} #{ordinal_day}, %Y") if style == "US"
83
+
84
+ return time.strftime("#{ordinal_day} #{month_type} %Y")
85
+ end
86
+ time.strftime("%d #{month_type} %Y")
87
+ end
88
+
89
+ def ordinal(number)
90
+ return "th" if (11..13).cover?(number)
91
+
92
+ case number % 10
93
+ when 1 then "st"
94
+ when 2 then "nd"
95
+ when 3 then "rd"
96
+ else "th"
97
+ end
98
+ end
99
+
100
+ def time(input)
101
+ date = Liquid::Utils.to_date(input)
102
+ unless date.respond_to?(:to_time)
103
+ raise Errors::InvalidDateError,
104
+ "Invalid Date: '#{input.inspect}' is not a valid datetime."
105
+ end
106
+ date.to_time.dup.localtime
107
+ end
108
+ end
109
+ end
110
+ end
@@ -1,64 +1,64 @@
1
- # frozen_string_literal: true
2
-
3
- module Jekyll
4
- module Filters
5
- module GroupingFilters
6
- # Group an array of items by a property
7
- #
8
- # input - the inputted Enumerable
9
- # property - the property
10
- #
11
- # Returns an array of Hashes, each looking something like this:
12
- # {"name" => "larry"
13
- # "items" => [...] } # all the items where `property` == "larry"
14
- def group_by(input, property)
15
- if groupable?(input)
16
- groups = input.group_by { |item| item_property(item, property).to_s }
17
- grouped_array(groups)
18
- else
19
- input
20
- end
21
- end
22
-
23
- # Group an array of items by an expression
24
- #
25
- # input - the object array
26
- # variable - the variable to assign each item to in the expression
27
- # expression -a Liquid comparison expression passed in as a string
28
- #
29
- # Returns the filtered array of objects
30
- def group_by_exp(input, variable, expression)
31
- return input unless groupable?(input)
32
-
33
- parsed_expr = parse_expression(expression)
34
- @context.stack do
35
- groups = input.group_by do |item|
36
- @context[variable] = item
37
- parsed_expr.render(@context)
38
- end
39
- grouped_array(groups)
40
- end
41
- end
42
-
43
- private
44
-
45
- def parse_expression(str)
46
- Liquid::Variable.new(str, Liquid::ParseContext.new)
47
- end
48
-
49
- def groupable?(element)
50
- element.respond_to?(:group_by)
51
- end
52
-
53
- def grouped_array(groups)
54
- groups.each_with_object([]) do |item, array|
55
- array << {
56
- "name" => item.first,
57
- "items" => item.last,
58
- "size" => item.last.size,
59
- }
60
- end
61
- end
62
- end
63
- end
64
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Filters
5
+ module GroupingFilters
6
+ # Group an array of items by a property
7
+ #
8
+ # input - the inputted Enumerable
9
+ # property - the property
10
+ #
11
+ # Returns an array of Hashes, each looking something like this:
12
+ # {"name" => "larry"
13
+ # "items" => [...] } # all the items where `property` == "larry"
14
+ def group_by(input, property)
15
+ if groupable?(input)
16
+ groups = input.group_by { |item| item_property(item, property).to_s }
17
+ grouped_array(groups)
18
+ else
19
+ input
20
+ end
21
+ end
22
+
23
+ # Group an array of items by an expression
24
+ #
25
+ # input - the object array
26
+ # variable - the variable to assign each item to in the expression
27
+ # expression -a Liquid comparison expression passed in as a string
28
+ #
29
+ # Returns the filtered array of objects
30
+ def group_by_exp(input, variable, expression)
31
+ return input unless groupable?(input)
32
+
33
+ parsed_expr = parse_expression(expression)
34
+ @context.stack do
35
+ groups = input.group_by do |item|
36
+ @context[variable] = item
37
+ parsed_expr.render(@context)
38
+ end
39
+ grouped_array(groups)
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def parse_expression(str)
46
+ Liquid::Variable.new(str, Liquid::ParseContext.new)
47
+ end
48
+
49
+ def groupable?(element)
50
+ element.respond_to?(:group_by)
51
+ end
52
+
53
+ def grouped_array(groups)
54
+ groups.each_with_object([]) do |item, array|
55
+ array << {
56
+ "name" => item.first,
57
+ "items" => item.last,
58
+ "size" => item.last.size,
59
+ }
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,98 +1,98 @@
1
- # frozen_string_literal: true
2
-
3
- module Jekyll
4
- module Filters
5
- module URLFilters
6
- # Produces an absolute URL based on site.url and site.baseurl.
7
- #
8
- # input - the URL to make absolute.
9
- #
10
- # Returns the absolute URL as a String.
11
- def absolute_url(input)
12
- return if input.nil?
13
-
14
- cache = if input.is_a?(String)
15
- (@context.registers[:site].filter_cache[:absolute_url] ||= {})
16
- else
17
- (@context.registers[:cached_absolute_url] ||= {})
18
- end
19
- cache[input] ||= compute_absolute_url(input)
20
-
21
- # Duplicate cached string so that the cached value is never mutated by
22
- # a subsequent filter.
23
- cache[input].dup
24
- end
25
-
26
- # Produces a URL relative to the domain root based on site.baseurl
27
- # unless it is already an absolute url with an authority (host).
28
- #
29
- # input - the URL to make relative to the domain root
30
- #
31
- # Returns a URL relative to the domain root as a String.
32
- def relative_url(input)
33
- return if input.nil?
34
-
35
- cache = if input.is_a?(String)
36
- (@context.registers[:site].filter_cache[:relative_url] ||= {})
37
- else
38
- (@context.registers[:cached_relative_url] ||= {})
39
- end
40
- cache[input] ||= compute_relative_url(input)
41
-
42
- # Duplicate cached string so that the cached value is never mutated by
43
- # a subsequent filter.
44
- cache[input].dup
45
- end
46
-
47
- # Strips trailing `/index.html` from URLs to create pretty permalinks
48
- #
49
- # input - the URL with a possible `/index.html`
50
- #
51
- # Returns a URL with the trailing `/index.html` removed
52
- def strip_index(input)
53
- return if input.nil? || input.to_s.empty?
54
-
55
- input.sub(%r!/index\.html?$!, "/")
56
- end
57
-
58
- private
59
-
60
- def compute_absolute_url(input)
61
- input = input.url if input.respond_to?(:url)
62
- return input if Addressable::URI.parse(input.to_s).absolute?
63
-
64
- site = @context.registers[:site]
65
- site_url = site.config["url"]
66
- return relative_url(input) if site_url.nil? || site_url == ""
67
-
68
- Addressable::URI.parse(
69
- site_url.to_s + relative_url(input)
70
- ).normalize.to_s
71
- end
72
-
73
- def compute_relative_url(input)
74
- input = input.url if input.respond_to?(:url)
75
- return input if Addressable::URI.parse(input.to_s).absolute?
76
-
77
- parts = [sanitized_baseurl, input]
78
- Addressable::URI.parse(
79
- parts.map! { |part| ensure_leading_slash(part.to_s) }.join
80
- ).normalize.to_s
81
- end
82
-
83
- def sanitized_baseurl
84
- site = @context.registers[:site]
85
- baseurl = site.config["baseurl"]
86
- return "" if baseurl.nil?
87
-
88
- baseurl.to_s.chomp("/")
89
- end
90
-
91
- def ensure_leading_slash(input)
92
- return input if input.nil? || input.empty? || input.start_with?("/")
93
-
94
- "/#{input}"
95
- end
96
- end
97
- end
98
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Filters
5
+ module URLFilters
6
+ # Produces an absolute URL based on site.url and site.baseurl.
7
+ #
8
+ # input - the URL to make absolute.
9
+ #
10
+ # Returns the absolute URL as a String.
11
+ def absolute_url(input)
12
+ return if input.nil?
13
+
14
+ cache = if input.is_a?(String)
15
+ (@context.registers[:site].filter_cache[:absolute_url] ||= {})
16
+ else
17
+ (@context.registers[:cached_absolute_url] ||= {})
18
+ end
19
+ cache[input] ||= compute_absolute_url(input)
20
+
21
+ # Duplicate cached string so that the cached value is never mutated by
22
+ # a subsequent filter.
23
+ cache[input].dup
24
+ end
25
+
26
+ # Produces a URL relative to the domain root based on site.baseurl
27
+ # unless it is already an absolute url with an authority (host).
28
+ #
29
+ # input - the URL to make relative to the domain root
30
+ #
31
+ # Returns a URL relative to the domain root as a String.
32
+ def relative_url(input)
33
+ return if input.nil?
34
+
35
+ cache = if input.is_a?(String)
36
+ (@context.registers[:site].filter_cache[:relative_url] ||= {})
37
+ else
38
+ (@context.registers[:cached_relative_url] ||= {})
39
+ end
40
+ cache[input] ||= compute_relative_url(input)
41
+
42
+ # Duplicate cached string so that the cached value is never mutated by
43
+ # a subsequent filter.
44
+ cache[input].dup
45
+ end
46
+
47
+ # Strips trailing `/index.html` from URLs to create pretty permalinks
48
+ #
49
+ # input - the URL with a possible `/index.html`
50
+ #
51
+ # Returns a URL with the trailing `/index.html` removed
52
+ def strip_index(input)
53
+ return if input.nil? || input.to_s.empty?
54
+
55
+ input.sub(%r!/index\.html?$!, "/")
56
+ end
57
+
58
+ private
59
+
60
+ def compute_absolute_url(input)
61
+ input = input.url if input.respond_to?(:url)
62
+ return input if Addressable::URI.parse(input.to_s).absolute?
63
+
64
+ site = @context.registers[:site]
65
+ site_url = site.config["url"]
66
+ return relative_url(input) if site_url.nil? || site_url == ""
67
+
68
+ Addressable::URI.parse(
69
+ site_url.to_s + relative_url(input)
70
+ ).normalize.to_s
71
+ end
72
+
73
+ def compute_relative_url(input)
74
+ input = input.url if input.respond_to?(:url)
75
+ return input if Addressable::URI.parse(input.to_s).absolute?
76
+
77
+ parts = [sanitized_baseurl, input]
78
+ Addressable::URI.parse(
79
+ parts.map! { |part| ensure_leading_slash(part.to_s) }.join
80
+ ).normalize.to_s
81
+ end
82
+
83
+ def sanitized_baseurl
84
+ site = @context.registers[:site]
85
+ baseurl = site.config["baseurl"]
86
+ return "" if baseurl.nil?
87
+
88
+ baseurl.to_s.chomp("/")
89
+ end
90
+
91
+ def ensure_leading_slash(input)
92
+ return input if input.nil? || input.empty? || input.start_with?("/")
93
+
94
+ "/#{input}"
95
+ end
96
+ end
97
+ end
98
+ end