qiita-markdown 0.43.0 → 0.44.1
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/.rubocop_todo.yml +14 -200
- data/CHANGELOG.md +10 -0
- data/README.md +2 -2
- data/benchmark/heading_anchor_rendering.rb +1 -1
- data/lib/qiita/markdown/embed/figma.rb +11 -0
- data/lib/qiita/markdown/filters/checkbox.rb +1 -1
- data/lib/qiita/markdown/filters/code_block.rb +13 -13
- data/lib/qiita/markdown/filters/custom_block.rb +1 -0
- data/lib/qiita/markdown/filters/external_link.rb +2 -0
- data/lib/qiita/markdown/filters/final_sanitizer.rb +118 -117
- data/lib/qiita/markdown/filters/footnote.rb +2 -0
- data/lib/qiita/markdown/filters/group_mention.rb +2 -2
- data/lib/qiita/markdown/filters/image_link.rb +6 -6
- data/lib/qiita/markdown/filters/inline_code_color.rb +8 -8
- data/lib/qiita/markdown/filters/mention.rb +11 -9
- data/lib/qiita/markdown/filters/simplify.rb +1 -0
- data/lib/qiita/markdown/filters/syntax_highlight.rb +4 -4
- data/lib/qiita/markdown/filters/truncate.rb +1 -3
- data/lib/qiita/markdown/filters/user_input_sanitizer.rb +26 -23
- data/lib/qiita/markdown/greenmat/heading_rendering.rb +2 -2
- data/lib/qiita/markdown/greenmat/html_toc_renderer.rb +1 -1
- data/lib/qiita/markdown/transformers/filter_attributes.rb +1 -0
- data/lib/qiita/markdown/transformers/filter_iframe.rb +2 -2
- data/lib/qiita/markdown/transformers/filter_script.rb +1 -1
- data/lib/qiita/markdown/transformers/strip_invalid_node.rb +1 -3
- data/lib/qiita/markdown/version.rb +1 -1
- data/lib/qiita/markdown.rb +1 -0
- data/qiita-markdown.gemspec +6 -6
- data/spec/qiita/markdown/filters/checkbox_spec.rb +42 -0
- data/spec/qiita/markdown/processor_spec.rb +54 -16
- data/spec/qiita/markdown/summary_processor_spec.rb +2 -2
- metadata +96 -99
@@ -12,41 +12,41 @@ module Qiita
|
|
12
12
|
class FinalSanitizer < HTML::Pipeline::Filter
|
13
13
|
RULE = {
|
14
14
|
attributes: {
|
15
|
-
"a" => [
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
"a" => %w[
|
16
|
+
data-hovercard-target-name
|
17
|
+
data-hovercard-target-type
|
18
|
+
href
|
19
|
+
rel
|
20
20
|
],
|
21
21
|
"blockquote" => Embed::Tweet::ATTRIBUTES,
|
22
|
-
"iframe" => [
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
22
|
+
"iframe" => %w[
|
23
|
+
allowfullscreen
|
24
|
+
frameborder
|
25
|
+
height
|
26
|
+
marginheight
|
27
|
+
marginwidth
|
28
|
+
scrolling
|
29
|
+
src
|
30
|
+
style
|
31
|
+
width
|
32
32
|
],
|
33
33
|
"img" => [
|
34
34
|
"src",
|
35
35
|
],
|
36
|
-
"input" => [
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
"input" => %w[
|
37
|
+
checked
|
38
|
+
disabled
|
39
|
+
type
|
40
40
|
],
|
41
|
-
"div" => [
|
42
|
-
|
43
|
-
|
41
|
+
"div" => %w[
|
42
|
+
itemscope
|
43
|
+
itemtype
|
44
44
|
],
|
45
45
|
"p" => Embed::CodePen::ATTRIBUTES,
|
46
|
-
"script" => [
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
"script" => %w[
|
47
|
+
async
|
48
|
+
src
|
49
|
+
type
|
50
50
|
].concat(
|
51
51
|
Embed::SpeekerDeck::ATTRIBUTES,
|
52
52
|
Embed::Docswell::ATTRIBUTES,
|
@@ -60,103 +60,104 @@ module Qiita
|
|
60
60
|
"th" => [
|
61
61
|
"style",
|
62
62
|
],
|
63
|
-
"video" => [
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
63
|
+
"video" => %w[
|
64
|
+
src
|
65
|
+
autoplay
|
66
|
+
controls
|
67
|
+
loop
|
68
|
+
muted
|
69
|
+
poster
|
70
70
|
],
|
71
|
-
all: [
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
71
|
+
all: %w[
|
72
|
+
abbr
|
73
|
+
align
|
74
|
+
alt
|
75
|
+
border
|
76
|
+
cellpadding
|
77
|
+
cellspacing
|
78
|
+
cite
|
79
|
+
class
|
80
|
+
color
|
81
|
+
cols
|
82
|
+
colspan
|
83
|
+
data-lang
|
84
|
+
datetime
|
85
|
+
height
|
86
|
+
hreflang
|
87
|
+
id
|
88
|
+
itemprop
|
89
|
+
lang
|
90
|
+
name
|
91
|
+
rowspan
|
92
|
+
tabindex
|
93
|
+
target
|
94
|
+
title
|
95
|
+
width
|
96
96
|
],
|
97
97
|
},
|
98
98
|
css: {
|
99
|
-
properties: [
|
100
|
-
|
101
|
-
|
99
|
+
properties: %w[
|
100
|
+
background-color
|
101
|
+
border
|
102
|
+
text-align
|
102
103
|
],
|
103
104
|
},
|
104
|
-
elements: [
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
105
|
+
elements: %w[
|
106
|
+
a
|
107
|
+
b
|
108
|
+
blockquote
|
109
|
+
br
|
110
|
+
code
|
111
|
+
dd
|
112
|
+
del
|
113
|
+
details
|
114
|
+
div
|
115
|
+
dl
|
116
|
+
dt
|
117
|
+
em
|
118
|
+
font
|
119
|
+
h1
|
120
|
+
h2
|
121
|
+
h3
|
122
|
+
h4
|
123
|
+
h5
|
124
|
+
h6
|
125
|
+
h7
|
126
|
+
h8
|
127
|
+
hr
|
128
|
+
i
|
129
|
+
img
|
130
|
+
input
|
131
|
+
ins
|
132
|
+
kbd
|
133
|
+
li
|
134
|
+
ol
|
135
|
+
p
|
136
|
+
pre
|
137
|
+
q
|
138
|
+
rp
|
139
|
+
rt
|
140
|
+
ruby
|
141
|
+
s
|
142
|
+
samp
|
143
|
+
script
|
144
|
+
iframe
|
145
|
+
span
|
146
|
+
strike
|
147
|
+
strong
|
148
|
+
sub
|
149
|
+
summary
|
150
|
+
sup
|
151
|
+
table
|
152
|
+
tbody
|
153
|
+
td
|
154
|
+
tfoot
|
155
|
+
th
|
156
|
+
thead
|
157
|
+
tr
|
158
|
+
tt
|
159
|
+
ul
|
160
|
+
var
|
160
161
|
],
|
161
162
|
protocols: {
|
162
163
|
"a" => {
|
@@ -6,6 +6,7 @@ module Qiita
|
|
6
6
|
doc.search("sup > a").each do |a|
|
7
7
|
footnote = find_footnote(a)
|
8
8
|
next unless footnote
|
9
|
+
|
9
10
|
a[:title] = footnote.text.gsub(/\A\n/, "").gsub(/ ↩\n\z/, "")
|
10
11
|
end
|
11
12
|
doc
|
@@ -16,6 +17,7 @@ module Qiita
|
|
16
17
|
def find_footnote(a)
|
17
18
|
href = a["href"]
|
18
19
|
return nil if !href || href.match(/\A#fn\d+\z/).nil?
|
20
|
+
|
19
21
|
doc.search(href).first
|
20
22
|
end
|
21
23
|
end
|
@@ -4,12 +4,12 @@ module Qiita
|
|
4
4
|
class ImageLink < HTML::Pipeline::Filter
|
5
5
|
def call
|
6
6
|
doc.search("img").each do |img|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
next if img.ancestors.any? { |ancestor| ancestor.name == "a" }
|
8
|
+
|
9
|
+
outer = Nokogiri::HTML.fragment(%(<a href="#{img['src']}" target="_blank"></a>))
|
10
|
+
inner = img.clone
|
11
|
+
outer.at("a").add_child(inner)
|
12
|
+
img.replace(outer)
|
13
13
|
end
|
14
14
|
doc
|
15
15
|
end
|
@@ -6,15 +6,15 @@ module Qiita
|
|
6
6
|
|
7
7
|
REGEXPS = Regexp.union(
|
8
8
|
/\#(?:\h{3}|\h{6})/,
|
9
|
-
/rgba?\(\s*(?:\d+(
|
10
|
-
/rgba?\(\s*(?:\d+%(
|
11
|
-
/rgba?\(\s*(?:\d
|
12
|
-
|
13
|
-
|
14
|
-
/hsla?\(\s*\d+(?:deg|rad|grad|turn)
|
9
|
+
/rgba?\(\s*(?:\d+(?:,|\s)\s*){2}\d+\s*\)/,
|
10
|
+
/rgba?\(\s*(?:\d+%(?:,|\s)\s*){2}\d+%\s*\)/,
|
11
|
+
/rgba?\(\s*(?:\d+,\s*){3}\d*\.?\d+%?\s*\)/,
|
12
|
+
%r{rgba?\(\s*(?:\d+\s*){2}\d+\s*/\s*\d?\.?\d+%?\s*\)},
|
13
|
+
%r{rgba?\(\s*(?:\d+%\s*){2}\d+%\s*/\s*\d?\.?\d+%?\s*\)},
|
14
|
+
/hsla?\(\s*\d+(?:deg|rad|grad|turn)?,\s*\d+%,\s*\d+%\s*\)/,
|
15
15
|
/hsla?\(\s*\d+(?:deg|rad|grad|turn)?\s+\d+%\s+\d+%\s*\)/,
|
16
|
-
/hsla?\(\s*\d+(?:deg|rad|grad|turn)
|
17
|
-
|
16
|
+
/hsla?\(\s*\d+(?:deg|rad|grad|turn)?,\s*(?:\d+%,\s*){2}\d?\.?\d+%?\s*\)/,
|
17
|
+
%r{hsla?\(\s*\d+(?:deg|rad|grad|turn)?\s+\d+%\s+\d+%\s*/\s*\d?\.?\d+%?\s*\)},
|
18
18
|
)
|
19
19
|
|
20
20
|
COLOR_CODE_PATTERN = /\A\s*(#{REGEXPS})\s*\z/
|
@@ -8,17 +8,17 @@ module Qiita
|
|
8
8
|
class Mention < HTML::Pipeline::MentionFilter
|
9
9
|
IGNORE_PARENTS = ::HTML::Pipeline::MentionFilter::IGNORE_PARENTS + Set["blockquote"]
|
10
10
|
|
11
|
-
MentionPattern =
|
11
|
+
MentionPattern = %r{
|
12
12
|
(?:^|\W)
|
13
|
-
@((
|
14
|
-
(
|
13
|
+
@((?>\w[\w-]{0,30}\w(?:@github)?))
|
14
|
+
(?!/)
|
15
15
|
(?=
|
16
16
|
\.+[ \t\W]|
|
17
17
|
\.+$|
|
18
18
|
[^0-9a-zA-Z_.]|
|
19
19
|
$
|
20
20
|
)
|
21
|
-
|
21
|
+
}ix
|
22
22
|
|
23
23
|
# @note Override to use another IGNORE_PARENTS
|
24
24
|
def call
|
@@ -28,8 +28,10 @@ module Qiita
|
|
28
28
|
content = node.to_html
|
29
29
|
next unless content.include?("@")
|
30
30
|
next if has_ancestor?(node, IGNORE_PARENTS)
|
31
|
+
|
31
32
|
html = mention_link_filter(content, base_url, info_url, username_pattern)
|
32
33
|
next if html == content
|
34
|
+
|
33
35
|
node.replace(html)
|
34
36
|
end
|
35
37
|
doc
|
@@ -38,22 +40,22 @@ module Qiita
|
|
38
40
|
# @note Override to use customized MentionPattern and allowed_usernames logic.
|
39
41
|
def mention_link_filter(text, _, _, _)
|
40
42
|
text.gsub(MentionPattern) do |match|
|
41
|
-
name =
|
43
|
+
name = ::Regexp.last_match(1)
|
42
44
|
case
|
43
45
|
when allowed_usernames && name == "all"
|
44
46
|
result[:mentioned_usernames] |= allowed_usernames
|
45
47
|
match.sub(
|
46
48
|
"@#{name}",
|
47
|
-
%
|
49
|
+
%(<a href="/" class="user-mention" title="#{name}">@#{name}</a>),
|
48
50
|
)
|
49
|
-
when allowed_usernames && !allowed_usernames.include?(name) || name == "all"
|
51
|
+
when (allowed_usernames && !allowed_usernames.include?(name)) || name == "all"
|
50
52
|
match
|
51
53
|
else
|
52
54
|
result[:mentioned_usernames] |= [name]
|
53
55
|
url = File.join(base_url, name)
|
54
56
|
match.sub(
|
55
57
|
"@#{name}",
|
56
|
-
%
|
58
|
+
%(<a href="#{url}" class="user-mention js-hovercard" title="#{name}" data-hovercard-target-type="user" data-hovercard-target-name="#{name}">@#{name}</a>),
|
57
59
|
)
|
58
60
|
end
|
59
61
|
end
|
@@ -66,7 +68,7 @@ module Qiita
|
|
66
68
|
end
|
67
69
|
|
68
70
|
def has_ancestor?(node, tags)
|
69
|
-
super || node.parent.parent && node.parent.parent["class"] == "code-lang"
|
71
|
+
super || (node.parent.parent && node.parent.parent["class"] == "code-lang")
|
70
72
|
end
|
71
73
|
end
|
72
74
|
end
|
@@ -54,7 +54,7 @@ module Qiita
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def call
|
57
|
-
outer = Nokogiri::HTML.fragment(%
|
57
|
+
outer = Nokogiri::HTML.fragment(%(<div class="code-frame" data-lang="#{language}">))
|
58
58
|
frame = outer.at("div")
|
59
59
|
frame.add_child(filename_node) if filename
|
60
60
|
frame.add_child(highlighted_node)
|
@@ -72,7 +72,7 @@ module Qiita
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def filename_node
|
75
|
-
%
|
75
|
+
%(<div class="code-lang"><span class="bold">#{filename}</span></div>)
|
76
76
|
end
|
77
77
|
|
78
78
|
def has_inline_php?
|
@@ -87,7 +87,7 @@ module Qiita
|
|
87
87
|
if specific_language && Rouge::Lexer.find(specific_language)
|
88
88
|
begin
|
89
89
|
highlight(specific_language).presence or raise
|
90
|
-
rescue
|
90
|
+
rescue StandardError
|
91
91
|
highlight(@default_language)
|
92
92
|
end
|
93
93
|
else
|
@@ -100,7 +100,7 @@ module Qiita
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def language_node
|
103
|
-
Nokogiri::HTML.fragment(%
|
103
|
+
Nokogiri::HTML.fragment(%(<div class="code-frame" data-lang="#{language}"></div>))
|
104
104
|
end
|
105
105
|
|
106
106
|
def specific_language
|
@@ -51,9 +51,7 @@ module Qiita
|
|
51
51
|
node.content.each_char.with_index do |char, index|
|
52
52
|
current_char_is_blank = char.strip.empty?
|
53
53
|
|
54
|
-
if !@previous_char_was_blank || !current_char_is_blank
|
55
|
-
@current_length += 1
|
56
|
-
end
|
54
|
+
@current_length += 1 if !@previous_char_was_blank || !current_char_is_blank
|
57
55
|
|
58
56
|
@previous_char_was_blank = current_char_is_blank
|
59
57
|
|
@@ -10,24 +10,24 @@ module Qiita
|
|
10
10
|
summary sup table tbody td tfoot th thead tr ul var
|
11
11
|
],
|
12
12
|
attributes: {
|
13
|
-
"a"
|
13
|
+
"a" => %w[class href rel title],
|
14
14
|
"blockquote" => %w[cite] + Embed::Tweet::ATTRIBUTES,
|
15
|
-
"code"
|
16
|
-
"div"
|
17
|
-
"font"
|
18
|
-
"h1"
|
19
|
-
"h2"
|
20
|
-
"h3"
|
21
|
-
"h4"
|
22
|
-
"h5"
|
23
|
-
"h6"
|
24
|
-
"img"
|
25
|
-
"ins"
|
26
|
-
"li"
|
27
|
-
"p"
|
28
|
-
"q"
|
29
|
-
"script"
|
30
|
-
"iframe"
|
15
|
+
"code" => %w[data-metadata],
|
16
|
+
"div" => %w[class data-type data-metadata],
|
17
|
+
"font" => %w[color],
|
18
|
+
"h1" => %w[id],
|
19
|
+
"h2" => %w[id],
|
20
|
+
"h3" => %w[id],
|
21
|
+
"h4" => %w[id],
|
22
|
+
"h5" => %w[id],
|
23
|
+
"h6" => %w[id],
|
24
|
+
"img" => %w[alt height src title width],
|
25
|
+
"ins" => %w[cite datetime],
|
26
|
+
"li" => %w[id],
|
27
|
+
"p" => Embed::CodePen::ATTRIBUTES,
|
28
|
+
"q" => %w[cite],
|
29
|
+
"script" => %w[async src id].concat(Embed::SpeekerDeck::ATTRIBUTES, Embed::Docswell::ATTRIBUTES),
|
30
|
+
"iframe" => %w[
|
31
31
|
allowfullscreen
|
32
32
|
frameborder
|
33
33
|
height
|
@@ -38,17 +38,20 @@ module Qiita
|
|
38
38
|
style
|
39
39
|
width
|
40
40
|
],
|
41
|
-
"sup"
|
42
|
-
"td"
|
43
|
-
"th"
|
41
|
+
"sup" => %w[id],
|
42
|
+
"td" => %w[colspan rowspan style],
|
43
|
+
"th" => %w[colspan rowspan style],
|
44
44
|
},
|
45
45
|
protocols: {
|
46
|
-
"a"
|
46
|
+
"a" => { "href" => ["http", "https", "mailto", :relative] },
|
47
47
|
"blockquote" => { "cite" => ["http", "https", :relative] },
|
48
|
-
"q"
|
48
|
+
"q" => { "cite" => ["http", "https", :relative] },
|
49
49
|
},
|
50
50
|
css: {
|
51
|
-
properties: %w[
|
51
|
+
properties: %w[
|
52
|
+
text-align
|
53
|
+
border
|
54
|
+
],
|
52
55
|
},
|
53
56
|
transformers: [
|
54
57
|
Transformers::FilterAttributes,
|
@@ -2,14 +2,14 @@ module Qiita
|
|
2
2
|
module Markdown
|
3
3
|
module Transformers
|
4
4
|
class FilterIframe
|
5
|
-
URL_WHITE_LIST = [
|
6
|
-
].flatten.freeze
|
5
|
+
URL_WHITE_LIST = [].flatten.freeze
|
7
6
|
|
8
7
|
HOST_WHITE_LIST = [
|
9
8
|
Embed::Youtube::SCRIPT_HOSTS,
|
10
9
|
Embed::SlideShare::SCRIPT_HOST,
|
11
10
|
Embed::GoogleSlide::SCRIPT_HOST,
|
12
11
|
Embed::Docswell::SCRIPT_HOSTS,
|
12
|
+
Embed::Figma::SCRIPT_HOST,
|
13
13
|
].flatten.freeze
|
14
14
|
|
15
15
|
def self.call(**args)
|
@@ -45,7 +45,7 @@ module Qiita
|
|
45
45
|
def host_of(url)
|
46
46
|
if url
|
47
47
|
scheme = URI.parse(url).scheme
|
48
|
-
Addressable::URI.parse(url).host if [
|
48
|
+
Addressable::URI.parse(url).host if %w[http https].include? scheme
|
49
49
|
end
|
50
50
|
rescue Addressable::URI::InvalidURIError, URI::InvalidURIError
|
51
51
|
nil
|
data/lib/qiita/markdown.rb
CHANGED
@@ -15,6 +15,7 @@ require "qiita/markdown/embed/slide_share"
|
|
15
15
|
require "qiita/markdown/embed/google_slide"
|
16
16
|
require "qiita/markdown/embed/speeker_deck"
|
17
17
|
require "qiita/markdown/embed/docswell"
|
18
|
+
require "qiita/markdown/embed/figma"
|
18
19
|
require "qiita/markdown/transformers/filter_attributes"
|
19
20
|
require "qiita/markdown/transformers/filter_script"
|
20
21
|
require "qiita/markdown/transformers/filter_iframe"
|