metanorma-nist 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.hound.yml +3 -0
  3. data/.rubocop.yml +10 -0
  4. data/.travis.yml +16 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +25 -0
  8. data/README.adoc +358 -0
  9. data/bin/console +14 -0
  10. data/bin/rspec +18 -0
  11. data/bin/setup +8 -0
  12. data/lib/asciidoctor/nist.rb +7 -0
  13. data/lib/asciidoctor/nist/biblio.rng +921 -0
  14. data/lib/asciidoctor/nist/converter.rb +271 -0
  15. data/lib/asciidoctor/nist/front.rb +150 -0
  16. data/lib/asciidoctor/nist/isodoc.rng +1063 -0
  17. data/lib/asciidoctor/nist/isostandard.rng +1071 -0
  18. data/lib/asciidoctor/nist/nist.rng +196 -0
  19. data/lib/isodoc/nist/html/commerce-logo-color.png +0 -0
  20. data/lib/isodoc/nist/html/deptofcommerce.png +0 -0
  21. data/lib/isodoc/nist/html/header.html +163 -0
  22. data/lib/isodoc/nist/html/html_nist_intro.html +46 -0
  23. data/lib/isodoc/nist/html/html_nist_titlepage.html +140 -0
  24. data/lib/isodoc/nist/html/htmlstyle.scss +1160 -0
  25. data/lib/isodoc/nist/html/logo.png +0 -0
  26. data/lib/isodoc/nist/html/nist.scss +749 -0
  27. data/lib/isodoc/nist/html/scripts.html +82 -0
  28. data/lib/isodoc/nist/html/scripts.pdf.html +70 -0
  29. data/lib/isodoc/nist/html/word_nist_intro.html +142 -0
  30. data/lib/isodoc/nist/html/word_nist_titlepage.html +247 -0
  31. data/lib/isodoc/nist/html/wordstyle.scss +1134 -0
  32. data/lib/isodoc/nist/html_convert.rb +454 -0
  33. data/lib/isodoc/nist/i18n-en.yaml +3 -0
  34. data/lib/isodoc/nist/metadata.rb +116 -0
  35. data/lib/isodoc/nist/pdf_convert.rb +456 -0
  36. data/lib/isodoc/nist/word_convert.rb +472 -0
  37. data/lib/metanorma-nist.rb +11 -0
  38. data/lib/metanorma/nist.rb +7 -0
  39. data/lib/metanorma/nist/processor.rb +43 -0
  40. data/lib/metanorma/nist/version.rb +5 -0
  41. data/metanorma-nist.gemspec +44 -0
  42. metadata +310 -0
