metanorma-ietf 2.3.5 → 2.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,167 +1,170 @@
1
1
  require "mathml2asciimath"
2
2
 
3
- module IsoDoc::Ietf
4
- class RfcConvert < ::IsoDoc::Convert
5
- def em_parse(node, out)
6
- out.em do |e|
7
- node.children.each { |n| parse(n, e) }
3
+ module IsoDoc
4
+ module Ietf
5
+ class RfcConvert < ::IsoDoc::Convert
6
+ def em_parse(node, out)
7
+ out.em do |e|
8
+ node.children.each { |n| parse(n, e) }
9
+ end
8
10
  end
9
- end
10
11
 
11
- def sup_parse(node, out)
12
- out.sup do |e|
13
- node.children.each { |n| parse(n, e) }
12
+ def sup_parse(node, out)
13
+ out.sup do |e|
14
+ node.children.each { |n| parse(n, e) }
15
+ end
14
16
  end
15
- end
16
17
 
17
- def sub_parse(node, out)
18
- out.sub do |e|
19
- node.children.each { |n| parse(n, e) }
18
+ def sub_parse(node, out)
19
+ out.sub do |e|
20
+ node.children.each { |n| parse(n, e) }
21
+ end
20
22
  end
21
- end
22
23
 
23
- def tt_parse(node, out)
24
- out.tt do |e|
25
- node.children.each { |n| parse(n, e) }
24
+ def tt_parse(node, out)
25
+ out.tt do |e|
26
+ node.children.each { |n| parse(n, e) }
27
+ end
26
28
  end
27
- end
28
29
 
29
- def strong_parse(node, out)
30
- out.strong do |e|
31
- node.children.each { |n| parse(n, e) }
30
+ def strong_parse(node, out)
31
+ out.strong do |e|
32
+ node.children.each { |n| parse(n, e) }
33
+ end
32
34
  end
33
- end
34
35
 
35
- def bcp14_parse(node, out)
36
- out.bcp14 do |e|
37
- node.children.each { |n| parse(n, e) }
36
+ def bcp14_parse(node, out)
37
+ out.bcp14 do |e|
38
+ node.children.each { |n| parse(n, e) }
39
+ end
38
40
  end
39
- end
40
41
 
41
- def strike_parse(node, out)
42
- node.children.each { |n| parse(n, out) }
43
- end
42
+ def strike_parse(node, out)
43
+ node.children.each { |n| parse(n, out) }
44
+ end
44
45
 
45
- def smallcap_parse(node, out)
46
- node.children.each { |n| parse(n, out) }
47
- end
46
+ def smallcap_parse(node, out)
47
+ node.children.each { |n| parse(n, out) }
48
+ end
48
49
 
49
- def keyword_parse(node, out)
50
- node.children.each { |n| parse(n, out) }
51
- end
50
+ def keyword_parse(node, out)
51
+ node.children.each { |n| parse(n, out) }
52
+ end
52
53
 
53
- def text_parse(node, out)
54
- return if node.nil? || node.text.nil?
55
- text = node.to_s
56
- out << text
57
- end
54
+ def text_parse(node, out)
55
+ return if node.nil? || node.text.nil?
58
56
 
59
- def stem_parse(node, out)
60
- stem = case node["type"]
61
- when "MathML" then MathML2AsciiMath.m2a(node.children.to_xml)
62
- else
63
- HTMLEntities.new.encode(node.text)
64
- end
65
- out << "#{@openmathdelim} #{stem} #{@closemathdelim}"
66
- end
57
+ text = node.to_s
58
+ out << text
59
+ end
67
60
 
68
- def page_break(_out)
69
- end
61
+ def stem_parse(node, out)
62
+ stem = case node["type"]
63
+ when "MathML" then MathML2AsciiMath.m2a(node.children.to_xml)
64
+ else HTMLEntities.new.encode(node.text)
65
+ end
66
+ out << "#{@openmathdelim} #{stem} #{@closemathdelim}"
67
+ end
70
68
 
71
- def pagebreak_parse(_node, _out)
72
- end
69
+ def page_break(_out); end
73
70
 
74
- def br_parse(node, out)
75
- if @sourcecode
71
+ def pagebreak_parse(_node, _out); end
72
+
73
+ def br_parse(_node, out)
76
74
  out.br
