metanorma-ogc 1.4.1 → 1.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/asciidoctor/ogc/cleanup.rb +145 -0
- data/lib/asciidoctor/ogc/converter.rb +9 -76
- data/lib/asciidoctor/ogc/isodoc.rng +17 -2
- data/lib/asciidoctor/ogc/validate.rb +35 -20
- data/lib/isodoc/ogc/base_convert.rb +47 -10
- data/lib/isodoc/ogc/html/htmlstyle.css +10 -0
- data/lib/isodoc/ogc/html/htmlstyle.scss +12 -0
- data/lib/isodoc/ogc/html/ogc.css +4 -0
- data/lib/isodoc/ogc/html/ogc.scss +4 -0
- data/lib/isodoc/ogc/i18n-en.yaml +10 -8
- data/lib/isodoc/ogc/ogc.abstract-specification-topic.xsl +28 -8
- data/lib/isodoc/ogc/ogc.best-practice.xsl +28 -8
- data/lib/isodoc/ogc/ogc.change-request-supporting-document.xsl +28 -8
- data/lib/isodoc/ogc/ogc.community-practice.xsl +28 -8
- data/lib/isodoc/ogc/ogc.community-standard.xsl +28 -8
- data/lib/isodoc/ogc/ogc.discussion-paper.xsl +28 -8
- data/lib/isodoc/ogc/ogc.engineering-report.xsl +28 -8
- data/lib/isodoc/ogc/ogc.other.xsl +28 -8
- data/lib/isodoc/ogc/ogc.policy.xsl +28 -8
- data/lib/isodoc/ogc/ogc.reference-model.xsl +28 -8
- data/lib/isodoc/ogc/ogc.release-notes.xsl +28 -8
- data/lib/isodoc/ogc/ogc.standard.xsl +28 -8
- data/lib/isodoc/ogc/ogc.test-suite.xsl +28 -8
- data/lib/isodoc/ogc/ogc.user-guide.xsl +28 -8
- data/lib/isodoc/ogc/ogc.white-paper.xsl +1 -0
- data/lib/isodoc/ogc/presentation_xml_convert.rb +6 -3
- data/lib/isodoc/ogc/reqt.rb +48 -36
- data/lib/isodoc/ogc/xref.rb +1 -2
- data/lib/metanorma/ogc/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aa7220c37ec4c4f57f175cc19ded9faa15b4cef238107d9dbf20fed3350e7964
|
4
|
+
data.tar.gz: 652a56390a308dc6d52c991b6492fbc733275a87b64bdc3b7ebcfd33519e7743
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6374615ac10104cb610e08015f44df88f2ba8a2d11f9830dc91cc4055a881d240d50a54f2db3fb253de8ff00a1900cfc4a6e76db5867b939c15b9abf791db35
|
7
|
+
data.tar.gz: e0c934e414bae2c2749033a9b62add64765d0adc44f72711f97c8f48eb1e15ccf311a5c46d7538cdaf5f91acfd39b2e13ab91432a41e593543090b19eb87aa71
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module Asciidoctor
|
2
|
+
module Ogc
|
3
|
+
class Converter < Standoc::Converter
|
4
|
+
def sections_cleanup(xml)
|
5
|
+
super
|
6
|
+
xml.xpath("//*[@inline-header]").each do |h|
|
7
|
+
h.delete("inline-header")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def make_preface(xml, sect)
|
12
|
+
super
|
13
|
+
insert_security(xml, sect)
|
14
|
+
insert_submitters(xml, sect)
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_id
|
18
|
+
%(id="_#{UUIDTools::UUID.random_create}")
|
19
|
+
end
|
20
|
+
|
21
|
+
def insert_security(xml, sect)
|
22
|
+
description = "document"
|
23
|
+
description = "standard" if %w(standard community-standard)
|
24
|
+
.include?(sect&.at("//bibdata/ext/doctype")&.text)
|
25
|
+
preface = sect.at("//preface") ||
|
26
|
+
sect.add_previous_sibling("<preface/>").first
|
27
|
+
sect = xml&.at("//clause[@type = 'security']")&.remove ||
|
28
|
+
"<clause type='security' #{add_id}>"\
|
29
|
+
"<title>Security considerations</title>"\
|
30
|
+
"<p>#{@i18n.security_empty.sub(/%/, description)}</p></clause>"
|
31
|
+
preface.add_child sect
|
32
|
+
end
|
33
|
+
|
34
|
+
def insert_submitters(xml, sect)
|
35
|
+
if xml.at("//submitters")
|
36
|
+
preface = sect.at("//preface") ||
|
37
|
+
sect.add_previous_sibling("<preface/>").first
|
38
|
+
submitters = xml.at("//submitters").remove
|
39
|
+
submitters.xpath(".//table").each do |t|
|
40
|
+
t["unnumbered"] = true
|
41
|
+
end
|
42
|
+
preface.add_child submitters.remove
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def termdef_boilerplate_cleanup(xmldoc); end
|
47
|
+
|
48
|
+
def bibdata_cleanup(xmldoc)
|
49
|
+
super
|
50
|
+
a = xmldoc.at("//bibdata/status/stage")
|
51
|
+
a.text == "published" and a.children = "approved"
|
52
|
+
end
|
53
|
+
|
54
|
+
def section_names_terms_cleanup(xml)
|
55
|
+
replace_title(xml, "//definitions[@type = 'symbols']", @i18n&.symbols)
|
56
|
+
replace_title(xml, "//definitions[@type = 'abbreviated_terms']",
|
57
|
+
@i18n&.abbrev)
|
58
|
+
replace_title(xml, "//definitions[not(@type)]", @i18n&.symbolsabbrev)
|
59
|
+
replace_title(xml, "//sections//terms#{SYMnoABBR} | "\
|
60
|
+
"//sections//clause[.//terms]#{SYMnoABBR}",
|
61
|
+
@i18n&.termsdefsymbols, true)
|
62
|
+
replace_title(xml, "//sections//terms#{ABBRnoSYM} | "\
|
63
|
+
"//sections//clause[.//terms]#{ABBRnoSYM}",
|
64
|
+
@i18n&.termsdefabbrev, true)
|
65
|
+
replace_title(xml, "//sections//terms#{SYMABBR} | "\
|
66
|
+
"//sections//clause[.//terms]#{SYMABBR}",
|
67
|
+
@i18n&.termsdefsymbolsabbrev, true)
|
68
|
+
replace_title(xml, "//sections//terms#{NO_SYMABBR} | "\
|
69
|
+
"//sections//clause[.//terms]#{NO_SYMABBR}",
|
70
|
+
@i18n&.termsdefsymbolsabbrev, true)
|
71
|
+
replace_title(xml, "//sections//terms[not(.//definitions)] | "\
|
72
|
+
"//sections//clause[.//terms][not(.//definitions)]",
|
73
|
+
@i18n&.termsdef, true)
|
74
|
+
end
|
75
|
+
|
76
|
+
def requirement_metadata_component_tags
|
77
|
+
%w(test-purpose test-method conditions part description reference
|
78
|
+
requirement permission recommendation)
|
79
|
+
end
|
80
|
+
|
81
|
+
def requirement_metadata1(reqt, dlist)
|
82
|
+
ins = super
|
83
|
+
dlist.xpath("./dt").each do |e|
|
84
|
+
next unless requirement_metadata_component_tags.include? e.text
|
85
|
+
|
86
|
+
ins.next = requirement_metadata1_component(e)
|
87
|
+
ins = ins.next
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def requirement_metadata1_component(term)
|
92
|
+
val = term.at("./following::dd")
|
93
|
+
val.name = term.text
|
94
|
+
if %w(requirement permission
|
95
|
+
recommendation).include?(term.text) && !val.text.empty?
|
96
|
+
val["label"] = val.text.strip
|
97
|
+
val.children.remove
|
98
|
+
end
|
99
|
+
val
|
100
|
+
end
|
101
|
+
|
102
|
+
def requirement_metadata(xmldoc)
|
103
|
+
super
|
104
|
+
xmldoc.xpath(REQRECPER).each do |r|
|
105
|
+
requirement_metadata_to_component(r)
|
106
|
+
requirement_metadata_to_requirement(r)
|
107
|
+
requirement_subparts_to_blocks(r)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def requirement_metadata_to_component(reqt)
|
112
|
+
reqt.xpath("./test-method | ./test-purpose | ./conditions | ./part | "\
|
113
|
+
"./reference")
|
114
|
+
.each do |c|
|
115
|
+
c["class"] = c.name
|
116
|
+
c.name = "component"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def requirement_metadata_to_requirement(reqt)
|
121
|
+
reqt.xpath("./requirement | ./permission | ./recommendation")
|
122
|
+
.each do |c|
|
123
|
+
c["id"] = Metanorma::Utils::anchor_or_uuid
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def requirement_subparts_to_blocks(reqt)
|
128
|
+
reqt.xpath("./component | ./description").each do |c|
|
129
|
+
%w(p ol ul dl table).include?(c&.elements&.first&.name) and next
|
130
|
+
c.children = "<p>#{c.children.to_xml}</p>"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# skip annex/terms/terms, which is empty node
|
135
|
+
def termdef_subclause_cleanup(xmldoc)
|
136
|
+
xmldoc.xpath("//annex//terms[terms]").each do |t|
|
137
|
+
next if t.parent.name == "terms"
|
138
|
+
|
139
|
+
t.children.each { |n| n.parent = t.parent }
|
140
|
+
t.remove
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -3,6 +3,7 @@ require "asciidoctor/standoc/converter"
|
|
3
3
|
require "fileutils"
|
4
4
|
require_relative "front"
|
5
5
|
require_relative "validate"
|
6
|
+
require_relative "cleanup"
|
6
7
|
|
7
8
|
module Asciidoctor
|
8
9
|
module Ogc
|
@@ -40,6 +41,14 @@ module Asciidoctor
|
|
40
41
|
d
|
41
42
|
end
|
42
43
|
|
44
|
+
def sectiontype(node, level = true)
|
45
|
+
ret = sectiontype_streamline(sectiontype1(node))
|
46
|
+
return ret if ret == "terms and definitions" &&
|
47
|
+
node.attr("style") == "appendix" && node.level == 1
|
48
|
+
|
49
|
+
super
|
50
|
+
end
|
51
|
+
|
43
52
|
def sectiontype_streamline(ret)
|
44
53
|
case ret
|
45
54
|
when "preface" then "foreword"
|
@@ -62,55 +71,6 @@ module Asciidoctor
|
|
62
71
|
false, "#{@filename}.pdf")
|
63
72
|
end
|
64
73
|
|
65
|
-
def validate(doc)
|
66
|
-
content_validate(doc)
|
67
|
-
schema_validate(formattedstr_strip(doc.dup),
|
68
|
-
File.join(File.dirname(__FILE__), "ogc.rng"))
|
69
|
-
end
|
70
|
-
|
71
|
-
def sections_cleanup(xml)
|
72
|
-
super
|
73
|
-
xml.xpath("//*[@inline-header]").each do |h|
|
74
|
-
h.delete("inline-header")
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def make_preface(xml, sect)
|
79
|
-
super
|
80
|
-
insert_security(xml, sect)
|
81
|
-
insert_submitters(xml, sect)
|
82
|
-
end
|
83
|
-
|
84
|
-
def add_id
|
85
|
-
%(id="_#{UUIDTools::UUID.random_create}")
|
86
|
-
end
|
87
|
-
|
88
|
-
def insert_security(xml, sect)
|
89
|
-
doctype = sect&.at("//bibdata/ext/doctype")&.text
|
90
|
-
description = if %w(standard
|
91
|
-
community-standard).include?(doctype)
|
92
|
-
"standard"
|
93
|
-
else
|
94
|
-
"document"
|
95
|
-
end
|
96
|
-
preface = sect.at("//preface") ||
|
97
|
-
sect.add_previous_sibling("<preface/>").first
|
98
|
-
sect = xml&.at("//clause[@type = 'security']")&.remove ||
|
99
|
-
"<clause type='security' #{add_id}>"\
|
100
|
-
"<title>Security Considerations</title>"\
|
101
|
-
"<p>#{@i18n.security_empty.sub(/%/, description)}</p></clause>"
|
102
|
-
preface.add_child sect
|
103
|
-
end
|
104
|
-
|
105
|
-
def insert_submitters(xml, sect)
|
106
|
-
if xml.at("//submitters")
|
107
|
-
preface = sect.at("//preface") ||
|
108
|
-
sect.add_previous_sibling("<preface/>").first
|
109
|
-
submitters = xml.at("//submitters").remove
|
110
|
-
preface.add_child submitters.remove
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
74
|
def clause_parse(attrs, xml, node)
|
115
75
|
case node&.attr("heading")&.downcase || node.title.downcase
|
116
76
|
when "submitters" then return submitters_parse(attrs, xml, node)
|
@@ -132,8 +92,6 @@ module Asciidoctor
|
|
132
92
|
nil
|
133
93
|
end
|
134
94
|
|
135
|
-
def termdef_boilerplate_cleanup(xmldoc); end
|
136
|
-
|
137
95
|
def term_def_parse(attrs, xml, node, _toplevel)
|
138
96
|
if node.attr("style") == "appendix" && node.level == 1
|
139
97
|
terms_annex_parse(attrs, xml, node)
|
@@ -160,31 +118,6 @@ module Asciidoctor
|
|
160
118
|
end
|
161
119
|
end
|
162
120
|
|
163
|
-
def bibdata_cleanup(xmldoc)
|
164
|
-
super
|
165
|
-
a = xmldoc.at("//bibdata/status/stage")
|
166
|
-
a.text == "published" and a.children = "approved"
|
167
|
-
end
|
168
|
-
|
169
|
-
def section_names_terms_cleanup(xml)
|
170
|
-
replace_title(xml, "//definitions[@type = 'symbols']", @i18n&.symbols)
|
171
|
-
replace_title(xml, "//definitions[@type = 'abbreviated_terms']",
|
172
|
-
@i18n&.abbrev)
|
173
|
-
replace_title(xml, "//definitions[not(@type)]", @i18n&.symbolsabbrev)
|
174
|
-
replace_title(xml, "//sections//terms#{SYMnoABBR} | //sections//clause[.//terms]#{SYMnoABBR}",
|
175
|
-
@i18n&.termsdefsymbols, true)
|
176
|
-
replace_title(xml, "//sections//terms#{ABBRnoSYM} | //sections//clause[.//terms]#{ABBRnoSYM}",
|
177
|
-
@i18n&.termsdefabbrev, true)
|
178
|
-
replace_title(xml, "//sections//terms#{SYMABBR} | //sections//clause[.//terms]#{SYMABBR}",
|
179
|
-
@i18n&.termsdefsymbolsabbrev, true)
|
180
|
-
replace_title(xml, "//sections//terms#{NO_SYMABBR} | //sections//clause[.//terms]#{NO_SYMABBR}",
|
181
|
-
@i18n&.termsdefsymbolsabbrev, true)
|
182
|
-
replace_title(
|
183
|
-
xml, "//sections//terms[not(.//definitions)] | //sections//clause[.//terms][not(.//definitions)]",
|
184
|
-
@i18n&.termsdef, true
|
185
|
-
)
|
186
|
-
end
|
187
|
-
|
188
121
|
def highlight_parse(text, xml)
|
189
122
|
xml.hi { |s| s << text }
|
190
123
|
end
|
@@ -990,6 +990,16 @@
|
|
990
990
|
<data type="boolean"/>
|
991
991
|
</attribute>
|
992
992
|
</optional>
|
993
|
+
<optional>
|
994
|
+
<attribute name="linkmention">
|
995
|
+
<data type="boolean"/>
|
996
|
+
</attribute>
|
997
|
+
</optional>
|
998
|
+
<optional>
|
999
|
+
<attribute name="linkref">
|
1000
|
+
<data type="boolean"/>
|
1001
|
+
</attribute>
|
1002
|
+
</optional>
|
993
1003
|
<optional>
|
994
1004
|
<element name="refterm">
|
995
1005
|
<zeroOrMore>
|
@@ -1700,7 +1710,9 @@
|
|
1700
1710
|
<zeroOrMore>
|
1701
1711
|
<ref name="termgrammar"/>
|
1702
1712
|
</zeroOrMore>
|
1703
|
-
<
|
1713
|
+
<oneOrMore>
|
1714
|
+
<ref name="termdefinition"/>
|
1715
|
+
</oneOrMore>
|
1704
1716
|
<zeroOrMore>
|
1705
1717
|
<ref name="termnote"/>
|
1706
1718
|
</zeroOrMore>
|
@@ -1763,7 +1775,7 @@
|
|
1763
1775
|
</oneOrMore>
|
1764
1776
|
</element>
|
1765
1777
|
</define>
|
1766
|
-
<define name="
|
1778
|
+
<define name="termdefinition">
|
1767
1779
|
<element name="definition">
|
1768
1780
|
<oneOrMore>
|
1769
1781
|
<choice>
|
@@ -1772,6 +1784,9 @@
|
|
1772
1784
|
<ref name="formula"/>
|
1773
1785
|
</choice>
|
1774
1786
|
</oneOrMore>
|
1787
|
+
<zeroOrMore>
|
1788
|
+
<ref name="termsource"/>
|
1789
|
+
</zeroOrMore>
|
1775
1790
|
</element>
|
1776
1791
|
</define>
|
1777
1792
|
<define name="termnote">
|
@@ -1,7 +1,13 @@
|
|
1
1
|
module Asciidoctor
|
2
2
|
module Ogc
|
3
3
|
class Converter < Standoc::Converter
|
4
|
-
def
|
4
|
+
def validate(doc)
|
5
|
+
content_validate(doc)
|
6
|
+
schema_validate(formattedstr_strip(doc.dup),
|
7
|
+
File.join(File.dirname(__FILE__), "ogc.rng"))
|
8
|
+
end
|
9
|
+
|
10
|
+
def title_validate(_root)
|
5
11
|
nil
|
6
12
|
end
|
7
13
|
|
@@ -18,8 +24,9 @@ module Asciidoctor
|
|
18
24
|
def stage_validate(xmldoc)
|
19
25
|
stage = xmldoc&.at("//bibdata/status/stage")&.text
|
20
26
|
%w(swg-draft oab-review public-rfc tc-vote
|
21
|
-
|
22
|
-
|
27
|
+
approved deprecated retired).include? stage or
|
28
|
+
@log.add("Document Attributes", nil,
|
29
|
+
"#{stage} is not a recognised status")
|
23
30
|
end
|
24
31
|
|
25
32
|
def version_validate(xmldoc)
|
@@ -27,10 +34,12 @@ module Asciidoctor
|
|
27
34
|
doctype = xmldoc&.at("//bibdata/ext/doctype")&.text
|
28
35
|
if %w(engineering-report discussion-paper).include? doctype
|
29
36
|
version.nil? or
|
30
|
-
|
37
|
+
@log.add("Document Attributes", nil,
|
38
|
+
"Version not permitted for #{doctype}")
|
31
39
|
else
|
32
40
|
version.nil? and
|
33
|
-
|
41
|
+
@log.add("Document Attributes", nil,
|
42
|
+
"Version required for #{doctype}")
|
34
43
|
end
|
35
44
|
end
|
36
45
|
|
@@ -41,7 +50,7 @@ module Asciidoctor
|
|
41
50
|
end
|
42
51
|
|
43
52
|
STANDARDTYPE = %w{standard standard-with-suite abstract-specification
|
44
|
-
|
53
|
+
community-standard profile}.freeze
|
45
54
|
|
46
55
|
# spec of permissible section sequence
|
47
56
|
# we skip normative references, it goes to end of list
|
@@ -49,24 +58,25 @@ module Asciidoctor
|
|
49
58
|
[
|
50
59
|
{
|
51
60
|
msg: "Prefatory material must be followed by (clause) Scope",
|
52
|
-
val: ["./self::clause[@type = 'scope']"
|
61
|
+
val: ["./self::clause[@type = 'scope']"],
|
53
62
|
},
|
54
63
|
{
|
55
64
|
msg: "Scope must be followed by Conformance",
|
56
|
-
val: ["./self::clause[@type = 'conformance']"
|
65
|
+
val: ["./self::clause[@type = 'conformance']"],
|
57
66
|
},
|
58
67
|
{
|
59
68
|
msg: "Normative References must be followed by "\
|
60
|
-
|
61
|
-
val: ["./self::terms | .//terms"]
|
69
|
+
"Terms and Definitions",
|
70
|
+
val: ["./self::terms | .//terms"],
|
62
71
|
},
|
63
|
-
|
72
|
+
].freeze
|
64
73
|
|
65
74
|
def seqcheck(names, msg, accepted)
|
66
75
|
n = names.shift
|
67
76
|
return [] if n.nil?
|
77
|
+
|
68
78
|
test = accepted.map { |a| n.at(a) }
|
69
|
-
if test.all?
|
79
|
+
if test.all?(&:nil?)
|
70
80
|
@log.add("Style", nil, msg)
|
71
81
|
end
|
72
82
|
names
|
@@ -74,7 +84,9 @@ module Asciidoctor
|
|
74
84
|
|
75
85
|
def sections_sequence_validate(root)
|
76
86
|
return unless STANDARDTYPE.include?(
|
77
|
-
root&.at("//bibdata/ext/doctype")&.text
|
87
|
+
root&.at("//bibdata/ext/doctype")&.text,
|
88
|
+
)
|
89
|
+
|
78
90
|
names = root.xpath("//sections/* | //bibliography/*")
|
79
91
|
names = seqcheck(names, SEQ[0][:msg], SEQ[0][:val])
|
80
92
|
names = seqcheck(names, SEQ[1][:msg], SEQ[1][:val])
|
@@ -84,25 +96,28 @@ module Asciidoctor
|
|
84
96
|
n = names.shift
|
85
97
|
end
|
86
98
|
if n.nil? || n.name != "clause"
|
87
|
-
@log.add("Style", nil,
|
99
|
+
@log.add("Style", nil,
|
100
|
+
"Document must contain at least one clause")
|
88
101
|
return
|
89
102
|
end
|
90
103
|
root.at("//references | //clause[descendant::references]"\
|
91
104
|
"[not(parent::clause)]") or
|
92
|
-
|
105
|
+
@log.add("Style", nil, "Normative References are mandatory")
|
93
106
|
end
|
94
107
|
|
95
108
|
def preface_sequence_validate(root)
|
96
|
-
root.at("//preface/abstract") or @log.add("Style", nil,
|
109
|
+
root.at("//preface/abstract") or @log.add("Style", nil,
|
110
|
+
"Abstract is missing!")
|
97
111
|
root.at("//bibdata/keyword | //bibdata/ext/keyword") or
|
98
112
|
@log.add("Style", nil, "Keywords are missing!")
|
99
|
-
root.at("//foreword") or @log.add("Style", nil,
|
113
|
+
root.at("//foreword") or @log.add("Style", nil,
|
114
|
+
"Preface is missing!")
|
100
115
|
root.at("//bibdata/contributor[role/@type = 'author']/organization/"\
|
101
116
|
"name") or
|
102
|
-
|
103
|
-
root.at("//submitters") or @log.add("Style", nil,
|
117
|
+
@log.add("Style", nil, "Submitting Organizations is missing!")
|
118
|
+
root.at("//submitters") or @log.add("Style", nil,
|
119
|
+
"Submitters is missing!")
|
104
120
|
end
|
105
121
|
end
|
106
122
|
end
|
107
123
|
end
|
108
|
-
|