@@ -0,0 +1,3 @@
1
+ clause: Section
2
+ annex: Appendix
3
+ draft_label: Revision
@@ -0,0 +1,116 @@
1
+ require "isodoc"
2
+
3
+ module IsoDoc
4
+ module NIST
5
+
6
+ class Metadata < IsoDoc::Metadata
7
+ def initialize(lang, script, labels)
8
+ super
9
+ set(:status, "XXX")
10
+ end
11
+
12
+ def title(isoxml, _out)
13
+ main = isoxml&.at(ns("//bibdata/title[@language='en']"))&.text
14
+ set(:doctitle, main)
15
+ end
16
+
17
+ def subtitle(isoxml, _out)
18
+ main = isoxml&.at(ns("//bibdata/subtitle[@language='en']"))&.text
19
+ set(:docsubtitle, main)
20
+ end
21
+
22
+ def author(isoxml, _out)
23
+ tc = isoxml.at(ns("//bibdata/editorialgroup/committee"))
24
+ set(:tc, tc.text.upcase) if tc
25
+ authors = isoxml.xpath(ns("//bibdata/contributor[role/@type = 'author' "\
26
+ "or xmlns:role/@type = 'editor']/person/name"))
27
+ set(:authors, extract_person_names(authors))
28
+ end
29
+
30
+ def extract_person_names(authors)
31
+ ret = []
32
+ authors.each do |a|
33
+ if a.at(ns("./completename"))
34
+ ret << a.at(ns("./completename")).text
35
+ else
36
+ fn = []
37
+ forenames = a.xpath(ns("./forename"))
38
+ forenames.each { |f| fn << f.text }
39
+ surname = a&.at(ns("./surname"))&.text
40
+ ret << fn.join(" ") + " " + surname
41
+ end
42
+ end
43
+ ret
44
+ end
45
+
46
+ def docid(isoxml, _out)
47
+ docnumber_node = isoxml.at(ns("//bibdata/docidentifier"))
48
+ docnumber = docnumber_node&.text
49
+ set(:docnumber, docnumber)
50
+ # TODO: for NIST SPs only!!!
51
+ docnumber and set(:docnumber_long,
52
+ docnumber.gsub("NIST SP", "NIST Special Publication"))
53
+ end
54
+
55
+ def status_abbr(status)
56
+ case status
57
+ when "working-draft" then "wd"
58
+ when "committee-draft" then "cd"
59
+ when "draft-standard" then "d"
60
+ else
61
+ ""
62
+ end
63
+ end
64
+
65
+ def draftinfo(draft, revdate)
66
+ draftinfo = ""
67
+ if draft
68
+ draftinfo = " #{@labels["draft_label"]} #{draft}"
69
+ #draftinfo += ", #{revdate}" if revdate
70
+ end
71
+ IsoDoc::Function::I18n::l10n(draftinfo, @lang, @script)
72
+ end
73
+
74
+ def version(isoxml, _out)
75
+ super
76
+ revdate = get[:revdate]
77
+ set(:revdate_monthyear, monthyr(revdate))
78
+ end
79
+
80
+ MONTHS = {
81
+ "01": "January",
82
+ "02": "February",
83
+ "03": "March",
84
+ "04": "April",
85
+ "05": "May",
86
+ "06": "June",
87
+ "07": "July",
88
+ "08": "August",
89
+ "09": "September",
90
+ "10": "October",
91
+ "11": "November",
92
+ "12": "December",
93
+ }.freeze
94
+
95
+ def monthyr(isodate)
96
+ m = /(?<yr>\d\d\d\d)-(?<mo>\d\d)/.match isodate
97
+ return isodate unless m && m[:yr] && m[:mo]
98
+ return "#{MONTHS[m[:mo].to_sym]} #{m[:yr]}"
99
+ end
100
+
101
+ def keywords(isoxml, _out)
102
+ keywords = []
103
+ isoxml.xpath(ns("//bibdata/keyword")).each do |kw|
104
+ keywords << kw.text
105
+ end
106
+ set(:keywords, keywords)
107
+ end
108
+
109
+ def url(xml, _out)
110
+ super
111
+ a = xml.at(ns("//bibdata/source[@type = 'email']")) and set(:email, a.text)
112
+ end
113
+
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,456 @@
1
+ require "isodoc"
2
+ require_relative "metadata"
3
+ require "fileutils"
4
+
5
+ module IsoDoc
6
+ module NIST
7
+ # A {Converter} implementation that generates PDF HTML output, and a
8
+ # document schema encapsulation of the document for validation
9
+ class PdfConvert < IsoDoc::PdfConvert
10
+ def initialize(options)
11
+ @libdir = File.dirname(__FILE__)
12
+ super
13
+ end
14
+ def convert1(docxml, filename, dir)
15
+ FileUtils.cp html_doc_path('logo.png'), "logo.png"
16
+ FileUtils.cp html_doc_path('commerce-logo-color.png'), "commerce-logo-color.png"
17
+ @files_to_delete << "logo.png"
18
+ @files_to_delete << "commerce-logo-color.png"
19
+ super
20
+ end
21
+
22
+ def default_fonts(options)
23
+ {
24
+ bodyfont: (options[:script] == "Hans" ? '"SimSun",serif' : '"Libre Baskerville",serif'),
25
+ headerfont: (options[:script] == "Hans" ? '"SimHei",sans-serif' : '"Libre Baskerville",serif'),
26
+ monospacefont: '"Space Mono",monospace'
27
+ }
28
+ end
29
+
30
+ def default_file_locations(_options)
31
+ {
32
+ htmlstylesheet: html_doc_path("htmlstyle.scss"),
33
+ htmlcoverpage: html_doc_path("html_nist_titlepage.html"),
34
+ htmlintropage: html_doc_path("html_nist_intro.html"),
35
+ scripts_pdf: html_doc_path("scripts.pdf.html"),
36
+ }
37
+ end
38
+
39
+ def metadata_init(lang, script, labels)
40
+ @meta = Metadata.new(lang, script, labels)
41
+ end
42
+
43
+ def html_head()
44
+ <<~HEAD.freeze
45
+ <title>{{ doctitle }}</title>
46
+ <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
47
+
48
+ <!--TOC script import-->
49
+ <script type="text/javascript" src="https://cdn.rawgit.com/jgallen23/toc/0.3.2/dist/toc.min.js"></script>
50
+
51
+ <!--Google fonts-->
52
+ <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,600,600i" rel="stylesheet">
53
+ <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i|Space+Mono:400,700" rel="stylesheet" />
54
+ <link href="https://fonts.googleapis.com/css?family=Libre+Baskerville:400,400i,700,700i" rel="stylesheet">
55
+
56
+ <!--Font awesome import for the link icon-->
57
+ <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.8/css/solid.css" integrity="sha384-v2Tw72dyUXeU3y4aM2Y0tBJQkGfplr39mxZqlTBDUZAb9BGoC40+rdFCG0m10lXk" crossorigin="anonymous">
58
+ <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.8/css/fontawesome.css" integrity="sha384-q3jl8XQu1OpdLgGFvNRnPdj5VIlCvgsDQTQB6owSOHWlAurxul7f+JpUOVdAiJ5P" crossorigin="anonymous">
59
+ <style class="anchorjs"></style>
60
+ HEAD
61
+ end
62
+
63
+ def make_body(xml, docxml)
64
+ body_attr = { lang: "EN-US", link: "blue", vlink: "#954F72", "xml:lang": "EN-US", class: "container" }
65
+ xml.body **body_attr do |body|
66
+ make_body1(body, docxml)
67
+ make_body2(body, docxml)
68
+ make_body3(body, docxml)
69
+ end
70
+ end
71
+
72
+ def html_toc(docxml)
73
+ docxml
74
+ end
75
+
76
+ def make_body3(body, docxml)
77
+ body.div **{ class: "main-section" } do |div3|
78
+ abstract docxml, div3
79
+ keywords docxml, div3
80
+ preface docxml, div3
81
+ middle docxml, div3
82
+ footnotes div3
83
+ comments div3
84
+ end
85
+ end
86
+
87
+ def abstract(isoxml, out)
88
+ f = isoxml.at(ns("//preface/abstract")) || return
89
+ page_break(out)
90
+ out.div **attr_code(id: f["id"]) do |s|
91
+ clause_name(nil, @abstract_lbl, s, class: "AbstractTitle")
92
+ f.elements.each { |e| parse(e, s) unless e.name == "title" }
93
+ end
94
+ end
95
+
96
+ def keywords(_docxml, out)
97
+ kw = @meta.get[:keywords]
98
+ kw.empty? and return
99
+ out.div **{ class: "Section3" } do |div|
100
+ clause_name(nil, "Keywords", div, class: "IntroTitle")
101
+ div.p kw.sort.join("; ")
102
+ end
103
+ end
104
+
105
+ FRONT_CLAUSE = "//*[parent::preface][not(local-name() = 'abstract')]".freeze
106
+
107
+ def preface(isoxml, out)
108
+ isoxml.xpath(ns(FRONT_CLAUSE)).each do |c|
109
+ foreword(isoxml, out) and next if c.name == "foreword"
110
+ next if skip_render(c, isoxml)
111
+ out.div **attr_code(id: c["id"]) do |s|
112
+ clause_name(get_anchors[c['id']][:label],
113
+ c&.at(ns("./title"))&.content, s, nil)
114
+ c.elements.reject { |c1| c1.name == "title" }.each do |c1|
115
+ parse(c1, s)
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ def skip_render(c, isoxml)
122
+ return false unless c.name == "reviewernote"
123
+ status = isoxml&.at(ns("//bibdata/status"))&.text
124
+ return true if status.nil?
125
+ return ["published", "withdrawn"].include? status
126
+ end
127
+
128
+ def term_defs_boilerplate(div, source, term, preface)
129
+ if source.empty? && term.nil?
130
+ div << @no_terms_boilerplate
131
+ else
132
+ div << term_defs_boilerplate_cont(source, term)
133
+ end
134
+ end
135
+
136
+ def i18n_init(lang, script)
137
+ super
138
+ end
139
+
140
+ def fileloc(loc)
141
+ File.join(File.dirname(__FILE__), loc)
142
+ end
143
+
144
+ def cleanup(docxml)
145
+ super
146
+ term_cleanup(docxml)
147
+ requirement_cleanup(docxml)
148
+ end
149
+
150
+ def term_cleanup(docxml)
151
+ docxml.xpath("//p[@class = 'Terms']").each do |d|
152
+ h2 = d.at("./preceding-sibling::*[@class = 'TermNum'][1]")
153
+ h2.add_child("&nbsp;")
154
+ h2.add_child(d.remove)
155
+ end
156
+ docxml
157
+ end
158
+
159
+ def requirement_cleanup(docxml)
160
+ docxml.xpath("//div[@class = 'recommend'][title]").each do |d|
161
+ title = d.at("./title")
162
+ title.name = "b"
163
+ n = title.next_element
164
+ n&.children&.first&.add_previous_sibling(" ")
165
+ n&.children&.first&.add_previous_sibling(title.remove)
166
+ end
167
+ docxml
168
+ end
169
+
170
+ def figure_parse(node, out)
171
+ return pseudocode_parse(node, out) if node["type"] == "pseudocode"
172
+ super
173
+ end
174
+
175
+ def pseudocode_parse(node, out)
176
+ @in_figure = true
177
+ name = node.at(ns("./name"))
178
+ out.div **attr_code(id: node["id"], class: "pseudocode") do |div|
179
+ node.children.each do |n|
180
+ parse(n, div) unless n.name == "name"
181
+ end
182
+ figure_name_parse(node, div, name) if name
183
+ end
184
+ @in_figure = false
185
+ end
186
+
187
+ def dl_parse(node, out)
188
+ return glossary_parse(node, out) if node["type"] == "glossary"
189
+ super
190
+ end
191
+
192
+ def glossary_parse(node, out)
193
+ out.dl **attr_code(id: node["id"], class: "glossary") do |v|
194
+ node.elements.select { |n| dt_dd? n }.each_slice(2) do |dt, dd|
195
+ v.dt **attr_code(id: dt["id"]) do |term|
196
+ dt_parse(dt, term)
197
+ end
198
+ v.dd **attr_code(id: dd["id"]) do |listitem|
199
+ dd.children.each { |n| parse(n, listitem) }
200
+ end
201
+ end
202
+ end
203
+ node.elements.reject { |n| dt_dd? n }.each { |n| parse(n, out) }
204
+ end
205
+
206
+ def error_parse(node, out)
207
+ case node.name
208
+ when "nistvariable" then nistvariable_parse(node, out)
209
+ when "recommendation" then recommendation_parse(node, out)
210
+ when "requirement" then requirement_parse(node, out)
211
+ when "permission" then permission_parse(node, out)
212
+ when "errata" then errata_parse(node, out)
213
+ else
214
+ super
215
+ end
216
+ end
217
+
218
+ def nistvariable_parse(node, out)
219
+ out.span **{class: "nistvariable"} do |s|
220
+ node.children.each { |n| parse(n, s) }
221
+ end
222
+ end
223
+
224
+ def recommendation_parse(node, out)
225
+ name = node["type"]
226
+ out.div **{ class: "recommend" } do |t|
227
+ t.title { |b| b << "Recommendation #{get_anchors[node['id']][:label]}:" }
228
+ node.children.each do |n|
229
+ parse(n, t)
230
+ end
231
+ end
232
+ end
233
+
234
+ def requirement_parse(node, out)
235
+ name = node["type"]
236
+ out.div **{ class: "recommend" } do |t|
237
+ t.title { |b| b << "Requirement #{get_anchors[node['id']][:label]}:" }
238
+ node.children.each do |n|
239
+ parse(n, t)
240
+ end
241
+ end
242
+ end
243
+
244
+ def permission_parse(node, out)
245
+ name = node["type"]
246
+ out.div **{ class: "recommend" } do |t|
247
+ t.title { |b| b << "Permission #{get_anchors[node['id']][:label]}:" }
248
+ node.children.each do |n|
249
+ parse(n, t)
250
+ end
251
+ end
252
+ end
253
+
254
+ def errata_parse(node, out)
255
+ out.table **make_table_attr(node) do |t|
256
+ t.thead do |h|
257
+ h.tr do |tr|
258
+ %w(Date Type Change Pages).each do |hdr|
259
+ tr.th hdr
260
+ end
261
+ end
262
+ end
263
+ t.tbody do |b|
264
+ node.xpath(ns("./row")).each do |row|
265
+ b.tr do |tr|
266
+ tr.td do |td|
267
+ row&.at(ns("./date"))&.children.each do |n|
268
+ parse(n, td)
269
+ end
270
+ end
271
+ tr.td do |td|
272
+ row&.at(ns("./type"))&.children.each do |n|
273
+ parse(n, td)
274
+ end
275
+ end
276
+ tr.td do |td|
277
+ row&.at(ns("./change"))&.children.each do |n|
278
+ parse(n, td)
279
+ end
280
+ end
281
+ tr.td do |td|
282
+ row&.at(ns("./pages"))&.children.each do |n|
283
+ parse(n, td)
284
+ end
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end
290
+ end
291
+
292
+ MIDDLE_CLAUSE = "//clause[parent::sections]|//terms[parent::sections]".freeze
293
+
294
+ def middle(isoxml, out)
295
+ middle_title(out)
296
+ clause isoxml, out
297
+ annex isoxml, out
298
+ bibliography isoxml, out
299
+ end
300
+
301
+ def bibliography(isoxml, out)
302
+ f = isoxml.at(ns("//bibliography/clause | //bibliography/references")) || return
303
+ page_break(out)
304
+ isoxml.xpath(ns("//bibliography/clause | //bibliography/references")).each do |f|
305
+ out.div do |div|
306
+ div.h1 **{ class: "Section3" } do |h1|
307
+ f&.at(ns("./title"))&.children.each { |n| parse(n, h1) }
308
+ end
309
+ f.elements.reject do |e|
310
+ ["reference", "title", "bibitem"].include? e.name
311
+ end.each { |e| parse(e, div) }
312
+ biblio_list(f, div, false)
313
+ end
314
+ end
315
+ end
316
+
317
+ def info(isoxml, out)
318
+ @meta.keywords isoxml, out
319
+ super
320
+ end
321
+
322
+ SECTIONS_XPATH =
323
+ "//foreword | //introduction | //reviewnote | //execsummary | //annex | "\
324
+ "//sections/clause | //bibliography/references | "\
325
+ "//bibliography/clause".freeze
326
+
327
+ def initial_anchor_names(d)
328
+ d.xpath("//xmlns:preface/child::*").each do |c|
329
+ preface_names(c)
330
+ end
331
+ sequential_asset_names(d.xpath("//xmlns:preface/child::*"))
332
+ clause_names(d, 0)
333
+ middle_section_asset_names(d)
334
+ termnote_anchor_names(d)
335
+ termexample_anchor_names(d)
336
+ end
337
+
338
+ def back_anchor_names(docxml)
339
+ docxml.xpath(ns("//annex")).each_with_index do |c, i|
340
+ annex_names(c, (65 + i).chr.to_s)
341
+ end
342
+ docxml.xpath(ns("//bibliography/clause | "\
343
+ "//bibliography/references")).each do |b|
344
+ preface_names(b)
345
+ end
346
+ docxml.xpath(ns("//bibitem[not(ancestor::bibitem)]")).each do |ref|
347
+ reference_names(ref)
348
+ end
349
+ end
350
+
351
+
352
+ def prefaceprefix(nodes)
353
+ i = 0
354
+ nodes.each do |n|
355
+ case n.name
356
+ when "executivesummary" then @anchors[n["id"]][:prefix] = "ES"
357
+ when "abstract" then @anchors[n["id"]][:prefix] = "ABS"
358
+ when "reviewernote" then @anchors[n["id"]][:prefix] = "NTR"
359
+ else
360
+ @anchors[n["id"]][:prefix] = "PR" + i.to_s
361
+ i += 1
362
+ end
363
+ end
364
+ end
365
+
366
+ def middle_section_asset_names(d)
367
+ prefaceprefix(d.xpath("//xmlns:preface/child::*"))
368
+ d.xpath("//xmlns:preface/child::*").each do |s|
369
+ hierarchical_asset_names(s, @anchors[s["id"]][:prefix])
370
+ end
371
+ d.xpath("//xmlns:sections/child::*").each do |s|
372
+ hierarchical_asset_names(s, @anchors[s["id"]][:label])
373
+ end
374
+ end
375
+
376
+ def hierarchical_asset_names(clause, num)
377
+ super
378
+ hierarchical_permission_names(clause, num)
379
+ hierarchical_requirement_names(clause, num)
380
+ hierarchical_recommendation_names(clause, num)
381
+ end
382
+
383
+ def hierarchical_permission_names(clause, num)
384
+ clause.xpath(ns(".//permission")).each_with_index do |t, i|
385
+ next if t["id"].nil? || t["id"].empty?
386
+ @anchors[t["id"]] = anchor_struct("#{num}.#{i + 1}",
387
+ t, "Permission", "permission")
388
+ end
389
+ end
390
+
391
+ def hierarchical_requirement_names(clause, num)
392
+ clause.xpath(ns(".//requirement")).each_with_index do |t, i|
393
+ next if t["id"].nil? || t["id"].empty?
394
+ @anchors[t["id"]] = anchor_struct("#{num}.#{i + 1}",
395
+ t, "Requirement", "requirement")
396
+ end
397
+ end
398
+
399
+ def hierarchical_recommendation_names(clause, num)
400
+ clause.xpath(ns(".//recommendation")).each_with_index do |t, i|
401
+ next if t["id"].nil? || t["id"].empty?
402
+ @anchors[t["id"]] = anchor_struct("#{num}.#{i + 1}",
403
+ t, "Recommendation", "recommendation")
404
+ end
405
+ end
406
+
407
+ def clause_names(docxml, sect_num)
408
+ q = "//xmlns:sections/child::*"
409
+ docxml.xpath(q).each_with_index do |c, i|
410
+ section_names(c, (i + sect_num), 1)
411
+ end
412
+ end
413
+
414
+ def get_linkend(node)
415
+ link = anchor_linkend(node, docid_l10n(node["target"] || "[#{node['citeas']}]"))
416
+ link += eref_localities(node.xpath(ns("./locality")), link)
417
+ contents = node.children.select { |c| c.name != "locality" }
418
+ return link if contents.nil? || contents.empty?
419
+ Nokogiri::XML::NodeSet.new(node.document, contents).to_xml
420
+ # so not <origin bibitemid="ISO7301" citeas="ISO 7301">
421
+ # <locality type="section"><reference>3.1</reference></locality></origin>
422
+ end
423
+
424
+ def load_yaml(lang, script)
425
+ y = if @i18nyaml then YAML.load_file(@i18nyaml)
426
+ elsif lang == "en"
427
+ YAML.load_file(File.join(File.dirname(__FILE__), "i18n-en.yaml"))
428
+ else
429
+ YAML.load_file(File.join(File.dirname(__FILE__), "i18n-en.yaml"))
430
+ end
431
+ super.merge(y)
432
+ end
433
+
434
+ def annex_name_lbl(clause, num)
435
+ l10n("<b>#{@annex_lbl} #{num}</b>")
436
+ end
437
+
438
+ def annex_name(annex, name, div)
439
+ div.h1 **{ class: "Annex" } do |t|
440
+ t << "#{get_anchors[annex['id']][:label]} &mdash; "
441
+ t.b do |b|
442
+ name&.children&.each { |c2| parse(c2, b) }
443
+ end
444
+ end
445
+ end
446
+
447
+ def hiersep
448
+ "-"
449
+ end
450
+
451
+
452
+
453
+ end
454
+ end
455
+ end
456
+