metanorma-standoc 1.3.29 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/lib/asciidoctor/standoc/base.rb +2 -25
  4. data/lib/asciidoctor/standoc/biblio.rng +13 -3
  5. data/lib/asciidoctor/standoc/blocks.rb +20 -17
  6. data/lib/asciidoctor/standoc/cleanup.rb +2 -0
  7. data/lib/asciidoctor/standoc/cleanup_block.rb +2 -2
  8. data/lib/asciidoctor/standoc/cleanup_section.rb +8 -125
  9. data/lib/asciidoctor/standoc/cleanup_terms.rb +134 -0
  10. data/lib/asciidoctor/standoc/datamodel/attributes_table_preprocessor.rb +57 -0
  11. data/lib/asciidoctor/standoc/datamodel/diagram_preprocessor.rb +102 -0
  12. data/lib/asciidoctor/standoc/datamodel/plantuml_renderer.rb +408 -0
  13. data/lib/asciidoctor/standoc/inline.rb +10 -5
  14. data/lib/asciidoctor/standoc/isodoc.rng +17 -1
  15. data/lib/asciidoctor/standoc/macros.rb +13 -8
  16. data/lib/asciidoctor/standoc/macros_yaml2text.rb +19 -13
  17. data/lib/asciidoctor/standoc/ref.rb +13 -56
  18. data/lib/asciidoctor/standoc/ref_sect.rb +124 -0
  19. data/lib/asciidoctor/standoc/section.rb +2 -46
  20. data/lib/asciidoctor/standoc/views/datamodel/model_representation.adoc.erb +30 -0
  21. data/lib/asciidoctor/standoc/views/datamodel/plantuml_representation.adoc.erb +20 -0
  22. data/lib/metanorma/standoc/version.rb +1 -1
  23. data/metanorma-standoc.gemspec +1 -1
  24. data/spec/asciidoctor-standoc/cleanup_spec.rb +51 -0
  25. data/spec/asciidoctor-standoc/datamodel/attributes_table_preprocessor_spec.rb +76 -0
  26. data/spec/asciidoctor-standoc/datamodel/diagram_preprocessor_spec.rb +72 -0
  27. data/spec/asciidoctor-standoc/inline_spec.rb +5 -1
  28. data/spec/asciidoctor-standoc/macros_spec.rb +50 -0
  29. data/spec/asciidoctor-standoc/macros_yaml2text_spec.rb +2 -1
  30. data/spec/asciidoctor-standoc/refs_spec.rb +0 -4
  31. data/spec/examples/datamodel/address_class_profile.adoc +4 -0
  32. data/spec/examples/datamodel/address_component_profile.adoc +4 -0
  33. data/spec/examples/datamodel/common_models_diagram.adoc +4 -0
  34. data/spec/examples/datamodel/models/models/AddressClassProfile.yml +90 -0
  35. data/spec/examples/datamodel/models/models/AddressComponentProfile.yml +63 -0
  36. data/spec/examples/datamodel/models/models/AddressComponentSpecification.yml +15 -0
  37. data/spec/examples/datamodel/models/models/AddressProfile.yml +36 -0
  38. data/spec/examples/datamodel/models/models/AttributeProfile.yml +32 -0
  39. data/spec/examples/datamodel/models/models/InterchangeAddressClassProfile.yml +79 -0
  40. data/spec/examples/datamodel/models/models/Localization copy.yml +23 -0
  41. data/spec/examples/datamodel/models/models/Localization.yml +23 -0
  42. data/spec/examples/datamodel/models/models/ProfileCompliantAddress.yml +36 -0
  43. data/spec/examples/datamodel/models/models/ProfileCompliantAddressComponent.yml +15 -0
  44. data/spec/examples/datamodel/models/models/Signature copy.yml +20 -0
  45. data/spec/examples/datamodel/models/models/Signature.yml +20 -0
  46. data/spec/examples/datamodel/models/models/TextDirectionCode copy.yml +16 -0
  47. data/spec/examples/datamodel/models/models/TextDirectionCode.yml +16 -0
  48. data/spec/examples/datamodel/models/models/Validity.yml +14 -0
  49. data/spec/examples/datamodel/models/models/iso19160-1/Address.yml +22 -0
  50. data/spec/examples/datamodel/models/models/iso19160-1/AddressComponent.yml +2 -0
  51. data/spec/examples/datamodel/models/style.uml.inc +37 -0
  52. data/spec/examples/datamodel/models/views/CommonModels.yml +9 -0
  53. data/spec/examples/datamodel/models/views/TopDown.yml +62 -0
  54. data/spec/examples/datamodel/top_down_diagram.adoc +4 -0
  55. data/spec/fixtures/macros_datamodel/address_class_profile.xml +149 -0
  56. data/spec/fixtures/macros_datamodel/address_component_profile.xml +71 -0
  57. data/spec/fixtures/macros_datamodel/common_models_diagram.xml +7 -0
  58. data/spec/fixtures/macros_datamodel/top_down_diagram.xml +7 -0
  59. data/spec/spec_helper.rb +13 -2
  60. data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec.yml +66 -66
  61. data/spec/vcr_cassettes/isobib_get_123.yml +36 -36
  62. data/spec/vcr_cassettes/isobib_get_123_2001.yml +16 -16
  63. data/spec/vcr_cassettes/isobib_get_124.yml +17 -17
  64. data/spec/vcr_cassettes/rfcbib_get_rfc8341.yml +8 -8
  65. data/spec/vcr_cassettes/separates_iev_citations_by_top_level_clause.yml +41 -38
  66. metadata +41 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e964fb25b29d5ecbe775bf397a17f9cf264fa3709e8756be7a9025bcf043250f