77
75
  end
78
- end
79
76
 
80
- def hr_parse(node, out)
81
- end
77
+ def hr_parse(node, out); end
82
78
 
83
- def link_parse(node, out)
84
- out.eref **attr_code(target: node["target"]) do |l|
85
- node.children.each { |n| parse(n, l) }
79
+ def link_parse(node, out)
80
+ out.eref **attr_code(target: node["target"]) do |l|
81
+ node.children.each { |n| parse(n, l) }
82
+ end
86
83
  end
87
- end
88
84
 
89
- def image_parse(node, out, caption)
90
- attrs = { src: node["src"], title: node["title"],
91
- align: node["align"], name: node["filename"],
92
- anchor: node["id"], type: "svg",
93
- alt: node["alt"] }
94
- out.artwork **attr_code(attrs)
95
- image_title_parse(out, caption)
96
- end
85
+ def image_parse(node, out, caption)
86
+ attrs = { src: node["src"], title: node["title"],
87
+ align: node["align"], name: node["filename"],
88
+ anchor: node["id"], type: "svg",
89
+ alt: node["alt"] }
90
+ out.artwork **attr_code(attrs)
91
+ image_title_parse(out, caption)
92
+ end
97
93
 
98
- def image_title_parse(out, caption)
99
- unless caption.nil?
100
- out.t **{ align: "center", keepWithPrevious: "true" } do |p|
101
- p << caption.to_s
94
+ def image_title_parse(out, caption)
95
+ unless caption.nil?
96
+ out.t **{ align: "center", keepWithPrevious: "true" } do |p|
97
+ p << caption.to_s
98
+ end
102
99
  end
103
100
  end
104
- end
105
101
 
106
- def xref_parse(node, out)
107
- out.xref **attr_code(target: node["target"], format: node["format"],
108
- relative: node["relative"]) do |l|
109
- l << get_linkend(node)
110
- end
111
- end
102
+ def xref_parse(node, out)
103
+ out.xref **attr_code(target: node["target"], format: node["format"],
104
+ relative: node["relative"]) do |l|
105
+ l << get_linkend(node)
106
+ end
107
+ end
112
108
 
113
- def get_linkend(node)
114
- contents = node.children.select { |c| !%w{locality localityStack}.include? c.name }.
115
- select { |c| !c.text? || /\S/.match(c) }
116
- !contents.empty? and
117
- return Nokogiri::XML::NodeSet.new(node.document, contents).to_xml
118
- ""
119
- end
109
+ def get_linkend(node)
110
+ contents = node.children.reject do |c|
111
+ %w{locality localityStack}.include? c.name
112
+ end
113
+ .select { |c| !c.text? || /\S/.match(c) }
114
+ !contents.empty? and
115
+ return Nokogiri::XML::NodeSet.new(node.document, contents).to_xml
116
+ ""
117
+ end
120
118
 
121
- def eref_parse(node, out)
122
- linkend = node.children.reject { |c| %w{locality localityStack}.include? c.name }
123
- relative = node["relative"] ||
124
- node.at(ns(".//locality[@type = 'anchor']/referenceFrom"))&.text || ""
125
- section = eref_clause(node.xpath(ns("./locality | ./localityStack")), nil) || ""
126
- section = "" if relative.empty?
127
- out.relref **attr_code(target: node["bibitemid"], section: section,
128
- relative: relative,
129
- displayFormat: node["displayFormat"]) do |l|
130
- linkend.each { |n| parse(n, l) }
131
- end
132
- end
119
+ def eref_parse(node, out)
120
+ linkend = node.children.reject do |c|
121
+ %w{locality localityStack}.include? c.name
122
+ end
123
+ relative = node["relative"] ||
124
+ node.at(ns(".//locality[@type = 'anchor']/referenceFrom"))&.text || ""
125
+ section = eref_clause(node.xpath(ns("./locality | ./localityStack")),
126
+ nil) || ""
127
+ #section = "" unless relative.empty?
128
+ out.relref **attr_code(target: node["bibitemid"], section: section,
129
+ relative: relative,
130
+ displayFormat: node["displayFormat"]) do |l|
131
+ linkend.each { |n| parse(n, l) }
132
+ end
133
+ end
133
134
 
