@abi-software/map-side-bar 1.2.0-beta.8 → 1.2.0-beta.9

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.
package/package-lock.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@abi-software/map-side-bar",
3
- "version": "1.2.0-beta.7",
3
+ "version": "1.2.0-beta.8",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
7
7
  "@abi-software/gallery": {
8
- "version": "0.3.0-beta.2",
9
- "resolved": "https://registry.npmjs.org/@abi-software/gallery/-/gallery-0.3.0-beta.2.tgz",
10
- "integrity": "sha512-sYfXNdM3jhRmEJy9gscPfafcyzL9otGqfc349SRRzFxeEsncZlFSEPU0huXgt5y3+FxF+VzKPoYhcENDmSDn9w==",
8
+ "version": "0.3.0-beta.3",
9
+ "resolved": "https://registry.npmjs.org/@abi-software/gallery/-/gallery-0.3.0-beta.3.tgz",
10
+ "integrity": "sha512-glHtCM2h+c3CyBetb80CA/2N+A7lPB6WPkSRpAO0y+7RIv8Vy7KkxY3Y4yz9YgoP88pOibdZCr9fmPaYMkd8kg==",
11
11
  "requires": {
12
12
  "@babel/code-frame": "^7.12.11",
13
13
  "axios": "^0.26.1",
@@ -54,9 +54,9 @@
54
54
  }
55
55
  },
56
56
  "core-js": {
57
- "version": "3.22.2",
58
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.22.2.tgz",
59
- "integrity": "sha512-Z5I2vzDnEIqO2YhELVMFcL1An2CIsFe9Q7byZhs8c/QxummxZlAHw33TUHbIte987LkisOgL0LwQ1P9D6VISnA=="
57
+ "version": "3.22.4",
58
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.22.4.tgz",
59
+ "integrity": "sha512-1uLykR+iOfYja+6Jn/57743gc9n73EWiOnSJJ4ba3B4fOEYDBv25MagmEZBxTp5cWq4b/KPx/l77zgsp28ju4w=="
60
60
  },
