govspeak 6.4.0 → 6.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -2
- data/README.md +6 -5
- data/Rakefile +1 -1
- data/lib/govspeak.rb +76 -76
- data/lib/govspeak/header_extractor.rb +1 -1
- data/lib/govspeak/html_sanitizer.rb +10 -15
- data/lib/govspeak/link_extractor.rb +3 -3
- data/lib/govspeak/post_processor.rb +30 -12
- data/lib/govspeak/presenters/attachment_presenter.rb +32 -32
- data/lib/govspeak/presenters/contact_presenter.rb +2 -2
- data/lib/govspeak/presenters/h_card_presenter.rb +3 -3
- data/lib/govspeak/presenters/image_presenter.rb +2 -2
- data/lib/govspeak/version.rb +1 -1
- data/lib/kramdown/parser/govuk.rb +3 -3
- data/test/blockquote_extra_quote_remover_test.rb +10 -10
- data/test/govspeak_attachments_image_test.rb +10 -10
- data/test/govspeak_attachments_inline_test.rb +18 -18
- data/test/govspeak_button_test.rb +21 -21
- data/test/govspeak_contacts_test.rb +18 -18
- data/test/govspeak_extract_contact_content_ids_test.rb +1 -1
- data/test/govspeak_images_bang_test.rb +14 -14
- data/test/govspeak_images_test.rb +16 -16
- data/test/govspeak_link_test.rb +1 -1
- data/test/govspeak_structured_headers_test.rb +2 -2
- data/test/govspeak_table_with_headers_test.rb +1 -1
- data/test/govspeak_test.rb +18 -18
- data/test/html_sanitizer_test.rb +25 -7
- data/test/html_validator_test.rb +2 -2
- data/test/presenters/h_card_presenter_test.rb +39 -39
- data/test/test_helper.rb +6 -6
- metadata +51 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a09b3928e9718aa64cd781681dbc8ac51d8534ddc27b26f54e8416b21f60e501
|
4
|
+
data.tar.gz: f8b6da3c1bf3c3b4c7792263853837886b42f7dc6abe6346d81289695ea7986d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9de09348513cfd10e76a9960bfe8012213a99447244cf9d89a2e3da8cb18f93b28e8b63dd63304de06c51a6e3b9e82bc5b1f2de43ff531dfb23f4832d1a18e0b
|
7
|
+
data.tar.gz: 0b464f824b1ff7f8ee77619d2dd1195a22c8777b79b675d9e7fbb1b88011075552f496b294e26d9b65908f2a26031de30ff93c1a12ba3a9743786a4219cfb2de
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,30 @@
|
|
1
|
+
## 6.5.4
|
2
|
+
|
3
|
+
* Require Sanitize to be at least 5.2.1 due to https://nvd.nist.gov/vuln/detail/CVE-2020-4054
|
4
|
+
|
5
|
+
## 6.5.3
|
6
|
+
|
7
|
+
* Use button component for buttons (PR#176)
|
8
|
+
|
9
|
+
## 6.5.2
|
10
|
+
|
11
|
+
* Allow `data` attributes on `div` tags (PR#173)
|
12
|
+
|
13
|
+
## 6.5.1
|
14
|
+
|
15
|
+
* Change unicode testing characters after external gem change
|
16
|
+
* Move from govuk-lint to rubocop-govuk
|
17
|
+
* Allow version 6 of actionview
|
18
|
+
|
19
|
+
## 6.5.0
|
20
|
+
|
21
|
+
* Allow data attributes on links
|
22
|
+
|
1
23
|
## 6.4.0
|
2
24
|
|
3
|
-
|
25
|
+
* Add table heading syntax that allows a table cell outside of `thead` to be marked as a table heading with a scope of row. (PR#161)
|
4
26
|
|
5
|
-
|
27
|
+
## 6.3.0
|
6
28
|
|
7
29
|
* Unicode characters forbidden in HTML are stripped from input
|
8
30
|
* Validation is now more lenient for HTML input
|
data/README.md
CHANGED
@@ -619,8 +619,7 @@ will output
|
|
619
619
|
|
620
620
|
An accessible way to add button links into content, that can also allow cross domain tracking with [Google Analytics](https://support.google.com/analytics/answer/7372977?hl=en)
|
621
621
|
|
622
|
-
This button component
|
623
|
-
Note: Ideally we'd use the original component directly but this currently isnt possible
|
622
|
+
This button component uses the component from the [components gem](https://components.publishing.service.gov.uk/component-guide/button) for use in govspeak.
|
624
623
|
|
625
624
|
You must use the [link](https://daringfireball.net/projects/markdown/syntax#link) syntax within the button tags.
|
626
625
|
|
@@ -635,7 +634,7 @@ To get the most basic button.
|
|
635
634
|
which outputs
|
636
635
|
|
637
636
|
```html
|
638
|
-
<a role="button" class="button" href="https://gov.uk/random">
|
637
|
+
<a role="button" class="gem-c-button govuk-button" href="https://gov.uk/random">
|
639
638
|
Continue
|
640
639
|
</a>
|
641
640
|
```
|
@@ -649,8 +648,9 @@ To turn a button into a ['Start now' button](https://www.gov.uk/service-manual/d
|
|
649
648
|
which outputs
|
650
649
|
|
651
650
|
```html
|
652
|
-
<a role="button" class="button button-start" href="https://gov.uk/random">
|
651
|
+
<a role="button" class="gem-c-button govuk-button govuk-button--start" href="https://gov.uk/random">
|
653
652
|
Start Now
|
653
|
+
<svg class="govuk-button__start-icon" xmlns="http://www.w3.org/2000/svg" width="17.5" height="19" viewBox="0 0 33 40" role="presentation" focusable="false"><path fill="currentColor" d="M0 0h13l20 20-20 20H0l20-20z"></path></svg>
|
654
654
|
</a>
|
655
655
|
```
|
656
656
|
|
@@ -667,12 +667,13 @@ which outputs
|
|
667
667
|
```html
|
668
668
|
<a
|
669
669
|
role="button"
|
670
|
-
class="button button-start"
|
670
|
+
class="gem-c-button govuk-button govuk-button--start"
|
671
671
|
href="https://example.com/external-service/start-now"
|
672
672
|
data-module="cross-domain-tracking"
|
673
673
|
data-tracking-code="UA-XXXXXX-Y"
|
674
674
|
data-tracking-name="govspeakButtonTracker"
|
675
675
|
>
|
676
676
|
Start Now
|
677
|
+
<svg class="govuk-button__start-icon" xmlns="http://www.w3.org/2000/svg" width="17.5" height="19" viewBox="0 0 33 40" role="presentation" focusable="false"><path fill="currentColor" d="M0 0h13l20 20-20 20H0l20-20z"></path></svg>
|
677
678
|
</a>
|
678
679
|
```
|
data/Rakefile
CHANGED
data/lib/govspeak.rb
CHANGED
@@ -1,32 +1,32 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
22
|
-
require
|
23
|
-
require
|
24
|
-
require
|
25
|
-
require
|
1
|
+
require "active_support/core_ext/hash"
|
2
|
+
require "active_support/core_ext/array"
|
3
|
+
require "erb"
|
4
|
+
require "govuk_publishing_components"
|
5
|
+
require "htmlentities"
|
6
|
+
require "kramdown"
|
7
|
+
require "kramdown/parser/govuk"
|
8
|
+
require "nokogiri"
|
9
|
+
require "nokogumbo"
|
10
|
+
require "rinku"
|
11
|
+
require "sanitize"
|
12
|
+
require "govspeak/header_extractor"
|
13
|
+
require "govspeak/structured_header_extractor"
|
14
|
+
require "govspeak/html_validator"
|
15
|
+
require "govspeak/html_sanitizer"
|
16
|
+
require "govspeak/kramdown_overrides"
|
17
|
+
require "govspeak/blockquote_extra_quote_remover"
|
18
|
+
require "govspeak/post_processor"
|
19
|
+
require "govspeak/link_extractor"
|
20
|
+
require "govspeak/template_renderer"
|
21
|
+
require "govspeak/presenters/attachment_presenter"
|
22
|
+
require "govspeak/presenters/contact_presenter"
|
23
|
+
require "govspeak/presenters/h_card_presenter"
|
24
|
+
require "govspeak/presenters/image_presenter"
|
25
|
+
require "govspeak/presenters/attachment_image_presenter"
|
26
26
|
|
27
27
|
module Govspeak
|
28
28
|
def self.root
|
29
|
-
File.expand_path(
|
29
|
+
File.expand_path("..", File.dirname(__FILE__))
|
30
30
|
end
|
31
31
|
|
32
32
|
class Document
|
@@ -44,8 +44,8 @@ module Govspeak
|
|
44
44
|
new(source, options).to_html
|
45
45
|
end
|
46
46
|
|
47
|
-
|
48
|
-
|
47
|
+
class << self
|
48
|
+
attr_reader :extensions
|
49
49
|
end
|
50
50
|
|
51
51
|
def initialize(source, options = {})
|
@@ -118,7 +118,7 @@ module Govspeak
|
|
118
118
|
def remove_forbidden_characters(source)
|
119
119
|
# These are characters that are not deemed not suitable for
|
120
120
|
# markup: https://www.w3.org/TR/unicode-xml/#Charlist
|
121
|
-
source.gsub(Sanitize::REGEX_UNSUITABLE_CHARS,
|
121
|
+
source.gsub(Sanitize::REGEX_UNSUITABLE_CHARS, "")
|
122
122
|
end
|
123
123
|
|
124
124
|
def self.extension(title, regexp = nil, &block)
|
@@ -147,7 +147,7 @@ module Govspeak
|
|
147
147
|
parser.new(body.strip).to_html.sub(/^<p>(.*)<\/p>$/, "<p><strong>\\1</strong></p>")
|
148
148
|
end
|
149
149
|
|
150
|
-
extension(
|
150
|
+
extension("button", %r{
|
151
151
|
(?:\r|\n|^) # non-capturing match to make sure start of line and linebreak
|
152
152
|
{button(.*?)} # match opening bracket and capture attributes
|
153
153
|
\s* # any whitespace between opening bracket and link
|
@@ -161,10 +161,10 @@ module Govspeak
|
|
161
161
|
{\/button} # match ending bracket
|
162
162
|
(?:\r|\n|$) # non-capturing match to make sure end of line and linebreak
|
163
163
|
}x) { |attributes, text, href|
|
164
|
-
button_classes = "button"
|
165
|
-
button_classes << " button-start" if attributes =~ /start/
|
164
|
+
button_classes = "govuk-button"
|
166
165
|
/cross-domain-tracking:(?<cross_domain_tracking>.[^\s*]+)/ =~ attributes
|
167
166
|
data_attribute = ""
|
167
|
+
data_attribute << " data-start='true'" if attributes =~ /start/
|
168
168
|
if cross_domain_tracking
|
169
169
|
data_attribute << " data-module='cross-domain-tracking'"
|
170
170
|
data_attribute << " data-tracking-code='#{cross_domain_tracking.strip}'"
|
@@ -176,51 +176,51 @@ module Govspeak
|
|
176
176
|
%{\n<a role="button" class="#{button_classes}" href="#{href}" #{data_attribute}>#{text}</a>\n}
|
177
177
|
}
|
178
178
|
|
179
|
-
extension(
|
179
|
+
extension("highlight-answer") { |body|
|
180
180
|
%{\n\n<div class="highlight-answer">
|
181
181
|
#{Govspeak::Document.new(body.strip).to_html}</div>\n}
|
182
182
|
}
|
183
183
|
|
184
|
-
extension(
|
184
|
+
extension("stat-headline", %r${stat-headline}(.*?){/stat-headline}$m) { |body|
|
185
185
|
%{\n\n<aside class="stat-headline">
|
186
186
|
#{Govspeak::Document.new(body.strip).to_html}</aside>\n}
|
187
187
|
}
|
188
188
|
|
189
189
|
# FIXME: these surrounded_by arguments look dodgy
|
190
|
-
extension(
|
190
|
+
extension("external", surrounded_by("x[", ")x")) { |body|
|
191
191
|
Kramdown::Document.new("[#{body.strip}){:rel='external'}").to_html
|
192
192
|
}
|
193
193
|
|
194
|
-
extension(
|
194
|
+
extension("informational", surrounded_by("^")) { |body|
|
195
195
|
%{\n\n<div role="note" aria-label="Information" class="application-notice info-notice">
|
196
196
|
#{Govspeak::Document.new(body.strip).to_html}</div>\n}
|
197
197
|
}
|
198
198
|
|
199
|
-
extension(
|
199
|
+
extension("important", surrounded_by("@")) { |body|
|
200
200
|
%{\n\n<div role="note" aria-label="Important" class="advisory">#{insert_strong_inside_p(body)}</div>\n}
|
201
201
|
}
|
202
202
|
|
203
|
-
extension(
|
203
|
+
extension("helpful", surrounded_by("%")) { |body|
|
204
204
|
%{\n\n<div role="note" aria-label="Warning" class="application-notice help-notice">\n#{Govspeak::Document.new(body.strip).to_html}</div>\n}
|
205
205
|
}
|
206
206
|
|
207
|
-
extension(
|
208
|
-
stacked =
|
209
|
-
compact =
|
210
|
-
negative =
|
207
|
+
extension("barchart", /{barchart(.*?)}/) do |captures|
|
208
|
+
stacked = ".mc-stacked" if captures.include? "stacked"
|
209
|
+
compact = ".compact" if captures.include? "compact"
|
210
|
+
negative = ".mc-negative" if captures.include? "negative"
|
211
211
|
|
212
212
|
[
|
213
|
-
|
214
|
-
|
213
|
+
"{:",
|
214
|
+
".js-barchart-table",
|
215
215
|
stacked,
|
216
216
|
compact,
|
217
217
|
negative,
|
218
|
-
|
219
|
-
|
220
|
-
].join(
|
218
|
+
".mc-auto-outdent",
|
219
|
+
"}",
|
220
|
+
].join(" ")
|
221
221
|
end
|
222
222
|
|
223
|
-
extension(
|
223
|
+
extension("attached-image", /^!!([0-9]+)/) do |image_number|
|
224
224
|
image = images[image_number.to_i - 1]
|
225
225
|
next "" unless image
|
226
226
|
|
@@ -228,7 +228,7 @@ module Govspeak
|
|
228
228
|
end
|
229
229
|
|
230
230
|
# DEPRECATED: use 'AttachmentLink:attachment-id' instead
|
231
|
-
extension(
|
231
|
+
extension("embed attachment inline", /\[embed:attachments:inline:\s*(.*?)\s*\]/) do |content_id|
|
232
232
|
attachment = attachments.detect { |a| a[:content_id] == content_id }
|
233
233
|
next "" unless attachment
|
234
234
|
|
@@ -243,7 +243,7 @@ module Govspeak
|
|
243
243
|
end
|
244
244
|
|
245
245
|
# DEPRECATED: use 'Image:image-id' instead
|
246
|
-
extension(
|
246
|
+
extension("attachment image", /\[embed:attachments:image:\s*(.*?)\s*\]/) do |content_id|
|
247
247
|
attachment = attachments.detect { |a| a[:content_id] == content_id }
|
248
248
|
next "" unless attachment
|
249
249
|
|
@@ -266,29 +266,29 @@ module Govspeak
|
|
266
266
|
lines << %{<figure#{id_attr} class="image embedded">}
|
267
267
|
lines << %{<div class="img"><img src="#{encode(image.url)}" alt="#{encode(image.alt_text)}"></div>}
|
268
268
|
lines << image.figcaption_html if image.figcaption?
|
269
|
-
lines <<
|
269
|
+
lines << "</figure>"
|
270
270
|
lines.join
|
271
271
|
end
|
272
272
|
|
273
|
-
wrap_with_div(
|
274
|
-
wrap_with_div(
|
275
|
-
wrap_with_div(
|
276
|
-
wrap_with_div(
|
277
|
-
wrap_with_div(
|
278
|
-
wrap_with_div(
|
279
|
-
wrap_with_div(
|
280
|
-
wrap_with_div(
|
273
|
+
wrap_with_div("summary", "$!")
|
274
|
+
wrap_with_div("form-download", "$D")
|
275
|
+
wrap_with_div("contact", "$C")
|
276
|
+
wrap_with_div("place", "$P", Govspeak::Document)
|
277
|
+
wrap_with_div("information", "$I", Govspeak::Document)
|
278
|
+
wrap_with_div("additional-information", "$AI")
|
279
|
+
wrap_with_div("example", "$E", Govspeak::Document)
|
280
|
+
wrap_with_div("call-to-action", "$CTA", Govspeak::Document)
|
281
281
|
|
282
|
-
extension(
|
282
|
+
extension("address", surrounded_by("$A")) { |body|
|
283
283
|
%{\n<div class="address"><div class="adr org fn"><p>\n#{body.sub("\n", '').gsub("\n", '<br />')}\n</p></div></div>\n}
|
284
284
|
}
|
285
285
|
|
286
286
|
extension("legislative list", /#{NEW_PARAGRAPH_LOOKBEHIND}\$LegislativeList\s*$(.*?)\$EndLegislativeList/m) do |body|
|
287
287
|
Govspeak::KramdownOverrides.with_kramdown_ordered_lists_disabled do
|
288
288
|
Kramdown::Document.new(body.strip).to_html.tap do |doc|
|
289
|
-
doc.gsub!(
|
290
|
-
doc.gsub!(
|
291
|
-
doc.sub!(
|
289
|
+
doc.gsub!("<ul>", "<ol>")
|
290
|
+
doc.gsub!("</ul>", "</ol>")
|
291
|
+
doc.sub!("<ol>", '<ol class="legislative-list">')
|
292
292
|
end
|
293
293
|
end
|
294
294
|
end
|
@@ -301,12 +301,12 @@ module Govspeak
|
|
301
301
|
end
|
302
302
|
|
303
303
|
def self.devolved_options
|
304
|
-
{
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
304
|
+
{ "scotland" => "Scotland",
|
305
|
+
"england" => "England",
|
306
|
+
"england-wales" => "England and Wales",
|
307
|
+
"northern-ireland" => "Northern Ireland",
|
308
|
+
"wales" => "Wales",
|
309
|
+
"london" => "London" }
|
310
310
|
end
|
311
311
|
|
312
312
|
devolved_options.each do |k, v|
|
@@ -333,7 +333,7 @@ module Govspeak
|
|
333
333
|
end
|
334
334
|
end
|
335
335
|
|
336
|
-
extension(
|
336
|
+
extension("embed link", /\[embed:link:\s*(.*?)\s*\]/) do |content_id|
|
337
337
|
link = links.detect { |l| l[:content_id] == content_id }
|
338
338
|
next "" unless link
|
339
339
|
|
@@ -344,28 +344,28 @@ module Govspeak
|
|
344
344
|
end
|
345
345
|
end
|
346
346
|
|
347
|
-
extension(
|
347
|
+
extension("Contact", /\[Contact:\s*(.*?)\s*\]/) do |content_id|
|
348
348
|
contact = contacts.detect { |c| c[:content_id] == content_id }
|
349
349
|
next "" unless contact
|
350
350
|
|
351
|
-
renderer = TemplateRenderer.new(
|
351
|
+
renderer = TemplateRenderer.new("contact.html.erb", locale)
|
352
352
|
renderer.render(contact: ContactPresenter.new(contact))
|
353
353
|
end
|
354
354
|
|
355
|
-
extension(
|
355
|
+
extension("Image", /#{NEW_PARAGRAPH_LOOKBEHIND}\[Image:\s*(.*?)\s*\]/) do |image_id|
|
356
356
|
image = images.detect { |c| c.is_a?(Hash) && c[:id] == image_id }
|
357
357
|
next "" unless image
|
358
358
|
|
359
359
|
render_image(ImagePresenter.new(image))
|
360
360
|
end
|
361
361
|
|
362
|
-
extension(
|
362
|
+
extension("Attachment", /#{NEW_PARAGRAPH_LOOKBEHIND}\[Attachment:\s*(.*?)\s*\]/) do |attachment_id|
|
363
363
|
next "" if attachments.none? { |a| a[:id] == attachment_id }
|
364
364
|
|
365
365
|
%{<govspeak-embed-attachment id="#{attachment_id}"></govspeak-embed-attachment>}
|
366
366
|
end
|
367
367
|
|
368
|
-
extension(
|
368
|
+
extension("AttachmentLink", /\[AttachmentLink:\s*(.*?)\s*\]/) do |attachment_id|
|
369
369
|
next "" if attachments.none? { |a| a[:id] == attachment_id }
|
370
370
|
|
371
371
|
%{<govspeak-embed-attachment-link id="#{attachment_id}"></govspeak-embed-attachment-link>}
|
@@ -384,5 +384,5 @@ module Govspeak
|
|
384
384
|
end
|
385
385
|
|
386
386
|
I18n.load_path.unshift(
|
387
|
-
*Dir.glob(File.expand_path(
|
387
|
+
*Dir.glob(File.expand_path("locales/*.yml", Govspeak.root)),
|
388
388
|
)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "addressable/uri"
|
2
2
|
|
3
3
|
class Govspeak::HtmlSanitizer
|
4
4
|
class ImageSourceWhitelister
|
@@ -10,7 +10,7 @@ class Govspeak::HtmlSanitizer
|
|
10
10
|
return unless sanitize_context[:node_name] == "img"
|
11
11
|
|
12
12
|
node = sanitize_context[:node]
|
13
|
-
image_uri = Addressable::URI.parse(node[
|
13
|
+
image_uri = Addressable::URI.parse(node["src"])
|
14
14
|
unless image_uri.relative? || @allowed_image_hosts.include?(image_uri.host)
|
15
15
|
node.unlink # the node isn't sanitary. Remove it from the document.
|
16
16
|
end
|
@@ -25,8 +25,8 @@ class Govspeak::HtmlSanitizer
|
|
25
25
|
|
26
26
|
# Kramdown uses text-align to allow table cells to be aligned
|
27
27
|
# http://kramdown.gettalong.org/quickref.html#tables
|
28
|
-
if invalid_style_attribute?(node[
|
29
|
-
node.remove_attribute(
|
28
|
+
if invalid_style_attribute?(node["style"])
|
29
|
+
node.remove_attribute("style")
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
@@ -48,25 +48,20 @@ class Govspeak::HtmlSanitizer
|
|
48
48
|
Sanitize.clean(@dirty_html, Sanitize::Config.merge(sanitize_config, transformers: transformers))
|
49
49
|
end
|
50
50
|
|
51
|
-
def button_sanitize_config
|
52
|
-
%w[
|
53
|
-
data-module
|
54
|
-
data-tracking-code
|
55
|
-
data-tracking-name
|
56
|
-
]
|
57
|
-
end
|
58
|
-
|
59
51
|
def sanitize_config
|
60
52
|
Sanitize::Config.merge(
|
61
53
|
Sanitize::Config::RELAXED,
|
62
|
-
elements: Sanitize::Config::RELAXED[:elements] + %w[govspeak-embed-attachment govspeak-embed-attachment-link],
|
54
|
+
elements: Sanitize::Config::RELAXED[:elements] + %w[govspeak-embed-attachment govspeak-embed-attachment-link svg path],
|
63
55
|
attributes: {
|
64
56
|
:all => Sanitize::Config::RELAXED[:attributes][:all] + %w[role aria-label],
|
65
|
-
"a" => Sanitize::Config::RELAXED[:attributes]["a"] +
|
57
|
+
"a" => Sanitize::Config::RELAXED[:attributes]["a"] + [:data],
|
58
|
+
"svg" => Sanitize::Config::RELAXED[:attributes][:all] + %w[xmlns width height viewbox focusable],
|
59
|
+
"path" => Sanitize::Config::RELAXED[:attributes][:all] + %w[fill d],
|
60
|
+
"div" => [:data],
|
66
61
|
"th" => Sanitize::Config::RELAXED[:attributes]["th"] + %w[style],
|
67
62
|
"td" => Sanitize::Config::RELAXED[:attributes]["td"] + %w[style],
|
68
63
|
"govspeak-embed-attachment" => %w[content-id],
|
69
|
-
}
|
64
|
+
},
|
70
65
|
)
|
71
66
|
end
|
72
67
|
end
|