134
- def eref_clause(refs, target)
135
- ret = []
136
- ret1 = ""
137
- refs.each do |l|
138
- if l.name == "localityStack"
139
- ret << ret1
140
- ret1 = ""
141
- ret << eref_clause1(l.elements, target)
142
- else
143
- ret1 += eref_clause1([l], target)
135
+ def eref_clause(refs, target)
136
+ ret = []
137
+ ret1 = ""
138
+ refs.each do |l|
139
+ if l.name == "localityStack"
140
+ ret << ret1
141
+ ret1 = ""
142
+ ret << eref_clause1(l.elements, target)
143
+ else ret1 += eref_clause1([l], target)
144
+ end
144
145
  end
146
+ ret << ret1
147
+ ret.reject { |c| c.nil? || c.empty? }.join("; ")
145
148
  end
146
- ret << ret1
147
- ret.reject { |c| c.nil? || c.empty? }.join("; ")
148
- end
149
149
 
150
- def eref_clause1(refs, target)
151
- refs.each do |l|
152
- next unless %w(clause section).include? l["type"]
153
- return l&.at(ns("./referenceFrom"))&.text
150
+ def eref_clause1(refs, _target)
151
+ refs.each do |l|
152
+ next unless %w(clause section).include? l["type"]
153
+
154
+ return l&.at(ns("./referenceFrom"))&.text
155
+ end
156
+ ""
154
157
  end
155
- return ""
156
- end
157
158
 
158
- def index_parse(node, out)
159
- out.iref nil, **attr_code(item: node.at(ns("./primary")).text, primary: node["primary"],
160
- subitem: node&.at(ns("./secondary"))&.text)
161
- end
159
+ def index_parse(node, out)
160
+ out.iref nil, **attr_code(item: node.at(ns("./primary")).text,
161
+ primary: node["primary"],
162
+ subitem: node&.at(ns("./secondary"))&.text)
163
+ end
162
164
 
163
- def bookmark_parse(node, out)
164
- out.bookmark nil, **attr_code(anchor: node["id"])
165
+ def bookmark_parse(node, out)
166
+ out.bookmark nil, **attr_code(anchor: node["id"])
167
+ end
165
168
  end
166
169
  end
167
170
  end
@@ -1,164 +1,188 @@
1
- module IsoDoc::Ietf
2
- class RfcConvert < ::IsoDoc::Convert
3
- # TODO displayreference will be implemented as combination of autofetch and user-provided citations
4
-
5
- def bibliography(isoxml, out)
6
- isoxml.xpath(ns("//references/bibitem/docidentifier")).each do |i|
7
- i.children = docid_prefix(i["type"], i.text)
8
- end
9
- isoxml.xpath(ns("//bibliography/references | "\
10
- "//bibliography/clause[.//references] | "\
11
- "//annex/clause[.//references] | "\
12
- "//annex/references | "\
13
- "//sections/clause[.//references]")).each do |f|
14
- bibliography1(f, out)
1
+ module IsoDoc
2
+ module Ietf
3
+ class RfcConvert < ::IsoDoc::Convert
4
+ # TODO displayreference will be implemented as combination of autofetch and user-provided citations
5
+
6
+ def bibliography(isoxml, out)
7
+ isoxml.xpath(ns("//references/bibitem/docidentifier")).each do |i|
8
+ i.children = docid_prefix(i["type"], i.text)
9
+ end
10
+ isoxml.xpath(ns("//bibliography/references | "\
11
+ "//bibliography/clause[.//references] | "\
12
+ "//annex/clause[.//references] | "\
13
+ "//annex/references | "\
14
+ "//sections/clause[.//references]")).each do |f|
15
+ bibliography1(f, out)
16
+ end
15
17
  end
16
- end
17
18
 
18
- def bibliography1(f, out)
19
- out.references **attr_code(anchor: f["id"]) do |div|
20
- title = f.at(ns("./title")) and div.name do |name|
21
- title.children.each { |n| parse(n, name) }
19
+ def bibliography1(node, out)
20
+ out.references **attr_code(anchor: node["id"]) do |div|
21
+ title = node.at(ns("./title")) and div.name do |name|
22
+ title.children.each { |n| parse(n, name) }
23
+ end
24
+ node.elements.select do |e|
25
+ %w(references clause).include? e.name
26
+ end.each { |e| bibliography1(e, out) }
27
+ node.elements.reject do |e|
28
+ %w(references title bibitem note).include? e.name
29
+ end.each { |e| parse(e, div) }
30
+ biblio_list(node, div, true)
22
31
  end