61
61
  "element-ui": {
62
62
  "version": "2.15.8",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abi-software/map-side-bar",
3
- "version": "1.2.0-beta.8",
3
+ "version": "1.2.0-beta.9",
4
4
  "main": "./dist/map-side-bar.common.js",
5
5
  "files": [
6
6
  "dist/*",
@@ -17,7 +17,7 @@
17
17
  "start": "vue-cli-service serve"
18
18
  },
19
19
  "dependencies": {
20
- "@abi-software/gallery": "^0.3.0-beta.2",
20
+ "@abi-software/gallery": "^0.3.0-beta.3",
21
21
  "@abi-software/svg-sprite": "^0.1.14",
22
22
  "algoliasearch": "^4.10.5",
23
23
  "element-ui": "^2.13.0",
@@ -0,0 +1,141 @@
1
+ <template>
2
+ <div v-if="categories['All'].size > 1" class="container" ref="container">
3
+ <template v-for="(item, key) in categories" >
4
+ <el-badge v-if="item.size > 0" :value="item.size" class="badge-item" :key="key">
5
+ <el-button
6
+ :class="[{ 'active': key == active},'badges-button']"
7
+ @click="categoryClicked(key)"
8
+ size="small">{{ key }}
9
+ </el-button>
10
+ </el-badge>
11
+ </template>
12
+ </div>
13
+ </template>
14
+
15
+
16
+ <script>
17
+ /* eslint-disable no-alert, no-console */
18
+ import Vue from "vue";
19
+ import { Badge, Button } from "element-ui";
20
+ import lang from "element-ui/lib/locale/lang/en";
21
+ import locale from "element-ui/lib/locale";
22
+
23
+ locale.use(lang);
24
+ Vue.use(Badge);
25
+ Vue.use(Button);
26
+
27
+ export default {
28
+ name: "BadgesGroup",
29
+ props: {
30
+ /**
31
+ * Object containing information for
32
+ * the required viewing.
33
+ */
34
+ additionalLinks: {
35
+ type: Array,
36
+ default: () => {
37
+ return [];
38
+ },
39
+ },
40
+ datasetBiolucida: {
41
+ type: Object,
42
+ default: () => {
43
+ return {};
44
+ },
45
+ },
46
+ entry: {
47
+ type: Object,
48
+ default: () => {
49
+ return {};
50
+ },
51
+ },
52
+ },
53
+ data: function () {
54
+ return {
55
+ categories: { "All": {size: 1} },
56
+ active: "All"
57
+ };
58
+ },
59
+ methods: {
60
+ addToCategories: function (array, name) {
61
+ if (array && array.length > 0) {
62
+ this.categories[name] = { size: array.length };
63
+ this.categories["All"].size += array.length;
64
+ }
65
+ },
66
+ addSimulationsToCategories: function (array) {
67
+ if (array) {
68
+ let size = 0;
69
+
70
+ array.forEach(item => {
71
+ if (item.description == "SED-ML file" || item.description == "CellML file") {
72
+ size = 1;
73
+ }
74
+ });
75
+ if (size > 0) {
76
+ this.categories["Simulations"] = { size };
77
+ this.categories["All"].size += size;
78
+ }
79
+ }
80
+ },
81
+ categoryClicked: function(name) {
82
+ this.active = name;
83
+ this.$emit("categoryChanged", name);
84
+ }
85
+ },
86
+ watch: {
87
+ datasetBiolucida: {
88
+ deep: true,
89
+ immediate: true,
90
+ handler: function (biolucidaData) {
91
+ if ("dataset_images" in biolucidaData) {
92
+ this.addToCategories(biolucidaData["dataset_images"], "Biolucida Images");
93
+ }
94
+ }
95
+ },
96
+ entry: {
97
+ deep: true,
98
+ immediate: true,
99
+ handler: function () {
100
+ this.addToCategories(this.entry.images, 'Images');
101
+ this.addToCategories(this.entry.scaffolds, 'Scaffolds');
102
+ this.addToCategories(this.entry.segmentation, 'Segmentations');
103
+ this.addSimulationsToCategories(this.additionalLinks);
104
+ this.addToCategories(this.entry.videos, 'Videos');
105
+ }
106
+ }
107
+ },
108
+ };
109
+ </script>
110
+
111
+ <!-- Add "scoped" attribute to limit CSS to this component only -->
112
+ <style scoped>
113
+ .badges-button,
114
+ .badges-button:hover,
115
+ .badges-button:focus
116
+ {
117
+ border-radius: 3px;
118
+ font-size: 0.75rem;
119
+ padding: 0.2rem 0.2rem;
120
+ background: #f9f2fc;
121
+ border: 1px solid #8300BF;
122
+ color: #8300BF;
123
+ }
124
+
125
+ .badges-button.active
126
+ {
127
+ background: #8300BF;
128
+ border: 1px solid #8300BF;
129
+ color: #fff;
130
+ }
131
+
132
+ .badge-item {
133
+ margin-top: 0.5rem;
134
+ margin-right: 1rem;
135
+ }
136
+
137
+ .badge-item >>> .el-badge__content {
138
+ background: #8300BF;
139
+ }
140
+
141
+ </style>
@@ -3,14 +3,38 @@
3
3
  <div v-show="showContextCard">
4
4
  <div v-show="showDetails" class="hide" @click="showDetails = !showDetails">Hide information<i class="el-icon-arrow-up"></i></div>
5
5
  <div v-show="!showDetails" class="hide" @click="showDetails = !showDetails">Show information<i class="el-icon-arrow-down"></i></div>
6
- <el-card v-if="showDetails && Object.keys(contextData).length !== 0" class="context-card card" >
7
- <img :src="entry.banner" class="context-image card-left">
6
+ <el-card v-if="showDetails && Object.keys(contextData).length !== 0" v-loading="loading" class="context-card card" >
7
+ <div class="card-left">
8
+ <img :src="entry.banner" class="context-image">
9
+ </div>
8
10
  <div class="card-right">
9
11
  <div class="title">{{contextData.heading}}</div>
10
12
  <div>{{contextData.description}}</div>
13
+ <br/>
14
+ <div v-if="contextData.views" class="subtitle">Scaffold Views</div>
11
15
  <template v-for="(view, i) in contextData.views">
16
+ <span v-bind:key="i+'_1'" @click="openViewFile(view)" class="context-card-item">
17
+ <img class="key-image" :src="getFileFromPath(view.thumbnail)">
18
+ {{view.description}}
19
+ </span>
12
20
  <br v-bind:key="i"/>
13
- <span v-bind:key="i+'_1'" @click="openViewFile(view)" class="scaffold-view"><img :src="getFileFromPath(view.thumbnail)"> {{view.description}}</span>
21
+ </template>
22
+ <div style="margin-bottom: 16px;"/>
23
+ <div v-if="contextData.samples && contextData.samples.length > 0" class="subtitle">Samples on Scaffold</div>
24
+ <template v-for="(sample, i) in contextData.samples">
25
+ <span v-bind:key="i+'_3'" class="context-card-item" @click="toggleSampleDetails(i)">
26
+ <div v-bind:key="i+'_6'" style="display: flex">
27
+ <div v-if="sample.color" class="color-box" :style="'background-color:'+ sample.color"></div>
28
+ <img class="key-image" v-else-if="sample.thumbnail" :src="getFileFromPath(sample.thumbnail)">
29
+ {{sample.heading}}
30
+ <i class="el-icon-warning-outline info"></i>
31
+ </div>
32
+ </span>
33
+ <div v-bind:key="i+'_4'" v-if="sampleDetails[i]">
34
+ {{sample.description}}
35
+ <a v-bind:key="i+'_5'" v-if="sampleDetails[i]" :href="generateFileLink(sample.path)" target="_blank">View Source</a>
36
+ </div>
37
+ <br v-bind:key="i+'_2'"/>
14
38
  </template>
15
39
  </div>
16
40
  </el-card>
@@ -26,6 +50,7 @@ import { Link, Icon, Card, Button, Select, Input } from "element-ui";
26
50
  import lang from "element-ui/lib/locale/lang/en";
27
51
  import locale from "element-ui/lib/locale";
28
52
  import EventBus from "./EventBus"
53
+ import hardcoded_info from './hardcoded-context-info'
29
54
 
30
55
  locale.use(lang);
31
56
  Vue.use(Link);
@@ -36,7 +61,6 @@ Vue.use(Select);
36
61
  Vue.use(Input);
37
62
 
38
63
 
39
-
40
64
  export default {
41
65
  name: "contextCard",
42
66
  props: {
@@ -50,15 +74,22 @@ export default {
50
74
  return {
51
75
  contextData: {},
52
76
  showDetails: true,
53
- showContextCard: true
77
+ showContextCard: true,
78
+ sampleDetails: {},
79
+ loading: false
54
80
  };
55
81
  },
56
82
  watch: {
57
83
  'entry.contextCardUrl': {
58
84
  handler(val){
59
85
  if (val) {
60
- this.getContextFile(val)
61
- this.showContextCard = true
86
+ // used for hardcoding data
87
+ if (val === true){
88
+ this.contextData = hardcoded_info[this.entry.discoverId]
89
+ } else {
90
+ this.getContextFile(val)
91
+ this.showContextCard = true
92
+ }
62
93
  } else {
63
94
  this.showContextCard = false
64
95
  }
@@ -68,6 +99,7 @@ export default {
68
99
  },
69
100
  methods: {
70
101
  getContextFile: function (contextFileUrl) {
102
+ this.loading = true
71
103
  fetch(contextFileUrl)
72
104
  .then((response) =>{
73
105
  if (!response.ok){
@@ -78,24 +110,42 @@ export default {
78
110
  })
79
111
  .then((data) => {
80
112
  this.contextData = data
113
+ console.log(data)
114
+ this.loading = false
81
115
  })
82
116
  .catch(() => {
83
117
  //set defaults if we hit an error
84
118
  this.thumbnail = require('@/../assets/missing-image.svg')
85
119
  this.discoverId = undefined
120
+ this.loading = false
86
121
  });
87
122
  },
88
123
  removeDoubleFilesPath: function(path){
89
- if (path.includes('files/files/')){
90
- return path.replace('files/files/', 'files/')
124
+ if (path.includes('files/')){
125
+ return path.replace('files/', '')
91
126
  } else {
92
127
  return path
93
128
  }
94
129
  },
130
+ toggleSampleDetails: function(i){
131
+ if (this.sampleDetails[i] === undefined){
132
+ Vue.set(this.sampleDetails, i, true)
133
+ } else {
134
+ Vue.set(this.sampleDetails, i, !this.sampleDetails[i])
135
+ }
136
+ },
95
137
  getFileFromPath: function(path){
138
+ // for hardcoded data
139
+ if(this.entry.contextCardUrl === true){
140
+ return path
141
+ }
96
142
  path = this.removeDoubleFilesPath(path)
97
143
  return `${this.entry.apiLocation}s3-resource/${this.entry.discoverId}/${this.entry.version}/files/${path}`
98
144
  },
145
+ generateFileLink(path){
146
+ return `https://sparc.science/file/${this.entry.discoverId}/${this.entry.version}?path=${encodeURI(path)}`
147
+
148
+ },
99
149
  openViewFile: function(view){
100
150
 
101
151
  // note that we assume that the view file is in the same directory as the scaffold (viewUrls take relative paths)
@@ -117,6 +167,19 @@ export default {
117
167
 
118
168
  .context-card{
119
169
  background-color: white;
170
+ max-height: 10 50px;
171
+ padding: 8px;
172
+ font-size: 14px;
173
+ }
174
+
175
+ .context-card-item{
176
+ cursor: pointer;
177
+ padding-bottom: 8px;
178
+ }
179
+
180
+ .key-image {
181
+ width: 25px;
182
+ height: auto;
120
183
  }
121
184
 
122
185
  .context-card >>> .el-card__body {
@@ -126,15 +189,25 @@ export default {
126
189
  }
127
190
 
128
191
  .context-image{
129
- width: 250px
192
+ width: 250px;
193
+ height: auto;
194
+ }
195
+
196
+ .color-box {
197
+ width: 16px;
198
+ height: 16px;
199
+ border: solid 1px #8300bf;
200
+ border-radius: 2px;
201
+ margin-right: 8px;
130
202
  }
131
203
 
132
204
  .card {
133
- padding-top: 18px;
134
205
  margin-bottom: 18px;
135
206
  position: relative;
136
207
  border: solid 1px #e4e7ed;
137
208
  display: flex;
209
+ width: 500px;
210
+ max-height: 480px;
138
211
  }
139
212
 
140
213
  .card-left{
@@ -151,7 +224,17 @@ export default {
151
224
  height: 25px;
152
225
  }
153
226
 
227
+ .info{
228
+ transform: rotate(180deg);
229
+ color: #8300bf;
230
+ margin-left: 8px;
231
+ }
232
+
154
233
  .title{
155
234
  font-weight: bold;
156
235
  }
236
+
237
+ .subtitle{
238
+ font-weight: bold;
239
+ }
157
240
  </style>
@@ -5,9 +5,6 @@
5
5
  <div class="seperator-path"></div>
6
6
  <div class="card" >
7
7
  <span class="card-left">
8
- <!--
9
- <img svg-inline class="banner-img" :src="thumbnail" @click="openDataset"/>
10
- -->
11
8
  <image-gallery v-if="dataIsReady"
12
9
  :datasetId="discoverId"
13
10
  :datasetVersion="version"
@@ -24,42 +21,26 @@
24
21
  :label="label"
25
22
  :datasetThumbnail="thumbnail"
26
23
  :dataset-biolucida="biolucidaData"
27
- @card-clicked="galleryClicked"/>
24
+ :category="currentCategory"
25
+ @card-clicked="galleryClicked"
26
+ />
28
27
  </span>
29
28
  <div class="card-right" >
30
29
  <div class="title" @click="cardClicked">{{entry.name}}</div>
31
30
  <div class="details">{{contributors}} {{entry.publishDate ? `(${publishYear})` : ''}}</div>
32
31
  <div class="details">{{samples}}</div>
33
32
  <div class="details">id: {{discoverId}}</div>
34
- <!--
35
- <div>
36
- <el-button v-if="!entry.simulation" @click="openDataset" size="mini" class="button" icon="el-icon-coin">View dataset</el-button>
37
- </div>
38
- <div>
39
- <el-button v-if="entry.scaffolds" @click="openScaffold" size="mini" class="button" icon="el-icon-view">View scaffold</el-button>
40
- </div>
41
- -->
42
- <!--
43
- <div>
44
- <el-button v-if="hasCSVFile" @click="openPlot" size="mini" class="button" icon="el-icon-view">View plot</el-button>
45
- </div>
46
- -->
47
33
  <div>
48
34
  <el-button v-if="entry.simulation" @click="openRepository" size="mini" class="button" icon="el-icon-view">View repository</el-button>
49
35
  </div>
50
- <!--
51
- <div>
52
- <el-button v-if="entry.simulation" @click="openSimulation" size="mini" class="button" icon="el-icon-view">View simulation</el-button>
53
- </div>
54
- -->
55
- <!--
56
- <div>
57
- <el-button v-if="entry.segmentation" @click="openSegmentation" size="mini" class="button" icon="el-icon-view">View segmentation</el-button>
58
- </div>
59
- <div>
60
- <el-button v-if="biolucidaData" @click="openImage" size="mini" class="button" icon="el-icon-view">View image</el-button>
36
+ <div class="badges-container">
37
+ <badges-group
38
+ :entry="entry"
39
+ :dataset-biolucida="biolucidaData"
40
+ :additionalLinks="simulationLinks"
41
+ @categoryChanged="categoryChanged"
42
+ />
61
43
  </div>
62
- -->
63
44
  </div>
64
45
 
65
46
  </div>
@@ -72,6 +53,7 @@
72
53
  <script>
73
54
  /* eslint-disable no-alert, no-console */
74
55
  import Vue from "vue";
56
+ import BadgesGroup from "./BadgesGroup.vue";
75
57
  import { Button, Icon } from "element-ui";
76
58
  import lang from "element-ui/lib/locale/lang/en";
77
59
  import locale from "element-ui/lib/locale";
@@ -83,13 +65,9 @@ locale.use(lang);
83
65
  Vue.use(Button);
84
66
  Vue.use(Icon);
85
67
 
86
- const capitalise = function(string){
87
- return string.replace(/\b\w/g, v => v.toUpperCase());
88
- }
89
-
90
68
  export default {
91
69
  name: "DatasetCard",
92
- components: { ImageGallery },
70
+ components: { BadgesGroup, ImageGallery },
93
71
  props: {
94
72
  /**
95
73
  * Object containing information for
@@ -108,8 +86,10 @@ export default {
108
86
  dataLocation: this.entry.doi,
109
87
  discoverId: undefined,
110
88
  cardOverflow: false,
89
+
111
90
  expanded: false,
112
- biolucidaData: undefined
91
+ biolucidaData: undefined,
92
+ currentCategory: "All"
113
93
  };
114
94
  },
115
95
  computed: {
@@ -170,37 +150,12 @@ export default {
170
150
  cardClicked: function(){
171
151
  this.openDataset()
172
152
  },
153
+ categoryChanged: function(name) {
154
+ this.currentCategory = name;
155
+ },
173
156
  galleryClicked: function(payload) {
174
157
  this.propogateCardAction(payload)
175
158
  },
176
- openScaffold: function(){
177
- let action = {
178
- label: capitalise(this.label),
179
- resource: this.getScaffoldPath(this.discoverId, this.version, this.entry.scaffolds[0].dataset.path),
180
- title: "View 3D scaffold",
181
- type: "Scaffold",
182
- discoverId: this.discoverId,
183
- apiLocation: this.envVars.API_LOCATION,
184
- version: this.version,
185
- contextCardUrl: this.entry.contextualInformation ? this.getFileFromPath(this.discoverId, this.version,this.entry.contextualInformation) : undefined,
186
- banner: this.thumbnail
187
- }
188
- this.propogateCardAction(action)
189
- },
190
- openPlot: function(){
191
- let action = {
192
- label: capitalise(this.label),
193
- resource: this.getFileFromPath(this.discoverId, this.version, this.entry.csvFiles[0].dataset.path),
194
- title: "View plot",
195
- type: "Plot",
196
- discoverId: this.discoverId,
197
- apiLocation: this.envVars.API_LOCATION,
198
- version: this.version,
199
- contextCardUrl: this.entry.contextualInformation ? this.getFileFromPath(this.discoverId, this.version,this.entry.contextualInformation) : undefined,
200
- banner: this.thumbnail
201
- }
202
- this.propogateCardAction(action)
203
- },
204
159
  openDataset: function(){
205
160
  window.open(this.dataLocation,'_blank');
206
161
  },
@@ -227,92 +182,9 @@ export default {
227
182
  }
228
183
  });
229
184
  },
230
- openSimulation: function() {
231
- let isSedmlResource = false;
232
- let resource = undefined;
233
- this.entry.additionalLinks.forEach(function(el) {
234
- if (el.description == "SED-ML file") {
235
- isSedmlResource = true;
236
- resource = el.uri;
237
- } else if (!isSedmlResource && (el.description == "CellML file")) {
238
- resource = el.uri;
239
- }
240
- });
241
- let action = {
242
- label: undefined,
243
- resource: resource,
244
- discoverId: this.dataLocation,
245
- apiLocation: this.envVars.API_LOCATION,
246
- version: this.version,
247
- contextCardUrl: this.entry.contextualInformation ? this.getFileFromPath(this.discoverId, this.version,this.entry.contextualInformation) : undefined,
248
- banner: this.thumbnail,
249
- title: "View simulation",
250
- name: this.entry.name,
251
- description: this.entry.description,
252
- type: "Simulation"
253
- }
254
- EventBus.$emit("PopoverActionClick", action)
255
- },
256
- openSegmentation: function() {
257
- if (this.entry.segmentation && this.entry.segmentation[0]) {
258
- const segmentation = this.entry.segmentation[0];
259
- const filePath = segmentation.dataset.path;
260
- const datasetId = this.discoverId;
261
- const datasetVersion = this.version;
262
- const prefix = this.envVars.NL_LINK_PREFIX;
263
- const resource = {
264
- share_link: `${prefix}/dataviewer?datasetId=${datasetId}&version=${datasetVersion}&path=files/${filePath}`
265
- };
266
- let action = {
267
- label: capitalise(this.label),
268
- resource: resource,
269
- dataset: this.dataLocation,
270
- datasetId: this.discoverId,
271
- title: "View segmentation",
272
- name: this.entry.name,
273
- description: this.entry.description,
274
- type: "Segmentation"
275
- };
276
- EventBus.$emit("PopoverActionClick", action);
277
- }
278
- },
279
- openImage: function() {
280
- if (this.biolucidaData) {
281
- const biolucidaData = this.biolucidaData;
282
- if ('dataset_images' in biolucidaData) {
283
- const image = biolucidaData['dataset_images'][0];
284
- const resource = {
285
- share_link: image.share_link,
286
- id: image.image_id,
287
- itemId: image.sourcepkg_id
288
- }
289
- let action = {
290
- label: capitalise(this.label),
291
- resource: resource,
292
- dataset: this.dataLocation,
293
- datasetId: this.discoverId,
294
- title: "View image",
295
- name: this.entry.name,
296
- description: this.entry.description,
297
- type: "Biolucida"
298
- };
299
- EventBus.$emit("PopoverActionClick", action);
300
- }
301
- }
302
- },
303
185
  propogateCardAction: function(action){
304
186
  EventBus.$emit("PopoverActionClick", action)
305
- if (action.contextCardUrl) {
306
- this.$emit('contextUpdate', action)
307
- }
308
- },
309
- getScaffoldPath: function(discoverId, version, scaffoldPath){
310
- let id = discoverId
311
- let path = `${this.envVars.API_LOCATION}s3-resource/${id}/${version}/files/${scaffoldPath}`
312
- return path
313
- },
314
- getFileFromPath: function(discoverId, version, path){
315
- return `${this.envVars.API_LOCATION}s3-resource/${discoverId}/${version}/files/${path}`
187
+ this.$emit('contextUpdate', action)
316
188
  },
317
189
  isOverflown: function(el){
318
190
  return el.clientHeight < el.scrollHeight
@@ -472,4 +344,8 @@ export default {
472
344
  letter-spacing: 1.05px;
473
345
  color: #484848;
474
346
  }
347
+
348
+ .badges-container {
349
+ margin-top:0.5rem;
350
+ }
475
351
  </style>