4
- data.tar.gz: d6085180737f332f6aa657eb129bf72f02ca0466b60e22276ddda03cd2cbd788
3
+ metadata.gz: 1e86d9135fffb1f112db82f9d052632aa36f134a76b03ff48e65dcb841c7cbb4
4
+ data.tar.gz: ea05dff988ebaf6b51c029d99cbadd195300c3c524acd301157a335257436c01
5
5
  SHA512:
6
- metadata.gz: baccf6cbbb3c32defcac92b95911e83f10f44338a2bb631a8c6ee86a7b00833583d27161b18ad45b2d80aeec8dcbe3dd349a39b4cfa08670f6cdbe1c8492323c
7
- data.tar.gz: 347739395332e6d3ac88ea0ec5bec557fa0d06dee7f7f2bbafa198e245131dd313c3f229f63bb3afc92c99eb4cd0cec8fc2a9f969e0cbf42a76271e45955e8eb
6
+ metadata.gz: beead161e217dba5841732200aa8cb897900ea90b1a449f092fe63ffe40ca59a6b904b098affd1a475723ac4663ba5dadd09fdab48b661376c4fe659360c79a4
7
+ data.tar.gz: 43b697f04165938864eab22a1d4d49613adef7ae1703995a5541018fe438f09ff21dd43e37e25ebeab9a0d514b50f64b64d2a8a47c8836b924d4578b9f076d19
data/.gitignore CHANGED
@@ -9,3 +9,4 @@ spec/examples/rice.doc
9
9
  spec/examples/rice.html
10
10
  spec/examples/rice.xml
11
11
  spec/examples/rice_files/
12
+ Gemfile.lock
@@ -15,6 +15,8 @@ module Asciidoctor
15
15
  XML_NAMESPACE = "https://www.metanorma.org/ns/standoc".freeze
16
16
 
17
17
  Asciidoctor::Extensions.register do
18
+ preprocessor Asciidoctor::Standoc::Datamodel::AttributesTablePreprocessor
19
+ preprocessor Asciidoctor::Standoc::Datamodel::DiagramPreprocessor
18
20
  preprocessor Asciidoctor::Standoc::Yaml2TextPreprocessor
19
21
  inline_macro Asciidoctor::Standoc::AltTermInlineMacro
20
22
  inline_macro Asciidoctor::Standoc::DeprecatedTermInlineMacro
@@ -125,31 +127,6 @@ module Asciidoctor
125
127
  i18n_init(lang, script)