23
- f.elements.select do |e|
24
- %w(references clause).include? e.name
25
- end.each { |e| bibliography1(e, out) }
26
- f.elements.reject do |e|
27
- %w(references title bibitem note).include? e.name
28
- end.each { |e| parse(e, div) }
29
- biblio_list(f, div, true)
30
32
  end
31
- end
32
33
 
33
- def biblio_list(f, div, biblio)
34
- i = 0
35
- f.xpath(ns("./bibitem | ./note")).each do |b|
36
- next if implicit_reference(b)
34
+ def biblio_list(node, div, biblio)
35
+ i = 0
36
+ node.xpath(ns("./bibitem | ./note")).each do |b|
37
+ next if implicit_reference(b)
37
38
 
38
- i += 1 if b.name == "bibitem"
39
- if b.name == "note" then note_parse(b, div)
40
- elsif is_ietf(b) then ietf_bibitem_entry(div, b, i)
41
- else
42
- nonstd_bibitem(div, b, i, biblio)
39
+ i += 1 if b.name == "bibitem"
40
+ if b.name == "note" then note_parse(b, div)
41
+ elsif ietf?(b) then ietf_bibitem_entry(div, b, i)
42
+ else
43
+ nonstd_bibitem(div, b, i, biblio)
44
+ end
43
45
  end
44
46
  end
45
- end
46
47
 
47
- def nonstd_bibitem(list, b, _ordinal, _bibliography)
48
- uris = b.xpath(ns("./uri"))
49
- target = nil
50
- uris&.each do |u|
51
- target = u.text if u["type"] == "src"
52
- end
53
- list.reference **attr_code(target: target,
54
- anchor: b["id"]) do |r|
55
- r.front do |f|
56
- relaton_to_title(b, f)
57
- relaton_to_author(b, f)
58
- relaton_to_date(b, f)
59
- relaton_to_keyword(b, f)
60
- relaton_to_abstract(b, f)
61
- end
62
- uris&.each do |u|
63
- r.format nil, **attr_code(target: u.text, type: u["type"])
48
+ def nonstd_bibitem(list, bib, _ordinal, _bibliography)
49
+ uris = bib.xpath(ns("./uri"))
50
+ target = nil
51
+ uris&.each { |u| target = u.text if u["type"] == "src" }
52
+ list.reference **attr_code(target: target,
53
+ anchor: bib["id"]) do |r|
54
+ nonstd_bibitem_front(r, bib)
55
+ uris&.each do |u|
56
+ r.format nil, **attr_code(target: u.text, type: u["type"])
57
+ end
58
+ docidentifier_render(bib, r)
64
59
  end
65
- docidentifiers = b.xpath(ns("./docidentifier"))
66
- id = render_identifier(bibitem_ref_code(b))
67
- !id[1].nil? and id[1] != "(NO ID)" and
68
- r.refcontent id[1]
60
+ end
61
+
62
+ def docidentifier_render(bib, out)
63
+ docidentifiers = bib.xpath(ns("./docidentifier"))
64
+ id = render_identifier(bibitem_ref_code(bib))
65
+ !id[1].nil? && id[1] != "(NO ID)" and out.refcontent id[1]
69
66
  docidentifiers&.each do |u|
70
- if %w(DOI IETF).include? u["type"]
71
- r.seriesInfo nil, **attr_code(value: u.text.sub(/^DOI /, ""),
72
- name: u["type"])
73
- end
67
+ u["type"] == "DOI" and
68
+ out.seriesInfo nil, **attr_code(value: u.text.sub(/^DOI /, ""),
69
+ name: "DOI")
70
+ u["type"] == "IETF" and docidentifier_ietf(u, out)
74
71
  end
75
72
  end
76
- end
77
73
 
78
- def relaton_to_title(b, f)
79
- title = b&.at(ns("./title")) || b&.at(ns("./formattedref")) or return
80
- f.title do |t|
81
- title.children.each { |n| parse(n, t) }
74
+ def docidentifier_ietf(ident, out)
75
+ if /^RFC /.match?(ident.text)
76
+ out.seriesInfo nil, **attr_code(value: ident.text.sub(/^RFC 0*/, ""),
77
+ name: "RFC")
78
+ elsif /^I-D\./.match?(ident.text)
79
+ out.seriesInfo nil, **attr_code(value: ident.text.sub(/^I-D\./, ""),
80
+ name: "Internet-Draft")
81
+ end
82
82
  end
