metanorma-standoc 1.9.0 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +3 -13
  3. data/.hound.yml +3 -1
  4. data/.rubocop.yml +4 -8
  5. data/lib/asciidoctor/standoc/base.rb +31 -35
  6. data/lib/asciidoctor/standoc/biblio.rng +1 -0
  7. data/lib/asciidoctor/standoc/blocks.rb +25 -9
  8. data/lib/asciidoctor/standoc/blocks_notes.rb +41 -24
  9. data/lib/asciidoctor/standoc/cleanup.rb +59 -84
  10. data/lib/asciidoctor/standoc/cleanup_block.rb +63 -85
  11. data/lib/asciidoctor/standoc/cleanup_boilerplate.rb +28 -15
  12. data/lib/asciidoctor/standoc/cleanup_footnotes.rb +1 -0
  13. data/lib/asciidoctor/standoc/cleanup_image.rb +71 -0
  14. data/lib/asciidoctor/standoc/cleanup_inline.rb +117 -77
  15. data/lib/asciidoctor/standoc/cleanup_maths.rb +36 -27
  16. data/lib/asciidoctor/standoc/cleanup_ref.rb +31 -15
  17. data/lib/asciidoctor/standoc/cleanup_ref_dl.rb +1 -1
  18. data/lib/asciidoctor/standoc/cleanup_reqt.rb +47 -0
  19. data/lib/asciidoctor/standoc/cleanup_section.rb +77 -135
  20. data/lib/asciidoctor/standoc/cleanup_section_names.rb +75 -0
  21. data/lib/asciidoctor/standoc/cleanup_terms.rb +19 -18
  22. data/lib/asciidoctor/standoc/converter.rb +7 -2
  23. data/lib/asciidoctor/standoc/datamodel/plantuml_renderer.rb +67 -66
  24. data/lib/asciidoctor/standoc/front.rb +35 -18
  25. data/lib/asciidoctor/standoc/front_contributor.rb +70 -45
  26. data/lib/asciidoctor/standoc/inline.rb +45 -34
  27. data/lib/asciidoctor/standoc/isodoc.rng +209 -4
  28. data/lib/asciidoctor/standoc/lists.rb +4 -2
  29. data/lib/asciidoctor/standoc/macros.rb +11 -11
  30. data/lib/asciidoctor/standoc/macros_form.rb +63 -0
  31. data/lib/asciidoctor/standoc/macros_plantuml.rb +19 -21
  32. data/lib/asciidoctor/standoc/macros_terms.rb +33 -23
  33. data/lib/asciidoctor/standoc/ref.rb +87 -112
  34. data/lib/asciidoctor/standoc/ref_date_id.rb +62 -0
  35. data/lib/asciidoctor/standoc/ref_sect.rb +20 -17
  36. data/lib/asciidoctor/standoc/section.rb +3 -1
  37. data/lib/asciidoctor/standoc/term_lookup_cleanup.rb +40 -27
  38. data/lib/asciidoctor/standoc/terms.rb +25 -18
  39. data/lib/asciidoctor/standoc/utils.rb +35 -9
  40. data/lib/asciidoctor/standoc/validate.rb +48 -33
  41. data/lib/metanorma-standoc.rb +0 -1
  42. data/lib/metanorma/standoc/version.rb +1 -1
  43. data/metanorma-standoc.gemspec +4 -4
  44. data/spec/asciidoctor/base_spec.rb +701 -508
  45. data/spec/asciidoctor/blocks_spec.rb +831 -738
  46. data/spec/asciidoctor/cleanup_sections_spec.rb +51 -14
  47. data/spec/asciidoctor/cleanup_spec.rb +889 -682
  48. data/spec/asciidoctor/inline_spec.rb +62 -14
  49. data/spec/asciidoctor/isobib_cache_spec.rb +404 -358
  50. data/spec/asciidoctor/lists_spec.rb +149 -137
  51. data/spec/asciidoctor/macros_plantuml_spec.rb +8 -8
  52. data/spec/asciidoctor/macros_spec.rb +923 -503
  53. data/spec/asciidoctor/macros_yaml2text_spec.rb +1 -1
  54. data/spec/asciidoctor/refs_dl_spec.rb +4 -4
  55. data/spec/asciidoctor/refs_spec.rb +1528 -1533
  56. data/spec/asciidoctor/section_spec.rb +405 -299
  57. data/spec/asciidoctor/table_spec.rb +6 -6
  58. data/spec/asciidoctor/validate_spec.rb +342 -304
  59. data/spec/spec_helper.rb +13 -9
  60. data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec.yml +54 -54
  61. data/spec/vcr_cassettes/isobib_get_123.yml +13 -13
  62. data/spec/vcr_cassettes/isobib_get_123_1.yml +25 -25
  63. data/spec/vcr_cassettes/isobib_get_123_1_fr.yml +37 -37
  64. data/spec/vcr_cassettes/isobib_get_123_2001.yml +12 -12
  65. data/spec/vcr_cassettes/isobib_get_124.yml +13 -13
  66. data/spec/vcr_cassettes/rfcbib_get_rfc8341.yml +14 -14
  67. data/spec/vcr_cassettes/separates_iev_citations_by_top_level_clause.yml +46 -46
  68. metadata +16 -15
  69. data/lib/liquid/custom_blocks/key_iterator.rb +0 -21
  70. data/lib/liquid/custom_blocks/with_json_nested_context.rb +0 -18
  71. data/lib/liquid/custom_blocks/with_yaml_nested_context.rb +0 -19
  72. data/lib/liquid/custom_filters/values.rb +0 -7
