isodoc 1.0.23 → 1.0.28

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/macos.yml +10 -2
  3. data/.github/workflows/ubuntu.yml +13 -3
  4. data/.github/workflows/windows.yml +10 -2
  5. data/isodoc.gemspec +1 -1
  6. data/lib/isodoc-yaml/i18n-en.yaml +3 -1
  7. data/lib/isodoc-yaml/i18n-fr.yaml +3 -1
  8. data/lib/isodoc-yaml/i18n-zh-Hans.yaml +3 -1
  9. data/lib/isodoc/base_style/reset.scss +1 -1
  10. data/lib/isodoc/convert.rb +1 -0
  11. data/lib/isodoc/function/blocks.rb +6 -1
  12. data/lib/isodoc/function/cleanup.rb +16 -2
  13. data/lib/isodoc/function/i18n.rb +5 -5
  14. data/lib/isodoc/function/inline.rb +77 -79
  15. data/lib/isodoc/function/inline_simple.rb +72 -0
  16. data/lib/isodoc/function/references.rb +49 -37
  17. data/lib/isodoc/function/section.rb +19 -8
  18. data/lib/isodoc/function/table.rb +0 -1
  19. data/lib/isodoc/function/to_word_html.rb +23 -13
  20. data/lib/isodoc/function/utils.rb +11 -5
  21. data/lib/isodoc/function/xref_gen.rb +2 -1
  22. data/lib/isodoc/function/xref_sect_gen.rb +24 -24
  23. data/lib/isodoc/headlesshtml_convert.rb +5 -0
  24. data/lib/isodoc/html_convert.rb +5 -0
  25. data/lib/isodoc/html_function/footnotes.rb +3 -3
  26. data/lib/isodoc/html_function/html.rb +15 -0
  27. data/lib/isodoc/html_function/postprocess.rb +6 -5
  28. data/lib/isodoc/metadata.rb +10 -3
  29. data/lib/isodoc/metadata_date.rb +19 -7
  30. data/lib/isodoc/pdf_convert.rb +5 -0
  31. data/lib/isodoc/version.rb +1 -1
  32. data/lib/isodoc/word_convert.rb +5 -0
  33. data/lib/isodoc/word_function/body.rb +0 -4
  34. data/lib/isodoc/word_function/footnotes.rb +3 -3
  35. data/lib/isodoc/word_function/postprocess.rb +13 -2
  36. data/lib/isodoc/xslfo_convert.rb +5 -0
  37. data/spec/assets/i18n.yaml +4 -1
  38. data/spec/isodoc/blocks_spec.rb +59 -8
  39. data/spec/isodoc/cleanup_spec.rb +317 -25
  40. data/spec/isodoc/footnotes_spec.rb +20 -5
  41. data/spec/isodoc/i18n_spec.rb +12 -12
  42. data/spec/isodoc/inline_spec.rb +118 -5
  43. data/spec/isodoc/metadata_spec.rb +8 -3
  44. data/spec/isodoc/postproc_spec.rb +34 -12
  45. data/spec/isodoc/ref_spec.rb +120 -51
  46. data/spec/isodoc/section_spec.rb +236 -207
  47. data/spec/isodoc/table_spec.rb +24 -24
  48. data/spec/isodoc/terms_spec.rb +50 -6
  49. data/spec/isodoc/xref_spec.rb +53 -26
  50. metadata +5 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 709ee937cab542be14ef994074c62d60df2e26ad5d858fbfd9dc9966f100a9be
4
- data.tar.gz: 809faa2a70a8d982909f15794928b19981f5e3aee33da0afdc9fc81d8a4d030b
3
+ metadata.gz: 6d2aca20ce5f2e96df3da6fbb1a8e675e3b2393f51720bac2bdc760e6e9c09a5
4
+ data.tar.gz: 6f83efeb115dbbb30f928d99e8f8c6da4172fc2a17d1792607d2cd38d6b5833c
5
5
  SHA512:
