isodoc 1.0.23 → 1.0.28

Sign up to get free protection for your applications and to get access to all the features.
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