@@ -37,14 +37,15 @@ module Asciidoctor
37
37
  end
38
38
  end
39
39
 
40
- def insert_thead(s)
41
- thead = s.at("./thead")
40
+ def insert_thead(table)
41
+ thead = table.at("./thead")
42
42
  return thead unless thead.nil?
43
- if tname = s.at("./name")
43
+
44
+ if tname = table.at("./name")
44
45
  thead = tname.add_next_sibling("<thead/>").first
45
46
  return thead
46
47
  end
47
- s.children.first.add_previous_sibling("<thead/>").first
48
+ table.children.first.add_previous_sibling("<thead/>").first
48
49
  end
49
50
 
50
51
  def header_rows_cleanup(xmldoc)
@@ -80,21 +81,21 @@ module Asciidoctor
80
81
  end
81
82
 
82
83
  # include where definition list inside stem block
83
- def formula_cleanup(x)
84
- formula_cleanup_where1(x)
85
- formula_cleanup_where2(x)
84
+ def formula_cleanup(formula)
85
+ formula_cleanup_where1(formula)
86
+ formula_cleanup_where2(formula)
86
87
  end
87
88
 
88
- def formula_cleanup_where1(x)
89
+ def formula_cleanup_where1(formula)
89
90
  q = "//formula/following-sibling::*[1][self::dl]"
90
- x.xpath(q).each do |s|
91
+ formula.xpath(q).each do |s|
91
92
  s["key"] == "true" and s.previous_element << s.remove
92
93
  end
93
94
  end
94
95
 
95
- def formula_cleanup_where2(x)
96
+ def formula_cleanup_where2(formula)
96
97
  q = "//formula/following-sibling::*[1][self::p]"
97
- x.xpath(q).each do |s|
98
+ formula.xpath(q).each do |s|
98
99
  if s.text =~ /^\s*where[^a-z]*$/i && s&.next_element&.name == "dl"
99
100
  s.next_element["key"] = "true"
100
101
  s.previous_element << s.next_element.remove
@@ -125,9 +126,10 @@ module Asciidoctor
125
126
  # examples containing only figures become subfigures of figures
126
127
  def subfigure_cleanup(xmldoc)
127
128
  xmldoc.xpath("//example[figure]").each do |e|
128
- next unless e.elements.map { |m| m.name }.reject do |m|
129
+ next unless e.elements.map(&:name).reject do |m|
129
130
  %w(name figure).include? m
130
131
  end.empty?
132
+
131
133
  e.name = "figure"
132
134
  end
133
135
  end
@@ -142,7 +144,7 @@ module Asciidoctor
142
144
  ELEMS_ALLOW_NOTES = %w[p formula ul ol dl figure].freeze
143
145
 
144
146
  # if a note is at the end of a section, it is left alone
145
- # if a note is followed by a non-note block,
147
+ # if a note is followed by a non-note block,
146
148
  # it is moved inside its preceding block if it is not delimited
147
149
  # (so there was no way of making that block include the note)
148
150
  def note_cleanup(xmldoc)
@@ -150,6 +152,7 @@ module Asciidoctor
150
152
  xmldoc.xpath(q).each do |n|
151
153
  next if n["keep-separate"] == "true"
152
154
  next unless n.ancestors("table").empty?
155
+
153
156
  prev = n.previous_element || next
154
157
  n.parent = prev if ELEMS_ALLOW_NOTES.include? prev.name