83
- end
84
83
 
85
- def relaton_to_author(b, f)
86
- auths = b.xpath(ns("./contributor[xmlns:role/@type = 'author' or "\
87
- "xmlns:role/@type = 'editor']"))
88
- auths.empty? and auths = b.xpath(ns("./contributor[xmlns:role/@type = "\
89
- "'publisher']"))
90
- auths.each do |a|
91
- role = a.at(ns("./role[@type = 'editor']")) ? "editor" : nil
92
- p = a&.at(ns("./person/name")) and
93
- relaton_person_to_author(p, role, f) or
94
- relaton_org_to_author(a&.at(ns("./organization")), role, f)
84
+ def nonstd_bibitem_front(ref, bib)
85
+ ref.front do |f|
86
+ relaton_to_title(bib, f)
87
+ relaton_to_author(bib, f)
88
+ relaton_to_date(bib, f)
89
+ relaton_to_keyword(bib, f)
90
+ relaton_to_abstract(bib, f)
91
+ end
95
92
  end
96
- end
97
93
 
98
- def relaton_person_to_author(p, role, f)
99
- fullname = p&.at(ns("./completename"))&.text
100
- surname = p&.at(ns("./surname"))&.text
101
- initials = p&.xpath(ns("./initial"))&.map { |i| i.text }&.join(" ") ||
102
- p&.xpath(ns("./forename"))&.map { |i| i.text[0] }&.join(" ")
103
- initials = nil if initials.empty?
104
- f.author nil,
105
- **attr_code(fullname: fullname, asciiFullname: fullname&.transliterate,
106
- role: role, surname: surname, initials: initials,
107
- asciiSurname: fullname ? surname&.transliterate : nil,
108
- asciiInitials: fullname ? initials&.transliterate : nil)
109
- end
94
+ def relaton_to_title(bib, node)
95
+ title = bib&.at(ns("./title")) || bib&.at(ns("./formattedref")) or
96
+ return
97
+ node.title do |t|
98
+ title.children.each { |n| parse(n, t) }
99
+ end
100
+ end
110
101
 
111
- def relaton_org_to_author(o, _role, f)
112
- name = o&.at(ns("./name"))&.text
113
- abbrev = o&.at(ns("./abbreviation"))&.text
114
- f.author do |_a|
115
- f.organization name, **attr_code(ascii: name&.transliterate,
116
- abbrev: abbrev)
102
+ def relaton_to_author(bib, node)
103
+ auths = bib.xpath(ns("./contributor[xmlns:role/@type = 'author' or "\
104
+ "xmlns:role/@type = 'editor']"))
105
+ auths.empty? and
106
+ auths = bib.xpath(ns("./contributor[xmlns:role/@type = "\
107
+ "'publisher']"))
108
+ auths.each do |a|
109
+ role = a.at(ns("./role[@type = 'editor']")) ? "editor" : nil
110
+ p = a&.at(ns("./person/name")) and
111
+ relaton_person_to_author(p, role, node) or
112
+ relaton_org_to_author(a&.at(ns("./organization")), role, node)
113
+ end
117
114
  end
118
- end
119
115
 
120
- def relaton_to_date(b, f)
121
- date = b.at(ns("./date[@type = 'published']")) ||
122
- b.at(ns("./date[@type = 'issued']")) ||
123
- b.at(ns("./date[@type = 'circulated']"))
124
- return unless date
116
+ def relaton_person_to_author(pers, role, node)
117
+ full = pers&.at(ns("./completename"))&.text
118
+ surname = pers&.at(ns("./surname"))&.text
119
+ initials = pers&.xpath(ns("./initial"))&.map do |i|
120
+ i.text
121
+ end&.join(" ") ||
122
+ pers&.xpath(ns("./forename"))&.map { |i| i.text[0] }&.join(" ")
123
+ initials = nil if initials.empty?
124
+ node.author nil, **attr_code(
125
+ fullname: full,
126
+ asciiFullname: full&.transliterate,
127
+ role: role, surname: surname,
128
+ initials: initials,
129
+ asciiSurname: full ? surname&.transliterate : nil,
130
+ asciiInitials: full ? initials&.transliterate : nil
131
+ )
132
+ end
125
133
 