6
- metadata.gz: 3b3a2e409e96f3f4e79723b06ebce456559280855c384ede05cbc15d5f9e217b7673228190190f1df84cc298ed5c89dd08e22a4eb89df5f1ff95fc978bba91e6
7
- data.tar.gz: 9d26571df295d108788bd45d527e2a9ac8ef4d8f6cc6b7ec1aabc18c508d505e88a09c909f96b1d252274f293dac1950fba9b52359c30625a4c185658a9536a2
6
+ metadata.gz: f3e63f2f2f04dea86c5b716be19385eec4273bdd57870ee1215f441e2b6494ea25c8c52c3edf40b8b657ed8d081667356b003e68ded049605f2985450e903649
7
+ data.tar.gz: a1f408ba884174786963f38a5912ec128a94223b5f9f66fb28662398a6785bb2b54a42554fc3c2bb004230d443a6a38b8d771a1cdf64ec12447ea4e132b46a2c
@@ -6,15 +6,23 @@ on:
6
6
  push:
7
7
  branches: [ master ]
8
8
  pull_request:
9
+ paths-ignore:
10
+ - .github/workflows/ubuntu.yml
11
+ - .github/workflows/windows.yml
9
12
 
10
13
  jobs:
11
14
  test-macos:
12
15
  name: Test on Ruby ${{ matrix.ruby }} macOS
13
16
  runs-on: macos-latest
17
+ continue-on-error: ${{ matrix.experimental }}
14
18
  strategy:
15
19
  fail-fast: false
16
20
  matrix:
17
21
  ruby: [ '2.6', '2.5', '2.4' ]
22
+ experimental: [false]
23
+ include:
24
+ - ruby: '2.7'
25
+ experimental: true
18
26
  steps:
19
27
  - uses: actions/checkout@master
20
28
  - name: Use Ruby
@@ -29,10 +37,10 @@ jobs:
29
37
  - name: Use Node
30
38
  uses: actions/setup-node@v1
31
39
  with:
32
- node-version: '8'
40
+ node-version: '12'
33
41
  - name: Install Puppeteer
34
42
  run: |
35
- npm install -g puppeteer
43
+ npm install -g puppeteer@3.0.1
36
44
  - name: Run specs
37
45
  run: |
38
46
  bundle exec rake
@@ -6,15 +6,23 @@ on:
6
6
  push:
7
7
  branches: [ master ]
8
8
  pull_request:
9
+ paths-ignore:
10
+ - .github/workflows/macos.yml
11
+ - .github/workflows/windows.yml
9
12
 
10
13
  jobs:
11
14
  test-linux:
12
15
  name: Test on Ruby ${{ matrix.ruby }} Ubuntu
13
16
  runs-on: ubuntu-latest
17
+ continue-on-error: ${{ matrix.experimental }}
14
18
  strategy:
15
19
  fail-fast: false
16
20
  matrix:
17
21
  ruby: [ '2.6', '2.5', '2.4' ]
22
+ experimental: [false]
23
+ include:
24
+ - ruby: '2.7'
25
+ experimental: true
18
26
  steps:
19
27
  - uses: actions/checkout@master
20
28
  - name: Use Ruby
@@ -29,15 +37,17 @@ jobs:
29
37
  - name: Use Node
30
38
  uses: actions/setup-node@v1
31
39
  with:
32
- node-version: '8'
40
+ node-version: '12'
33
41
  - name: Install Puppeteer
34
42
  run: |
35
- npm install -g puppeteer
43
+ sudo apt-get update
44
+ sudo apt-get install libgbm1
45
+ npm install -g puppeteer@3.0.1
36
46
  - name: Run specs
37
47
  run: |
38
48
  bundle exec rake
39
49
  - name: Trigger dependent repositories
40
- if: github.ref == 'refs/heads/master'
50
+ if: github.ref == 'refs/heads/master' && matrix.ruby == '2.6'
41
51
  env:
42
52
  GH_USERNAME: ${{ secrets.PAT_USERNAME }}
43
53
  GH_ACCESS_TOKEN: ${{ secrets.PAT_TOKEN }}
@@ -6,15 +6,23 @@ on:
6
6
  push:
7
7
  branches: [ master ]
8
8
  pull_request:
9
+ paths-ignore:
10
+ - .github/workflows/macos.yml
11
+ - .github/workflows/ubuntu.yml
9
12
 
10
13
  jobs:
11
14
  test-windows:
12
15
  name: Test on Ruby ${{ matrix.ruby }} Windows
13
16
  runs-on: windows-latest
17
+ continue-on-error: ${{ matrix.experimental }}
14
18
  strategy:
15
19
  fail-fast: false
16
20
  matrix:
17
21
  ruby: [ '2.6', '2.5', '2.4' ]
22
+ experimental: [false]
23
+ include:
24
+ - ruby: '2.7'
25
+ experimental: true
18
26
  steps:
19
27
  - uses: actions/checkout@master
20
28
  - name: Use Ruby
@@ -31,10 +39,10 @@ jobs:
31
39
  - name: Use Node
32
40
  uses: actions/setup-node@v1
33
41
  with:
34
- node-version: '8'
42
+ node-version: '12'
35
43
  - name: Install Puppeteer
36
44
  run: |
37
- npm install -g puppeteer
45
+ npm install -g puppeteer@3.0.1
38
46
  - name: Run specs
39
47
  run: |
40
48
  bundle exec rake
@@ -36,7 +36,7 @@ Gem::Specification.new do |spec|
36
36
  spec.add_dependency "html2doc", "~> 1.0.0"
37
37
  spec.add_dependency "liquid"
38
38
  spec.add_dependency "roman-numerals"
39
- spec.add_dependency "sassc", "~> 2.2.1"
39
+ spec.add_dependency "sassc", "~> 2.4.0"
40
40
  spec.add_dependency "metanorma", "~> 1.0.0"
41
41
  spec.add_dependency "rake", "~> 12.0"
42
42
 
@@ -6,7 +6,9 @@ introduction: Introduction
6
6
  foreword: Foreword
7
7
  abstract: Abstract
8
8
  termsdef: Terms and definitions
9
- termsdefsymbols: Terms, definitions, symbols and abbreviated terms
9
+ termsdefsymbolsabbrev: Terms, definitions, symbols and abbreviated terms
10
+ termsdefsymbols: Terms, definitions and symbols
11
+ termsdefabbrev: Terms, definitions and abbreviated terms
10
12
  normref: Normative references
11
13
  bibliography: Bibliography
12
14
  clause: Clause
@@ -6,7 +6,9 @@ introduction: Introduction
6
6
  foreword: Avant-propos
7
7
  abstract: Résumé
8
8
  termsdef: Terms et définitions
9
- termsdefsymbols: Terms, définitions, symboles et termes abrégés
9
+ termsdefsymbolsabbrev: Terms, définitions, symboles et termes abrégés
10
+ termsdefsymbols: Terms, définitions et symboles
11
+ termsdefabbrev: Terms, définitions et termes abrégés
10
12
  normref: Références normatives
11
13
  bibliography: Bibliographie
12
14
  clause: Article
@@ -6,7 +6,9 @@ introduction: 引言
6
6
  foreword: 前言
7
7
  abstract: 摘要
8
8
  termsdef: 术语和定义
9
- termsdefsymbols: 术语、定义、符号、代号和缩略语
9
+ termsdefsymbolsabbrev: 术语、定义、符号、代号和缩略语
10
+ termsdefsymbols: 术语、定义、符号和代号
11
+ termsdefabbrev: 术语、定义、符号和缩略语
10
12
  normref: 规范性引用文件
11
13
  bibliography: 参考文献
12
14
  clause: 条
@@ -125,5 +125,5 @@ b, strong {
125
125
  }
126
126
 
127
127
  div.document-stage-band, div.document-type-band {
128
- background-color: #000000;
128
+ background-color: #333333;
129
129
  }
@@ -48,6 +48,7 @@ module IsoDoc
48
48
  @olstyle = options[:olstyle]
49
49
  @datauriimage = options[:datauriimage]
50
50
  @suppressheadingnumbers = options[:suppressheadingnumbers]
51
+ @break_up_urls_in_tables = options[:break_up_urls_in_tables] == "true"
51
52
  @termdomain = ""
52
53
  @termexample = false
53
54
  @note = false
@@ -141,7 +141,7 @@ module IsoDoc::Function
141
141
  def admonition_parse(node, out)
142
142
  type = node["type"]
143
143
  name = admonition_name(node, type)
144
- out.div **{ class: admonition_class(node) } do |t|
144
+ out.div **{ id: node["id"], class: admonition_class(node) } do |t|
145
145
  admonition_name_parse(node, t, name) if name
146
146
  node.children.each { |n| parse(n, t) unless n.name == "name" }
147
147
  end
@@ -222,5 +222,10 @@ module IsoDoc::Function
222
222
  quote_attribution(node, out)