155
158
  end
@@ -159,100 +162,75 @@ module Asciidoctor
159
162
  end
160
163
  end
161
164
 
162
- def requirement_cleanup(x)
163
- requirement_descriptions(x)
164
- requirement_inherit(x)
165
- end
166
-
167
- def requirement_inherit(x)
168
- x.xpath("//requirement | //recommendation | //permission").each do |r|
169
- ins = r.at("./classification") ||
170
- r.at("./description | ./measurementtarget | ./specification | "\
171
- "./verification | ./import | ./description | ./requirement | "\
172
- "./recommendation | ./permission")
173
- r.xpath("./*//inherit").each { |i| ins.previous = i }
165
+ def link_callouts_to_annotations(callouts, annotations)
166
+ callouts.each_with_index do |c, i|
167
+ c["target"] = "_#{UUIDTools::UUID.random_create}"
168
+ annotations[i]["id"] = c["target"]
174
169
  end
175
170
  end
176
171
 
177
- def requirement_descriptions(x)
178
- x.xpath("//requirement | //recommendation | //permission").each do |r|
179
- r.children.each do |e|
180
- unless e.element? && (reqt_subpart(e.name) ||
181
- %w(requirement recommendation permission).include?(e.name))
182
- t = Nokogiri::XML::Element.new("description", x)
183
- e.before(t)
184
- t.children = e.remove
185
- end
186
- end
187
- requirement_cleanup1(r)
172
+ def align_callouts_to_annotations(xmldoc)
173
+ xmldoc.xpath("//sourcecode").each do |x|
174
+ callouts = x.elements.select { |e| e.name == "callout" }
175
+ annotations = x.elements.select { |e| e.name == "annotation" }
176
+ callouts.size == annotations.size and
177
+ link_callouts_to_annotations(callouts, annotations)
188
178
  end
189
179
  end
190
180
 
191
- def requirement_cleanup1(r)
192
- while d = r.at("./description[following-sibling::*[1]"\
193
- "[self::description]]")
194
- n = d.next.remove
195
- d << n.children
196
- end
197
- r.xpath("./description[normalize-space(.)='']").each do |d|
198
- d.replace("\n")
181
+ def merge_annotations_into_sourcecode(xmldoc)
182
+ xmldoc.xpath("//sourcecode").each do |x|
183
+ while x&.next_element&.name == "annotation"
184
+ x.next_element.parent = x
185
+ end
199
186
  end
200
187
  end
201
188
 
202
- def svgmap_cleanup(xmldoc)
203
- svgmap_moveattrs(xmldoc)
204
- svgmap_populate(xmldoc)
205
- Metanorma::Utils::svgmap_rewrite(xmldoc, @localdir)
189
+ def callout_cleanup(xmldoc)
190
+ merge_annotations_into_sourcecode(xmldoc)
191
+ align_callouts_to_annotations(xmldoc)
206
192
  end
207
193
 