126
128
  end
127
129
 
128
- def init_bib_caches(node)
129
- return if @no_isobib
130
- global = !@no_isobib_cache && !node.attr("local-cache-only")
131
- local = node.attr("local-cache") || node.attr("local-cache-only")
132
- local = nil if @no_isobib_cache
133
- @bibdb = Relaton::DbCache.init_bib_caches(
134
- local_cache: local,
135
- flush_caches: node.attr("flush-caches"),
136
- global_cache: global)
137
- end
138
-
139
- def init_iev_caches(node)
140
- unless (@no_isobib_cache || @no_isobib)
141
- node.attr("local-cache-only") or
142
- @iev_globalname = global_ievcache_name
143
- @iev_localname = local_ievcache_name(node.attr("local-cache") ||
144
- node.attr("local-cache-only"))
145
- if node.attr("flush-caches")
146
- FileUtils.rm_f @iev_globalname unless @iev_globalname.nil?
147
- FileUtils.rm_f @iev_localname unless @iev_localname.nil?
148
- end
149
- end
150
- #@iev = Iev::Db.new(globalname, localname) unless @no_isobib
151
- end
152
-
153
130
  def default_fonts(node)
154
131
  b = node.attr("body-font") ||
155
132
  (node.attr("script") == "Hans" ? '"SimSun",serif' :
@@ -661,9 +661,9 @@
661
661
  <optional>
662
662
  <ref name="status"/>
663
663
  </optional>
664
- <optional>
664
+ <zeroOrMore>
665
665
  <ref name="copyright"/>
666
- </optional>
666
+ </zeroOrMore>
667
667
  <zeroOrMore>
668
668
  <ref name="docrelation"/>
669
669
  </zeroOrMore>
@@ -1021,7 +1021,17 @@
1021
1021
  <optional>
1022
1022
  <ref name="to"/>
1023
1023
  </optional>
1024
- <ref name="owner"/>
1024
+ <oneOrMore>
1025
+ <ref name="owner"/>
1026
+ </oneOrMore>
1027
+ <optional>
1028
+ <ref name="copyright_scope"/>
1029
+ </optional>
1030
+ </element>
1031
+ </define>
1032
+ <define name="copyright_scope">
1033
+ <element name="scope">
1034
+ <text/>
1025
1035
  </element>
1026
1036
  </define>
1027
1037
  <define name="from">
@@ -21,15 +21,21 @@ module Asciidoctor
21
21
  subsequence: node.attr("subsequence") )
22
22
  end
23
23
 
24
+ def termnote_attr(node)
25
+ attr_code(id_attr(node).merge(
26
+ "keep-separate": node.attr("keep-separate")))
27
+ end
28
+
24
29
  def note_attr(node)
25
- attr_code(id_attr(node).merge("keep-separate": node.attr("keep-separate")))
30
+ attr_code(id_attr(node).merge(
31
+ "keep-separate": node.attr("keep-separate"),
32
+ beforeclauses: node.attr("beforeclauses") == "true" ? "true" : nil))
26
33
  end
27
34
 
28
35
  # We append each contained block to its parent
29
36
  def open(node)
30
37
  role = node.role || node.attr("style")
31
- Utils::reqt_subpart(role) and
32
- return requirement_subpart(node)
38
+ Utils::reqt_subpart(role) and return requirement_subpart(node)
33
39
  result = []
34
40
  node.blocks.each do |b|
35
41
  result << send(b.context, b)
@@ -53,18 +59,16 @@ module Asciidoctor
53
59
 
54
60
  # NOTE: html escaping is performed by Nokogiri
55
61
  def stem(node)
56
- stem_content = node.lines.join("\n")
57
62
  noko do |xml|
58
63
  xml.formula **formula_attr(node) do |s|
59
- stem_parse(stem_content, s, node.style.to_sym)
64
+ stem_parse(node.lines.join("\n"), s, node.style.to_sym)
60
65
  end
61
66
  end
62
67
  end
63
68
 
64
69
  def sidebar_attrs(node)
