metanorma-ietf 2.3.5 → 2.4.2
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/lib/asciidoctor/ietf/cleanup.rb +99 -0
- data/lib/asciidoctor/ietf/converter.rb +23 -89
- data/lib/asciidoctor/ietf/front.rb +1 -1
- data/lib/asciidoctor/ietf/ietf.rng +5 -2
- data/lib/asciidoctor/ietf/isodoc.rng +93 -18
- data/lib/asciidoctor/ietf/macros.rb +17 -0
- data/lib/asciidoctor/ietf/reqt.rng +15 -4
- data/lib/asciidoctor/ietf/validate.rb +3 -54
- data/lib/isodoc/ietf/blocks.rb +181 -174
- data/lib/isodoc/ietf/cleanup.rb +19 -1
- data/lib/isodoc/ietf/inline.rb +127 -124
- data/lib/isodoc/ietf/references.rb +152 -128
- data/lib/isodoc/ietf/section.rb +1 -1
- data/lib/isodoc/ietf/validation.rb +158 -157
- data/lib/metanorma/ietf/version.rb +1 -1
- data/metanorma-ietf.gemspec +1 -1
- metadata +12 -10
data/lib/isodoc/ietf/inline.rb
CHANGED
@@ -1,167 +1,170 @@
|
|
1
1
|
require "mathml2asciimath"
|
2
2
|
|
3
|
-
module IsoDoc
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
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
|
-
|
36
|
-
|
37
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
42
|
+
def strike_parse(node, out)
|
43
|
+
node.children.each { |n| parse(n, out) }
|
44
|
+
end
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
46
|
+
def smallcap_parse(node, out)
|
47
|
+
node.children.each { |n| parse(n, out) }
|
48
|
+
end
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
|
50
|
+
def keyword_parse(node, out)
|
51
|
+
node.children.each { |n| parse(n, out) }
|
52
|
+
end
|
52
53
|
|
53
|
-
|
54
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
69
|
-
|
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
|
-
|
72
|
-
end
|
69
|
+
def page_break(_out); end
|
73
70
|
|
74
|
-
|
75
|
-
|
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
|
-
|
81
|
-
end
|
77
|
+
def hr_parse(node, out); end
|
82
78
|
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
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
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
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
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
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
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
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
|
-
|
164
|
-
|
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
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
isoxml
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
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
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
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
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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
|
-
|
158
|
-
|
180
|
+
def ietf?(bib)
|
181
|
+
return false if !@xinclude
|
159
182
|
|
160
|
-
|
161
|
-
|
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
|
data/lib/isodoc/ietf/section.rb
CHANGED
@@ -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/
|
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",
|