rubyword 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -2
- data/CHANGELOG.md +44 -0
- data/README.cn.md +167 -0
- data/README.md +24 -3
- data/bin/run-example +11 -0
- data/doc/README.md +18 -1
- data/doc/doc-information.md +16 -0
- data/doc/footer.md +19 -0
- data/doc/header.md +11 -0
- data/doc/image.md +10 -0
- data/doc/link.md +10 -0
- data/doc/list.md +17 -0
- data/doc/paragraph.md +27 -0
- data/doc/table.md +21 -0
- data/doc/text.md +26 -1
- data/doc/title.md +14 -0
- data/doc/toc.md +11 -0
- data/example/doc_information.rb +13 -0
- data/example/footer.rb +5 -0
- data/example/header.rb +5 -0
- data/example/image.rb +7 -0
- data/example/link.rb +7 -0
- data/example/list.rb +11 -0
- data/example/paragraph.rb +17 -0
- data/example/result/.keep +0 -0
- data/example/result/doc-information.docx +0 -0
- data/example/result/footer.docx +0 -0
- data/example/result/header.docx +0 -0
- data/example/result/image.docx +0 -0
- data/example/result/link.docx +0 -0
- data/example/result/list.docx +0 -0
- data/example/result/paragraph.docx +0 -0
- data/example/result/table.docx +0 -0
- data/example/result/test.docx +0 -0
- data/example/result/text.docx +0 -0
- data/example/table.rb +18 -0
- data/example/test.rb +117 -0
- data/example/text.rb +9 -0
- data/example/toc.rb +24 -0
- data/lib/rubyword.rb +1 -0
- data/lib/rubyword/configuration.rb +1 -1
- data/lib/rubyword/document.rb +29 -41
- data/lib/rubyword/element/base.rb +12 -0
- data/lib/rubyword/element/link.rb +6 -5
- data/lib/rubyword/element/list.rb +4 -2
- data/lib/rubyword/element/paragraph.rb +38 -0
- data/lib/rubyword/element/section.rb +23 -10
- data/lib/rubyword/element/table.rb +60 -0
- data/lib/rubyword/element/text.rb +14 -79
- data/lib/rubyword/style.rb +4 -0
- data/lib/rubyword/version.rb +1 -1
- data/lib/rubyword/writer.rb +0 -4
- data/lib/rubyword/writer/part/document.rb +51 -44
- data/lib/rubyword/writer/part/footer.rb +1 -6
- data/lib/rubyword/writer/part/header.rb +2 -6
- data/lib/rubyword/writer/style/base.rb +0 -1
- data/lib/rubyword/writer/style/paragraph.rb +47 -0
- data/lib/rubyword/writer/style/section.rb +0 -1
- data/lib/rubyword/writer/style/word.rb +40 -0
- data/rubyword.gemspec +1 -2
- data/spec/rubyword/document_spec.rb +18 -3
- data/spec/rubyword/element/page_break_spec.rb +55 -0
- data/spec/rubyword/element/text_break_spec.rb +46 -0
- data/spec/rubyword/element/text_spec.rb +50 -0
- data/spec/spec_helper.rb +3 -30
- metadata +50 -20
- data/CHANGELOG.txt +0 -10
@@ -7,6 +7,18 @@ module Rubyword
|
|
7
7
|
@rubyword = rubyword
|
8
8
|
@section = section
|
9
9
|
end
|
10
|
+
|
11
|
+
# filter html special char
|
12
|
+
def filter_text(text)
|
13
|
+
text = text.to_s
|
14
|
+
text.gsub('&', '&')
|
15
|
+
text.gsub('\'', ''')
|
16
|
+
text.gsub('"', '"')
|
17
|
+
text.gsub('<', '<')
|
18
|
+
text.gsub('>', '>')
|
19
|
+
text
|
20
|
+
end
|
21
|
+
|
10
22
|
end
|
11
23
|
end
|
12
24
|
end
|
@@ -3,16 +3,17 @@ module Rubyword
|
|
3
3
|
module Element
|
4
4
|
class Link < Base
|
5
5
|
attr_accessor :links
|
6
|
-
def save(text, link, style)
|
6
|
+
def save(text, link, style=nil)
|
7
7
|
@links ||= Queue.new
|
8
|
-
|
8
|
+
return if text.nil?
|
9
|
+
text = filter_text(text)
|
9
10
|
@rubyword.rels_documents << {
|
10
11
|
Id: "rId#{@rubyword.init_rid}",
|
11
12
|
Type: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink',
|
12
13
|
Target: link,
|
13
14
|
TargetMode: 'External'
|
14
15
|
}
|
15
|
-
@links << { rId: @rubyword.init_rid, text: text, link: link }
|
16
|
+
@links << { rId: @rubyword.init_rid, text: text, link: link, style: style }
|
16
17
|
@rubyword.init_rid = @rubyword.init_rid + 1
|
17
18
|
end
|
18
19
|
|
@@ -20,10 +21,10 @@ module Rubyword
|
|
20
21
|
@xml = xml
|
21
22
|
link = self.links.pop
|
22
23
|
@xml.send('w:p') {
|
24
|
+
Writer::Style::Paragraph.new(@section, @xml, @rubyword).write(link[:style])
|
23
25
|
@xml.send('hyperlink', 'r:id' => "rId#{link[:rId]}", 'w:history' => '1') {
|
24
26
|
@xml.send('w:r') {
|
25
|
-
|
26
|
-
@xml.send('w:rPr')
|
27
|
+
Writer::Style::Word.new(@section, @xml, @rubyword).write(link[:style])
|
27
28
|
@xml.send('w:t', {'xml:space' => 'preserve'}, link[:text])
|
28
29
|
}
|
29
30
|
}
|
@@ -7,9 +7,11 @@ module Rubyword
|
|
7
7
|
# write document and numbering
|
8
8
|
def save(text, level, style)
|
9
9
|
@lists ||= Queue.new
|
10
|
+
text = filter_text(text)
|
10
11
|
@lists << {
|
11
12
|
level: level.to_i - 1,
|
12
|
-
text: text
|
13
|
+
text: text,
|
14
|
+
style: style
|
13
15
|
}
|
14
16
|
end
|
15
17
|
|
@@ -24,7 +26,7 @@ module Rubyword
|
|
24
26
|
}
|
25
27
|
}
|
26
28
|
@xml.send('w:r') {
|
27
|
-
@xml.
|
29
|
+
Writer::Style::Word.new(@section, @xml, @rubyword).write(list[:style])
|
28
30
|
@xml.send('w:t', {'xml:space' => 'preserve'}, list[:text])
|
29
31
|
}
|
30
32
|
}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Rubyword
|
3
|
+
module Element
|
4
|
+
class Paragraph < Text
|
5
|
+
attr_accessor :paragraphs, :style
|
6
|
+
def initialize(rubyword, section=nil, style)
|
7
|
+
super(rubyword, section)
|
8
|
+
@style = style
|
9
|
+
end
|
10
|
+
|
11
|
+
def text(text, style=nil)
|
12
|
+
@paragraphs ||= []
|
13
|
+
text = filter_text(text)
|
14
|
+
@paragraphs << {
|
15
|
+
text: text,
|
16
|
+
style: style
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
def write(section=nil, xml=nil)
|
21
|
+
@xml = xml
|
22
|
+
@xml.send('w:p') {
|
23
|
+
@paragraphs.each do |p|
|
24
|
+
Writer::Style::Paragraph.new(@section, @xml, @rubyword).write(p[:style])
|
25
|
+
@xml.send('w:r') do
|
26
|
+
Writer::Style::Word.new(@section, @xml, @rubyword).write(p[:style])
|
27
|
+
@xml.send('w:t', {'xml:space' => 'preserve'}, p[:text])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_missing(name, *arg)
|
34
|
+
@section.send(name.to_sym, *arg)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
1
|
require_relative 'base'
|
3
2
|
require_relative 'text'
|
4
3
|
require_relative 'link'
|
@@ -6,23 +5,37 @@ require_relative 'list'
|
|
6
5
|
require_relative 'image'
|
7
6
|
require_relative 'page_break'
|
8
7
|
require_relative 'text_break'
|
8
|
+
require_relative 'paragraph'
|
9
|
+
require_relative 'table'
|
9
10
|
module Rubyword
|
10
11
|
module Element
|
11
12
|
class Section
|
12
13
|
attr_accessor :section_id, :style, :rubyword, :section_objects, :objects, :titles
|
14
|
+
|
13
15
|
def initialize(section_count, style = nil, rubyword=nil)
|
14
16
|
@section_id = section_count
|
15
17
|
@style = style
|
16
18
|
@rubyword = rubyword
|
17
19
|
@section_objects = []
|
18
20
|
@objects = []
|
21
|
+
@text_blocks = []
|
22
|
+
end
|
23
|
+
|
24
|
+
def p(style=nil, &block)
|
25
|
+
object ||= Paragraph.new(@rubyword, self, style)
|
26
|
+
return nil unless block_given?
|
27
|
+
object.instance_eval(&block)
|
28
|
+
@objects << object
|
19
29
|
end
|
20
30
|
|
21
|
-
def
|
22
|
-
|
31
|
+
def table(style=nil, &block)
|
32
|
+
object ||= Table.new(@rubyword, self, style)
|
33
|
+
return nil unless block_given?
|
34
|
+
object.instance_eval(&block)
|
35
|
+
@objects << object
|
23
36
|
end
|
24
37
|
|
25
|
-
|
38
|
+
def text(text, style=nil)
|
26
39
|
object ||= Text.new(@rubyword, self)
|
27
40
|
object.save(text, __callee__.to_s, style)
|
28
41
|
@objects << object
|
@@ -38,18 +51,18 @@ module Rubyword
|
|
38
51
|
@objects << object
|
39
52
|
end
|
40
53
|
|
41
|
-
def image(url)
|
42
|
-
object ||= Image.new(@rubyword)
|
43
|
-
object.save(url)
|
44
|
-
@objects << object
|
45
|
-
end
|
46
|
-
|
47
54
|
def link(text, link, style=nil)
|
48
55
|
object ||= Link.new(@rubyword)
|
49
56
|
object.save(text, link, style)
|
50
57
|
@objects << object
|
51
58
|
end
|
52
59
|
|
60
|
+
def image(url)
|
61
|
+
object ||= Image.new(@rubyword)
|
62
|
+
object.save(url)
|
63
|
+
@objects << object
|
64
|
+
end
|
65
|
+
|
53
66
|
def page_break(break_num=1)
|
54
67
|
object ||= PageBreak.new(@rubyword)
|
55
68
|
object.save(break_num)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Rubyword
|
3
|
+
module Element
|
4
|
+
class Table < Base
|
5
|
+
attr_accessor :trs
|
6
|
+
def initialize(rubyword, section=nil, style)
|
7
|
+
super(rubyword, section)
|
8
|
+
@style = style
|
9
|
+
@trs ||= []
|
10
|
+
end
|
11
|
+
|
12
|
+
def tr(style=nil, &block)
|
13
|
+
return unless block_given?
|
14
|
+
tr = Tr.new
|
15
|
+
tr.instance_eval(&block)
|
16
|
+
@trs << tr
|
17
|
+
end
|
18
|
+
|
19
|
+
def write(section=nil, xml=nil)
|
20
|
+
@xml = xml
|
21
|
+
@xml.send('w:tbl') {
|
22
|
+
@xml.send('w:tblGrid') {
|
23
|
+
@xml.send('w:gridCol', 'w:w' => '1750', 'w:type' => 'dxa')
|
24
|
+
@xml.send('w:gridCol', 'w:w' => '1750', 'w:type' => 'dxa')
|
25
|
+
@xml.send('w:gridCol', 'w:w' => '1750', 'w:type' => 'dxa')
|
26
|
+
}
|
27
|
+
@trs.each do |tr|
|
28
|
+
@xml.send('w:tr') {
|
29
|
+
@xml.send('w:trPr')
|
30
|
+
tr.texts.each do |text|
|
31
|
+
@xml.send('w:tc') {
|
32
|
+
@xml.send('w:tcPr') {
|
33
|
+
@xml.send('w:tcW', 'w:w'=>'1750', 'w:type' => 'dxa')
|
34
|
+
}
|
35
|
+
@xml.send('w:p') {
|
36
|
+
Writer::Style::Paragraph.new(@section, @xml, @rubyword).write(text[:style])
|
37
|
+
@xml.send('w:r') do
|
38
|
+
Writer::Style::Word.new(@section, @xml, @rubyword).write(text[:style])
|
39
|
+
@xml.send('w:t', {'xml:space' => 'preserve'}, text[:text])
|
40
|
+
end
|
41
|
+
}
|
42
|
+
}
|
43
|
+
end
|
44
|
+
}
|
45
|
+
end
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
class Tr
|
52
|
+
attr_accessor :texts
|
53
|
+
def th(text, style=nil)
|
54
|
+
@texts ||= []
|
55
|
+
@texts << { text: text, style: style }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -1,31 +1,15 @@
|
|
1
|
-
# -*- encoding : utf-8 -*-
|
2
1
|
module Rubyword
|
3
2
|
module Element
|
4
3
|
class Text < Base
|
5
4
|
attr_accessor :texts
|
6
|
-
|
5
|
+
# Toc indent size
|
7
6
|
IndentSize = 200
|
8
|
-
|
9
|
-
|
10
|
-
color: 'w:color',
|
11
|
-
underline: 'w:u',
|
12
|
-
blod: 'w:b',
|
13
|
-
all_caps: 'w:caps',
|
14
|
-
italic: 'w:i',
|
15
|
-
bgcolor: 'w:highlight'
|
16
|
-
}.freeze
|
17
|
-
ParagraphStyleList = {
|
18
|
-
text_align: 'w:jc',
|
19
|
-
spacing: 'w:spacing',
|
20
|
-
indent_left: 'w:ind',
|
21
|
-
indent_right: 'w:ind',
|
22
|
-
indent_between: 'w:ind'
|
23
|
-
}.freeze
|
24
|
-
|
25
|
-
def save(text, type, style)
|
7
|
+
|
8
|
+
def save(text, type=nil, style=nil)
|
26
9
|
@texts ||= Queue.new
|
27
10
|
@section.titles ||= []
|
28
|
-
return if text.nil?
|
11
|
+
return if text.nil? || type.nil?
|
12
|
+
text = filter_text(text)
|
29
13
|
if type == 'text'
|
30
14
|
text(text, style)
|
31
15
|
else
|
@@ -34,21 +18,21 @@ module Rubyword
|
|
34
18
|
end
|
35
19
|
|
36
20
|
def text(text, style)
|
37
|
-
@texts << { size: 'normal', text: text
|
21
|
+
@texts << { size: 'normal', text: text, style: style }
|
38
22
|
end
|
39
23
|
|
40
24
|
(1..4).each do |num|
|
41
25
|
define_method "title_#{num}" do |text, style|
|
42
26
|
@rubyword.relation_rids << {rid: @rubyword.init_rid, type: "title_#{num}"}
|
43
|
-
|
27
|
+
title_info = {
|
44
28
|
indent: (num - 1) * IndentSize,
|
45
29
|
size: "title_#{num}",
|
46
30
|
text: text.to_s,
|
47
31
|
rid: @rubyword.init_rid,
|
48
32
|
style: style
|
49
33
|
}
|
50
|
-
@section.titles <<
|
51
|
-
@texts <<
|
34
|
+
@section.titles << title_info if (style && !style[:ignore_dir]) || style.nil?
|
35
|
+
@texts << title_info
|
52
36
|
@rubyword.init_rid = @rubyword.init_rid + 1
|
53
37
|
end
|
54
38
|
|
@@ -67,68 +51,19 @@ module Rubyword
|
|
67
51
|
end
|
68
52
|
|
69
53
|
def write(section=nil, xml=nil)
|
70
|
-
@xml = xml
|
54
|
+
@xml, @section = xml, section
|
71
55
|
text = self.texts.pop
|
56
|
+
return if text.to_s.empty?
|
72
57
|
eval "write_#{text[:size]}(text)"
|
73
58
|
end
|
74
59
|
|
75
60
|
def write_normal(text)
|
76
|
-
@xml.send('w:p')
|
77
|
-
|
61
|
+
@xml.send('w:p') {
|
62
|
+
Writer::Style::Paragraph.new(@section, @xml, @rubyword).write(text[:style])
|
78
63
|
@xml.send('w:r') do
|
79
|
-
|
64
|
+
Writer::Style::Word.new(@section, @xml, @rubyword).write(text[:style])
|
80
65
|
@xml.send('w:t', {'xml:space' => 'preserve'}, text[:text])
|
81
66
|
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def write_word_style(style)
|
86
|
-
if style.is_a?(Hash)
|
87
|
-
@xml.send('w:rPr') {
|
88
|
-
style.keys.each do |style_name|
|
89
|
-
style_name = style_name.to_sym
|
90
|
-
if WordStyleList.keys.include?(style_name)
|
91
|
-
value =style[style_name]
|
92
|
-
attribute = if !!value != value # not a bool type
|
93
|
-
{'w:val' => value}
|
94
|
-
else
|
95
|
-
nil
|
96
|
-
end
|
97
|
-
doc_style = WordStyleList[style_name]
|
98
|
-
@xml.send(doc_style, attribute)
|
99
|
-
@xml.send('w:szCs', attribute) if style_name == :font_size
|
100
|
-
end
|
101
|
-
end
|
102
|
-
}
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def write_paragraph_style(style)
|
107
|
-
return unless style.is_a?(Hash)
|
108
|
-
@xml.send('w:pPr') {
|
109
|
-
style.keys.each do |style_name|
|
110
|
-
style_name = style_name.to_sym
|
111
|
-
next unless ParagraphStyleList.keys.include?(style_name)
|
112
|
-
value =style[style_name]
|
113
|
-
attribute = case style_name.to_s
|
114
|
-
when 'spacing'
|
115
|
-
{'w:after' => value}
|
116
|
-
when 'indent_left'
|
117
|
-
{'w:left' => value}
|
118
|
-
when 'indent_right'
|
119
|
-
{'w:right' => value}
|
120
|
-
when 'indent_between'
|
121
|
-
v = value.split '-'
|
122
|
-
next unless v.is_a?(Array)
|
123
|
-
{ 'w:left' => v[0].to_i, 'w:right' => v[1].to_i }
|
124
|
-
when !!value == value
|
125
|
-
nil
|
126
|
-
else
|
127
|
-
{'w:val' => value}
|
128
|
-
end
|
129
|
-
doc_style = ParagraphStyleList[style_name]
|
130
|
-
@xml.send(doc_style, attribute)
|
131
|
-
end
|
132
67
|
}
|
133
68
|
end
|
134
69
|
|
data/lib/rubyword/version.rb
CHANGED
data/lib/rubyword/writer.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# -*- encoding : utf-8 -*-
|
2
1
|
require_relative "writer/part/base"
|
3
2
|
require_relative "writer/part/toc"
|
4
3
|
require_relative "writer/part/content_types"
|
@@ -16,9 +15,6 @@ require_relative "writer/part/theme"
|
|
16
15
|
require_relative "writer/part/web_settings"
|
17
16
|
require_relative "writer/part/footer"
|
18
17
|
require_relative "writer/part/header"
|
19
|
-
# style file
|
20
|
-
require_relative 'writer/style/base'
|
21
|
-
require_relative 'writer/style/section'
|
22
18
|
|
23
19
|
module Rubyword
|
24
20
|
module Writer
|
@@ -1,56 +1,63 @@
|
|
1
1
|
# -*- encoding : utf-8 -*
|
2
2
|
module Rubyword
|
3
3
|
module Writer
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
4
|
+
module Part
|
5
|
+
class Document < Base
|
6
|
+
include Toc
|
7
|
+
DOCUMENT_ATTR = {
|
8
|
+
'xmlns:ve' => "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
9
|
+
'xmlns:o' => "urn:schemas-microsoft-com:office:office",
|
10
|
+
'xmlns:r' => "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
11
|
+
'xmlns:m' => "http://schemas.openxmlformats.org/officeDocument/2006/math",
|
12
|
+
'xmlns:v' => "urn:schemas-microsoft-com:vml",
|
13
|
+
'xmlns:wp' => "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
|
14
|
+
'xmlns:w10' => "urn:schemas-microsoft-com:office:word",
|
15
|
+
'xmlns:w' => "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
|
16
|
+
'xmlns:wne' => "http://schemas.microsoft.com/office/word/2006/wordml"
|
17
|
+
}
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
def write
|
20
|
+
builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
|
21
|
+
xml.send('w:document', DOCUMENT_ATTR) {
|
22
|
+
xml.send('w:body') {
|
23
|
+
section_write(xml)
|
24
|
+
}
|
23
25
|
}
|
24
|
-
|
26
|
+
end
|
27
|
+
builder.to_xml
|
25
28
|
end
|
26
|
-
builder.to_xml
|
27
|
-
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
@object_blocks << Style::Section.new(section, xml, @rubyword).write
|
42
|
-
else
|
43
|
-
p_block = xml.send('w:p') {
|
44
|
-
xml.send('w:pPr') {
|
45
|
-
@object_blocks << Style::Section.new(section, xml, @rubyword).write
|
46
|
-
}
|
30
|
+
def section_write(xml)
|
31
|
+
@object_blocks = []
|
32
|
+
sections_count = @rubyword.sections.count
|
33
|
+
current_section = 0
|
34
|
+
# write TOC
|
35
|
+
write_toc(@rubyword, xml)
|
36
|
+
# To write all sections xml.
|
37
|
+
@rubyword.sections.each do |section|
|
38
|
+
current_section = current_section + 1
|
39
|
+
section.objects.each{|object|
|
40
|
+
# text should save in block
|
41
|
+
object.write(section, xml)
|
47
42
|
}
|
48
|
-
|
43
|
+
# write in the last
|
44
|
+
if current_section == sections_count
|
45
|
+
Style::Section.new(section, xml, @rubyword).write
|
46
|
+
else
|
47
|
+
xml.send('w:p') {
|
48
|
+
xml.send('w:pPr') {
|
49
|
+
Style::Section.new(section, xml, @rubyword).write
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
if @rubyword.sections.empty?
|
56
|
+
Style::Section.new(section, xml, @rubyword).write
|
49
57
|
end
|
50
58
|
end
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
end # end of class
|
59
|
+
|
60
|
+
end # end of class
|
61
|
+
end
|
55
62
|
end
|
56
63
|
end
|