rsyntaxtree 0.9.3 → 1.0.6
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/.gitignore +2 -0
- data/.tags +179 -0
- data/Gemfile +2 -0
- data/README.md +35 -29
- data/Rakefile +7 -0
- data/bin/rsyntaxtree +42 -31
- data/fonts/OpenMoji-Black.ttf +0 -0
- data/fonts/OpenMoji-Color.ttf +0 -0
- data/img/elements/circle.png +0 -0
- data/img/elements/circle_abc.png +0 -0
- data/img/elements/circle_bold.png +0 -0
- data/img/elements/circle_hatched.png +0 -0
- data/img/elements/circle_one.png +0 -0
- data/img/elements/connector.png +0 -0
- data/img/elements/connector_bold.png +0 -0
- data/img/elements/square.png +0 -0
- data/img/elements/square_abc.png +0 -0
- data/img/elements/square_bold.png +0 -0
- data/img/elements/square_hatched.png +0 -0
- data/img/elements/square_one.png +0 -0
- data/img/rsyntaxtree.png +0 -0
- data/img/sample.png +0 -0
- data/img/sample.svg +38 -0
- data/lib/rsyntaxtree/base_graph.rb +262 -0
- data/lib/rsyntaxtree/element.rb +156 -25
- data/lib/rsyntaxtree/elementlist.rb +16 -13
- data/lib/rsyntaxtree/markup_parser.rb +208 -0
- data/lib/rsyntaxtree/string_parser.rb +187 -204
- data/lib/rsyntaxtree/svg_graph.rb +471 -298
- data/lib/rsyntaxtree/utils.rb +49 -6
- data/lib/rsyntaxtree/version.rb +1 -1
- data/lib/rsyntaxtree.rb +144 -161
- data/rsyntaxtree.gemspec +2 -0
- data/test/markup_parser_test.rb +207 -0
- metadata +52 -11
- data/fonts/latinmodern-math.otf +0 -0
- data/fonts/lmroman10-bold.otf +0 -0
- data/fonts/lmroman10-bolditalic.otf +0 -0
- data/fonts/lmroman10-italic.otf +0 -0
- data/fonts/lmroman10-regular.otf +0 -0
- data/lib/rsyntaxtree/error_message.rb +0 -68
- data/lib/rsyntaxtree/graph.rb +0 -312
- data/lib/rsyntaxtree/tree_graph.rb +0 -327
@@ -0,0 +1,262 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
#==========================
|
5
|
+
# graph.rb
|
6
|
+
#==========================
|
7
|
+
#
|
8
|
+
# Image utility functions to inspect text font metrics
|
9
|
+
# Copyright (c) 2007-2021 Yoichiro Hasebe <yohasebe@gmail.com>
|
10
|
+
|
11
|
+
require 'utils'
|
12
|
+
|
13
|
+
module RSyntaxTree
|
14
|
+
class BaseGraph
|
15
|
+
|
16
|
+
def initialize(element_list, params)
|
17
|
+
|
18
|
+
@element_list = element_list
|
19
|
+
@symmetrize = params[:symmetrize]
|
20
|
+
|
21
|
+
if params[:color]
|
22
|
+
@col_node = "blue"
|
23
|
+
@col_leaf = "green"
|
24
|
+
@col_path = "purple"
|
25
|
+
else
|
26
|
+
@col_node = "black"
|
27
|
+
@col_leaf = "black"
|
28
|
+
@col_path = "black"
|
29
|
+
end
|
30
|
+
|
31
|
+
@col_bg = "none"
|
32
|
+
@col_fg = "black"
|
33
|
+
@col_line = "black"
|
34
|
+
|
35
|
+
@leafstyle = params[:leafstyle]
|
36
|
+
@fontset = params[:fontset]
|
37
|
+
@fontsize = params[:fontsize]
|
38
|
+
end
|
39
|
+
|
40
|
+
def calculate_level
|
41
|
+
@element_list.get_elements.select{|e| e.type == 2}.each do |e|
|
42
|
+
e.level = @element_list.get_id(e.parent).level + 1
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def calculate_width(id = 1)
|
47
|
+
target = @element_list.get_id(id)
|
48
|
+
if target.children.empty?
|
49
|
+
target.width = target.content_width + $h_gap_between_nodes * 4
|
50
|
+
|
51
|
+
parent = @element_list.get_id(target.parent)
|
52
|
+
while parent && parent.children.size == 1
|
53
|
+
w = parent.content_width
|
54
|
+
target.width = w + $h_gap_between_nodes * 4 if w > target.content_width
|
55
|
+
parent = @element_list.get_id(parent.parent)
|
56
|
+
end
|
57
|
+
return target.width
|
58
|
+
else
|
59
|
+
if target.width != 0
|
60
|
+
return target.width
|
61
|
+
else
|
62
|
+
accum_array = []
|
63
|
+
target.children.each do |c|
|
64
|
+
accum_array << calculate_width(c)
|
65
|
+
end
|
66
|
+
if @symmetrize
|
67
|
+
accum_width = accum_array.max * target.children.size
|
68
|
+
else
|
69
|
+
accum_width = accum_array.sum
|
70
|
+
end
|
71
|
+
|
72
|
+
target.width = [accum_width, target.content_width].max
|
73
|
+
end
|
74
|
+
|
75
|
+
return target.width
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def calculate_height(id = 1)
|
80
|
+
target = @element_list.get_id(id)
|
81
|
+
if id == 1
|
82
|
+
target.vertical_indent = 0
|
83
|
+
else
|
84
|
+
parent = @element_list.get_id(target.parent)
|
85
|
+
|
86
|
+
if !target.triangle && @leafstyle == "nothing" && ETYPE_LEAF == target.type && parent.children.size == 1
|
87
|
+
vertical_indent = parent.vertical_indent + parent.content_height
|
88
|
+
else
|
89
|
+
vertical_indent = parent.vertical_indent + parent.content_height + $height_connector
|
90
|
+
end
|
91
|
+
|
92
|
+
target.vertical_indent = vertical_indent
|
93
|
+
end
|
94
|
+
|
95
|
+
if target.children.empty?
|
96
|
+
target.height = target.content_height
|
97
|
+
vertical_end = target.vertical_indent + target.content_height
|
98
|
+
else
|
99
|
+
accum_array = []
|
100
|
+
target.children.each do |c|
|
101
|
+
accum_array << calculate_height(c)
|
102
|
+
end
|
103
|
+
target.height = accum_array.max - target.vertical_indent
|
104
|
+
vertical_end = accum_array.max
|
105
|
+
end
|
106
|
+
return vertical_end
|
107
|
+
end
|
108
|
+
|
109
|
+
def make_balance(id = 1)
|
110
|
+
target = @element_list.get_id(id)
|
111
|
+
if target.children.empty?
|
112
|
+
parent = @element_list.get_id(target.parent)
|
113
|
+
accum_array = []
|
114
|
+
parent.children.each do |c|
|
115
|
+
accum_array << @element_list.get_id(c).width
|
116
|
+
end
|
117
|
+
max = accum_array.max
|
118
|
+
parent.children.each do |c|
|
119
|
+
@element_list.get_id(c).width = max
|
120
|
+
end
|
121
|
+
return max
|
122
|
+
else
|
123
|
+
accum_array = []
|
124
|
+
target.children.each do |c|
|
125
|
+
accum_array << make_balance(c)
|
126
|
+
end
|
127
|
+
accum_width = accum_array.max
|
128
|
+
max = [accum_width, target.content_width].max
|
129
|
+
target.children.each do |c|
|
130
|
+
@element_list.get_id(c).width = max
|
131
|
+
end
|
132
|
+
return target.width
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def calculate_indent
|
137
|
+
node_groups = @element_list.get_elements.group_by {|e| e.parent}
|
138
|
+
node_groups.each do |k, v|
|
139
|
+
next if k == 0
|
140
|
+
parent = @element_list.get_id(k)
|
141
|
+
if @symmetrize
|
142
|
+
num_leaves = v.size
|
143
|
+
partition_width = parent.width / num_leaves
|
144
|
+
left_offset = parent.horizontal_indent + parent.content_width / 2.0 - parent.width / 2.0
|
145
|
+
v.each do |e|
|
146
|
+
indent = left_offset + (partition_width - e.content_width) / 2.0
|
147
|
+
e.horizontal_indent = indent
|
148
|
+
left_offset += partition_width
|
149
|
+
end
|
150
|
+
else
|
151
|
+
left_offset = parent.horizontal_indent + parent.content_width / 2.0 - parent.width / 2.0
|
152
|
+
v.each do |e|
|
153
|
+
indent = left_offset + (e.width - e.content_width) / 2.0
|
154
|
+
e.horizontal_indent = indent
|
155
|
+
left_offset += e.width
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def draw_elements
|
162
|
+
@element_list.get_elements.each do |element|
|
163
|
+
draw_element(element)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def draw_connector(id = 1)
|
168
|
+
parent = @element_list.get_id(id)
|
169
|
+
children = parent.children.map{|c| @element_list.get_id(c)}
|
170
|
+
|
171
|
+
if children.empty?
|
172
|
+
;
|
173
|
+
elsif children.size == 1
|
174
|
+
child = children[0]
|
175
|
+
if (@leafstyle == "auto")
|
176
|
+
if child.contains_phrase || child.triangle
|
177
|
+
triangle_to_parent(parent, child)
|
178
|
+
else
|
179
|
+
line_to_parent(parent, child)
|
180
|
+
end
|
181
|
+
elsif(@leafstyle == "bar")
|
182
|
+
if child.triangle
|
183
|
+
triangle_to_parent(parent, child)
|
184
|
+
else
|
185
|
+
line_to_parent(parent, child)
|
186
|
+
end
|
187
|
+
elsif(@leafstyle == "nothing")
|
188
|
+
if child.triangle
|
189
|
+
triangle_to_parent(parent, child)
|
190
|
+
elsif ETYPE_LEAF == child.type
|
191
|
+
child.vertical_indent = parent.vertical_indent + parent.content_height + $height_connector / 2
|
192
|
+
end
|
193
|
+
end
|
194
|
+
else
|
195
|
+
children.each do |child|
|
196
|
+
line_to_parent(parent, child)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
parent.children.each do |c|
|
201
|
+
draw_connector(c)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def get_leftmost(id = 1)
|
206
|
+
target = @element_list.get_id(id)
|
207
|
+
target_indent = target.horizontal_indent
|
208
|
+
children_indent = target.children.map{|c| get_leftmost(c)}
|
209
|
+
(children_indent << target_indent).min
|
210
|
+
end
|
211
|
+
|
212
|
+
def get_rightmost(id = 1)
|
213
|
+
target = @element_list.get_id(id)
|
214
|
+
target_right_end = target.horizontal_indent + target.content_width
|
215
|
+
children_right_end = target.children.map{|c| get_rightmost(c)}
|
216
|
+
(children_right_end << target_right_end).max
|
217
|
+
end
|
218
|
+
|
219
|
+
def node_centering
|
220
|
+
node_groups = @element_list.get_elements.group_by {|e| e.parent}
|
221
|
+
node_groups.sort_by{|k, v| -k}.each do |k, v|
|
222
|
+
next if k == 0
|
223
|
+
parent = @element_list.get_id(k)
|
224
|
+
child_positions =v.map {|child| child.horizontal_indent + child.content_width / 2 }
|
225
|
+
parent.horizontal_indent = child_positions.min + (child_positions.max - child_positions.min - parent.content_width) / 2
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def parse_list
|
230
|
+
calculate_level
|
231
|
+
calculate_width
|
232
|
+
make_balance if @symmetrize
|
233
|
+
calculate_indent
|
234
|
+
node_centering
|
235
|
+
|
236
|
+
top = @element_list.get_id(1)
|
237
|
+
diff = top.horizontal_indent
|
238
|
+
@element_list.get_elements.each do |e|
|
239
|
+
e.horizontal_indent -= diff
|
240
|
+
end
|
241
|
+
|
242
|
+
offset_l = (top.horizontal_indent - get_leftmost) + $h_gap_between_nodes
|
243
|
+
offset_r = top.width / 2 - offset_l - $h_gap_between_nodes
|
244
|
+
|
245
|
+
@element_list.get_elements.each do |e|
|
246
|
+
e.horizontal_indent += offset_l
|
247
|
+
end
|
248
|
+
|
249
|
+
calculate_height
|
250
|
+
draw_elements
|
251
|
+
draw_connector
|
252
|
+
num_paths = draw_paths
|
253
|
+
|
254
|
+
# width = get_rightmost - get_leftmost + $h_gap_between_nodes * 2
|
255
|
+
width = get_rightmost - get_leftmost + $h_gap_between_nodes
|
256
|
+
height = @element_list.get_id(1).height
|
257
|
+
height = @height if @height > height
|
258
|
+
|
259
|
+
return {height: height, width: width}
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
data/lib/rsyntaxtree/element.rb
CHANGED
@@ -6,43 +6,174 @@
|
|
6
6
|
#==========================
|
7
7
|
#
|
8
8
|
# Aa class that represents a basic tree element, either node or leaf.
|
9
|
-
#
|
10
|
-
# This file is part of RSyntaxTree, which is a ruby port of Andre Eisenbach's
|
11
|
-
# excellent program phpSyntaxTree.
|
12
|
-
#
|
13
9
|
# Copyright (c) 2007-2021 Yoichiro Hasebe <yohasebe@gmail.com>
|
14
|
-
# Copyright (c) 2003-2004 Andre Eisenbach <andre@ironcreek.net>
|
15
10
|
|
16
|
-
|
11
|
+
require "markup_parser"
|
12
|
+
require 'utils'
|
13
|
+
|
14
|
+
module RSyntaxTree
|
15
|
+
class Element
|
17
16
|
|
18
|
-
attr_accessor :id,
|
19
|
-
|
17
|
+
attr_accessor :id,
|
18
|
+
:parent, :type, :level,
|
19
|
+
:width, :height,
|
20
|
+
:content, :content_width, :content_height,
|
21
|
+
:horizontal_indent, :vertical_indent,
|
22
|
+
:triangle, :enclosure, :children, :parent,
|
23
|
+
:font, :fontsize, :contains_phrase,
|
24
|
+
:path
|
25
|
+
|
26
|
+
def initialize(id, parent, content, level, fontset, fontsize)
|
27
|
+
@type = ETYPE_LEAF
|
20
28
|
@id = id # Unique element id
|
21
29
|
@parent = parent # Parent element id
|
30
|
+
@children = [] # Child element ids
|
22
31
|
@type = type # Element type
|
23
32
|
@level = level # Element level in the tree (0=top etc...)
|
24
|
-
@width = 0 # Width of the
|
25
|
-
@
|
33
|
+
@width = 0 # Width of the part of the tree including itself and it governs
|
34
|
+
@content_width = 0 # Width of the content
|
35
|
+
@horizontal_indent = 0 # Drawing offset
|
36
|
+
@vertical_indent = 0 # Drawing offset
|
26
37
|
content = content.strip
|
27
|
-
|
28
|
-
|
29
|
-
@
|
38
|
+
|
39
|
+
if /.+?\^?((?:\+\>?\d+)+)\^?\z/m =~ content
|
40
|
+
@path = $1.sub(/\A\+/, "").split("+")
|
30
41
|
else
|
31
|
-
@
|
32
|
-
@triangle = false # draw triangle instead of stright bar when in auto mode
|
42
|
+
@path = []
|
33
43
|
end
|
34
|
-
|
44
|
+
|
45
|
+
@fontset = fontset
|
46
|
+
@fontsize = fontsize
|
47
|
+
|
48
|
+
parsed = Markup.parse(content)
|
49
|
+
|
50
|
+
if parsed[:status] == :success
|
51
|
+
results = parsed[:results]
|
52
|
+
else
|
53
|
+
error_text = "Error: input text contains an invalid string"
|
54
|
+
error_text += "\n > " + content
|
55
|
+
raise RSTError, error_text
|
56
|
+
end
|
57
|
+
@content = results[:contents]
|
58
|
+
@paths = results[:paths]
|
59
|
+
@enclosure = results[:enclosure]
|
60
|
+
@triangle = results[:triangle]
|
61
|
+
|
62
|
+
@contains_phrase = false
|
63
|
+
setup
|
35
64
|
end
|
36
65
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
66
|
+
def setup
|
67
|
+
total_width = 0
|
68
|
+
total_height = 0
|
69
|
+
@content.each_with_index do |content, idx|
|
70
|
+
content_width = 0
|
71
|
+
case content[:type]
|
72
|
+
when :border, :bborder
|
73
|
+
height = $single_line_height / 2
|
74
|
+
content[:height] = height
|
75
|
+
total_height += height
|
76
|
+
when :text
|
77
|
+
row_width = 0
|
78
|
+
elements_height = []
|
79
|
+
content[:elements].each do |e|
|
80
|
+
text = e[:text]
|
81
|
+
e[:text] = text.gsub(" ", WHITESPACE_BLOCK).gsub(">", '>').gsub("<", '<')
|
82
|
+
|
83
|
+
|
84
|
+
@contains_phrase = true if text.include?(" ")
|
85
|
+
decoration = e[:decoration]
|
86
|
+
fontsize = decoration.include?(:small) || decoration.include?(:small) ? @fontsize * SUBSCRIPT_CONST : @fontsize
|
87
|
+
fontsize = decoration.include?(:subscript) || decoration.include?(:superscript) ? fontsize * SUBSCRIPT_CONST : fontsize
|
88
|
+
style = decoration.include?(:italic) || decoration.include?(:bolditalic) ? :italic : :normal
|
89
|
+
weight = decoration.include?(:bold) || decoration.include?(:bolditalic) ? :bold : :normal
|
90
|
+
|
91
|
+
# e[:cjk] = false
|
92
|
+
# if e[:decoration].include?(:math)
|
93
|
+
# font = @fontset[:math]
|
94
|
+
# elsif text.contains_cjk?
|
95
|
+
# font = @fontset[:cjk]
|
96
|
+
# e[:cjk] = true
|
97
|
+
# elsif decoration.include? :bolditalic
|
98
|
+
if decoration.include? :bolditalic
|
99
|
+
font = @fontset[:bolditalic]
|
100
|
+
elsif decoration.include? :bold
|
101
|
+
font = @fontset[:bold]
|
102
|
+
elsif decoration.include? :italic
|
103
|
+
font = @fontset[:italic]
|
104
|
+
else
|
105
|
+
font = @fontset[:normal]
|
106
|
+
end
|
107
|
+
|
108
|
+
standard_metrics = FontMetrics.get_metrics('X', @fontset[:normal], fontsize, :normal, :normal)
|
109
|
+
height = standard_metrics.height
|
110
|
+
if /\A[\<\>]+\z/ =~ text
|
111
|
+
width = standard_metrics.width * text.size / 2
|
112
|
+
elsif text.contains_emoji?
|
113
|
+
segments = text.split_by_emoji
|
114
|
+
width = 0
|
115
|
+
segments.each do |seg|
|
116
|
+
if /\s/ =~ seg[:char]
|
117
|
+
ch = 't'
|
118
|
+
else
|
119
|
+
ch = seg[:char]
|
120
|
+
end
|
121
|
+
if seg[:type] == :emoji
|
122
|
+
this_font = @fontset[:emoji]
|
123
|
+
metrics = FontMetrics.get_metrics(ch, this_font, fontsize, style, weight)
|
124
|
+
width += metrics.width
|
125
|
+
else
|
126
|
+
this_font = font
|
127
|
+
metrics = FontMetrics.get_metrics(ch, this_font, fontsize, style, weight)
|
128
|
+
width += metrics.width
|
129
|
+
end
|
130
|
+
end
|
131
|
+
else
|
132
|
+
text.gsub!("\\\\", 'i')
|
133
|
+
text.gsub!("\\", "")
|
134
|
+
text.gsub!(" ", "x")
|
135
|
+
text.gsub!("%", "X")
|
136
|
+
metrics = FontMetrics.get_metrics(text, font, fontsize, style, weight)
|
137
|
+
width = metrics.width
|
138
|
+
end
|
139
|
+
|
140
|
+
if e[:decoration].include?(:box) || e[:decoration].include?(:circle) || e[:decoration].include?(:bar)
|
141
|
+
if e[:text].size == 1
|
142
|
+
e[:content_width] = width
|
143
|
+
width += (height - width)
|
144
|
+
else
|
145
|
+
e[:content_width] = width
|
146
|
+
width += $width_half_X
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
if e[:decoration].include?(:whitespace)
|
151
|
+
width = $width_half_X / 2 * e[:text].size / 4
|
152
|
+
e[:text] = ""
|
153
|
+
end
|
154
|
+
|
155
|
+
e[:height] = height
|
156
|
+
elements_height << height + $box_vertical_margin / 2
|
157
|
+
|
158
|
+
e[:width] = width
|
159
|
+
row_width += width
|
160
|
+
end
|
161
|
+
|
162
|
+
if @enclosure != :none
|
163
|
+
total_height += (elements_height.max + $height_connector_to_text)
|
164
|
+
else
|
165
|
+
total_height += elements_height.max
|
166
|
+
end
|
167
|
+
content_width += row_width
|
168
|
+
end
|
169
|
+
total_width = content_width if total_width < content_width
|
170
|
+
end
|
171
|
+
@content_width = total_width
|
172
|
+
@content_height = total_height
|
46
173
|
end
|
47
174
|
|
175
|
+
def add_child(child_id)
|
176
|
+
@children << child_id
|
177
|
+
end
|
178
|
+
end
|
48
179
|
end
|
@@ -6,23 +6,25 @@
|
|
6
6
|
#==========================
|
7
7
|
#
|
8
8
|
# Contains a list of unordered tree elements with a defined parent
|
9
|
-
#
|
10
|
-
# This file is part of RSyntaxTree, which is a ruby port of Andre Eisenbach's
|
11
|
-
# excellent program phpSyntaxTree.
|
12
|
-
#
|
13
9
|
# Copyright (c) 2007-2021 Yoichiro Hasebe <yohasebe@gmail.com>
|
14
|
-
# Copyright (c) 2003-2004 Andre Eisenbach <andre@ironcreek.net>
|
15
10
|
|
16
11
|
require 'element'
|
17
12
|
|
18
|
-
|
13
|
+
module RSyntaxTree
|
14
|
+
class ElementList
|
19
15
|
|
20
|
-
attr_accessor :elements, :iterator
|
16
|
+
attr_accessor :elements, :iterator
|
21
17
|
def initialize
|
22
18
|
@elements = Array.new # The element array
|
23
19
|
@iterator = -1 # Iterator index (used for get_first / get_next)
|
24
20
|
end
|
25
21
|
|
22
|
+
def set_hierarchy
|
23
|
+
@elements.each do |e|
|
24
|
+
get_id(e.parent).add_child(e.id) unless e.parent == 0
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
26
28
|
def add(element)
|
27
29
|
@elements << element
|
28
30
|
if(element.parent != 0)
|
@@ -41,12 +43,12 @@ class ElementList
|
|
41
43
|
end
|
42
44
|
|
43
45
|
def get_next
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
@iterator += 1
|
47
|
+
if @elements[@iterator]
|
48
|
+
return @elements[@iterator]
|
49
|
+
else
|
50
|
+
return nil
|
51
|
+
end
|
50
52
|
end
|
51
53
|
|
52
54
|
def get_id(id)
|
@@ -118,4 +120,5 @@ class ElementList
|
|
118
120
|
end
|
119
121
|
return maxlevel + 1;
|
120
122
|
end
|
123
|
+
end
|
121
124
|
end
|