126
- attr = date_attr(date&.at(ns("./on | ./from"))&.text) || return
127
- f.date **attr_code(attr)
128
- end
134
+ def relaton_org_to_author(org, _role, node)
135
+ name = org&.at(ns("./name"))&.text
136
+ abbrev = org&.at(ns("./abbreviation"))&.text
137
+ node.author do |_a|
138
+ node.organization name, **attr_code(ascii: name&.transliterate,
139
+ abbrev: abbrev)
140
+ end
141
+ end
142
+
143
+ def relaton_to_date(bib, node)
144
+ date = bib.at(ns("./date[@type = 'published']")) ||
145
+ bib.at(ns("./date[@type = 'issued']")) ||
146
+ bib.at(ns("./date[@type = 'circulated']"))
147
+ return unless date
129
148
 
130
- def relaton_to_keyword(b, f)
131
- b.xpath(ns("./keyword")).each do |k|
132
- f.keyword do |keyword|
133
- k.children.each { |n| parse(n, keyword) }
149
+ attr = date_attr(date&.at(ns("./on | ./from"))&.text) || return
150
+ node.date **attr_code(attr)
151
+ end
152
+
153
+ def relaton_to_keyword(bib, node)
154
+ bib.xpath(ns("./keyword")).each do |k|
155
+ node.keyword do |keyword|
156
+ k.children.each { |n| parse(n, keyword) }
157
+ end
134
158
  end
135
159
  end
136
- end
137
160
 
138
- def relaton_to_abstract(b, f)
139
- b.xpath(ns("./abstract")).each do |k|
140
- f.abstract do |abstract|
141
- if k.at(ns("./p"))
142
- k.children.each { |n| parse(n, abstract) }
143
- else
144
- abstract.t do |t|
145
- k.children.each { |n| parse(n, t) }
161
+ def relaton_to_abstract(bib, node)
162
+ bib.xpath(ns("./abstract")).each do |k|
163
+ node.abstract do |abstract|
164
+ if k.at(ns("./p"))
165
+ k.children.each { |n| parse(n, abstract) }
166
+ else
167
+ abstract.t do |t|
168
+ k.children.each { |n| parse(n, t) }
169
+ end
146
170
  end
147
171
  end
148
172
  end
149
173
  end
150
- end
151
174
 
152
- def ietf_bibitem_entry(div, b, _i)
153
- url = b&.at(ns("./uri[@type = 'xml']"))&.text
154
- div << "<xi:include href='#{url}'/>"
155
- end
175
+ def ietf_bibitem_entry(div, bib, _idx)
176
+ url = bib&.at(ns("./uri[@type = 'xml']"))&.text
177
+ div << "<xi:include href='#{url}'/>"
178
+ end
156
179
 
157
- def is_ietf(b)
158
- return false if !@xinclude
180
+ def ietf?(bib)
181
+ return false if !@xinclude
159
182
 
160
- url = b.at(ns("./uri[@type = 'xml']")) or return false
161
- /xml2rfc\.tools\.ietf\.org/.match(url)
183
+ url = bib.at(ns("./uri[@type = 'xml']")) or return false
184
+ /xml2rfc\.tools\.ietf\.org/.match(url)
185
+ end
162
186
  end
163
187
  end
164
188
  end
@@ -33,7 +33,7 @@ module IsoDoc::Ietf
33
33
  strict: node&.at(ns("//pi/strict"))&.text || "yes",
34
34
  compact: node&.at(ns("//pi/compact"))&.text || "yes",
35
35
  subcompact: node&.at(ns("//pi/subcompact"))&.text || "no",
36
- toc: node&.at(ns("//pi/toc-include"))&.text,
36
+ toc: node&.at(ns("//pi/tocinclude"))&.text,
37
37
  tocdepth: node&.at(ns("//pi/toc-depth"))&.text || "4",
38
38
  symrefs: node&.at(ns("//pi/sym-refs"))&.text || "yes",
39
39
  sortrefs: node&.at(ns("//pi/sort-refs"))&.text || "yes",