metanorma-standoc 1.9.4 → 1.10.0
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/.github/workflows/rake.yml +1 -1
- data/.rubocop.yml +1 -1
- data/lib/asciidoctor/standoc/cleanup_inline.rb +117 -77
- data/lib/asciidoctor/standoc/cleanup_ref.rb +7 -0
- data/lib/asciidoctor/standoc/cleanup_terms.rb +19 -18
- data/lib/asciidoctor/standoc/inline.rb +20 -17
- data/lib/asciidoctor/standoc/isodoc.rng +18 -1
- data/lib/asciidoctor/standoc/macros_plantuml.rb +19 -21
- data/lib/asciidoctor/standoc/macros_terms.rb +33 -23
- data/lib/asciidoctor/standoc/term_lookup_cleanup.rb +10 -12
- data/lib/asciidoctor/standoc/terms.rb +1 -1
- data/lib/asciidoctor/standoc/validate.rb +21 -8
- data/lib/metanorma/standoc/version.rb +1 -1
- data/metanorma-standoc.gemspec +2 -2
- data/spec/asciidoctor/blocks_spec.rb +6 -6
- data/spec/asciidoctor/cleanup_spec.rb +37 -6
- data/spec/asciidoctor/isobib_cache_spec.rb +4 -6
- data/spec/asciidoctor/lists_spec.rb +147 -135
- data/spec/asciidoctor/macros_spec.rb +505 -181
- data/spec/asciidoctor/refs_spec.rb +12 -12
- data/spec/asciidoctor/validate_spec.rb +66 -20
- data/spec/vcr_cassettes/dated_iso_ref_joint_iso_iec.yml +42 -42
- data/spec/vcr_cassettes/isobib_get_123.yml +12 -12
- data/spec/vcr_cassettes/isobib_get_123_1.yml +26 -26
- data/spec/vcr_cassettes/isobib_get_123_1_fr.yml +35 -35
- data/spec/vcr_cassettes/isobib_get_123_2001.yml +13 -13
- data/spec/vcr_cassettes/isobib_get_124.yml +12 -12
- data/spec/vcr_cassettes/rfcbib_get_rfc8341.yml +13 -13
- data/spec/vcr_cassettes/separates_iev_citations_by_top_level_clause.yml +51 -61
- metadata +5 -5
@@ -955,7 +955,24 @@
|
|
955
955
|
<define name="concept">
|
956
956
|
<element name="concept">
|
957
957
|
<optional>
|
958
|
-
<
|
958
|
+
<element name="refterm">
|
959
|
+
<zeroOrMore>
|
960
|
+
<choice>
|
961
|
+
<ref name="PureTextElement"/>
|
962
|
+
<ref name="stem"/>
|
963
|
+
</choice>
|
964
|
+
</zeroOrMore>
|
965
|
+
</element>
|
966
|
+
</optional>
|
967
|
+
<optional>
|
968
|
+
<element name="renderterm">
|
969
|
+
<zeroOrMore>
|
970
|
+
<choice>
|
971
|
+
<ref name="PureTextElement"/>
|
972
|
+
<ref name="stem"/>
|
973
|
+
</choice>
|
974
|
+
</zeroOrMore>
|
975
|
+
</element>
|
959
976
|
</optional>
|
960
977
|
<choice>
|
961
978
|
<ref name="eref"/>
|
@@ -4,8 +4,8 @@ module Asciidoctor
|
|
4
4
|
# https://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby
|
5
5
|
def self.plantuml_installed?
|
6
6
|
cmd = "plantuml"
|
7
|
-
exts = ENV[
|
8
|
-
ENV[
|
7
|
+
exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
|
8
|
+
ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
|
9
9
|
exts.each do |ext|
|
10
10
|
exe = File.join(path, "#{cmd}#{ext}")
|
11
11
|
return exe if File.executable?(exe) && !File.directory?(exe)
|
@@ -15,7 +15,7 @@ module Asciidoctor
|
|
15
15
|
nil
|
16
16
|
end
|
17
17
|
|
18
|
-
def self.run
|
18
|
+
def self.run(umlfile, outfile)
|
19
19
|
system "plantuml #{umlfile.path}" or (warn $? and return false)
|
20
20
|
i = 0
|
21
21
|
until !Gem.win_platform? || File.exist?(outfile) || i == 15
|
@@ -29,9 +29,9 @@ module Asciidoctor
|
|
29
29
|
# sleep need for windows because dot works in separate process and
|
30
30
|
# plantuml process may finish earlier then dot, as result png file
|
31
31
|
# maybe not created yet after plantuml finish
|
32
|
-
def self.generate_file
|
32
|
+
def self.generate_file(parent, reader)
|
33
33
|
localdir = Metanorma::Utils::localdir(parent.document)
|
34
|
-
imagesdir = parent.document.attr(
|
34
|
+
imagesdir = parent.document.attr("imagesdir")
|
35
35
|
umlfile, outfile = save_plantuml parent, reader, localdir
|
36
36
|
run(umlfile, outfile) or raise "No image output from PlantUML (#{umlfile}, #{outfile})!"
|
37
37
|
umlfile.unlink
|
@@ -40,7 +40,7 @@ module Asciidoctor
|
|
40
40
|
File.writable?(localdir) or raise "Destination path #{path} not writable for PlantUML!"
|
41
41
|
path.mkpath
|
42
42
|
File.writable?(path) or raise "Destination path #{path} not writable for PlantUML!"
|
43
|
-
#File.exist?(path) or raise "Destination path #{path} already exists for PlantUML!"
|
43
|
+
# File.exist?(path) or raise "Destination path #{path} already exists for PlantUML!"
|
44
44
|
|
45
45
|
# Warning: metanorma/metanorma-standoc#187
|
46
46
|
# Windows Ruby 2.4 will crash if a Tempfile is "mv"ed.
|
@@ -51,21 +51,21 @@ module Asciidoctor
|
|
51
51
|
imagesdir ? filename : File.join(path, filename)
|
52
52
|
end
|
53
53
|
|
54
|
-
def self.save_plantuml
|
54
|
+
def self.save_plantuml(_parent, reader, _localdir)
|
55
55
|
src = reader.source
|
56
56
|
reader.lines.first.sub(/\s+$/, "").match /^@startuml($| )/ or
|
57
57
|
src = "@startuml\n#{src}\n@enduml\n"
|
58
58
|
/^@startuml (?<fn>[^\n]+)\n/ =~ src
|
59
|
-
Tempfile.open(["plantuml", ".pml"], :
|
59
|
+
Tempfile.open(["plantuml", ".pml"], encoding: "utf-8") do |f|
|
60
60
|
f.write(src)
|
61
61
|
[f, File.join(File.dirname(f.path),
|
62
62
|
(fn || File.basename(f.path, ".pml")) + ".png")]
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
-
def self.generate_attrs
|
67
|
-
|
68
|
-
inject({}) do |memo, key|
|
66
|
+
def self.generate_attrs(attrs)
|
67
|
+
%w(id align float title role width height alt)
|
68
|
+
.inject({}) do |memo, key|
|
69
69
|
memo[key] = attrs[key] if attrs.has_key? key
|
70
70
|
memo
|
71
71
|
end
|
@@ -81,19 +81,17 @@ module Asciidoctor
|
|
81
81
|
def abort(parent, reader, attrs, msg)
|
82
82
|
warn msg
|
83
83
|
attrs["language"] = "plantuml"
|
84
|
-
create_listing_block parent, reader.source, attrs.reject { |k,
|
84
|
+
create_listing_block parent, reader.source, attrs.reject { |k, _v| k == 1 }
|
85
85
|
end
|
86
86
|
|
87
87
|
def process(parent, reader, attrs)
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
abort(parent, reader, attrs, e.message)
|
96
|
-
end
|
88
|
+
PlantUMLBlockMacroBackend.plantuml_installed?
|
89
|
+
filename = PlantUMLBlockMacroBackend.generate_file(parent, reader)
|
90
|
+
through_attrs = PlantUMLBlockMacroBackend.generate_attrs attrs
|
91
|
+
through_attrs["target"] = filename
|
92
|
+
create_image_block parent, through_attrs
|
93
|
+
rescue StandardError => e
|
94
|
+
abort(parent, reader, attrs, e.message)
|
97
95
|
end
|
98
96
|
end
|
99
97
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "csv"
|
2
|
+
|
1
3
|
module Asciidoctor
|
2
4
|
module Standoc
|
3
5
|
class AltTermInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
@@ -36,46 +38,54 @@ module Asciidoctor
|
|
36
38
|
end
|
37
39
|
end
|
38
40
|
|
39
|
-
# Macro to transform `term[X,Y]` into em, termxref xml
|
40
41
|
class TermRefInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
41
42
|
use_dsl
|
42
43
|
named :term
|
43
|
-
name_positional_attributes
|
44
|
+
name_positional_attributes "name", "termxref"
|
44
45
|
using_format :short
|
45
46
|
|
46
47
|
def process(_parent, _target, attrs)
|
47
|
-
termref = attrs[
|
48
|
-
"<
|
48
|
+
termref = attrs["termxref"] || attrs["name"]
|
49
|
+
"<concept><termxref>#{attrs['name']}</termxref>"\
|
50
|
+
"<renderterm>#{termref}</renderterm><xrefrender/></concept>"
|
49
51
|
end
|
50
52
|
end
|
51
53
|
|
54
|
+
# Possibilities:
|
55
|
+
# {{<<id>>, term}}
|
56
|
+
# {{<<id>>, term, text}}
|
57
|
+
# {{<<termbase:id>>, term}}
|
58
|
+
# {{<<termbase:id>>, term, text}}
|
59
|
+
# {{term}} equivalent to term:[term]
|
60
|
+
# {{term, text}} equivalent to term:[term, text]
|
61
|
+
# text may optionally be followed by crossreference-rendering, options=""
|
52
62
|
class ConceptInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
|
53
63
|
use_dsl
|
54
64
|
named :concept
|
55
|
-
name_positional_attributes "id", "word", "term"
|
56
|
-
# match %r{concept:(?<target>[^\[]*)\[(?<content>|.*?[^\\])\]$}
|
57
65
|
match /\{\{(?<content>|.*?[^\\])\}\}/
|
58
66
|
using_format :short
|
59
67
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
attrs[
|
66
|
-
attrs[
|
67
|
-
|
68
|
+
def preprocess_attrs(target)
|
69
|
+
m = /^(?<id><<.+?>>)?(?<rest>.*)$/.match(target)
|
70
|
+
ret = { id: m[:id]&.sub(/^<</, "")&.sub(/>>$/, "") }
|
71
|
+
m2 = /^(?<rest>.*)(?<opt>,option=.+)?$/.match(m[:rest].sub(/^,/, ""))
|
72
|
+
ret[:opt] = m2[:opt]&.sub(/^,option=/, "")
|
73
|
+
attrs = CSV.parse_line(m2[:rest]) || []
|
74
|
+
ret.merge(term: attrs[0], word: attrs[1] || attrs[0],
|
75
|
+
xrefrender: attrs[2])
|
68
76
|
end
|
69
77
|
|
70
|
-
def process(parent,
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
78
|
+
def process(parent, target, _attrs)
|
79
|
+
attrs = preprocess_attrs(target)
|
80
|
+
termout = Asciidoctor::Inline.new(parent, :quoted, attrs[:term]).convert
|
81
|
+
wordout = Asciidoctor::Inline.new(parent, :quoted, attrs[:word]).convert
|
82
|
+
xrefout = Asciidoctor::Inline.new(parent, :quoted,
|
83
|
+
attrs[:xrefrender]).convert
|
84
|
+
attrs[:id] and return "<concept key='#{attrs[:id]}'><refterm>"\
|
85
|
+
"#{termout}</refterm><renderterm>#{wordout}</renderterm>"\
|
86
|
+
"<xrefrender>#{xrefout}</xrefrender></concept>"
|
87
|
+
"<concept><termxref>#{termout}</termxref><renderterm>#{wordout}"\
|
88
|
+
"</renderterm><xrefrender>#{xrefout}</xrefrender></concept>"
|
79
89
|
end
|
80
90
|
end
|
81
91
|
end
|
@@ -3,8 +3,6 @@
|
|
3
3
|
module Asciidoctor
|
4
4
|
module Standoc
|
5
5
|
# Intelligent term lookup xml modifier
|
6
|
-
# Lookup all `term` and `calause` tags and replace `termxref` tags with
|
7
|
-
# `xref`:target tag
|
8
6
|
class TermLookupCleanup
|
9
7
|
AUTOMATIC_GENERATED_ID_REGEXP = /\A_/.freeze
|
10
8
|
EXISTING_TERM_REGEXP = /\Aterm-/.freeze
|
@@ -41,7 +39,9 @@ module Asciidoctor
|
|
41
39
|
remove_missing_ref(node, target)
|
42
40
|
next
|
43
41
|
end
|
44
|
-
|
42
|
+
x = node.at("../xrefrender")
|
43
|
+
modify_ref_node(x, target)
|
44
|
+
node.name = "refterm"
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
@@ -49,20 +49,18 @@ module Asciidoctor
|
|
49
49
|
log.add("AsciiDoc Input", node,
|
50
50
|
%(Error: Term reference in `term[#{target}]` missing: \
|
51
51
|
"#{target}" is not defined in document))
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
node.
|
58
|
-
|
52
|
+
node.name = "strong"
|
53
|
+
node.at("../xrefrender").remove
|
54
|
+
display = node&.at("../renderterm")&.remove&.children
|
55
|
+
display = [] if display.nil? || display&.to_xml == node.text
|
56
|
+
d = display.empty? ? "" : ", display <tt>#{display.to_xml}</tt>"
|
57
|
+
node.children = "term <tt>#{node.text}</tt>#{d} "\
|
58
|
+
"not resolved via ID <tt>#{target}</tt>"
|
59
59
|
end
|
60
60
|
|
61
61
|
def modify_ref_node(node, target)
|
62
62
|
node.name = "xref"
|
63
63
|
node["target"] = termlookup[target]
|
64
|
-
node.children.remove
|
65
|
-
node.remove_attribute("defaultref")
|
66
64
|
end
|
67
65
|
|
68
66
|
def replace_automatic_generated_ids_terms
|
@@ -19,10 +19,10 @@ module Asciidoctor
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def iev_validate(xmldoc)
|
22
|
+
@iev = init_iev or return
|
22
23
|
xmldoc.xpath("//term").each do |t|
|
23
24
|
/^IEC 60050-/.match(t&.at("./termsource/origin/@citeas")&.text) &&
|
24
25
|
loc = t.xpath(SOURCELOCALITY)&.text or next
|
25
|
-
@iev = init_iev or return
|
26
26
|
iev = @iev.fetch(loc, xmldoc&.at("//language")&.text || "en") or next
|
27
27
|
pref = t.xpath("./preferred").inject([]) do |m, x|
|
28
28
|
m << x&.text&.downcase
|
@@ -38,6 +38,7 @@ module Asciidoctor
|
|
38
38
|
norm_ref_validate(doc)
|
39
39
|
repeat_id_validate(doc.root)
|
40
40
|
iev_validate(doc.root)
|
41
|
+
concept_validate(doc)
|
41
42
|
end
|
42
43
|
|
43
44
|
def norm_ref_validate(doc)
|
@@ -54,6 +55,20 @@ module Asciidoctor
|
|
54
55
|
clean_abort("Numeric reference in normative references", doc.to_xml)
|
55
56
|
end
|
56
57
|
|
58
|
+
def concept_validate(doc)
|
59
|
+
found = false
|
60
|
+
doc.xpath("//concept/xref").each do |x|
|
61
|
+
next if doc.at("//term[@id = '#{x['target']}']")
|
62
|
+
|
63
|
+
ref = x&.at("../refterm")&.text
|
64
|
+
@log.add("Anchors", x, "Concept #{ref} is pointing to "\
|
65
|
+
"#{x['target']}, which is not a term")
|
66
|
+
found = true
|
67
|
+
end
|
68
|
+
found and
|
69
|
+
clean_abort("Concept not cross-referencing term", doc.to_xml)
|
70
|
+
end
|
71
|
+
|
57
72
|
def repeat_id_validate1(ids, elem)
|
58
73
|
if ids[elem["id"]]
|
59
74
|
@log.add("Anchors", elem, "Anchor #{elem['id']} has already been "\
|
@@ -78,13 +93,11 @@ module Asciidoctor
|
|
78
93
|
|
79
94
|
def schema_validate(doc, schema)
|
80
95
|
Tempfile.open(["tmp", ".xml"], encoding: "UTF-8") do |f|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
f.close!
|
87
|
-
end
|
96
|
+
schema_validate1(f, doc, schema)
|
97
|
+
rescue Jing::Error => e
|
98
|
+
clean_abort("Jing failed with error: #{e}", doc.to_xml)
|
99
|
+
ensure
|
100
|
+
f.close!
|
88
101
|
end
|
89
102
|
end
|
90
103
|
|
data/metanorma-standoc.gemspec
CHANGED
@@ -24,11 +24,11 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.require_paths = ["lib"]
|
25
25
|
spec.files = `git ls-files`.split("\n")
|
26
26
|
spec.test_files = `git ls-files -- {spec}/*`.split("\n")
|
27
|
-
spec.required_ruby_version = Gem::Requirement.new(">= 2.
|
27
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
|
28
28
|
|
29
29
|
spec.add_dependency "asciidoctor", "~> 2.0.0"
|
30
30
|
spec.add_dependency "iev", "~> 0.2.1"
|
31
|
-
spec.add_dependency "isodoc", "~> 1.
|
31
|
+
spec.add_dependency "isodoc", "~> 1.7.0"
|
32
32
|
spec.add_dependency "metanorma-plugin-datastruct"
|
33
33
|
spec.add_dependency "metanorma-plugin-lutaml"
|
34
34
|
spec.add_dependency "ruby-jing"
|
@@ -1051,13 +1051,13 @@ RSpec.describe Asciidoctor::Standoc do
|
|
1051
1051
|
Definition
|
1052
1052
|
|
1053
1053
|
[.source]
|
1054
|
-
{{IEV:xyz}}
|
1054
|
+
{{<<IEV:xyz>>}}
|
1055
1055
|
|
1056
1056
|
[.source]
|
1057
|
-
{{IEV:xyz
|
1057
|
+
{{<<IEV:xyz>>,t1}}
|
1058
1058
|
|
1059
1059
|
[.source]
|
1060
|
-
{{IEV:xyz
|
1060
|
+
{{<<IEV:xyz>>,t1,t2}}
|
1061
1061
|
INPUT
|
1062
1062
|
output = <<~OUTPUT
|
1063
1063
|
#{BLANK_HDR}
|
@@ -1094,12 +1094,12 @@ RSpec.describe Asciidoctor::Standoc do
|
|
1094
1094
|
</termsource>
|
1095
1095
|
<termsource status='identical'>
|
1096
1096
|
<origin citeas=''>
|
1097
|
-
<termref base='IEV' target='xyz'
|
1097
|
+
<termref base='IEV' target='xyz'/>
|
1098
1098
|
</origin>
|
1099
1099
|
</termsource>
|
1100
1100
|
<termsource status='identical'>
|
1101
1101
|
<origin citeas=''>
|
1102
|
-
<termref base='IEV' target='xyz'
|
1102
|
+
<termref base='IEV' target='xyz'/>
|
1103
1103
|
</origin>
|
1104
1104
|
</termsource>
|
1105
1105
|
</term>
|
@@ -1126,7 +1126,7 @@ RSpec.describe Asciidoctor::Standoc do
|
|
1126
1126
|
Definition
|
1127
1127
|
|
1128
1128
|
[.source]
|
1129
|
-
{{IEV:xyz}}, with adjustments
|
1129
|
+
{{<<IEV:xyz>>}}, with adjustments
|
1130
1130
|
INPUT
|
1131
1131
|
output = <<~OUTPUT
|
1132
1132
|
#{BLANK_HDR}
|
@@ -1618,11 +1618,42 @@ RSpec.describe Asciidoctor::Standoc do
|
|
1618
1618
|
</standard-document>
|
1619
1619
|
INPUT
|
1620
1620
|
output = <<~OUTPUT
|
1621
|
-
|
1622
|
-
|
1623
|
-
|
1621
|
+
#{BLANK_HDR}
|
1622
|
+
<sections>
|
1623
|
+
<stem type="MathML"><math xmlns="http://www.w3.org/1998/Math/MathML"><mfrac><mn>1</mn><mi>r</mi></mfrac></math></stem>
|
1624
1624
|
</sections>
|
1625
|
-
|
1625
|
+
</standard-document>
|
1626
|
+
OUTPUT
|
1627
|
+
expect(Asciidoctor::Standoc::Converter.new(nil, *OPTIONS)
|
1628
|
+
.cleanup(Nokogiri::XML(input)).to_xml)
|
1629
|
+
.to be_equivalent_to xmlpp(output)
|
1630
|
+
end
|
1631
|
+
|
1632
|
+
it "removes nested bibitem IDs" do
|
1633
|
+
input = <<~INPUT
|
1634
|
+
#{BLANK_HDR}
|
1635
|
+
<bibliography>
|
1636
|
+
<references normative="true"><title>Normative</title>
|
1637
|
+
<bibitem id="A">
|
1638
|
+
<relation type="includes">
|
1639
|
+
<bibitem id="B"/>
|
1640
|
+
</relation>
|
1641
|
+
</bibitem>
|
1642
|
+
</bibliography>
|
1643
|
+
</standard-document>
|
1644
|
+
INPUT
|
1645
|
+
output = <<~OUTPUT
|
1646
|
+
#{BLANK_HDR}
|
1647
|
+
<bibliography>
|
1648
|
+
<references normative="true"><title>Normative</title>
|
1649
|
+
<bibitem id="A">
|
1650
|
+
<relation type="includes">
|
1651
|
+
<bibitem id="B"/>
|
1652
|
+
</relation>
|
1653
|
+
</bibitem>
|
1654
|
+
</references>
|
1655
|
+
</bibliography>
|
1656
|
+
</standard-document>
|
1626
1657
|
OUTPUT
|
1627
1658
|
expect(Asciidoctor::Standoc::Converter.new(nil, *OPTIONS)
|
1628
1659
|
.cleanup(Nokogiri::XML(input)).to_xml)
|
@@ -1879,7 +1910,7 @@ RSpec.describe Asciidoctor::Standoc do
|
|
1879
1910
|
.to be_equivalent_to xmlpp(output)
|
1880
1911
|
end
|
1881
1912
|
|
1882
|
-
it "sorts symbols lists" do
|
1913
|
+
it "sorts symbols lists #1" do
|
1883
1914
|
input = <<~INPUT
|
1884
1915
|
#{ASCIIDOC_BLANK_HDR}
|
1885
1916
|
|
@@ -1943,7 +1974,7 @@ RSpec.describe Asciidoctor::Standoc do
|
|
1943
1974
|
.to be_equivalent_to xmlpp(output)
|
1944
1975
|
end
|
1945
1976
|
|
1946
|
-
it "sorts symbols lists" do
|
1977
|
+
it "sorts symbols lists #2" do
|
1947
1978
|
input = <<~INPUT
|
1948
1979
|
#{ASCIIDOC_BLANK_HDR}
|
1949
1980
|
|