移動端自適應
# 移動端自適應
Apache EChartsTM 工作在使用者指定高寬的 DOM 節點(容器)中。ECharts 的『元件』和『系列』都在這個 DOM 節點中,每個節點都可以由使用者指定位置。圖表庫內部並不適宜實現 DOM 文件流佈局,因此採用類似絕對佈局的簡單容易理解的佈局方式。但是有時候容器尺寸極端時,這種方式並不能自動避免元件重疊的情況,尤其在移動端小屏的情況下。
另外,有時會出現一個圖表需要同時在PC、移動端上展現的場景。這需要 ECharts 內部元件隨著容器尺寸變化而變化的能力。
爲了解決這個問題,ECharts 完善了元件的定位設定,並且實現了類似 CSS Media Query (opens new window) 的自適應能力。
# 1. ECharts元件的定位和佈局
大部分『元件』和『系列』會遵循兩種定位方式:
left/right/top/bottom/width/height 定位方式:
這六個量中,每個量都可以是『絕對值』或者『百分比』或者『位置描述』。
絕對值
單位是瀏覽器畫素(px),用
number
形式書寫(不寫單位)。例如{left: 23, height: 400}
。百分比
表示占 DOM 容器高寬的百分之多少,用
string
形式書寫。例如{right: '30%', bottom: '40%'}
。位置描述
- 可以設定
left: 'center'
,表示水平居中。 - 可以設定
top: 'middle'
,表示垂直居中。
- 可以設定
這六個量的概念,和 CSS 中六個量的概念類似:
- left:距離 DOM 容器左邊界的距離。
- right:距離 DOM 容器右邊界的距離。
- top:距離 DOM 容器上邊界的距離。
- bottom:距離 DOM 容器下邊界的距離。
- width:寬度。
- height:高度。
在橫向,left
、right
、width
三個量中,只需兩個量有值即可,因為任兩個量可以決定元件的位置和大小,例如 left
和 right
或者 right
和 width
都可以決定元件的位置和大小。
縱向,top
、bottom
、height
三個量,和橫向類同不贅述。
center
/ radius
定位方式:
center
是一個陣列,表示
[x, y]
,其中,x
、y
可以是『絕對值』或者『百分比』,含義和前述相同。radius
是一個陣列,表示
[內半徑, 外半徑]
,其中,內外半徑可以是『絕對值』或者『百分比』,含義和前述相同。在自適應容器大小時,百分比設定是很有用的。
橫向(horizontal)和縱向(vertical)
ECharts的『外觀狹長』型的元件(如 legend
、visualMap
、dataZoom
、timeline
等),大多提供了『橫向佈局』『縱向佈局』的選擇。例如,在細長的移動端螢幕上,可能適合使用『縱向佈局』;在PC寬屏上,可能適合使用『橫向佈局』。
橫縱向佈局的設定,一般在『元件』或者『系列』的 orient
或者 layout
配置項上,設定為 'horizontal'
或者 'vertical'
。
與 ECharts2 的相容:
ECharts2 中的 x/x2/y/y2
的命名方式仍被相容,對應于 left/right/top/bottom
。但是建議寫 left/right/top/bottom
。
位置描述中,為相容 ECharts2,可以支援一些看起來略奇怪的設定:left: 'right'
、left: 'left'
、top: 'bottom'
、top: 'top'
。這些語句分別等效于:right: 0
、left: 0
、bottom: 0
、top: 0
,寫成後者就不奇怪了。
# 2. Media Query
Media Query (opens new window) 提供了『隨著容器尺寸改變而改變』的能力。
如下例子,可嘗試拖動右下角的圓點,隨著尺寸變化,legend 和 系列會自動改變佈局位置和方式。
- FastWeb TUgURLFrame示例:
如需在FastWeb中的TUgURLFrame中實現上述效果,則修改其HTML
屬性,使用下列內容:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ECharts</title>
<!-- 引入 echarts.js -->
<script src="https://cdn.staticfile.org/echarts/5.1.2/echarts.min.js"></script>
<script src="https://cdn.staticfile.org/jquery/1.10.0/jquery.min.js"></script>
</head>
<body>
<!-- 為ECharts準備一個具備大小(寬高)的Dom -->
<div id="main" style="width: 100%;height:600px;"></div>
<script type="text/javascript">
// 基於準備好的dom,初始化echarts實體
var myChart = echarts.init(document.getElementById('main'));
var option;
var ROOT_PATH = 'https://cdn.jsdelivr.net/gh/apache/echarts-website@asf-site/examples';
$.when(
$.getScript(ROOT_PATH + '/data/asset/data/timelineGDP.js'),
$.getScript(ROOT_PATH + '/data/asset/data/draggable.js')
).done(function () {
draggable.init(
$('div[_echarts_instance_]')[0],
myChart,
{
width: 700,
height: 400,
throttle: 70
}
);
myChart.hideLoading();
option = {
baseOption: {
title : {
text: '南丁格爾玫瑰圖',
subtext: '純屬虛構',
x:'center'
},
tooltip : {
trigger: 'item',
formatter: "{a} <br/>{b} : {c} ({d}%)"
},
legend: {
data:['rose1','rose2','rose3','rose4','rose5','rose6','rose7','rose8']
},
toolbox: {
show : true,
feature : {
mark : {show: true},
dataView : {show: true, readOnly: false},
magicType : {
show: true,
type: ['pie', 'funnel']
},
restore : {show: true},
saveAsImage : {show: true}
}
},
calculable : true,
series : [
{
name:'半徑模式',
type:'pie',
roseType : 'radius',
label: {
normal: {
show: false
},
emphasis: {
show: true
}
},
lableLine: {
normal: {
show: false
},
emphasis: {
show: true
}
},
data:[
{value:10, name:'rose1'},
{value:5, name:'rose2'},
{value:15, name:'rose3'},
{value:25, name:'rose4'},
{value:20, name:'rose5'},
{value:35, name:'rose6'},
{value:30, name:'rose7'},
{value:40, name:'rose8'}
]
},
{
name:'面積模式',
type:'pie',
roseType : 'area',
data:[
{value:10, name:'rose1'},
{value:5, name:'rose2'},
{value:15, name:'rose3'},
{value:25, name:'rose4'},
{value:20, name:'rose5'},
{value:35, name:'rose6'},
{value:30, name:'rose7'},
{value:40, name:'rose8'}
]
}
]
},
media: [
{
option: {
legend: {
right: 'center',
bottom: 0,
orient: 'horizontal'
},
series: [
{
radius: [20, '50%'],
center: ['25%', '50%']
},
{
radius: [30, '50%'],
center: ['75%', '50%']
}
]
}
},
{
query: {
minAspectRatio: 1
},
option: {
legend: {
right: 'center',
bottom: 0,
orient: 'horizontal'
},
series: [
{
radius: [20, '50%'],
center: ['25%', '50%']
},
{
radius: [30, '50%'],
center: ['75%', '50%']
}
]
}
},
{
query: {
maxAspectRatio: 1
},
option: {
legend: {
right: 'center',
bottom: 0,
orient: 'horizontal'
},
series: [
{
radius: [20, '50%'],
center: ['50%', '30%']
},
{
radius: [30, '50%'],
center: ['50%', '70%']
}
]
}
},
{
query: {
maxWidth: 500
},
option: {
legend: {
right: 10,
top: '15%',
orient: 'vertical'
},
series: [
{
radius: [20, '50%'],
center: ['50%', '30%']
},
{
radius: [30, '50%'],
center: ['50%', '75%']
}
]
}
}
]
};
myChart.setOption(option);
});
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
要在 option 中設定 Media Query 須遵循如下格式:
option = {
// 這裡是基本的『原子option』。
title: {...},
legend: {...},
series: [{...}, {...}, ...],
...,
media: [ // 這裡定義了 media query 的逐條規則。
{
query: {...}, // 這裡寫規則。
option: { // 這裡寫此規則滿足下的option。
legend: {...},
...
}
},
{
query: {...}, // 第二個規則。
option: { // 第二個規則對應的option。
legend: {...},
...
}
},
{ // 這條里沒有寫規則,表示『預設』,
option: { // 即所有規則都不滿足時,採納這個option。
legend: {...},
...
}
}
]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
上面的例子中,baseOption
、以及 media
每個 option 都是『原子 option』,即普通的含有各元件、系列定義的 option。而由『原子option』組合成的整個 option,我們稱為『複合 option』。baseOption
是必然被使用的,此外,滿足了某個 query
條件時,對應的 option 會被使用 chart.mergeOption()
來 merge 進去。
query:
每個 query
類似於這樣:
{
minWidth: 200,
maxHeight: 300,
minAspectRatio: 1.3
}
2
3
4
5
現在支援三個屬性:width
、height
、aspectRatio
(長寬比)。每個屬性都可以加上 min
或 max
字首。比如,minWidth: 200
表示『大於等於200px寬度』。兩個屬性一起寫表示『並且』,比如:{minWidth: 200, maxHeight: 300}
表示『大於等於200px寬度,並且小於等於300px高度』。
option:
media
中的 option 既然是『原子 option』,理論上可以寫任何 option 的配置項。但是一般我們只寫跟佈局定位相關的,例如擷取上面例子中的一部分 query option:
media: [
...,
{
query: {
maxAspectRatio: 1 // 當長寬比小於1時。
},
option: {
legend: { // legend 放在底部中間。
right: 'center',
bottom: 0,
orient: 'horizontal' // legend 橫向佈局。
},
series: [ // 兩個餅圖左右佈局。
{
radius: [20, '50%'],
center: ['50%', '30%']
},
{
radius: [30, '50%'],
center: ['50%', '70%']
}
]
}
},
{
query: {
maxWidth: 500 // 當容器寬度小於 500 時。
},
option: {
legend: {
right: 10, // legend 放置在右側中間。
top: '15%',
orient: 'vertical' // 縱向佈局。
},
series: [ // 兩個餅圖上下佈局。
{
radius: [20, '50%'],
center: ['50%', '30%']
},
{
radius: [30, '50%'],
center: ['50%', '75%']
}
]
}
},
...
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
多個 query 被滿足時的優先順序:
注意,可以有多個 query
同時被滿足,會都被 mergeOption
,定義在後的后被 merge(即優先順序更高)。
預設 query:
如果 media
中有某項不寫 query
,則表示『預設值』,即所有規則都不滿足時,採納這個option。
容器大小實時變化時的注意事項:
在不少情況下,並不需要容器DOM節點任意隨著拖拽變化大小,而是隻是根據不同終端設定幾個典型尺寸。
但是如果容器DOM節點需要能任意隨著拖拽變化大小,那麼目前使用時需要注意這件事:某個配置項,如果在某一個 query option
中出現,那麼在其他 query option
中也必須出現,否則不能夠迴歸到原來的狀態。(left/right/top/bottom/width/height
不受這個限制。)
『複合 option』 中的 media
不支援 merge
也就是說,當第二(或三、四、五 ...)次 chart.setOption(rawOption)
時,如果 rawOption
是 複合option
(即包含 media
列表),那麼新的 rawOption.media
列表不會和老的 media
列表進行 merge,而是簡單替代。當然,baseOption
仍然會正常和老的 option 進行merge。
其實,很少有場景需要使用『複合 option』來多次 setOption
,而我們推薦的做法是,使用 mediaQuery 時,第一次setOption使用『複合 option』,後面 setOption
時僅使用 『原子 option』,也就是僅僅用 setOption 來改變 baseOption
。
最後看一個和時間軸結合的例子:
- FastWeb TUgURLFrame示例:
如需在FastWeb中的TUgURLFrame中實現上述效果,則修改其HTML
屬性,使用下列內容:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ECharts</title>
<!-- 引入 echarts.js -->
<script src="https://cdn.staticfile.org/echarts/5.1.2/echarts.min.js"></script>
<script src="https://cdn.staticfile.org/jquery/1.10.0/jquery.min.js"></script>
</head>
<body>
<!-- 為ECharts準備一個具備大小(寬高)的Dom -->
<div id="main" style="width: 100%;height:600px;"></div>
<script type="text/javascript">
// 基於準備好的dom,初始化echarts實體
var myChart = echarts.init(document.getElementById('main'));
var option;
var ROOT_PATH = 'https://cdn.jsdelivr.net/gh/apache/echarts-website@asf-site/examples';
$.when(
$.getScript(ROOT_PATH + '/data/asset/data/timelineGDP.js'),
$.getScript(ROOT_PATH + '/data/asset/data/draggable.js')
).done(function () {
draggable.init(
$('div[_echarts_instance_]')[0],
myChart,
{
width: 700,
height: 630,
lockY: true,
throttle: 70
}
);
myChart.hideLoading();
var categoryData = [
'北京','天津','河北','山西','內蒙古','遼寧','吉林','黑龍江',
'上海','江蘇','浙江','安徽','福建','江西','山東','河南',
'湖北','湖南','廣東','廣西','海南','重慶','四川','貴州',
'雲南','西藏','陜西','甘肅','青海','寧夏','新疆'
];
option = {
baseOption: {
timeline: {
axisType: 'category',
autoPlay: true,
playInterval: 1000,
data: [
'2002-01-01', '2003-01-01', '2004-01-01',
'2005-01-01', '2006-01-01', '2007-01-01',
'2008-01-01', '2009-01-01', '2010-01-01',
'2011-01-01'
],
label: {
formatter : function(s) {
return (new Date(s)).getFullYear();
}
}
},
title: {
subtext: 'Media Query 示例'
},
tooltip: {
trigger:'axis',
axisPointer: {
type: 'shadow'
}
},
xAxis: {
type: 'value',
name: 'GDP(億元)',
max: 30000,
data: null
},
yAxis: {
type: 'category',
data: categoryData,
axisLabel: {interval: 0},
splitLine: {show: false}
},
legend: {
data: ['第一產業', '第二產業', '第三產業', 'GDP', '金融', '房地產'],
selected: {
'GDP': false, '金融': false, '房地產': false
}
},
calculable : true,
series: [
{name: 'GDP', type: 'bar'},
{name: '金融', type: 'bar'},
{name: '房地產', type: 'bar'},
{name: '第一產業', type: 'bar'},
{name: '第二產業', type: 'bar'},
{name: '第三產業', type: 'bar'},
{name: 'GDP佔比', type: 'pie'}
]
},
media: [
{
option: {
legend: {
orient: 'horizontal',
left: 'right',
itemGap: 10
},
grid: {
left: '10%',
top: 80,
right: 90,
bottom: 100
},
xAxis: {
nameLocation: 'end',
nameGap: 10,
splitNumber: 5,
splitLine: {
show: true
}
},
timeline: {
orient: 'horizontal',
inverse: false,
left: '20%',
right: '20%',
bottom: 10,
height: 40
},
series: [
{name: 'GDP佔比', center: ['75%', '30%'], radius: '28%'}
]
}
},
{
query: {maxWidth: 670, minWidth: 550},
option: {
legend: {
orient: 'horizontal',
left: 200,
itemGap: 5
},
grid: {
left: '10%',
top: 80,
right: 90,
bottom: 100
},
xAxis: {
nameLocation: 'end',
nameGap: 10,
splitNumber: 5,
splitLine: {
show: true
}
},
timeline: {
orient: 'horizontal',
inverse: false,
left: '20%',
right: '20%',
bottom: 10,
height: 40
},
series: [
{name: 'GDP佔比', center: ['75%', '30%'], radius: '28%'}
]
}
},
{
query: {maxWidth: 550},
option: {
legend: {
orient: 'vertical',
left: 'right',
itemGap: 5
},
grid: {
left: 55,
top: '32%',
right: 100,
bottom: 50
},
xAxis: {
nameLocation: 'middle',
nameGap: 25,
splitNumber: 3
},
timeline: {
orient: 'vertical',
inverse: true,
right: 10,
top: 150,
bottom: 10,
width: 55
},
series: [
{name: 'GDP佔比', center: ['45%', '20%'], radius: '28%'}
]
}
}
],
options: [
{
title: {text: '2002全國宏觀經濟指標'},
series: [
{data: dataMap.dataGDP['2002']},
{data: dataMap.dataFinancial['2002']},
{data: dataMap.dataEstate['2002']},
{data: dataMap.dataPI['2002']},
{data: dataMap.dataSI['2002']},
{data: dataMap.dataTI['2002']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2002sum']},
{name: '第二產業', value: dataMap.dataSI['2002sum']},
{name: '第三產業', value: dataMap.dataTI['2002sum']}
]}
]
},
{
title : {text: '2003全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2003']},
{data: dataMap.dataFinancial['2003']},
{data: dataMap.dataEstate['2003']},
{data: dataMap.dataPI['2003']},
{data: dataMap.dataSI['2003']},
{data: dataMap.dataTI['2003']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2003sum']},
{name: '第二產業', value: dataMap.dataSI['2003sum']},
{name: '第三產業', value: dataMap.dataTI['2003sum']}
]}
]
},
{
title : {text: '2004全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2004']},
{data: dataMap.dataFinancial['2004']},
{data: dataMap.dataEstate['2004']},
{data: dataMap.dataPI['2004']},
{data: dataMap.dataSI['2004']},
{data: dataMap.dataTI['2004']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2004sum']},
{name: '第二產業', value: dataMap.dataSI['2004sum']},
{name: '第三產業', value: dataMap.dataTI['2004sum']}
]}
]
},
{
title : {text: '2005全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2005']},
{data: dataMap.dataFinancial['2005']},
{data: dataMap.dataEstate['2005']},
{data: dataMap.dataPI['2005']},
{data: dataMap.dataSI['2005']},
{data: dataMap.dataTI['2005']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2005sum']},
{name: '第二產業', value: dataMap.dataSI['2005sum']},
{name: '第三產業', value: dataMap.dataTI['2005sum']}
]}
]
},
{
title : {text: '2006全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2006']},
{data: dataMap.dataFinancial['2006']},
{data: dataMap.dataEstate['2006']},
{data: dataMap.dataPI['2006']},
{data: dataMap.dataSI['2006']},
{data: dataMap.dataTI['2006']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2006sum']},
{name: '第二產業', value: dataMap.dataSI['2006sum']},
{name: '第三產業', value: dataMap.dataTI['2006sum']}
]}
]
},
{
title : {text: '2007全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2007']},
{data: dataMap.dataFinancial['2007']},
{data: dataMap.dataEstate['2007']},
{data: dataMap.dataPI['2007']},
{data: dataMap.dataSI['2007']},
{data: dataMap.dataTI['2007']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2007sum']},
{name: '第二產業', value: dataMap.dataSI['2007sum']},
{name: '第三產業', value: dataMap.dataTI['2007sum']}
]}
]
},
{
title : {text: '2008全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2008']},
{data: dataMap.dataFinancial['2008']},
{data: dataMap.dataEstate['2008']},
{data: dataMap.dataPI['2008']},
{data: dataMap.dataSI['2008']},
{data: dataMap.dataTI['2008']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2008sum']},
{name: '第二產業', value: dataMap.dataSI['2008sum']},
{name: '第三產業', value: dataMap.dataTI['2008sum']}
]}
]
},
{
title : {text: '2009全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2009']},
{data: dataMap.dataFinancial['2009']},
{data: dataMap.dataEstate['2009']},
{data: dataMap.dataPI['2009']},
{data: dataMap.dataSI['2009']},
{data: dataMap.dataTI['2009']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2009sum']},
{name: '第二產業', value: dataMap.dataSI['2009sum']},
{name: '第三產業', value: dataMap.dataTI['2009sum']}
]}
]
},
{
title : {text: '2010全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2010']},
{data: dataMap.dataFinancial['2010']},
{data: dataMap.dataEstate['2010']},
{data: dataMap.dataPI['2010']},
{data: dataMap.dataSI['2010']},
{data: dataMap.dataTI['2010']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2010sum']},
{name: '第二產業', value: dataMap.dataSI['2010sum']},
{name: '第三產業', value: dataMap.dataTI['2010sum']}
]}
]
},
{
title : {text: '2011全國宏觀經濟指標'},
series : [
{data: dataMap.dataGDP['2011']},
{data: dataMap.dataFinancial['2011']},
{data: dataMap.dataEstate['2011']},
{data: dataMap.dataPI['2011']},
{data: dataMap.dataSI['2011']},
{data: dataMap.dataTI['2011']},
{data: [
{name: '第一產業', value: dataMap.dataPI['2011sum']},
{name: '第二產業', value: dataMap.dataSI['2011sum']},
{name: '第三產業', value: dataMap.dataTI['2011sum']}
]}
]
}
]
};
myChart.setOption(option);
});
</script>
</body>
</html>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375