223
223
  end
224
224
  end
225
+
226
+ def passthrough_parse(node, out)
227
+ return if node["format"] and !(node["format"].split(/,/).include? @format.to_s)
228
+ out.passthrough node.text
229
+ end
225
230
  end
226
231
  end
@@ -1,13 +1,25 @@
1
1
  module IsoDoc::Function
2
2
  module Cleanup
3
3
  def textcleanup(docxml)
4
+ docxml = termref_cleanup(passthrough_cleanup(docxml))
5
+ end
6
+
7
+ def termref_cleanup(docxml)
4
8
  docxml.
9
+ gsub(%r{\s*\[/TERMREF\]\s*</p>\s*<p>\s*\[TERMREF\]}, "; ").
5
10
  gsub(/\[TERMREF\]\s*/, l10n("[#{@source_lbl}: ")).
6
11
  gsub(/\s*\[MODIFICATION\]\s*\[\/TERMREF\]/, l10n(", #{@modified_lbl} [/TERMREF]")).
7
- gsub(/\s*\[\/TERMREF\]\s*/, l10n("]")).
12
+ gsub(%r{\s*\[\/TERMREF\]\s*}, l10n("]")).
8
13
  gsub(/\s*\[MODIFICATION\]/, l10n(", #{@modified_lbl} &mdash; "))
9
14
  end
10
15
 
16
+ def passthrough_cleanup(docxml)
17
+ docxml.split(%r{(<passthrough>|</passthrough>)}).each_slice(4).map do |a|
18
+ a.size > 2 and a[2] = HTMLEntities.new.decode(a[2])
19
+ [a[0], a[2]]
20
+ end.join
21
+ end
22
+
11
23
  def cleanup(docxml)
12
24
  comment_cleanup(docxml)
13
25
  footnote_cleanup(docxml)
@@ -20,6 +32,7 @@ module IsoDoc::Function
20
32
  end
21
33
 
22
34
  def table_long_strings_cleanup(docxml)
35
+ return unless @break_up_urls_in_tables == true
23
36
  docxml.xpath("//td | //th").each do |d|
24
37
  d.traverse do |n|
25
38
  next unless n.text?
@@ -30,6 +43,7 @@ module IsoDoc::Function
30
43
  end
31
44
 
32
45
  def break_up_long_strings(t)
46
+ return t if t.match(/^\s*$/)
33
47
  t.split(/(?=\s)/).map do |w|
34
48
  (/^\s*$/.match(t) or w.size < 30) ? w :
35
49
  w.scan(/.{,30}/).map do |w1|
@@ -116,7 +130,7 @@ module IsoDoc::Function
116
130
  end
117
131
 
118
132
  def footnote_cleanup(docxml)
119
- docxml.xpath('//a[@epub:type = "footnote"]/sup').each_with_index do |x, i|
133
+ docxml.xpath('//a[@class = "FootnoteRef"]/sup').each_with_index do |x, i|
120
134
  x.content = (i + 1).to_s
121
135
  end
122
136
  docxml
@@ -77,8 +77,8 @@ module IsoDoc::Function
77
77
  end
78
78
 
79
79
  # TODO: move to localization file
80
- def eref_localities1_zh(target, type, from, to)
81
- ret = ", 第#{from.text}" if from
80
+ def eref_localities1_zh(target, type, from, to, delim)
81
+ ret = "#{delim} 第#{from.text}" if from
82
82
  ret += "&ndash;#{to}" if to
83
83
  loc = (@locality[type] || type.sub(/^locality:/, "").capitalize )
84
84
  ret += " #{loc}"
@@ -86,9 +86,9 @@ module IsoDoc::Function
86
86
  end
87
87
 
88
88
  # TODO: move to localization file
89
- def eref_localities1(target, type, from, to, lang = "en")
90
- return l10n(eref_localities1_zh(target, type, from, to)) if lang == "zh"
91
- ret = ","
89
+ def eref_localities1(target, type, from, to, delim, lang = "en")
90
+ return l10n(eref_localities1_zh(target, type, from, to, delim)) if lang == "zh"
91
+ ret = delim
92
92
  loc = @locality[type] || type.sub(/^locality:/, "").capitalize
93
93
  ret += " #{loc}"
94
94
  ret += " #{from.text}" if from
