govspeak 6.5.4 → 6.5.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/Rakefile +6 -3
- data/lib/govspeak.rb +54 -52
- data/lib/govspeak/html_sanitizer.rb +3 -3
- data/lib/govspeak/kramdown_overrides.rb +2 -2
- data/lib/govspeak/link_extractor.rb +3 -3
- data/lib/govspeak/post_processor.rb +18 -5
- data/lib/govspeak/presenters/attachment_presenter.rb +26 -26
- data/lib/govspeak/presenters/image_presenter.rb +2 -2
- data/lib/govspeak/structured_header_extractor.rb +2 -2
- data/lib/govspeak/version.rb +1 -1
- data/lib/kramdown/parser/govuk.rb +4 -5
- data/test/blockquote_extra_quote_remover_test.rb +25 -27
- data/test/govspeak_attachment_link_test.rb +0 -2
- data/test/govspeak_attachment_test.rb +0 -2
- data/test/govspeak_attachments_image_test.rb +2 -4
- data/test/govspeak_attachments_inline_test.rb +4 -6
- data/test/govspeak_button_test.rb +8 -10
- data/test/govspeak_contacts_test.rb +11 -13
- data/test/govspeak_extract_contact_content_ids_test.rb +0 -2
- data/test/govspeak_footnote_test.rb +46 -0
- data/test/govspeak_images_bang_test.rb +30 -32
- data/test/govspeak_images_test.rb +36 -38
- data/test/govspeak_link_extractor_test.rb +1 -1
- data/test/govspeak_link_test.rb +0 -2
- data/test/govspeak_structured_headers_test.rb +5 -4
- data/test/govspeak_table_with_headers_test.rb +67 -20
- data/test/govspeak_test.rb +80 -83
- data/test/govspeak_test_helper.rb +1 -1
- data/test/html_sanitizer_test.rb +0 -1
- data/test/presenters/h_card_presenter_test.rb +0 -2
- data/test/test_helper.rb +6 -2
- metadata +42 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 948aced50c3d3842255ea4206a651229de59b2beef42e0c50e55e204354d9b5f
|
4
|
+
data.tar.gz: cc55abe4b54b112a3aa1cb350a94270b11b9f0c11a5ca71479f55bb0efbf657d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e0c1b0d6ca1fe4999e4135536328ecdc60bd34f368d986a808cee6de687f58198a080c19beffd47df6d80130edd2010eb1a09ccd840ea8adb9375c0b747e533
|
7
|
+
data.tar.gz: e47aeeabecc4ffccff7342d1a1ad2e1ddd6cd8aabf5850f740fa3bf5ecdd9395bbded84222d55a5c8998748d7491a649793c550ab49564cd098486623684b275
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
## 6.5.9
|
2
|
+
|
3
|
+
* Adjust footnote markup to accommodate multiple references (PR#198)
|
4
|
+
|
5
|
+
## 6.5.8
|
6
|
+
|
7
|
+
* Customise footnote markup for accessibility (PR#192)
|
8
|
+
|
9
|
+
## 6.5.7
|
10
|
+
|
11
|
+
* Update GOV.UK Publishing components to version to 23.0.0 or greater
|
12
|
+
|
13
|
+
## 6.5.6
|
14
|
+
|
15
|
+
* Update Kramdown version to 2.3.0 or greater
|
16
|
+
|
17
|
+
## 6.5.5
|
18
|
+
|
19
|
+
* Prevent links in table headers being stripped (PR#187)
|
20
|
+
|
1
21
|
## 6.5.4
|
2
22
|
|
3
23
|
* Require Sanitize to be at least 5.2.1 due to https://nvd.nist.gov/vuln/detail/CVE-2020-4054
|
data/Rakefile
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
require "rake"
|
2
2
|
require "rake/testtask"
|
3
|
+
require "rubocop/rake_task"
|
3
4
|
require "bundler"
|
4
5
|
|
5
6
|
Bundler::GemHelper.install_tasks
|
6
7
|
|
8
|
+
RuboCop::RakeTask.new
|
9
|
+
|
7
10
|
desc "Run basic tests"
|
8
|
-
Rake::TestTask.new("test")
|
11
|
+
Rake::TestTask.new("test") do |t|
|
9
12
|
t.libs << "test"
|
10
13
|
t.pattern = "test/*_test.rb"
|
11
14
|
t.verbose = true
|
12
15
|
t.warning = true
|
13
|
-
|
16
|
+
end
|
14
17
|
|
15
|
-
task default: [
|
18
|
+
task default: %i[test rubocop]
|
data/lib/govspeak.rb
CHANGED
@@ -57,7 +57,9 @@ module Govspeak
|
|
57
57
|
@links = Array.wrap(options.delete(:links))
|
58
58
|
@contacts = Array.wrap(options.delete(:contacts))
|
59
59
|
@locale = options.fetch(:locale, "en")
|
60
|
-
@options = { input: PARSER_CLASS_NAME,
|
60
|
+
@options = { input: PARSER_CLASS_NAME,
|
61
|
+
sanitize: true,
|
62
|
+
syntax_highlighter: nil }.merge(options)
|
61
63
|
@options[:entity_output] = :symbolic
|
62
64
|
end
|
63
65
|
|
@@ -108,9 +110,9 @@ module Govspeak
|
|
108
110
|
source = Govspeak::BlockquoteExtraQuoteRemover.remove(source)
|
109
111
|
source = remove_forbidden_characters(source)
|
110
112
|
self.class.extensions.each do |_, regexp, block|
|
111
|
-
source.gsub!(regexp)
|
113
|
+
source.gsub!(regexp) do
|
112
114
|
instance_exec(*Regexp.last_match.captures, &block)
|
113
|
-
|
115
|
+
end
|
114
116
|
end
|
115
117
|
source
|
116
118
|
end
|
@@ -127,20 +129,20 @@ module Govspeak
|
|
127
129
|
end
|
128
130
|
|
129
131
|
def self.surrounded_by(open, close = nil)
|
130
|
-
open = Regexp
|
132
|
+
open = Regexp.escape(open)
|
131
133
|
if close
|
132
|
-
close = Regexp
|
133
|
-
%r
|
134
|
+
close = Regexp.escape(close)
|
135
|
+
%r{(?:\r|\n|^)#{open}(.*?)#{close} *(\r|\n|$)?}m
|
134
136
|
else
|
135
|
-
%r
|
137
|
+
%r{(?:\r|\n|^)#{open}(.*?)#{open}? *(\r|\n|$)}m
|
136
138
|
end
|
137
139
|
end
|
138
140
|
|
139
141
|
def self.wrap_with_div(class_name, character, parser = Kramdown::Document)
|
140
|
-
extension(class_name, surrounded_by(character))
|
142
|
+
extension(class_name, surrounded_by(character)) do |body|
|
141
143
|
content = parser ? parser.new("#{body.strip}\n").to_html : body.strip
|
142
|
-
%
|
143
|
-
|
144
|
+
%(\n<div class="#{class_name}">\n#{content}</div>\n)
|
145
|
+
end
|
144
146
|
end
|
145
147
|
|
146
148
|
def insert_strong_inside_p(body, parser = Govspeak::Document)
|
@@ -160,7 +162,7 @@ module Govspeak
|
|
160
162
|
\s* # any whitespace between opening bracket and link
|
161
163
|
{\/button} # match ending bracket
|
162
164
|
(?:\r|\n|$) # non-capturing match to make sure end of line and linebreak
|
163
|
-
}x)
|
165
|
+
}x) do |attributes, text, href|
|
164
166
|
button_classes = "govuk-button"
|
165
167
|
/cross-domain-tracking:(?<cross_domain_tracking>.[^\s*]+)/ =~ attributes
|
166
168
|
data_attribute = ""
|
@@ -173,36 +175,36 @@ module Govspeak
|
|
173
175
|
text = text.strip
|
174
176
|
href = href.strip
|
175
177
|
|
176
|
-
%
|
177
|
-
|
178
|
+
%(\n<a role="button" class="#{button_classes}" href="#{href}" #{data_attribute}>#{text}</a>\n)
|
179
|
+
end
|
178
180
|
|
179
|
-
extension("highlight-answer")
|
180
|
-
%
|
181
|
-
#{Govspeak::Document.new(body.strip).to_html}</div>\n
|
182
|
-
|
181
|
+
extension("highlight-answer") do |body|
|
182
|
+
%(\n\n<div class="highlight-answer">
|
183
|
+
#{Govspeak::Document.new(body.strip).to_html}</div>\n)
|
184
|
+
end
|
183
185
|
|
184
|
-
extension("stat-headline", %r${stat-headline}(.*?){/stat-headline}$m)
|
185
|
-
%
|
186
|
-
#{Govspeak::Document.new(body.strip).to_html}</aside>\n
|
187
|
-
|
186
|
+
extension("stat-headline", %r${stat-headline}(.*?){/stat-headline}$m) do |body|
|
187
|
+
%(\n\n<aside class="stat-headline">
|
188
|
+
#{Govspeak::Document.new(body.strip).to_html}</aside>\n)
|
189
|
+
end
|
188
190
|
|
189
191
|
# FIXME: these surrounded_by arguments look dodgy
|
190
|
-
extension("external", surrounded_by("x[", ")x"))
|
192
|
+
extension("external", surrounded_by("x[", ")x")) do |body|
|
191
193
|
Kramdown::Document.new("[#{body.strip}){:rel='external'}").to_html
|
192
|
-
|
194
|
+
end
|
193
195
|
|
194
|
-
extension("informational", surrounded_by("^"))
|
195
|
-
%
|
196
|
-
#{Govspeak::Document.new(body.strip).to_html}</div>\n
|
197
|
-
|
196
|
+
extension("informational", surrounded_by("^")) do |body|
|
197
|
+
%(\n\n<div role="note" aria-label="Information" class="application-notice info-notice">
|
198
|
+
#{Govspeak::Document.new(body.strip).to_html}</div>\n)
|
199
|
+
end
|
198
200
|
|
199
|
-
extension("important", surrounded_by("@"))
|
200
|
-
%
|
201
|
-
|
201
|
+
extension("important", surrounded_by("@")) do |body|
|
202
|
+
%(\n\n<div role="note" aria-label="Important" class="advisory">#{insert_strong_inside_p(body)}</div>\n)
|
203
|
+
end
|
202
204
|
|
203
|
-
extension("helpful", surrounded_by("%"))
|
204
|
-
%
|
205
|
-
|
205
|
+
extension("helpful", surrounded_by("%")) do |body|
|
206
|
+
%(\n\n<div role="note" aria-label="Warning" class="application-notice help-notice">\n#{Govspeak::Document.new(body.strip).to_html}</div>\n)
|
207
|
+
end
|
206
208
|
|
207
209
|
extension("barchart", /{barchart(.*?)}/) do |captures|
|
208
210
|
stacked = ".mc-stacked" if captures.include? "stacked"
|
@@ -210,13 +212,13 @@ module Govspeak
|
|
210
212
|
negative = ".mc-negative" if captures.include? "negative"
|
211
213
|
|
212
214
|
[
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
215
|
+
"{:",
|
216
|
+
".js-barchart-table",
|
217
|
+
stacked,
|
218
|
+
compact,
|
219
|
+
negative,
|
220
|
+
".mc-auto-outdent",
|
221
|
+
"}",
|
220
222
|
].join(" ")
|
221
223
|
end
|
222
224
|
|
@@ -234,12 +236,12 @@ module Govspeak
|
|
234
236
|
|
235
237
|
attachment = AttachmentPresenter.new(attachment)
|
236
238
|
|
237
|
-
span_id = attachment.id ? %
|
239
|
+
span_id = attachment.id ? %( id="attachment_#{attachment.id}") : ""
|
238
240
|
# new lines inside our title cause problems with govspeak rendering as this is expected to be on one line.
|
239
241
|
title = (attachment.title || "").tr("\n", " ")
|
240
242
|
link = attachment.link(title, attachment.url)
|
241
243
|
attributes = attachment.attachment_attributes.empty? ? "" : " (#{attachment.attachment_attributes})"
|
242
|
-
%
|
244
|
+
%(<span#{span_id} class="attachment-inline">#{link}#{attributes}</span>)
|
243
245
|
end
|
244
246
|
|
245
247
|
# DEPRECATED: use 'Image:image-id' instead
|
@@ -261,10 +263,10 @@ module Govspeak
|
|
261
263
|
#
|
262
264
|
# This issue is not considered a bug by kramdown: https://github.com/gettalong/kramdown/issues/191
|
263
265
|
def render_image(image)
|
264
|
-
id_attr = image.id ? %
|
266
|
+
id_attr = image.id ? %( id="attachment_#{image.id}") : ""
|
265
267
|
lines = []
|
266
|
-
lines << %
|
267
|
-
lines << %
|
268
|
+
lines << %(<figure#{id_attr} class="image embedded">)
|
269
|
+
lines << %(<div class="img"><img src="#{encode(image.url)}" alt="#{encode(image.alt_text)}"></div>)
|
268
270
|
lines << image.figcaption_html if image.figcaption?
|
269
271
|
lines << "</figure>"
|
270
272
|
lines.join
|
@@ -279,9 +281,9 @@ module Govspeak
|
|
279
281
|
wrap_with_div("example", "$E", Govspeak::Document)
|
280
282
|
wrap_with_div("call-to-action", "$CTA", Govspeak::Document)
|
281
283
|
|
282
|
-
extension("address", surrounded_by("$A"))
|
283
|
-
%
|
284
|
-
|
284
|
+
extension("address", surrounded_by("$A")) do |body|
|
285
|
+
%(\n<div class="address"><div class="adr org fn"><p>\n#{body.sub("\n", '').gsub("\n", '<br />')}\n</p></div></div>\n)
|
286
|
+
end
|
285
287
|
|
286
288
|
extension("legislative list", /#{NEW_PARAGRAPH_LOOKBEHIND}\$LegislativeList\s*$(.*?)\$EndLegislativeList/m) do |body|
|
287
289
|
Govspeak::KramdownOverrides.with_kramdown_ordered_lists_disabled do
|
@@ -295,9 +297,9 @@ module Govspeak
|
|
295
297
|
|
296
298
|
extension("numbered list", /^[ \t]*((s\d+\.\s.*(?:\n|$))+)/) do |body|
|
297
299
|
body.gsub!(/s(\d+)\.\s(.*)(?:\n|$)/) do
|
298
|
-
"<li>#{Govspeak::Document.new(
|
300
|
+
"<li>#{Govspeak::Document.new(Regexp.last_match(2).strip).to_html}</li>\n"
|
299
301
|
end
|
300
|
-
%
|
302
|
+
%(<ol class="steps">\n#{body}</ol>)
|
301
303
|
end
|
302
304
|
|
303
305
|
def self.devolved_options
|
@@ -362,13 +364,13 @@ module Govspeak
|
|
362
364
|
extension("Attachment", /#{NEW_PARAGRAPH_LOOKBEHIND}\[Attachment:\s*(.*?)\s*\]/) do |attachment_id|
|
363
365
|
next "" if attachments.none? { |a| a[:id] == attachment_id }
|
364
366
|
|
365
|
-
%
|
367
|
+
%(<govspeak-embed-attachment id="#{attachment_id}"></govspeak-embed-attachment>)
|
366
368
|
end
|
367
369
|
|
368
370
|
extension("AttachmentLink", /\[AttachmentLink:\s*(.*?)\s*\]/) do |attachment_id|
|
369
371
|
next "" if attachments.none? { |a| a[:id] == attachment_id }
|
370
372
|
|
371
|
-
%
|
373
|
+
%(<govspeak-embed-attachment-link id="#{attachment_id}"></govspeak-embed-attachment-link>)
|
372
374
|
end
|
373
375
|
|
374
376
|
private
|
@@ -54,12 +54,12 @@ class Govspeak::HtmlSanitizer
|
|
54
54
|
elements: Sanitize::Config::RELAXED[:elements] + %w[govspeak-embed-attachment govspeak-embed-attachment-link svg path],
|
55
55
|
attributes: {
|
56
56
|
:all => Sanitize::Config::RELAXED[:attributes][:all] + %w[role aria-label],
|
57
|
-
"a"
|
57
|
+
"a" => Sanitize::Config::RELAXED[:attributes]["a"] + [:data],
|
58
58
|
"svg" => Sanitize::Config::RELAXED[:attributes][:all] + %w[xmlns width height viewbox focusable],
|
59
59
|
"path" => Sanitize::Config::RELAXED[:attributes][:all] + %w[fill d],
|
60
60
|
"div" => [:data],
|
61
|
-
"th"
|
62
|
-
"td"
|
61
|
+
"th" => Sanitize::Config::RELAXED[:attributes]["th"] + %w[style],
|
62
|
+
"td" => Sanitize::Config::RELAXED[:attributes]["td"] + %w[style],
|
63
63
|
"govspeak-embed-attachment" => %w[content-id],
|
64
64
|
},
|
65
65
|
)
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module Govspeak
|
2
2
|
module KramdownOverrides
|
3
|
-
#
|
3
|
+
# This depends on two internal parts of Kramdown.
|
4
4
|
# 1. Parser registry (kramdown/parser/kramdown.rb#define_parser)
|
5
5
|
# 2. Kramdown list regexes (kramdown/parser/kramdown/list.rb)
|
6
|
-
#
|
6
|
+
# Updating the Kramdown gem therefore also means updating this file to to
|
7
7
|
# match Kramdown's internals.
|
8
8
|
|
9
9
|
def self.with_kramdown_ordered_lists_disabled
|
@@ -14,9 +14,9 @@ module Govspeak
|
|
14
14
|
attr_reader :document, :website_root
|
15
15
|
|
16
16
|
def extract_links
|
17
|
-
document_anchors
|
18
|
-
map { |link| extract_href_from_link(link) }
|
19
|
-
reject(&:blank?)
|
17
|
+
document_anchors
|
18
|
+
.map { |link| extract_href_from_link(link) }
|
19
|
+
.reject(&:blank?)
|
20
20
|
end
|
21
21
|
|
22
22
|
def extract_href_from_link(link)
|
@@ -92,11 +92,13 @@ module Govspeak
|
|
92
92
|
end
|
93
93
|
|
94
94
|
document.css(":not(thead) tr td:first-child").map do |el|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
95
|
+
next unless el.content.match?(/^#($|\s.*$)/)
|
96
|
+
|
97
|
+
# Replace '# ' and '#', but not '#Word'.
|
98
|
+
# This runs on the first child of the element to preserve any links
|
99
|
+
el.children.first.content = el.children.first.content.gsub(/^#($|\s)/, "")
|
100
|
+
el.name = "th" if el.content.present? # This also prevents a `th` with nothing inside it; a `td` is preferable.
|
101
|
+
el[:scope] = "row" if el.content.present? # `scope` shouldn't be used if there's nothing in the table heading.
|
100
102
|
end
|
101
103
|
end
|
102
104
|
|
@@ -118,6 +120,17 @@ module Govspeak
|
|
118
120
|
end
|
119
121
|
end
|
120
122
|
|
123
|
+
extension("use custom footnotes") do |document|
|
124
|
+
document.css("a.footnote").map do |el|
|
125
|
+
footnote_number = el[:href].gsub(/\D/, "")
|
126
|
+
el.content = "[footnote #{footnote_number}]"
|
127
|
+
end
|
128
|
+
document.css("[role='doc-backlink']").map do |el|
|
129
|
+
backlink_number = " " + el.css("sup")[0].content if el.css("sup")[0].present?
|
130
|
+
el["aria-label"] = "go to where this is referenced#{backlink_number}"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
121
134
|
attr_reader :input, :govspeak_document
|
122
135
|
|
123
136
|
def initialize(html, govspeak_document)
|
@@ -55,46 +55,46 @@ module Govspeak
|
|
55
55
|
|
56
56
|
def humanized_content_type(file_extension)
|
57
57
|
file_extension_vs_humanized_content_type = {
|
58
|
-
"chm"
|
59
|
-
"csv"
|
58
|
+
"chm" => file_abbr_tag("CHM", "Microsoft Compiled HTML Help"),
|
59
|
+
"csv" => file_abbr_tag("CSV", "Comma-separated Values"),
|
60
60
|
"diff" => file_abbr_tag("DIFF", "Plain text differences"),
|
61
|
-
"doc"
|
61
|
+
"doc" => MS_WORD_DOCUMENT_HUMANIZED_CONTENT_TYPE,
|
62
62
|
"docx" => MS_WORD_DOCUMENT_HUMANIZED_CONTENT_TYPE,
|
63
|
-
"dot"
|
64
|
-
"dxf"
|
65
|
-
"eps"
|
66
|
-
"gif"
|
67
|
-
"gml"
|
63
|
+
"dot" => file_abbr_tag("DOT", "MS Word Document Template"),
|
64
|
+
"dxf" => file_abbr_tag("DXF", "AutoCAD Drawing Exchange Format"),
|
65
|
+
"eps" => file_abbr_tag("EPS", "Encapsulated PostScript"),
|
66
|
+
"gif" => file_abbr_tag("GIF", "Graphics Interchange Format"),
|
67
|
+
"gml" => file_abbr_tag("GML", "Geography Markup Language"),
|
68
68
|
"html" => file_abbr_tag("HTML", "Hypertext Markup Language"),
|
69
69
|
"ics" => file_abbr_tag("ICS", "iCalendar file"),
|
70
|
-
"jpg"
|
71
|
-
"odp"
|
72
|
-
"ods"
|
73
|
-
"odt"
|
74
|
-
"pdf"
|
75
|
-
"png"
|
76
|
-
"ppt"
|
70
|
+
"jpg" => "JPEG",
|
71
|
+
"odp" => file_abbr_tag("ODP", "OpenDocument Presentation"),
|
72
|
+
"ods" => file_abbr_tag("ODS", "OpenDocument Spreadsheet"),
|
73
|
+
"odt" => file_abbr_tag("ODT", "OpenDocument Text document"),
|
74
|
+
"pdf" => file_abbr_tag("PDF", "Portable Document Format"),
|
75
|
+
"png" => file_abbr_tag("PNG", "Portable Network Graphic"),
|
76
|
+
"ppt" => MS_POWERPOINT_PRESENTATION_HUMANIZED_CONTENT_TYPE,
|
77
77
|
"pptx" => MS_POWERPOINT_PRESENTATION_HUMANIZED_CONTENT_TYPE,
|
78
|
-
"ps"
|
79
|
-
"rdf"
|
80
|
-
"rtf"
|
81
|
-
"sch"
|
82
|
-
"txt"
|
78
|
+
"ps" => file_abbr_tag("PS", "PostScript"),
|
79
|
+
"rdf" => file_abbr_tag("RDF", "Resource Description Framework"),
|
80
|
+
"rtf" => file_abbr_tag("RTF", "Rich Text Format"),
|
81
|
+
"sch" => file_abbr_tag("SCH", "XML based Schematic"),
|
82
|
+
"txt" => "Plain text",
|
83
83
|
"wsdl" => file_abbr_tag("WSDL", "Web Services Description Language"),
|
84
|
-
"xls"
|
84
|
+
"xls" => MS_EXCEL_SPREADSHEET_HUMANIZED_CONTENT_TYPE,
|
85
85
|
"xlsm" => file_abbr_tag("XLSM", "MS Excel Macro-Enabled Workbook"),
|
86
86
|
"xlsx" => MS_EXCEL_SPREADSHEET_HUMANIZED_CONTENT_TYPE,
|
87
|
-
"xlt"
|
88
|
-
"xsd"
|
87
|
+
"xlt" => file_abbr_tag("XLT", "MS Excel Spreadsheet Template"),
|
88
|
+
"xsd" => file_abbr_tag("XSD", "XML Schema"),
|
89
89
|
"xslt" => file_abbr_tag("XSLT", "Extensible Stylesheet Language Transformation"),
|
90
|
-
"zip"
|
90
|
+
"zip" => file_abbr_tag("ZIP", "Zip archive"),
|
91
91
|
}
|
92
92
|
file_extension_vs_humanized_content_type.fetch(file_extension.to_s.downcase, "")
|
93
93
|
end
|
94
94
|
|
95
95
|
def link(body, url, options = {})
|
96
|
-
options_str = options.map { |k, v| %
|
97
|
-
%
|
96
|
+
options_str = options.map { |k, v| %(#{encode(k)}="#{encode(v)}") }.join(" ")
|
97
|
+
%(<a href="#{encode(url)}" #{options_str}>#{body}</a>)
|
98
98
|
end
|
99
99
|
|
100
100
|
private
|
@@ -33,8 +33,8 @@ module Govspeak
|
|
33
33
|
def figcaption_html
|
34
34
|
lines = []
|
35
35
|
lines << "<figcaption>"
|
36
|
-
lines << %
|
37
|
-
lines << %
|
36
|
+
lines << %(<p>#{caption}</p>) if caption.present?
|
37
|
+
lines << %(<p>#{I18n.t('govspeak.image.figure.credit', credit: credit)}</p>) if credit.present?
|
38
38
|
lines << "</figcaption>"
|
39
39
|
lines.join
|
40
40
|
end
|
@@ -48,9 +48,9 @@ module Govspeak
|
|
48
48
|
private :doc, :stack, :structured_headers
|
49
49
|
|
50
50
|
def headers_list
|
51
|
-
@headers_list ||= doc.headers.map
|
51
|
+
@headers_list ||= doc.headers.map do |h|
|
52
52
|
StructuredHeader.new(h.text, h.level, h.id, [])
|
53
|
-
|
53
|
+
end
|
54
54
|
end
|
55
55
|
|
56
56
|
def add_top_level(header)
|
data/lib/govspeak/version.rb
CHANGED