65
70
  todo_attrs(node).merge(attr_code(
66
- from: node.attr("from"),
67
- to: node.attr("to") || node.attr("from") ))
71
+ from: node.attr("from"), to: node.attr("to") || node.attr("from") ))
68
72
  end
69
73
 
70
74
  def sidebar(node)
@@ -95,7 +99,7 @@ module Asciidoctor
95
99
 
96
100
  def termnote(n)
97
101
  noko do |xml|
98
- xml.termnote **note_attr(n) do |ex|
102
+ xml.termnote **termnote_attr(n) do |ex|
99
103
  wrap_in_para(n, ex)
100
104
  end
101
105
  end.join("\n")
@@ -111,12 +115,11 @@ module Asciidoctor
111
115
 
112
116
  def admonition_attrs(node)
113
117
  name = node.attr("name")
114
- if type = node.attr("type")
115
- ["danger", "safety precautions"].each do |t|
116
- name = t if type.casecmp(t).zero?
117
- end
118
+ a = node.attr("type") and ["danger", "safety precautions"].each do |t|
119
+ name = t if a.casecmp(t).zero?
118
120
  end
119
- attr_code(id: Utils::anchor_or_uuid(node), type: name)
121
+ attr_code(id: Utils::anchor_or_uuid(node), type: name,
122
+ beforeclauses: node.attr("beforeclauses") == "true" ? "true" : nil)
120
123
  end
121
124
 
122
125
  def admonition(node)
@@ -149,6 +152,8 @@ module Asciidoctor
149
152
  end
150
153
 
151
154
  def pseudocode_example(node)
155
+ # prevent A's and other subs inappropriate for pseudocode
156
+ node.blocks.each { |b| b.remove_sub(:replacements) }
152
157
  noko do |xml|
153
158
  xml.figure **id_unnum_attr(node).merge(class: "pseudocode") do |ex|
154
159
  figure_title(node, ex)
@@ -189,8 +194,7 @@ module Asciidoctor
189
194
  end
190
195
 
191
196
  def para_attrs(node)
192
- attr_code(align: node.attr("align"),
193
- id: Utils::anchor_or_uuid(node))
197
+ attr_code(align: node.attr("align"), id: Utils::anchor_or_uuid(node))
194
198
  end
195
199
 
196
200
  def paragraph(node)
@@ -213,9 +217,8 @@ module Asciidoctor
213
217
  s << m[:text]
214
218
  end
215
219
  end
216
- if node.attr("attribution")
220
+ node.attr("attribution") and
217
221
  out.author { |a| a << node.attr("attribution") }
218
- end
219
222
  end
220
223
 
221
224
  def quote(node)
@@ -7,6 +7,7 @@ require_relative "./cleanup_footnotes.rb"
7
7
  require_relative "./cleanup_ref.rb"
8
8
  require_relative "./cleanup_boilerplate.rb"
9
9
  require_relative "./cleanup_section.rb"
10
+ require_relative "./cleanup_terms.rb"
10
11
  require_relative "./cleanup_inline.rb"
11
12
  require "relaton_iev"
12
13
 
@@ -39,6 +40,7 @@ module Asciidoctor
39
40
  figure_cleanup(xmldoc)
40
41
  ref_cleanup(xmldoc)
41
42
  note_cleanup(xmldoc)
43
+ clausebefore_cleanup(xmldoc)
42
44
  ref_dl_cleanup(xmldoc)
43
45
  normref_cleanup(xmldoc)
44
46
  biblio_cleanup(xmldoc)
@@ -147,7 +147,7 @@ module Asciidoctor
147
147
 
148
148
  def requirement_inherit(x)
149
149
  x.xpath("//requirement | //recommendation | //permission").each do |r|
150
- ins = r.at("./classification") ||
150
+ ins = r.at("./classification") ||
151
151
  r.at("./description | ./measurementtarget | ./specification | "\
152
152
  "./verification | ./import | ./description | ./requirement | "\
153
153
  "./recommendation | ./permission")
@@ -158,7 +158,7 @@ module Asciidoctor
158
158
  def requirement_descriptions(x)
159
159
  x.xpath("//requirement | //recommendation | //permission").each do |r|