@@ -1,28 +1,7 @@
1
+ require_relative "inline_simple"
2
+
1
3
  module IsoDoc::Function
2
4
  module Inline
3
- def section_break(body)
4
- body.br
5
- end
6
-
7
- def page_break(out)
8
- out.br
9
- end
10
-
11
- def pagebreak_parse(_node, out)
12
- out.br
13
- end
14
-
15
- def hr_parse(node, out)
16
- out.hr
17
- end
18
-
19
- def br_parse(node, out)
20
- out.br
21
- end
22
-
23
- def index_parse(node, out)
24
- end
25
-
26
5
  def link_parse(node, out)
27
6
  out.a **attr_code(href: node["target"], title: node["alt"]) do |l|
28
7
  if node.text.empty?
@@ -50,17 +29,39 @@ module IsoDoc::Function
50
29
  (container && get_note_container_id(node) != container &&
51
30
  @anchors[node["target"]]) &&
52
31
  linkend = prefix_container(container, linkend, node["target"])
32
+ linkend = capitalise_xref(node, linkend)
53
33
  end
54
34
  linkend || "???"
55
35
  end
56
36
 
37
+ def capitalise_xref(node, linkend)
38
+ return linkend unless %w(Latn Cyrl Grek).include? @script
39
+ return linkend&.capitalize if node["case"] == "capital"
40
+ return linkend&.downcase if node["case"] == "lowercase"
41
+ return linkend if linkend[0,1].match(/\p{Upper}/)
42
+ prec = nearest_block_parent(node).xpath("./descendant-or-self::text()") &
43
+ node.xpath("./preceding::text()")
44
+ (prec.empty? || /(?!<[^.].)\.\s+$/.match(prec.map { |p| p.text }.join)) ?
45
+ linkend&.capitalize : linkend
46
+ end
47
+
48
+ def nearest_block_parent(node)
49
+ until %w(p title td th name formula
50
+ li dt dd sourcecode pre).include?(node.name)
51
+ node = node.parent
52
+ end
53
+ node
54
+ end
55
+
57
56
  def get_linkend(node)
58
- contents = node.children.select { |c| c.name != "locality" }.
59
- select { |c| !c.text? || /\S/.match(c) }
57
+ contents = node.children.select do |c|
58
+ !%w{locality localityStack}.include? c.name
59
+ end.select { |c| !c.text? || /\S/.match(c) }
60
60
  !contents.empty? and
61
61
  return Nokogiri::XML::NodeSet.new(node.document, contents).to_xml
62
62
  link = anchor_linkend(node, docid_l10n(node["target"] || node["citeas"]))
63
- link + eref_localities(node.xpath(ns("./locality")), link)
63
+ link + eref_localities(node.xpath(ns("./locality | ./localityStack")),
64
+ link)
64
65
  # so not <origin bibitemid="ISO7301" citeas="ISO 7301">
65
66
  # <locality type="section"><reference>3.1</reference></locality></origin>
66
67
  end
@@ -73,16 +74,29 @@ module IsoDoc::Function
73
74
 
74
75
  def eref_localities(refs, target)
75
76
  ret = ""
76
- refs.each do |r|
77
- ret += if r["type"] == "whole" then l10n(", #{@whole_of_text}")
78
- else
79
- eref_localities1(target, r["type"], r.at(ns("./referenceFrom")),
80
- r.at(ns("./referenceTo")), @lang)
81
- end
77
+ refs.each_with_index do |r, i|
78
+ delim = ","
79
+ delim = ";" if r.name == "localityStack" && i>0
80
+ if r.name == "localityStack"
81
+ r.elements.each_with_index do |rr, j|
82
+ ret += eref_localities0(rr, j, target, delim)
83
+ delim = ","
84
+ end
85
+ else
86
+ ret += eref_localities0(r, i, target, delim)
87
+ end
82
88
  end
83
89
  ret
84
90
  end
85
91
 
92
+ def eref_localities0(r, i, target, delim)
93
+ if r["type"] == "whole" then l10n("#{delim} #{@whole_of_text}")
94
+ else
95
+ eref_localities1(target, r["type"], r.at(ns("./referenceFrom")),
96
+ r.at(ns("./referenceTo")), delim, @lang)
97
+ end
98
+ end
99
+
86
100
  def eref_parse(node, out)