208
- def guid?(x)
209
- /^_[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i.
210
- match(x)
211
- end
194
+ def sourcecode_cleanup(xmldoc)
195
+ xmldoc.xpath("//sourcecode").each do |x|
196
+ x.traverse do |n|
197
+ next unless n.text?
198
+ next unless /#{Regexp.escape(@sourcecode_markup_start)}/
199
+ .match?(n.text)
212
200
 
213
- def svgmap_moveattrs(xmldoc)
214
- xmldoc.xpath("//svgmap").each do |s|
215
- f = s.at(".//figure") or next
216
- if t = s.at("./name") and !f.at("./name")
217
- f.children.first.previous = t.remove
201
+ n.replace(sourcecode_markup(n))
218
202
  end
219
- if s["id"] && guid?(f["id"])
220
- f["id"] = s["id"]
221
- s.delete("id")
222
- end
223
- svgmap_moveattrs1(s, f)
224
203
  end
225
204
  end
226
205
 
227
- def svgmap_moveattrs1(s, f)
228
- %w(unnumbered number subsequence keep-with-next
229
- keep-lines-together).each do |a|
230
- next if f[a] || !s[a]
231
- f[a] = s[a]
232
- s.delete(a)
233
- end
206
+ def safe_noko(text, doc)
207
+ Nokogiri::XML::Text.new(text, doc).to_xml(
208
+ encoding: "US-ASCII",
209
+ save_with: Nokogiri::XML::Node::SaveOptions::NO_DECLARATION
210
+ )
234
211
  end
235
212
 
236
- def svgmap_populate(xmldoc)
237
- xmldoc.xpath("//svgmap").each do |s|
238
- s1 = s.dup
239
- s.children.remove
240
- f = s1.at(".//figure") and s << f
241
- s1.xpath(".//li").each do |li|
242
- t = li&.at(".//eref | .//link | .//xref") or next
243
- href = t.xpath("./following-sibling::node()")
244
- href.empty? or
245
- s << %[<target href="#{svgmap_target(href)}">#{t.to_xml}</target>]
246
- end
213
+ def sourcecode_markup(node)
214
+ acc = []
215
+ node.text.split(/(#{Regexp.escape(@sourcecode_markup_start)}|
216
+ #{Regexp.escape(@sourcecode_markup_end)})/x)
217
+ .each_slice(4).map do |a|
218
+ acc << safe_noko(a[0], node.document)
219
+ next unless a.size == 4
220
+
221
+ acc << Asciidoctor.convert(
222
+ a[2], doctype: :inline, backend: (self&.backend&.to_sym || :standoc)
223
+ )
247
224
  end
225
+ acc.join
248
226
  end
249
227
 
250
- def svgmap_target(nodeset)
251
- nodeset.each do |n|
252
- next unless n.name == "link"
253
- n.children = n["target"]
228
+ def form_cleanup(xmldoc)
229
+ xmldoc.xpath("//select").each do |s|
230
+ while s&.next_element&.name == "option"
231
+ s << s.next_element
232
+ end
254
233
  end
255
- nodeset.text.sub(/^[,; ]/, "").strip
256
234
  end
257
235
  end
258
236
  end
@@ -38,17 +38,23 @@ module Asciidoctor
38
38
  end
39
39
  end
40
40
 
41
- def norm_ref_preface(f)
42
- refs = f.elements.select do |e|
43
- ["reference", "bibitem"].include? e.name
41
+ def norm_ref_preface(ref)
42
+ if ref.at("./note[@type = 'boilerplate']")
43
+ unwrap_boilerplate_clauses(ref, ".")
44
+ else
45
+ refs = ref.elements.select do |e|
46
+ %w(references bibitem).include? e.name
47
+ end
48
+ pref = refs.empty? ? @i18n.norm_empty_pref : @i18n.norm_with_refs_pref
49
+ ref.at("./title").next = "<p>#{pref}</p>"
44
50
  end
45
- f.at("./title").next =
46
- "<p>#{(refs.empty? ? @i18n.norm_empty_pref : @i18n.norm_with_refs_pref)}</p>"
47
51
  end
48
52
 
49
- TERM_CLAUSE = "//sections/terms | //sections/clause[descendant::terms]".freeze
53
+ TERM_CLAUSE = "//sections/terms | "\
54
+ "//sections/clause[descendant::terms]".freeze
50
55
 
51
- NORM_REF = "//bibliography/references[@normative = 'true']".freeze
56
+ NORM_REF = "//bibliography/references[@normative = 'true'] | "\
57
+ "//bibliography/clause[.//references[@normative = 'true']]".freeze
52
58
 
53
59
  def boilerplate_isodoc(xmldoc)
54
60
  x = xmldoc.dup
@@ -67,25 +73,32 @@ module Asciidoctor
67
73
  xmldoc.xpath("//terms/p | //terms/ul").each(&:remove)
68
74
  end
69
75
 
70
- def termdef_unwrap_boilerplate_clauses(xmldoc)
71
- xmldoc.xpath(self.class::TERM_CLAUSE).each do |f|
72
- f.xpath(".//clause[@type = 'boilerplate']").each do |c|
76
+ def unwrap_boilerplate_clauses(xmldoc, xpath)
77
+ xmldoc.xpath(xpath).each do |f|
78
+ f.xpath(".//clause[@type = 'boilerplate'] | "\
79
+ ".//note[@type = 'boilerplate']").each do |c|
73
80
  c&.at("./title")&.remove
74
81
  c.replace(c.children)
75
82
  end
76
83
  end
77
84
  end
78
85
 
79
- def boilerplate_cleanup(xmldoc)
80
- isodoc = boilerplate_isodoc(xmldoc)
81
- termdef_boilerplate_cleanup(xmldoc)
86
+ def termdef_boilerplate_insert(xmldoc, isodoc, once = false)
82
87
  xmldoc.xpath(self.class::TERM_CLAUSE).each do |f|
83
88
  next if f.at("./clause[@type = 'boilerplate']")
84
89
 
85
- term_defs_boilerplate(f.at("./title"), xmldoc.xpath(".//termdocsource"),
90
+ term_defs_boilerplate(f.at("./title"),
91
+ xmldoc.xpath(".//termdocsource"),
86
92
  f.at(".//term"), f.at(".//p"), isodoc)
93
+ break if once
87
94
  end
88
- termdef_unwrap_boilerplate_clauses(xmldoc)
95
+ end
96
+
97
+ def boilerplate_cleanup(xmldoc)
98
+ isodoc = boilerplate_isodoc(xmldoc)
99
+ termdef_boilerplate_cleanup(xmldoc)
100
+ termdef_boilerplate_insert(xmldoc, isodoc)
101
+ unwrap_boilerplate_clauses(xmldoc, self.class::TERM_CLAUSE)
89
102
  f = xmldoc.at(self.class::NORM_REF) and norm_ref_preface(f)
90
103
  initial_boilerplate(xmldoc, isodoc)
91
104
  end
@@ -20,6 +20,7 @@ module Asciidoctor
20
20
  nomatches = true
21
21
  xmldoc.xpath(q).each do |s|
22
22
  next if s.children.map { |c| c.text? && /[[:alpha:]]/.match(c.text) }.any?
23
+
23
24
  s.previous_element << s.first_element_child.remove
24
25
  s.remove
25
26
  nomatches = false
@@ -0,0 +1,71 @@
1
+ module Asciidoctor
2
+ module Standoc
3
+ module Cleanup
4
+ def svgmap_cleanup(xmldoc)
5
+ svgmap_moveattrs(xmldoc)
6
+ svgmap_populate(xmldoc)
7
+ Metanorma::Utils::svgmap_rewrite(xmldoc, @localdir)
8
+ end
9
+
10
+ def guid?(str)
11
+ /^_[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i
12
+ .match(str)
13
+ end
14
+
15
+ def svgmap_moveattrs(xmldoc)
16
+ xmldoc.xpath("//svgmap").each do |s|
17
+ f = s.at(".//figure") or next
18
+ if (t = s.at("./name")) && !f.at("./name")
19
+ f.children.first.previous = t.remove
20
+ end
21
+ if s["id"] && guid?(f["id"])
22
+ f["id"] = s["id"]
23
+ s.delete("id")
24
+ end
25
+ svgmap_moveattrs1(s, f)
26
+ end
27
+ end
28
+
29
+ def svgmap_moveattrs1(s, f)
30
+ %w(unnumbered number subsequence keep-with-next
31
+ keep-lines-together).each do |a|
32
+ next if f[a] || !s[a]
33
+
34
+ f[a] = s[a]
35
+ s.delete(a)
36
+ end
37
+ end
38
+
39
+ def svgmap_populate(xmldoc)
40
+ xmldoc.xpath("//svgmap").each do |s|
41
+ s1 = s.dup
42
+ s.children.remove
43
+ f = s1.at(".//figure") and s << f
44
+ s1.xpath(".//li").each do |li|
45
+ t = li&.at(".//eref | .//link | .//xref") or next
46
+ href = t.xpath("./following-sibling::node()")
47
+ href.empty? or
48
+ s << %[<target href="#{svgmap_target(href)}">#{t.to_xml}</target>]
49
+ end
50
+ end
51
+ end
52
+
53
+ def svgmap_target(nodeset)
54
+ nodeset.each do |n|
55
+ next unless n.name == "link"
56
+
57
+ n.children = n["target"]
58
+ end
59
+ nodeset.text.sub(/^[,; ]/, "").strip
60
+ end
61
+
62
+ def img_cleanup(xmldoc)
63
+ return xmldoc unless @datauriimage
64
+
65
+ xmldoc.xpath("//image").each do |i|
66
+ i["src"] = Metanorma::Utils::datauri(i["src"], @localdir)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -3,32 +3,54 @@ require "metanorma-utils"
3
3
  module Asciidoctor
4
4
  module Standoc
5
5
  module Cleanup
6
- def empty_text_before_first_element(x)
7
- x.children.each do |c|
8
- return false if c.text? and /\S/.match(c.text)
6
+ def empty_text_before_first_element(elem)
7
+ elem.children.each do |c|
8
+ return false if c.text? && /\S/.match(c.text)
9
9
  return true if c.element?
10
10
  end
11
11
  true
12
12
  end
13
13
 
14
- def strip_initial_space(x)
15
- if x.children[0].text?
16
- if !/\S/.match(x.children[0].text)
17
- x.children[0].remove
18
- else
19
- x.children[0].content = x.children[0].text.gsub(/^ /, "")
20
- end
14
+ def strip_initial_space(elem)
15
+ return unless elem.children[0].text?
16
+
17
+ if /\S/.match?(elem.children[0].text)
18
+ elem.children[0].content = elem.children[0].text.gsub(/^ /, "")
19
+ else
20
+ elem.children[0].remove
21
21
  end
22
22
  end
23
23
 
24
24
  def bookmark_cleanup(xmldoc)
25
+ li_bookmark_cleanup(xmldoc)
26
+ dt_bookmark_cleanup(xmldoc)
27
+ end
28
+
29
+ def bookmark_to_id(elem, bookmark)
30
+ parent = bookmark.parent
31
+ elem["id"] = bookmark.remove["id"]
32
+ strip_initial_space(parent)
33
+ end
34
+
35
+ def li_bookmark_cleanup(xmldoc)
25
36
  xmldoc.xpath("//li[descendant::bookmark]").each do |x|
26
- if x&.elements&.first&.name == "p" &&
27
- x&.elements&.first&.elements&.first&.name == "bookmark"
28
- if empty_text_before_first_element(x.elements[0])
29
- x["id"] = x.elements[0].elements[0].remove["id"]
30
- strip_initial_space(x.elements[0])
31
- end
37
+ if x.at("./*[1][local-name() = 'p']/"\
38
+ "*[1][local-name() = 'bookmark']") &&
39
+ empty_text_before_first_element(x.elements[0])
40
+ bookmark_to_id(x, x.elements[0].elements[0])
41
+ end
42
+ end
43
+ end
44
+
45
+ def dt_bookmark_cleanup(xmldoc)
46
+ xmldoc.xpath("//dt[descendant::bookmark]").each do |x|
47
+ if x.at("./*[1][local-name() = 'p']/"\
48
+ "*[1][local-name() = 'bookmark']") &&
49
+ empty_text_before_first_element(x.elements[0])
50
+ bookmark_to_id(x, x.elements[0].elements[0])
51
+ elsif x.at("./*[1][local-name() = 'bookmark']") &&
52
+ empty_text_before_first_element(x)
53
+ bookmark_to_id(x, x.elements[0])
32
54
  end
33
55
  end
34
56
  end
@@ -46,65 +68,67 @@ module Asciidoctor
46
68
  LOCALITY_RE = Regexp.new(LOCALITY_REGEX_STR.gsub(/\s/, ""),
47
69
  Regexp::IGNORECASE | Regexp::MULTILINE)
48
70
 
49
- def tq(x)
50
- x.sub(/^"/, "").sub(/"$/, "")
71
+ def tq(text)
72
+ text.sub(/^"/, "").sub(/"$/, "")
51
73
  end
52
74
 
53
- def extract_localities(x)
54
- f = x&.children&.first or return
75
+ def extract_localities(elem)
76
+ f = elem&.children&.first or return
55
77
  f.text? or return
56
78
  head = f.remove.text
57
- tail = x&.children&.remove
58
- extract_localities1(x, head)
59
- tail and x << tail
79
+ tail = elem&.children&.remove
80
+ extract_localities1(elem, head)
81
+ tail and elem << tail
60
82
  end
61
83
 
62
- def extract_localities1(x, text)
63
- b = x.add_child("<localityStack/>").first if LOCALITY_RE.match text
84
+ def extract_localities1(elem, text)
85
+ b = elem.add_child("<localityStack/>").first if LOCALITY_RE.match text
64
86
  while (m = LOCALITY_RE.match text)
65
87
  ref = m[:ref] ? "<referenceFrom>#{tq m[:ref]}</referenceFrom>" : ""
66
88
  refto = m[:to] ? "<referenceTo>#{tq m[:to]}</referenceTo>" : ""
67
89
  loc = m[:locality]&.downcase || m[:locality2]&.downcase
68
90
  b.add_child("<locality type='#{loc}'>#{ref}#{refto}</locality>")
69
91
  text = m[:text]
70
- b = x.add_child("<localityStack/>").first if m[:punct] == ";"
92
+ b = elem.add_child("<localityStack/>").first if m[:punct] == ";"
71
93
  end
72
- x.add_child(text) if text
94
+ elem.add_child(text) if text
73
95
  end
74
96
 
75
- def xref_to_eref(x)
76
- x["bibitemid"] = x["target"]
77
- unless x["citeas"] = @anchors&.dig(x["target"], :xref)
78
- @internal_eref_namespaces.include?(x["type"]) or
79
- @log.add("Crossreferences", x,
80
- "#{x['target']} does not have a corresponding anchor ID in the bibliography!")
97
+ def xref_to_eref(elem)
98
+ elem["bibitemid"] = elem["target"]
99
+ unless elem["citeas"] = @anchors&.dig(elem["target"], :xref)
100
+ @internal_eref_namespaces.include?(elem["type"]) or
101
+ @log.add("Crossreferences", elem,
102
+ "#{elem['target']} does not have a corresponding "\
103
+ "anchor ID in the bibliography!")
81
104
  end
82
- x.delete("target")
83
- extract_localities(x) unless x.children.empty?
105
+ elem.delete("target")
106
+ extract_localities(elem) unless elem.children.empty?
84
107
  end
85
108
 
86
109
  def xref_cleanup(xmldoc)
87
110
  xmldoc.xpath("//xref").each do |x|
88
111
  /:/.match(x["target"]) and xref_to_internal_eref(x)
89
112
  next unless x.name == "xref"
113
+
90
114
  if refid? x["target"]
91
115
  x.name = "eref"
92
116
  xref_to_eref(x)
93
- else
94
- x.delete("type")
117
+ else x.delete("type")
95
118
  end
96
119
  end
97
120
  end
98
121
 
99
- def xref_to_internal_eref(x)
100
- a = x["target"].split(":", 3)
101
- unless a.size < 2 || a[0].empty? || a[1].empty?
102
- x["target"] = "#{a[0]}_#{a[1]}"
103
- a.size > 2 and x.children = %{anchor="#{a[2..-1].join("")}",#{x&.children&.text}}
104
- x["type"] = a[0]
122
+ def xref_to_internal_eref(elem)
123
+ a = elem["target"].split(":", 3)
124
+ unless a.size < 2 || a[0].empty? || a[1].empty?
125
+ elem["target"] = "#{a[0]}_#{a[1]}"
126
+ a.size > 2 and
127
+ elem.children = %{anchor="#{a[2..-1].join}",#{elem&.children&.text}}
128
+ elem["type"] = a[0]
105
129
  @internal_eref_namespaces << a[0]
106
- x.name = "eref"
107
- xref_to_eref(x)
130
+ elem.name = "eref"
131
+ xref_to_eref(elem)
108
132
  end
109
133
  end
110
134
 
@@ -116,10 +140,11 @@ module Asciidoctor
116
140
 
117
141
  def origin_cleanup(xmldoc)
118
142
  xmldoc.xpath("//origin/concept[termref]").each do |x|
119
- x.replace(x.children)
143
+ t = x.at("./termref")
144
+ x.replace(t)
120
145
  end
121
146
  xmldoc.xpath("//origin").each do |x|
122
- x["citeas"] = @anchors&.dig(x["bibitemid"], :xref) ||
147
+ x["citeas"] = @anchors&.dig(x["bibitemid"], :xref) or
123
148
  @log.add("Crossreferences", x,
124
149
  "#{x['bibitemid']} does not have a corresponding anchor "\
125
150
  "ID in the bibliography!")
@@ -128,68 +153,83 @@ module Asciidoctor
128
153
  end
129
154
 
130
155
  def concept_cleanup(xmldoc)
131
- xmldoc.xpath("//concept").each do |x|
132
- x.delete("term") if x["term"].empty?
133
- if /:/.match(x["key"]) then concept_termbase_cleanup(x)
156
+ xmldoc.xpath("//concept[not(termxref)]").each do |x|
157
+ term = x.at("./refterm")
158
+ term&.remove if term&.text&.empty?
159
+ x.children.remove if x&.children&.text&.strip&.empty?
160
+ key_extract_locality(x)
161
+ if /:/.match?(x["key"]) then concept_termbase_cleanup(x)
134
162
  elsif refid? x["key"] then concept_eref_cleanup(x)
135
- else
136
- concept_xref_cleanup(x)
163
+ else concept_xref_cleanup(x)
137
164
  end
138
165
  x.delete("key")
139
166
  end
140
167
  end
141
168
 
142
- def concept_termbase_cleanup(x)
143
- text = x&.children&.first&.remove&.text
144
- termbase, key = x["key"].split(/:/, 2)
145
- x.add_child(%(<termref base="#{termbase}" target="#{key}">) +
146
- "#{text}</termref>")
169
+ def key_extract_locality(elem)
170
+ return unless /,/.match?(elem["key"])
171
+
172
+ elem.add_child("<locality>#{elem['key'].sub(/^[^,]+,/, '')}</locality>")
173
+ elem["key"] = elem["key"].sub(/,.*$/, "")
174
+ end
175
+
176
+ def concept_termbase_cleanup(elem)
177
+ t = elem&.at("./xrefrender")&.remove&.children
178
+ termbase, key = elem["key"].split(/:/, 2)
179
+ elem.add_child(%(<termref base="#{termbase}" target="#{key}">) +
180
+ "#{t&.to_xml}</termref>")
147
181
  end
148
182
 
149
- def concept_xref_cleanup(x)
150
- text = x&.children&.first&.remove&.text
151
- x.add_child(%(<xref target="#{x['key']}">#{text}</xref>))
183
+ def concept_xref_cleanup(elem)
184
+ t = elem&.at("./xrefrender")&.remove&.children
185
+ elem.add_child(%(<xref target="#{elem['key']}">#{t&.to_xml}</xref>))
152
186
  end
153
187
 
154
- def concept_eref_cleanup(x)
155
- x.children = "<eref>#{x.children.to_xml}</eref>"
156
- extract_localities(x.first_element_child)
188
+ def concept_eref_cleanup(elem)
189
+ t = elem&.at("./xrefrender")&.remove&.children&.to_xml
190
+ l = elem&.at("./locality")&.remove&.children&.to_xml
191
+ elem.add_child "<eref bibitemid='#{elem['key']}'>#{l}</eref>"
192
+ extract_localities(elem.elements[-1])
193
+ elem.elements[-1].add_child(t) if t
157
194
  end
158
195
 
159
- def to_xreftarget(s)
160
- return Metanorma::Utils::to_ncname(s) unless /^[^#]+#.+$/.match(s)
161
- /^(?<pref>[^#]+)#(?<suff>.+)$/ =~ s
162
- pref = pref.gsub(%r([#{Metanorma::Utils::NAMECHAR}]), "_")
163
- suff = suff.gsub(%r([#{Metanorma::Utils::NAMECHAR}]), "_")
196
+ def to_xreftarget(str)
197
+ return Metanorma::Utils::to_ncname(str) unless /^[^#]+#.+$/.match?(str)
198
+
199
+ /^(?<pref>[^#]+)#(?<suff>.+)$/ =~ str
200
+ pref = pref.gsub(%r([#{Metanorma::Utils::NAMECHAR}])o, "_")
201
+ suff = suff.gsub(%r([#{Metanorma::Utils::NAMECHAR}])o, "_")
164
202
  "#{pref}##{suff}"
165
203
  end
166
204
 
167
205
  IDREF = "//*/@id | //review/@from | //review/@to | "\
168
206
  "//callout/@target | //citation/@bibitemid | //eref/@bibitemid".freeze
169
207
 
170
- def anchor_cleanup(x)
171
- anchor_cleanup1(x)
172
- xreftarget_cleanup(x)
208
+ def anchor_cleanup(elem)
209
+ anchor_cleanup1(elem)
210
+ xreftarget_cleanup(elem)
173
211
  end
174
212
 
175
- def anchor_cleanup1(x)
176
- x.xpath(IDREF).each do |s|
213
+ def anchor_cleanup1(elem)
214
+ elem.xpath(IDREF).each do |s|
177
215
  if (ret = Metanorma::Utils::to_ncname(s.value)) != (orig = s.value)
178
216
  s.value = ret
179
217
  output = s.parent.dup
180
218
  output.children.remove
181
- @log.add("Anchors", s.parent, "normalised identifier in #{output} from #{orig}")
219
+ @log.add("Anchors", s.parent,
220
+ "normalised identifier in #{output} from #{orig}")
182
221
  end
183
222
  end
184
223
  end
185
224
 
186
- def xreftarget_cleanup(x)
187
- x.xpath("//xref/@target").each do |s|
225
+ def xreftarget_cleanup(elem)
226
+ elem.xpath("//xref/@target").each do |s|
188
227
  if (ret = to_xreftarget(s.value)) != (orig = s.value)
189
228
  s.value = ret
190
229
  output = s.parent.dup
191
230
  output.children.remove
192
- @log.add("Anchors", s.parent, "normalised identifier in #{output} from #{orig}")
231
+ @log.add("Anchors", s.parent,
232
+ "normalised identifier in #{output} from #{orig}")
193
233
  end
194
234
  end
195
235
  end