160
160
  r.children.each do |e|
161
- unless e.element? && (Utils::reqt_subpart(e.name) ||
161
+ unless e.element? && (Utils::reqt_subpart(e.name) ||
162
162
  %w(requirement recommendation permission).include?(e.name))
163
163
  t = Nokogiri::XML::Element.new("description", x)
164
164
  e.before(t)
@@ -138,133 +138,16 @@ module Asciidoctor
138
138
  end
139
139
  end
140
140
 
141
- def termdef_stem_cleanup(xmldoc)
142
- xmldoc.xpath("//term/p/stem").each do |a|
143
- if a.parent.elements.size == 1 # para contains just a stem expression
144
- t = Nokogiri::XML::Element.new("admitted", xmldoc)
145
- parent = a.parent
146
- t.children = a.remove
147
- parent.replace(t)
148
- end
149
- end
150
- end
151
-
152
- def termdomain_cleanup(xmldoc)
153
- xmldoc.xpath("//p/domain").each do |a|
154
- prev = a.parent.previous
155
- prev.next = a.remove
156
- end
157
- end
158
-
159
- def termdomain1_cleanup(xmldoc)
160
- xmldoc.xpath("//domain").each do |d|
161
- defn = d.at("../definition") and
162
- defn.previous = d.remove
163
- end
164
- end
165
-
166
- def termdefinition_cleanup(xmldoc)
167
- xmldoc.xpath("//term").each do |d|
168
- first_child = d.at("./p | ./figure | ./formula") || next
169
- t = Nokogiri::XML::Element.new("definition", xmldoc)
170
- first_child.replace(t)
171
- t << first_child.remove
172
- d.xpath("./p | ./figure | ./formula").each { |n| t << n.remove }
173
- end
174
- end
175
-
176
- def termdef_unnest_cleanup(xmldoc)
177
- # release termdef tags from surrounding paras
178
- nodes = xmldoc.xpath("//p/admitted | //p/deprecates")
179
- while !nodes.empty?
180
- nodes[0].parent.replace(nodes[0].parent.children)
181
- nodes = xmldoc.xpath("//p/admitted | //p/deprecates")
182
- end
183
- end
184
-
185
- def termdef_boilerplate_cleanup(xmldoc)
186
- xmldoc.xpath("//terms/p | //terms/ul").each(&:remove)
187
- end
188
-
189
- def termdef_subclause_cleanup(xmldoc)
190
- xmldoc.xpath("//terms[terms]").each { |t| t.name = "clause" }
191
- end
192
-
193
- def termdocsource_cleanup(xmldoc)
194
- f = xmldoc.at("//preface | //sections")
195
- xmldoc.xpath("//termdocsource").each do |s|
196
- f.previous = s.remove
141
+ def clausebefore_cleanup(xmldoc)
142
+ return unless xmldoc.at("//sections")
143
+ unless ins = xmldoc.at("//sections").children.first
144
+ xmldoc.at("//sections") << " "
145
+ ins = xmldoc.at("//sections").children.first
197
146
  end
198
- end
199
-
200
- def term_children_cleanup(xmldoc)
201
- xmldoc.xpath("//term").each do |t|
202
- t.xpath("./termnote").each { |n| t << n.remove }
203
- t.xpath("./termexample").each { |n| t << n.remove }
204
- t.xpath("./termsource").each { |n| t << n.remove }
205
- end
206
- end
207
-
208
- def termdef_from_termbase(xmldoc)
209
- xmldoc.xpath("//term").each do |x|
210
- if c = x.at("./origin/termref") and !x.at("./definition")
211
- x.at("./origin").previous = fetch_termbase(c["base"], c.text)
212
- end
213
- end
214
- end
215
-
216
- def termdef_cleanup(xmldoc)
217
- termdef_from_termbase(xmldoc)
218
- termdef_unnest_cleanup(xmldoc)
219
- termdef_stem_cleanup(xmldoc)
220
- termdomain_cleanup(xmldoc)
221
- termdefinition_cleanup(xmldoc)
222
- termdomain1_cleanup(xmldoc)
223
- termdef_boilerplate_cleanup(xmldoc)
224
- termdef_subclause_cleanup(xmldoc)
225
- term_children_cleanup(xmldoc)
226
- termdocsource_cleanup(xmldoc)
227
- end
228
-
229
- # Indices sort after letter but before any following
230
- # letter (x, x_m, x_1, xa); we use colon to force that sort order.
231
- # Numbers sort *after* letters; we use thorn to force that sort order.
232
- def symbol_key(x)
233
- key = x.dup
234
- key.traverse do |n|
235
- next unless n.name == "math"
236
- n.replace(grkletters(MathML2AsciiMath.m2a(n.to_xml)))
237
- end
238
- ret = Nokogiri::XML(key.to_xml)
239
- HTMLEntities.new.decode(ret.text).
240
- gsub(/[\[\]\{\}<>\(\)]/, "").strip.
241
- gsub(/[[:punct]]|[_^]/, ":\\0").gsub(/`/, "").
242
- gsub(/[0-9]+/, "þ\\0")
243
- end
244
-
245
- def grkletters(x)
246
- x.gsub(/\b(alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega)\b/i, "&\\1;")
247
- end
248
-
249
- def extract_symbols_list(dl)
250
- dl_out = []
251
- dl.xpath("./dt | ./dd").each do |dtd|
252
- if dtd.name == "dt"
253
- dl_out << { dt: dtd.remove, key: symbol_key(dtd) }
254
- else
255
- dl_out.last[:dd] = dtd.remove
256
- end
257
- end
258
- dl_out
259
- end
260
-
261
- def symbols_cleanup(docxml)
262
- docxml.xpath("//definitions/dl").each do |dl|
263
- dl_out = extract_symbols_list(dl)
264
- dl_out.sort! { |a, b| a[:key] <=> b[:key] || a[:dt] <=> b[:dt] }
265
- dl.children = dl_out.map { |d| d[:dt].to_s + d[:dd].to_s }.join("\n")
147
+ xmldoc.xpath("//*[@beforeclauses = 'true']").each do |x|
148
+ x.delete("beforeclauses")
149
+ ins.previous = x.remove
266
150
  end
267
- docxml
268
151
  end
269
152
  end
270
153
  end
@@ -0,0 +1,134 @@
1
+ module Asciidoctor
2
+ module Standoc
3
+ module Cleanup
4
+ def termdef_stem_cleanup(xmldoc)
5
+ xmldoc.xpath("//term/p/stem").each do |a|
6
+ if a.parent.elements.size == 1 # para contains just a stem expression
7
+ t = Nokogiri::XML::Element.new("admitted", xmldoc)
8
+ parent = a.parent
9
+ t.children = a.remove
10
+ parent.replace(t)
11
+ end
12
+ end
13
+ end
14
+
15
+ def termdomain_cleanup(xmldoc)
16
+ xmldoc.xpath("//p/domain").each do |a|
17
+ prev = a.parent.previous
18
+ prev.next = a.remove
19
+ end
20
+ end
21
+
22
+ def termdomain1_cleanup(xmldoc)
23
+ xmldoc.xpath("//domain").each do |d|
24
+ defn = d.at("../definition") and
25
+ defn.previous = d.remove
26
+ end
27
+ end
28
+
29
+ def termdefinition_cleanup(xmldoc)
30
+ xmldoc.xpath("//term").each do |d|
31
+ first_child = d.at("./p | ./figure | ./formula") || next
32
+ t = Nokogiri::XML::Element.new("definition", xmldoc)
33
+ first_child.replace(t)
34
+ t << first_child.remove
35
+ d.xpath("./p | ./figure | ./formula").each { |n| t << n.remove }
36
+ end
37
+ end
38
+
39
+ def termdef_unnest_cleanup(xmldoc)
40
+ # release termdef tags from surrounding paras
41
+ nodes = xmldoc.xpath("//p/admitted | //p/deprecates")
42
+ while !nodes.empty?
43
+ nodes[0].parent.replace(nodes[0].parent.children)
44
+ nodes = xmldoc.xpath("//p/admitted | //p/deprecates")
45
+ end
46
+ end
47
+
48
+ def termdef_boilerplate_cleanup(xmldoc)
49
+ xmldoc.xpath("//terms/p | //terms/ul").each(&:remove)
50
+ end
51
+
52
+ def termdef_subclause_cleanup(xmldoc)
53
+ xmldoc.xpath("//terms[terms]").each { |t| t.name = "clause" }
54
+ end
55
+
56
+ def termdocsource_cleanup(xmldoc)
57
+ f = xmldoc.at("//preface | //sections")
58
+ xmldoc.xpath("//termdocsource").each do |s|
59
+ f.previous = s.remove
60
+ end
61
+ end
62
+
63
+ def term_children_cleanup(xmldoc)
64
+ xmldoc.xpath("//term").each do |t|
65
+ t.xpath("./termnote").each { |n| t << n.remove }
66
+ t.xpath("./termexample").each { |n| t << n.remove }
67
+ t.xpath("./termsource").each { |n| t << n.remove }
68
+ end
69
+ end
70
+
71
+ def termdef_from_termbase(xmldoc)
72
+ xmldoc.xpath("//term").each do |x|
73
+ if c = x.at("./origin/termref") and !x.at("./definition")
74
+ x.at("./origin").previous = fetch_termbase(c["base"], c.text)
75
+ end
76
+ end
77
+ end
78
+
79
+ def termdef_cleanup(xmldoc)
80
+ termdef_from_termbase(xmldoc)
81
+ termdef_unnest_cleanup(xmldoc)
82
+ termdef_stem_cleanup(xmldoc)
83
+ termdomain_cleanup(xmldoc)
84
+ termdefinition_cleanup(xmldoc)
85
+ termdomain1_cleanup(xmldoc)
86
+ termdef_boilerplate_cleanup(xmldoc)
87
+ termdef_subclause_cleanup(xmldoc)
88
+ term_children_cleanup(xmldoc)
89
+ termdocsource_cleanup(xmldoc)
90
+ end
91
+
92
+ # Indices sort after letter but before any following
93
+ # letter (x, x_m, x_1, xa); we use colon to force that sort order.
94
+ # Numbers sort *after* letters; we use thorn to force that sort order.
95
+ def symbol_key(x)
96
+ key = x.dup
97
+ key.traverse do |n|
98
+ next unless n.name == "math"
99
+ n.replace(grkletters(MathML2AsciiMath.m2a(n.to_xml)))
100
+ end
101
+ ret = Nokogiri::XML(key.to_xml)
102
+ HTMLEntities.new.decode(ret.text).
103
+ gsub(/[\[\]\{\}<>\(\)]/, "").strip.
104
+ gsub(/[[:punct]]|[_^]/, ":\\0").gsub(/`/, "").
105
+ gsub(/[0-9]+/, "þ\\0")
106
+ end
107
+
108
+ def grkletters(x)
109
+ x.gsub(/\b(alpha|beta|gamma|delta|epsilon|zeta|eta|theta|iota|kappa|lambda|mu|nu|xi|omicron|pi|rho|sigma|tau|upsilon|phi|chi|psi|omega)\b/i, "&\\1;")
110
+ end
111
+
112
+ def extract_symbols_list(dl)
113
+ dl_out = []
114
+ dl.xpath("./dt | ./dd").each do |dtd|
115
+ if dtd.name == "dt"
116
+ dl_out << { dt: dtd.remove, key: symbol_key(dtd) }
117
+ else
118
+ dl_out.last[:dd] = dtd.remove
119
+ end
120
+ end
121
+ dl_out
122
+ end
123
+
124
+ def symbols_cleanup(docxml)
125
+ docxml.xpath("//definitions/dl").each do |dl|
126
+ dl_out = extract_symbols_list(dl)
127
+ dl_out.sort! { |a, b| a[:key] <=> b[:key] || a[:dt] <=> b[:dt] }
128
+ dl.children = dl_out.map { |d| d[:dt].to_s + d[:dd].to_s }.join("\n")
129
+ end
130
+ docxml
131
+ end
132
+ end
133
+ end
134
+ end