87
101
  linkend = get_linkend(node)
88
102
  if node["type"] == "footnote"
@@ -94,13 +108,22 @@ module IsoDoc::Function
94
108
  end
95
109
  end
96
110
 
111
+ def origin_parse(node, out)
112
+ if t = node.at(ns("./termref"))
113
+ termrefelem_parse(t, out)
114
+ else
115
+ eref_parse(node, out)
116
+ end
117
+ end
118
+
97
119
  def termrefelem_parse(node, out)
98
120
  out << "Termbase #{node['base']}, term ID #{node['target']}"
99
121
  end
100
122
 
101
123
  def concept_parse(node, out)
102
- content = node.first_element_child.children.select { |c| c.name != "locality" }.
103
- select { |c| !c.text? || /\S/.match(c) }
124
+ content = node.first_element_child.children.select do |c|
125
+ !%w{locality localityStack}.include? c.name
126
+ end.select { |c| !c.text? || /\S/.match(c) }
104
127
  if content.empty?
105
128
  out << "[Term defined in "
106
129
  parse(node.first_element_child, out)
@@ -112,7 +135,8 @@ module IsoDoc::Function
112
135
 
113
136
  def stem_parse(node, out)
114
137
  ooml = if node["type"] == "AsciiMath"
115
- "#{@openmathdelim}#{HTMLEntities.new.encode(node.text)}#{@closemathdelim}"
138
+ "#{@openmathdelim}#{HTMLEntities.new.encode(node.text)}"\
139
+ "#{@closemathdelim}"
116
140
  elsif node["type"] == "MathML" then node.first_element_child.to_s
117
141
  else
118
142
  HTMLEntities.new.encode(node.text)
@@ -154,57 +178,31 @@ module IsoDoc::Function
154
178
  out << text
155
179
  end
156
180
 
157
- def bookmark_parse(node, out)
158
- out.a **attr_code(id: node["id"])
159
- end
160
-
161
- def keyword_parse(node, out)
162
- out.span **{ class: "keyword" } do |s|
163
- node.children.each { |n| parse(n, s) }
164
- end
165
- end
166
-
167
- def em_parse(node, out)
168
- out.i do |e|
169
- node.children.each { |n| parse(n, e) }
170
- end
171
- end
172
-
173
- def strong_parse(node, out)
174
- out.b do |e|
175
- node.children.each { |n| parse(n, e) }
176
- end
177
- end
178
-
179
- def sup_parse(node, out)
180
- out.sup do |e|
181
- node.children.each { |n| parse(n, e) }
182
- end
183
- end
184
-
185
- def sub_parse(node, out)
186
- out.sub do |e|
187
- node.children.each { |n| parse(n, e) }
188
- end
189
- end
190
-
191
- def tt_parse(node, out)
192
- out.tt do |e|
193
- node.children.each { |n| parse(n, e) }
181
+ def error_parse(node, out)
182
+ text = node.to_xml.gsub(/</, "&lt;").gsub(/>/, "&gt;")
183
+ out.para do |p|
184
+ p.b(**{ role: "strong" }) { |e| e << text }
194
185
  end
195
186
  end
196
187
 
197
- def strike_parse(node, out)
198
- out.s do |e|
199
- node.children.each { |n| parse(n, e) }
188
+ def variant_parse(node, out)
189
+ if node["lang"] == @lang && node["script"] == @script
190
+ node.children.each { |n| parse(n, out) }
191
+ else
192
+ return if found_matching_variant_sibling(node)
193
+ return unless !node.at("./preceding-sibling::xmlns:variant")
194
+ node.children.each { |n| parse(n, out) }
200
195
  end
201
196
  end
202
197
 
203
- def error_parse(node, out)
204
- text = node.to_xml.gsub(/</, "&lt;").gsub(/>/, "&gt;")
205
- out.para do |p|
206
- p.b(**{ role: "strong" }) { |e| e << text }
198
+ def found_matching_variant_sibling(node)
199
+ prev = node.xpath("./preceding-sibling::xmlns:variant")
200
+ foll = node.xpath("./following-sibling::xmlns:variant")
201
+ found = false
202
+ (prev + foll).each do |n|
203
+ found = true if n["lang"] == @lang && n["script"] == @script
207
204
  end
205
+ found
208
206
  end
209
207
  end
210
208
  end