@abi-software/mapintegratedvuer 1.21.1 → 1.22.0
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/README.md +23 -5
- package/assets/location-icon.html +33 -1
- package/assets/styleguide.css +18 -18
- package/cypress.config.js +7 -9
- package/dist/{ConnectivityGraph-CZMROk9r.js → ConnectivityGraph-BBZ3Dgat.js} +3 -3
- package/dist/{ContentMixin-Vn34n5t2.js → ContentMixin-B-O_IwPg.js} +16 -18
- package/dist/{DynamicMarkerMixin-BS6t7w_i.js → DynamicMarkerMixin-B9iqtP_f.js} +1 -1
- package/dist/{Flatmap-BejxA0uz.js → Flatmap-BoEFI50b.js} +4 -4
- package/dist/{Iframe-BWbhTjCR.js → Iframe-Slj-LQTF.js} +4 -4
- package/dist/{MultiFlatmap-nlmsH-Ib.js → MultiFlatmap-l6aBn6x3.js} +17 -5
- package/dist/{Plot-ChbPsDZX.js → Plot-CIhEip5b.js} +2 -2
- package/dist/{Scaffold-C6f7c7Ha.js → Scaffold-D5ZGCDvt.js} +4 -4
- package/dist/{Simulation-DcJScRYo.js → Simulation-C5o5DXRb.js} +2 -2
- package/dist/{connectivities-UXTqf7_0.js → connectivities-Br3MpoAv.js} +154 -95
- package/dist/index.html +9 -6
- package/dist/mapintegratedvuer.js +5506 -5582
- package/dist/mapintegratedvuer.umd.cjs +196 -196
- package/dist/style.css +1 -1
- package/eslint.config.js +39 -15
- package/package.json +12 -6
- package/public/index.html +9 -6
- package/src/App.vue +224 -255
- package/src/assets/_variables.scss +12 -10
- package/src/assets/header-icon.scss +8 -9
- package/src/assets/mapicon-species-style.css +10 -8
- package/src/assets/styles.scss +1 -1
- package/src/components/ContentBar.vue +99 -106
- package/src/components/ContentVuer.vue +36 -39
- package/src/components/ContextCard.vue +238 -199
- package/src/components/ContextHelp.vue +22 -26
- package/src/components/CustomSplitter.vue +32 -43
- package/src/components/DatasetHeader.vue +37 -41
- package/src/components/DialogToolbarContent.vue +292 -256
- package/src/components/FlatmapContextCard.vue +52 -57
- package/src/components/MapContent.vue +111 -87
- package/src/components/ResizeSensor.vue +8 -13
- package/src/components/SearchControls.vue +26 -35
- package/src/components/SimulatedData.js +196 -219
- package/src/components/SplitDialog.vue +139 -129
- package/src/components/SplitFlow.vue +433 -239
- package/src/components/index.js +1 -3
- package/src/components/scripts/simulation_data.js +393 -397
- package/src/components/scripts/utilities.js +108 -78
- package/src/components/viewers/CellDLViewer.vue +9 -9
- package/src/components/viewers/ConnectivityGraph.vue +9 -12
- package/src/components/viewers/Flatmap.vue +23 -25
- package/src/components/viewers/Iframe.vue +8 -10
- package/src/components/viewers/MultiFlatmap.vue +73 -69
- package/src/components/viewers/Plot.vue +6 -7
- package/src/components/viewers/Scaffold.vue +73 -63
- package/src/components/viewers/Simulation.vue +10 -15
- package/src/main.js +16 -21
- package/src/mixins/ContentMixin.js +208 -178
- package/src/mixins/DynamicMarkerMixin.js +14 -18
- package/src/mixins/RetrieveContextCardMixin.js +35 -30
- package/src/mixins/S3Bucket.vue +10 -13
- package/src/services/knowledgeMapping.js +53 -53
- package/src/services/tagging.js +4 -4
- package/src/stores/connectivities.js +46 -8
- package/src/stores/entries.js +7 -9
- package/src/stores/index.js +7 -7
- package/src/stores/settings.js +23 -31
- package/src/stores/splitFlow.js +395 -298
- package/vite.bundle-build.js +24 -26
- package/vite.config.js +22 -21
- package/vite.static-build.js +4 -4
- package/vitest.workspace.js +1 -3
- package/vuese-generator.js +47 -55
|
@@ -15,9 +15,7 @@
|
|
|
15
15
|
/>
|
|
16
16
|
</el-header>
|
|
17
17
|
<el-main class="dialog-main">
|
|
18
|
-
<div
|
|
19
|
-
style="width: 100%; height: 100%; position: relative; overflow: hidden"
|
|
20
|
-
>
|
|
18
|
+
<div style="width: 100%; height: 100%; position: relative; overflow: hidden">
|
|
21
19
|
<SideBar
|
|
22
20
|
ref="sideBar"
|
|
23
21
|
:envVars="envVars"
|
|
@@ -66,36 +64,28 @@
|
|
|
66
64
|
</template>
|
|
67
65
|
|
|
68
66
|
<script>
|
|
69
|
-
/* eslint-disable no-alert, no-console */
|
|
70
67
|
import { provide, markRaw, computed } from 'vue';
|
|
71
68
|
import Tagging from '../services/tagging.js';
|
|
72
|
-
import DialogToolbarContent from
|
|
73
|
-
import EventBus from
|
|
74
|
-
import SplitDialog from
|
|
69
|
+
import DialogToolbarContent from './DialogToolbarContent.vue';
|
|
70
|
+
import EventBus from './EventBus';
|
|
71
|
+
import SplitDialog from './SplitDialog.vue';
|
|
75
72
|
// import contextCards from './context-cards'
|
|
76
|
-
import { SideBar } from
|
|
77
|
-
import
|
|
78
|
-
import {
|
|
79
|
-
queryForwardBackwardConnections
|
|
80
|
-
} from '@abi-software/map-utilities';
|
|
73
|
+
import { SideBar } from '@abi-software/map-side-bar';
|
|
74
|
+
import '@abi-software/map-side-bar/dist/style.css';
|
|
75
|
+
import { queryForwardBackwardConnections } from '@abi-software/map-utilities';
|
|
81
76
|
import {
|
|
82
77
|
capitalise,
|
|
83
78
|
getNewMapEntry,
|
|
84
79
|
initialDefaultState,
|
|
85
80
|
intersectArrays,
|
|
86
|
-
} from
|
|
87
|
-
import { AnnotationService } from '@abi-software/sparc-annotation'
|
|
81
|
+
} from './scripts/utilities.js';
|
|
82
|
+
import { AnnotationService } from '@abi-software/sparc-annotation';
|
|
88
83
|
import { mapStores } from 'pinia';
|
|
89
84
|
import { useEntriesStore } from '../stores/entries';
|
|
90
|
-
import { useMainStore } from '../stores/index'
|
|
85
|
+
import { useMainStore } from '../stores/index';
|
|
91
86
|
import { useSettingsStore } from '../stores/settings';
|
|
92
87
|
import { useSplitFlowStore } from '../stores/splitFlow';
|
|
93
88
|
import { useConnectivitiesStore } from '../stores/connectivities';
|
|
94
|
-
import {
|
|
95
|
-
ElContainer as Container,
|
|
96
|
-
ElHeader as Header,
|
|
97
|
-
ElMain as Main,
|
|
98
|
-
} from "element-plus";
|
|
99
89
|
|
|
100
90
|
const getAllFacetLabels = (children) => {
|
|
101
91
|
const labels = [];
|
|
@@ -108,12 +98,12 @@ const getAllFacetLabels = (children) => {
|
|
|
108
98
|
});
|
|
109
99
|
}
|
|
110
100
|
return labels;
|
|
111
|
-
}
|
|
101
|
+
};
|
|
112
102
|
|
|
113
103
|
const getFacetsFromAction = (action) => {
|
|
114
104
|
const actionFacets = action.facets ? action.facets : action.labels ? action.labels : [];
|
|
115
105
|
return actionFacets;
|
|
116
|
-
}
|
|
106
|
+
};
|
|
117
107
|
|
|
118
108
|
const getAnatomyTermsForFilters = (action, availableNameCurieMapping) => {
|
|
119
109
|
const facets = [];
|
|
@@ -126,17 +116,14 @@ const getAnatomyTermsForFilters = (action, availableNameCurieMapping) => {
|
|
|
126
116
|
}
|
|
127
117
|
}
|
|
128
118
|
return facets;
|
|
129
|
-
}
|
|
119
|
+
};
|
|
130
120
|
|
|
131
121
|
/**
|
|
132
122
|
* Component of the floating dialogs.
|
|
133
123
|
*/
|
|
134
124
|
export default {
|
|
135
|
-
name:
|
|
125
|
+
name: 'SplitFlow',
|
|
136
126
|
components: {
|
|
137
|
-
Container,
|
|
138
|
-
Header,
|
|
139
|
-
Main,
|
|
140
127
|
DialogToolbarContent,
|
|
141
128
|
SplitDialog,
|
|
142
129
|
SideBar,
|
|
@@ -147,8 +134,11 @@ export default {
|
|
|
147
134
|
let annotator = markRaw(new AnnotationService(`${settings.flatmapAPI}annotator`));
|
|
148
135
|
provide('userApiKey', mainStore.userToken);
|
|
149
136
|
provide('$annotator', annotator);
|
|
150
|
-
provide(
|
|
151
|
-
|
|
137
|
+
provide(
|
|
138
|
+
'showLongLabel',
|
|
139
|
+
computed(() => props.showLongLabel),
|
|
140
|
+
);
|
|
141
|
+
return { annotator };
|
|
152
142
|
},
|
|
153
143
|
props: {
|
|
154
144
|
state: {
|
|
@@ -168,6 +158,11 @@ export default {
|
|
|
168
158
|
startUp: true,
|
|
169
159
|
sidebarStateRestored: false,
|
|
170
160
|
sidebarAnnotationState: false,
|
|
161
|
+
// true while a permalink's sidebar.connectivityEntries are being resolved across the active viewers,
|
|
162
|
+
// used to avoid flashing the full/unfiltered connectivity list
|
|
163
|
+
// or dropping entries resolved by other viewers.
|
|
164
|
+
restoringConnectivityState: false,
|
|
165
|
+
restoreAttempts: 0,
|
|
171
166
|
search: '',
|
|
172
167
|
expanded: '',
|
|
173
168
|
filterTriggered: false,
|
|
@@ -187,7 +182,7 @@ export default {
|
|
|
187
182
|
filterVisibility: true,
|
|
188
183
|
filterOptions: [],
|
|
189
184
|
annotationHighlight: [],
|
|
190
|
-
}
|
|
185
|
+
};
|
|
191
186
|
},
|
|
192
187
|
watch: {
|
|
193
188
|
state: {
|
|
@@ -218,29 +213,27 @@ export default {
|
|
|
218
213
|
const filterExpression = {
|
|
219
214
|
OR: [
|
|
220
215
|
{ NOT: { 'tile-layer': 'pathways' } },
|
|
221
|
-
{ NOT: {
|
|
216
|
+
{ NOT: { HAS: 'nerves' } },
|
|
222
217
|
{
|
|
223
|
-
AND: [
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
]
|
|
227
|
-
}
|
|
228
|
-
]
|
|
218
|
+
AND: [{ 'tile-layer': 'pathways' }, { models: this.connectivityHighlight }],
|
|
219
|
+
},
|
|
220
|
+
],
|
|
229
221
|
};
|
|
230
222
|
const validFilter = this.filterVisibility && this.connectivityProcessed;
|
|
231
223
|
const payload = validFilter ? filterExpression : undefined;
|
|
232
224
|
EventBus.emit('filter-visibility', payload);
|
|
233
225
|
},
|
|
234
226
|
onConnectivityCollapseChange: function (payload) {
|
|
235
|
-
this.expanded = payload.id
|
|
227
|
+
this.expanded = payload.id;
|
|
236
228
|
const splitdialog = this.$refs.splitdialog;
|
|
237
229
|
if (splitdialog) {
|
|
238
230
|
const activeContents = splitdialog.getActiveContents();
|
|
239
|
-
const hasFlatmap = activeContents.find(c => c.viewerType.includes('Flatmap'));
|
|
240
|
-
const hasHumanMaleFlatmap = activeContents.find(c => c.activeSpecies ===
|
|
241
|
-
let nonFlatmapLoad = false
|
|
242
|
-
activeContents.forEach(content => {
|
|
243
|
-
const isFlatmap =
|
|
231
|
+
const hasFlatmap = activeContents.find((c) => c.viewerType.includes('Flatmap'));
|
|
232
|
+
const hasHumanMaleFlatmap = activeContents.find((c) => c.activeSpecies === 'Human Male');
|
|
233
|
+
let nonFlatmapLoad = false;
|
|
234
|
+
activeContents.forEach((content) => {
|
|
235
|
+
const isFlatmap =
|
|
236
|
+
content.viewerType === 'Flatmap' || content.viewerType === 'MultiFlatmap';
|
|
244
237
|
// minimise connectivity detail fetch
|
|
245
238
|
const shouldLoad =
|
|
246
239
|
(hasFlatmap && isFlatmap) ||
|
|
@@ -265,7 +258,7 @@ export default {
|
|
|
265
258
|
if (splitdialog) {
|
|
266
259
|
const activeContents = splitdialog.getActiveContents();
|
|
267
260
|
|
|
268
|
-
activeContents.forEach(content => {
|
|
261
|
+
activeContents.forEach((content) => {
|
|
269
262
|
if (content?.$refs['viewer']) {
|
|
270
263
|
const contentViewer = content.$refs['viewer'];
|
|
271
264
|
const flatmapRef = contentViewer.flatmapRef;
|
|
@@ -273,7 +266,8 @@ export default {
|
|
|
273
266
|
let flatmap = null;
|
|
274
267
|
|
|
275
268
|
if (flatmapRef) flatmap = flatmapRef;
|
|
276
|
-
if (multiflatmapRef
|
|
269
|
+
if (multiflatmapRef && contentViewer.flatmapIsReady())
|
|
270
|
+
flatmap = multiflatmapRef.getCurrentFlatmap();
|
|
277
271
|
|
|
278
272
|
if (flatmap) {
|
|
279
273
|
activeFlatmaps.push(flatmap);
|
|
@@ -283,12 +277,31 @@ export default {
|
|
|
283
277
|
}
|
|
284
278
|
return activeFlatmaps;
|
|
285
279
|
},
|
|
280
|
+
// Scaffold viewers are not Flatmap/MultiFlatmap instances so they're not returned by getActiveFlatmaps,
|
|
281
|
+
// but their own nervesKnowledge (populated once loadConnectivityExplorerConfig resolves)
|
|
282
|
+
// carries the nerve-label data needed to restore their connectivity entries.
|
|
283
|
+
getActiveScaffolds: function () {
|
|
284
|
+
const activeScaffolds = [];
|
|
285
|
+
let splitdialog = this.$refs.splitdialog;
|
|
286
|
+
|
|
287
|
+
if (splitdialog) {
|
|
288
|
+
const activeContents = splitdialog.getActiveContents();
|
|
289
|
+
|
|
290
|
+
activeContents.forEach((content) => {
|
|
291
|
+
const contentViewer = content?.$refs['viewer'];
|
|
292
|
+
if (contentViewer?.scaffoldRef) {
|
|
293
|
+
activeScaffolds.push(contentViewer);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
return activeScaffolds;
|
|
298
|
+
},
|
|
286
299
|
/**
|
|
287
300
|
* Callback when an action is performed (open new dialogs).
|
|
288
301
|
*/
|
|
289
302
|
actionClick: function (action) {
|
|
290
303
|
if (action) {
|
|
291
|
-
if (!this.availableFacets ||
|
|
304
|
+
if (!this.availableFacets || this.availableFacets.length === 0) {
|
|
292
305
|
const availableFacetsRaw = localStorage.getItem('available-anatomy-facets');
|
|
293
306
|
const availableFacetsAll = availableFacetsRaw ? JSON.parse(availableFacetsRaw) : [];
|
|
294
307
|
|
|
@@ -296,7 +309,10 @@ export default {
|
|
|
296
309
|
this.availableFacets = markRaw([...new Set(getAllFacetLabels(availableFacetsAll))]);
|
|
297
310
|
}
|
|
298
311
|
|
|
299
|
-
if (
|
|
312
|
+
if (
|
|
313
|
+
!this.availableNameCurieMapping ||
|
|
314
|
+
Object.keys(this.availableNameCurieMapping).length === 0
|
|
315
|
+
) {
|
|
300
316
|
const availableDataRaw = localStorage.getItem('available-name-curie-mapping');
|
|
301
317
|
const availableData = availableDataRaw ? JSON.parse(availableDataRaw) : {};
|
|
302
318
|
|
|
@@ -304,7 +320,7 @@ export default {
|
|
|
304
320
|
this.availableNameCurieMapping = markRaw(availableData);
|
|
305
321
|
}
|
|
306
322
|
|
|
307
|
-
if (action.type ==
|
|
323
|
+
if (action.type == 'Search') {
|
|
308
324
|
if (action.nervePath) {
|
|
309
325
|
this.openSearch([action.filter], action.label);
|
|
310
326
|
} else {
|
|
@@ -315,67 +331,67 @@ export default {
|
|
|
315
331
|
? 'portal_maps_featured_dataset_search'
|
|
316
332
|
: 'portal_maps_action_search';
|
|
317
333
|
Tagging.sendEvent({
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
334
|
+
event: 'interaction_event',
|
|
335
|
+
event_name: eventName,
|
|
336
|
+
category: action.term || 'filter',
|
|
337
|
+
location: 'map_location_pin',
|
|
322
338
|
});
|
|
323
339
|
this.filterTriggered = true;
|
|
324
340
|
}
|
|
325
|
-
} else if (action.type ==
|
|
326
|
-
window.open(action.resource,
|
|
327
|
-
} else if (action.type ==
|
|
341
|
+
} else if (action.type == 'URL') {
|
|
342
|
+
window.open(action.resource, '_blank');
|
|
343
|
+
} else if (action.type == 'Facet') {
|
|
328
344
|
if (this.$refs.sideBar) {
|
|
329
345
|
const sendAction = {
|
|
330
|
-
facetPropPath:
|
|
331
|
-
facetSubPropPath:
|
|
332
|
-
term:
|
|
346
|
+
facetPropPath: 'anatomy.organ.category.name',
|
|
347
|
+
facetSubPropPath: 'anatomy.organ.name',
|
|
348
|
+
term: 'Anatomical structure',
|
|
333
349
|
};
|
|
334
350
|
const filters = [];
|
|
335
351
|
const facets = getAnatomyTermsForFilters(action, this.availableNameCurieMapping);
|
|
336
352
|
const actionFacets = getFacetsFromAction(action);
|
|
337
353
|
const facetString = actionFacets.join(', ');
|
|
338
|
-
facets.forEach(facet => filters.push({...sendAction, facet}));
|
|
354
|
+
facets.forEach((facet) => filters.push({ ...sendAction, facet }));
|
|
339
355
|
this.$refs.sideBar.addFilter(filters);
|
|
340
356
|
// GA Tagging
|
|
341
357
|
// Event tracking for map action search/filter data
|
|
342
358
|
Tagging.sendEvent({
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
359
|
+
event: 'interaction_event',
|
|
360
|
+
event_name: 'portal_maps_action_filter',
|
|
361
|
+
category: facetString || 'filter_reset',
|
|
362
|
+
location: 'map_location_pin',
|
|
347
363
|
});
|
|
348
364
|
this.filterTriggered = true;
|
|
349
365
|
}
|
|
350
|
-
} else if (action.type ==
|
|
366
|
+
} else if (action.type == 'Facets') {
|
|
351
367
|
const facets = [];
|
|
352
368
|
const facetsArray = getAnatomyTermsForFilters(action, this.availableNameCurieMapping);
|
|
353
369
|
const filterValuesArray = intersectArrays(this.availableFacets, facetsArray);
|
|
354
370
|
const filterValues = filterValuesArray.join(', ');
|
|
355
371
|
|
|
356
|
-
this.settingsStore.facets.species.forEach(e => {
|
|
372
|
+
this.settingsStore.facets.species.forEach((e) => {
|
|
357
373
|
facets.push({
|
|
358
374
|
facet: capitalise(e),
|
|
359
|
-
term:
|
|
360
|
-
facetPropPath:
|
|
375
|
+
term: 'Species',
|
|
376
|
+
facetPropPath: 'organisms.primary.species.name',
|
|
361
377
|
});
|
|
362
378
|
});
|
|
363
379
|
facets.push(
|
|
364
|
-
...filterValuesArray.map(val => ({
|
|
380
|
+
...filterValuesArray.map((val) => ({
|
|
365
381
|
facet: capitalise(val),
|
|
366
|
-
term:
|
|
367
|
-
facetPropPath:
|
|
368
|
-
facetSubPropPath:
|
|
369
|
-
}))
|
|
382
|
+
term: 'Anatomical structure',
|
|
383
|
+
facetPropPath: 'anatomy.organ.category.name',
|
|
384
|
+
facetSubPropPath: 'anatomy.organ.name',
|
|
385
|
+
})),
|
|
370
386
|
);
|
|
371
|
-
this.openSearch(facets,
|
|
387
|
+
this.openSearch(facets, '');
|
|
372
388
|
// GA Tagging
|
|
373
389
|
// Event tracking for map action search/filter data
|
|
374
390
|
Tagging.sendEvent({
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
391
|
+
event: 'interaction_event',
|
|
392
|
+
event_name: 'portal_maps_action_filter',
|
|
393
|
+
category: filterValues || 'filter_reset',
|
|
394
|
+
location: 'map_popup_button',
|
|
379
395
|
});
|
|
380
396
|
this.filterTriggered = true;
|
|
381
397
|
} else {
|
|
@@ -393,7 +409,7 @@ export default {
|
|
|
393
409
|
if (type) categoryValues.push(type);
|
|
394
410
|
if (datasetId) categoryValues.push('(' + id + ')');
|
|
395
411
|
if (resource) {
|
|
396
|
-
if (type ===
|
|
412
|
+
if (type === 'Plot') {
|
|
397
413
|
filePath = resource.dataSource.url;
|
|
398
414
|
} else {
|
|
399
415
|
filePath = typeof resource === 'string' ? resource : resource.share_link;
|
|
@@ -403,12 +419,12 @@ export default {
|
|
|
403
419
|
// GA Tagging
|
|
404
420
|
// Event tracking for map sidebar gallery click
|
|
405
421
|
Tagging.sendEvent({
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
422
|
+
event: 'interaction_event',
|
|
423
|
+
event_name: 'portal_maps_gallery_click',
|
|
424
|
+
category: categoryValues.join(' '),
|
|
425
|
+
location: 'map_sidebar_gallery',
|
|
426
|
+
dataset_id: id ? id + '' : '', // change to string format
|
|
427
|
+
file_path: filePath,
|
|
412
428
|
});
|
|
413
429
|
},
|
|
414
430
|
onDisplaySearch: function (payload, tracking = true) {
|
|
@@ -417,7 +433,7 @@ export default {
|
|
|
417
433
|
let splitdialog = this.$refs.splitdialog;
|
|
418
434
|
if (splitdialog) {
|
|
419
435
|
const activeContents = splitdialog.getActiveContents();
|
|
420
|
-
activeContents.forEach(content => {
|
|
436
|
+
activeContents.forEach((content) => {
|
|
421
437
|
if (content.search(payload.term)) {
|
|
422
438
|
searchFound = true;
|
|
423
439
|
}
|
|
@@ -429,29 +445,31 @@ export default {
|
|
|
429
445
|
// GA Tagging
|
|
430
446
|
// Event tracking for map on display search
|
|
431
447
|
Tagging.sendEvent({
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
448
|
+
event: 'interaction_event',
|
|
449
|
+
event_name: 'portal_maps_display_search',
|
|
450
|
+
category: payload.term,
|
|
451
|
+
location: 'map_toolbar',
|
|
436
452
|
});
|
|
437
453
|
}
|
|
438
454
|
},
|
|
439
|
-
fetchSuggestions: function(payload) {
|
|
455
|
+
fetchSuggestions: function (payload) {
|
|
440
456
|
const suggestions = [];
|
|
441
457
|
//Search all active viewers when global callback is on
|
|
442
458
|
let splitdialog = this.$refs.splitdialog;
|
|
443
459
|
const activeContents = splitdialog.getActiveContents();
|
|
444
460
|
//Push new suggestions into the pre-existing suggestions array
|
|
445
|
-
activeContents.forEach(content =>
|
|
461
|
+
activeContents.forEach((content) =>
|
|
462
|
+
content.searchSuggestions(payload.data.term, suggestions),
|
|
463
|
+
);
|
|
446
464
|
const parsed = [];
|
|
447
465
|
//Remove double quote as it is used as a speical character
|
|
448
|
-
suggestions.forEach(suggestion => {
|
|
449
|
-
parsed.push(suggestion.replaceAll("
|
|
466
|
+
suggestions.forEach((suggestion) => {
|
|
467
|
+
parsed.push(suggestion.replaceAll('"', ''));
|
|
450
468
|
});
|
|
451
469
|
const unique = new Set(parsed);
|
|
452
470
|
suggestions.length = 0;
|
|
453
471
|
for (const item of unique) {
|
|
454
|
-
suggestions.push({
|
|
472
|
+
suggestions.push({ value: '"' + item + '"' });
|
|
455
473
|
}
|
|
456
474
|
payload.data.cb(suggestions);
|
|
457
475
|
},
|
|
@@ -467,7 +485,7 @@ export default {
|
|
|
467
485
|
// offset sidebar only on singlepanel and 2horpanel views
|
|
468
486
|
EventBus.emit('show-connectivity', {
|
|
469
487
|
featureIds: featureIds,
|
|
470
|
-
offset: activeView === 'singlepanel' || activeView === '2horpanel'
|
|
488
|
+
offset: activeView === 'singlepanel' || activeView === '2horpanel',
|
|
471
489
|
});
|
|
472
490
|
}
|
|
473
491
|
},
|
|
@@ -475,23 +493,25 @@ export default {
|
|
|
475
493
|
// expand connectivity card and show connectivity info
|
|
476
494
|
// if expanded exist, payload should be an array of one element
|
|
477
495
|
// skip payload not match the expanded in multiple views
|
|
478
|
-
const isMatched = payload.some(entry => entry.featureId[0] === this.expanded);
|
|
496
|
+
const isMatched = payload.some((entry) => entry.featureId[0] === this.expanded);
|
|
479
497
|
if (this.expanded && this.connectivityExplorerClicked.length && !isMatched) {
|
|
480
498
|
this.connectivityExplorerClicked.pop();
|
|
481
499
|
return;
|
|
482
500
|
}
|
|
483
501
|
|
|
484
502
|
// Remove duplicate items from payload
|
|
485
|
-
const uniquePayload = [
|
|
486
|
-
|
|
503
|
+
const uniquePayload = [
|
|
504
|
+
...new Map(payload.map((entry) => [entry.featureId[0], entry])).values(),
|
|
505
|
+
];
|
|
506
|
+
const mappedPayload = uniquePayload.map((entry) => {
|
|
487
507
|
let result = {
|
|
488
508
|
...entry,
|
|
489
509
|
label: entry.title,
|
|
490
510
|
id: entry.featureId[0],
|
|
491
|
-
}
|
|
492
|
-
const ck = this.connectivityKnowledge.find(ck => ck.id === result.id);
|
|
511
|
+
};
|
|
512
|
+
const ck = this.connectivityKnowledge.find((ck) => ck.id === result.id);
|
|
493
513
|
if (entry.ready) {
|
|
494
|
-
result['nerve-label'] = entry['nerve-label'] || ck['nerve-label'];
|
|
514
|
+
result['nerve-label'] = entry['nerve-label'] || (ck && ck['nerve-label']);
|
|
495
515
|
}
|
|
496
516
|
if (ck && ck['long-label']) {
|
|
497
517
|
result['long-label'] = ck['long-label'];
|
|
@@ -502,13 +522,56 @@ export default {
|
|
|
502
522
|
return result;
|
|
503
523
|
});
|
|
504
524
|
|
|
525
|
+
// While restoring a permalink's sidebar connectivityEntries,
|
|
526
|
+
// popups from multiple active viewers (e.g. two MultiFlatmaps + a Scaffold)
|
|
527
|
+
// resolve concurrently and each only reports the subset it recognises.
|
|
528
|
+
// Merge by id (keeping richer fields like nerve-label/long-label, e.g. from a Scaffold viewer,
|
|
529
|
+
// if a later duplicate for the same id lacks them) instead of replacing,
|
|
530
|
+
// so entries resolved by one viewer are not dropped or downgraded when another viewer's payload arrives.
|
|
531
|
+
if (this.restoringConnectivityState) {
|
|
532
|
+
const merged = new Map(this.connectivityEntry.map((entry) => [entry.id, entry]));
|
|
533
|
+
mappedPayload.forEach((entry) => {
|
|
534
|
+
const existing = merged.get(entry.id);
|
|
535
|
+
merged.set(
|
|
536
|
+
entry.id,
|
|
537
|
+
existing
|
|
538
|
+
? {
|
|
539
|
+
...existing,
|
|
540
|
+
...entry,
|
|
541
|
+
// Viewers emit placeholders (ready: false) before their resolved entries (ready: true),
|
|
542
|
+
// so a late placeholder from one viewer must not downgrade an entry another viewer already resolved.
|
|
543
|
+
ready: existing.ready || entry.ready,
|
|
544
|
+
'nerve-label': entry['nerve-label'] || existing['nerve-label'],
|
|
545
|
+
'long-label': entry['long-label'] || existing['long-label'],
|
|
546
|
+
'expert-consultants':
|
|
547
|
+
entry['expert-consultants'] || existing['expert-consultants'],
|
|
548
|
+
}
|
|
549
|
+
: entry,
|
|
550
|
+
);
|
|
551
|
+
});
|
|
552
|
+
this.connectivityEntry = Array.from(merged.values());
|
|
553
|
+
// Entries resolve asynchronously via connectivity-info-open,
|
|
554
|
+
// which is not tied to connectivity-knowledge ticks, so re-run the restore completion check here.
|
|
555
|
+
// Otherwise the last entries becoming ready never re-evaluates allEntriesReady
|
|
556
|
+
// and the restore never finalises.
|
|
557
|
+
this.checkConnectivityRestoreComplete(this.state?.sidebar);
|
|
558
|
+
} else {
|
|
559
|
+
this.connectivityEntry = mappedPayload;
|
|
560
|
+
}
|
|
561
|
+
|
|
505
562
|
// Fetch long-label from global connectivities if not exist in the payload,
|
|
506
563
|
// this is for the case when user click on the flatmap paths/features directly without going through sidebar list,
|
|
507
|
-
// which will only have id and label in the payload
|
|
508
|
-
|
|
509
|
-
|
|
564
|
+
// which will only have id and label in the payload.
|
|
565
|
+
// Skipped while restoring since connectivityEntry[0] may not relate to the entries just merged in above.
|
|
566
|
+
if (
|
|
567
|
+
!this.restoringConnectivityState &&
|
|
568
|
+
!this.connectivityEntry[0]['long-label'] &&
|
|
569
|
+
this.connectivityEntry[0].mapuuid
|
|
570
|
+
) {
|
|
571
|
+
const connectivityData =
|
|
572
|
+
this.connectivitiesStore.globalConnectivities[this.connectivityEntry[0].mapuuid] || [];
|
|
510
573
|
if (connectivityData.length) {
|
|
511
|
-
const ck = connectivityData.find(ck => ck.id === this.connectivityEntry[0].id);
|
|
574
|
+
const ck = connectivityData.find((ck) => ck.id === this.connectivityEntry[0].id);
|
|
512
575
|
if (ck && ck['long-label']) {
|
|
513
576
|
this.connectivityEntry[0]['long-label'] = ck['long-label'];
|
|
514
577
|
}
|
|
@@ -517,13 +580,13 @@ export default {
|
|
|
517
580
|
|
|
518
581
|
if (this.connectivityExplorerClicked.length) {
|
|
519
582
|
// only remove clicked if not placeholder entry
|
|
520
|
-
if (this.connectivityEntry.every(entry => entry.ready)) {
|
|
583
|
+
if (this.connectivityEntry.every((entry) => entry.ready)) {
|
|
521
584
|
this.connectivityExplorerClicked.pop();
|
|
522
585
|
}
|
|
523
586
|
} else {
|
|
524
587
|
// click on the flatmap paths/features directly
|
|
525
588
|
// or onDisplaySearch is performed
|
|
526
|
-
const connectivityEntries = this.connectivityEntry.map(entry => entry.id);
|
|
589
|
+
const connectivityEntries = this.connectivityEntry.map((entry) => entry.id);
|
|
527
590
|
const flatmapAPI = this.settingsStore.flatmapAPI;
|
|
528
591
|
const { viewingMode } = this.settingsStore.globalSettings;
|
|
529
592
|
const knowledgeSource = this.connectivityEntry[0].mapuuid || '';
|
|
@@ -532,11 +595,12 @@ export default {
|
|
|
532
595
|
|
|
533
596
|
// fetch forward/backward connections on Neuron Connection mode
|
|
534
597
|
if (viewingMode === 'Neuron Connection' && connectivityEntries.length && knowledgeSource) {
|
|
535
|
-
forwardBackwardConnections = await queryForwardBackwardConnections(
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
598
|
+
forwardBackwardConnections = await queryForwardBackwardConnections(
|
|
599
|
+
flatmapAPI,
|
|
600
|
+
knowledgeSource,
|
|
601
|
+
connectivityEntries,
|
|
602
|
+
);
|
|
603
|
+
const allConnections = [...connectivityEntries, ...forwardBackwardConnections];
|
|
540
604
|
const availableConnectivities = this.connectivitiesStore.getUniqueConnectivitiesByKeys;
|
|
541
605
|
mappedConnections = allConnections.map((connId) => {
|
|
542
606
|
return availableConnectivities.find((ac) => ac.id === connId);
|
|
@@ -547,12 +611,12 @@ export default {
|
|
|
547
611
|
if (forwardBackwardConnections.length) {
|
|
548
612
|
this.connectivityEntry = [];
|
|
549
613
|
this.connectivityKnowledge = mappedConnections;
|
|
550
|
-
this.connectivityHighlight = this.connectivityKnowledge.map(ck => ck.id);
|
|
614
|
+
this.connectivityHighlight = this.connectivityKnowledge.map((ck) => ck.id);
|
|
551
615
|
this.connectivityProcessed = true;
|
|
552
616
|
} else {
|
|
553
617
|
this.connectivityKnowledge = this.connectivityEntry;
|
|
554
|
-
if (this.connectivityKnowledge.every(ck => ck.ready)) {
|
|
555
|
-
this.connectivityHighlight = this.connectivityKnowledge.map(ck => ck.id);
|
|
618
|
+
if (this.connectivityKnowledge.every((ck) => ck.ready)) {
|
|
619
|
+
this.connectivityHighlight = this.connectivityKnowledge.map((ck) => ck.id);
|
|
556
620
|
this.connectivityProcessed = true;
|
|
557
621
|
}
|
|
558
622
|
}
|
|
@@ -567,7 +631,7 @@ export default {
|
|
|
567
631
|
this.annotationEntry = payload.annotationEntry;
|
|
568
632
|
// If drawing, `entry.models` may be undefined; use an empty array instead of [undefined]
|
|
569
633
|
// to prevent errors on highlight
|
|
570
|
-
this.annotationHighlight = this.annotationEntry.map(entry => entry.models).filter(Boolean);
|
|
634
|
+
this.annotationHighlight = this.annotationEntry.map((entry) => entry.models).filter(Boolean);
|
|
571
635
|
if (payload.commitCallback) {
|
|
572
636
|
this.annotationCallback = markRaw(payload.commitCallback);
|
|
573
637
|
}
|
|
@@ -589,7 +653,7 @@ export default {
|
|
|
589
653
|
this.confirmCommentCallback = markRaw(payload.confirmComment);
|
|
590
654
|
}
|
|
591
655
|
if (this.$refs.sideBar) {
|
|
592
|
-
this.$refs.sideBar.tabClicked({id: 3, type: 'annotation'});
|
|
656
|
+
this.$refs.sideBar.tabClicked({ id: 3, type: 'annotation' });
|
|
593
657
|
this.$refs.sideBar.setDrawerOpen(true);
|
|
594
658
|
}
|
|
595
659
|
},
|
|
@@ -609,74 +673,95 @@ export default {
|
|
|
609
673
|
connectivityInfo: data.connectivityInfo,
|
|
610
674
|
resource: data.entry,
|
|
611
675
|
type: 'ConnectivityGraph',
|
|
612
|
-
label: data.title || data.label || data.entry ||'Connectivity Graph',
|
|
676
|
+
label: data.title || data.label || data.entry || 'Connectivity Graph',
|
|
613
677
|
graphPayload: { ...data },
|
|
614
678
|
mapServer: this.settingsStore.flatmapAPI,
|
|
615
679
|
sckanVersion: data.sckanVersion,
|
|
616
680
|
});
|
|
617
681
|
|
|
618
|
-
this.splitFlowStore.updateActiveView(
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
682
|
+
this.splitFlowStore.updateActiveView(
|
|
683
|
+
{
|
|
684
|
+
view: '2vertpanel',
|
|
685
|
+
entries: this.entries,
|
|
686
|
+
},
|
|
687
|
+
false,
|
|
688
|
+
);
|
|
622
689
|
|
|
623
690
|
if (previousPrimaryId && previousPrimaryId !== connectivityGraphId) {
|
|
624
|
-
this.splitFlowStore.assignOrSwapPaneWithIds(
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
691
|
+
this.splitFlowStore.assignOrSwapPaneWithIds(
|
|
692
|
+
{
|
|
693
|
+
source: connectivityGraphId,
|
|
694
|
+
target: previousPrimaryId,
|
|
695
|
+
},
|
|
696
|
+
false,
|
|
697
|
+
);
|
|
628
698
|
}
|
|
629
699
|
|
|
630
700
|
const secondPaneId = this.splitFlowStore.customLayout?.['pane-2']?.id;
|
|
631
701
|
if (secondPaneId && secondPaneId !== connectivityGraphId) {
|
|
632
|
-
this.splitFlowStore.assignOrSwapPaneWithIds(
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
702
|
+
this.splitFlowStore.assignOrSwapPaneWithIds(
|
|
703
|
+
{
|
|
704
|
+
source: connectivityGraphId,
|
|
705
|
+
target: secondPaneId,
|
|
706
|
+
},
|
|
707
|
+
false,
|
|
708
|
+
);
|
|
636
709
|
}
|
|
637
710
|
this.splitFlowStore.updateSplitPanels();
|
|
638
711
|
},
|
|
639
712
|
hoverChanged: function (data) {
|
|
640
|
-
let hoverAnatomies = [],
|
|
713
|
+
let hoverAnatomies = [],
|
|
714
|
+
hoverOrgans = [],
|
|
715
|
+
hoverDOI = '',
|
|
716
|
+
hoverConnectivity = [];
|
|
641
717
|
if (data.tabType === 'dataset') {
|
|
642
718
|
hoverAnatomies = data.anatomy ? data.anatomy : [];
|
|
643
719
|
hoverOrgans = data.organs ? data.organs : [];
|
|
644
720
|
hoverDOI = data.doi ? data.doi : '';
|
|
645
721
|
} else if (data.tabType === 'connectivity') {
|
|
646
722
|
hoverConnectivity = data.id ? [data.id] : this.connectivityHighlight;
|
|
647
|
-
hoverOrgans = data['nerve-label']
|
|
723
|
+
hoverOrgans = data['nerve-label']
|
|
724
|
+
? data['nerve-label'].flatMap((nerve) => nerve.subNerves)
|
|
725
|
+
: [];
|
|
648
726
|
} else if (data.tabType === 'annotation') {
|
|
649
727
|
hoverConnectivity = data.models ? [data.models] : this.annotationHighlight;
|
|
650
728
|
}
|
|
651
|
-
this.settingsStore.updateHoverFeatures(
|
|
652
|
-
|
|
729
|
+
this.settingsStore.updateHoverFeatures(
|
|
730
|
+
hoverAnatomies,
|
|
731
|
+
hoverOrgans,
|
|
732
|
+
hoverDOI,
|
|
733
|
+
hoverConnectivity,
|
|
734
|
+
);
|
|
735
|
+
EventBus.emit('hoverUpdate', { connectivityProcessed: this.connectivityProcessed });
|
|
653
736
|
},
|
|
654
737
|
searchChanged: function (data) {
|
|
655
738
|
if (data.tabType === 'dataset') {
|
|
656
|
-
if (data && data.type ==
|
|
739
|
+
if (data && data.type == 'reset-update') {
|
|
657
740
|
this.settingsStore.updateAppliedFacets([]);
|
|
658
741
|
}
|
|
659
|
-
if (data && data.type ==
|
|
742
|
+
if (data && data.type == 'query-update') {
|
|
660
743
|
this.search = data.value;
|
|
661
744
|
if (this.search && !this.filterTriggered) {
|
|
662
745
|
// GA Tagging
|
|
663
746
|
// Event tracking for map action search/filter data
|
|
664
747
|
Tagging.sendEvent({
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
748
|
+
event: 'interaction_event',
|
|
749
|
+
event_name: 'portal_maps_action_search',
|
|
750
|
+
category: this.search,
|
|
751
|
+
location: 'map_sidebar_dataset_search',
|
|
669
752
|
});
|
|
670
753
|
}
|
|
671
754
|
this.filterTriggered = false; // reset for next action
|
|
672
755
|
}
|
|
673
|
-
if (data && data.type ==
|
|
756
|
+
if (data && data.type == 'filter-update') {
|
|
674
757
|
this.settingsStore.updateFacets(data.value);
|
|
675
758
|
// Remove filter event from maps' popup
|
|
676
759
|
if (!this.filterTriggered) {
|
|
677
|
-
const filterValuesArray = data.value
|
|
678
|
-
|
|
679
|
-
|
|
760
|
+
const filterValuesArray = data.value
|
|
761
|
+
.filter((val) => {
|
|
762
|
+
return val.facet && val.facet.toLowerCase() !== 'show all';
|
|
763
|
+
})
|
|
764
|
+
.map((val) => val.facet);
|
|
680
765
|
const labels = filterValuesArray.map((val) => val.toLowerCase());
|
|
681
766
|
const newFacets = [...new Set([...labels])];
|
|
682
767
|
this.settingsStore.updateAppliedFacets(newFacets);
|
|
@@ -684,16 +769,16 @@ export default {
|
|
|
684
769
|
// GA Tagging
|
|
685
770
|
// Event tracking for map action search/filter data
|
|
686
771
|
Tagging.sendEvent({
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
772
|
+
event: 'interaction_event',
|
|
773
|
+
event_name: 'portal_maps_action_filter',
|
|
774
|
+
category: filterValues || 'filter_reset',
|
|
775
|
+
location: 'map_sidebar_dataset_filter',
|
|
691
776
|
});
|
|
692
777
|
}
|
|
693
778
|
this.filterTriggered = false; // reset for next action
|
|
694
779
|
}
|
|
695
780
|
} else if (data.tabType === 'connectivity') {
|
|
696
|
-
if (data && data.type ==
|
|
781
|
+
if (data && data.type == 'reset-update') {
|
|
697
782
|
const activeFlatmaps = this.getActiveFlatmaps();
|
|
698
783
|
activeFlatmaps.forEach((activeFlatmap) => {
|
|
699
784
|
activeFlatmap.resetConnectivityfilters(data.value);
|
|
@@ -706,28 +791,29 @@ export default {
|
|
|
706
791
|
activeFlatmaps.forEach((activeFlatmap) => {
|
|
707
792
|
activeFlatmap.updateConnectivityFilters(data.filter);
|
|
708
793
|
});
|
|
709
|
-
EventBus.emit(
|
|
794
|
+
EventBus.emit('connectivity-query-filter', data);
|
|
710
795
|
|
|
711
|
-
const filterValues = data.filter
|
|
796
|
+
const filterValues = data.filter
|
|
797
|
+
.filter((f) => f.facet && f.facet.toLowerCase() !== 'show all')
|
|
712
798
|
.map((f) => f.tagLabel)
|
|
713
799
|
.join(', ');
|
|
714
800
|
const searchValue = data.query;
|
|
715
801
|
|
|
716
802
|
if (filterValues) {
|
|
717
803
|
Tagging.sendEvent({
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
804
|
+
event: 'interaction_event',
|
|
805
|
+
event_name: 'portal_maps_action_filter',
|
|
806
|
+
category: filterValues,
|
|
807
|
+
location: 'map_sidebar_connectivity_filter',
|
|
722
808
|
});
|
|
723
809
|
}
|
|
724
810
|
|
|
725
811
|
if (searchValue) {
|
|
726
812
|
Tagging.sendEvent({
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
813
|
+
event: 'interaction_event',
|
|
814
|
+
event_name: 'portal_maps_action_search',
|
|
815
|
+
category: searchValue,
|
|
816
|
+
location: 'map_sidebar_connectivity_search',
|
|
731
817
|
});
|
|
732
818
|
}
|
|
733
819
|
}
|
|
@@ -735,31 +821,41 @@ export default {
|
|
|
735
821
|
},
|
|
736
822
|
updateMarkers: function (data) {
|
|
737
823
|
this.settingsStore.updateMarkers(data);
|
|
738
|
-
EventBus.emit(
|
|
824
|
+
EventBus.emit('markerUpdate');
|
|
739
825
|
},
|
|
740
826
|
updateScaffoldMarkers: function (data) {
|
|
741
827
|
this.settingsStore.updateNumberOfDatasetsForFacets(data);
|
|
742
828
|
},
|
|
743
|
-
getNewEntryId: function() {
|
|
829
|
+
getNewEntryId: function () {
|
|
744
830
|
if (this.entries.length) {
|
|
745
|
-
return
|
|
831
|
+
return this.entries[this.entries.length - 1].id + 1;
|
|
746
832
|
}
|
|
747
833
|
return 1;
|
|
748
834
|
},
|
|
749
835
|
/**
|
|
750
836
|
* Add new entry which will sequentially create a
|
|
751
837
|
* new dialog.
|
|
838
|
+
* Set `options.openInSplitView` with `options.sourceId` to open the
|
|
839
|
+
* new entry next to the source entry instead of the primary pane.
|
|
752
840
|
*/
|
|
753
|
-
createNewEntry: function (data) {
|
|
841
|
+
createNewEntry: function (data, options = {}) {
|
|
754
842
|
let newEntry = {};
|
|
755
843
|
newEntry.viewUrl = undefined;
|
|
756
844
|
newEntry.state = undefined;
|
|
757
845
|
Object.assign(newEntry, data);
|
|
758
|
-
newEntry.mode =
|
|
846
|
+
newEntry.mode = 'normal';
|
|
759
847
|
newEntry.id = this.getNewEntryId();
|
|
760
848
|
newEntry.discoverId = data.discoverId;
|
|
761
849
|
this.entriesStore.addNewEntry(newEntry);
|
|
762
|
-
|
|
850
|
+
if (options.openInSplitView) {
|
|
851
|
+
this.splitFlowStore.placeEntryInSplitView({
|
|
852
|
+
id: newEntry.id,
|
|
853
|
+
sourceId: options.sourceId,
|
|
854
|
+
entries: this.entries,
|
|
855
|
+
});
|
|
856
|
+
} else {
|
|
857
|
+
this.splitFlowStore.setIdToPrimaryPane(newEntry.id);
|
|
858
|
+
}
|
|
763
859
|
//close sidebar on entry creation to see the context card
|
|
764
860
|
if (this.$refs.sideBar) {
|
|
765
861
|
this.$refs.sideBar.setDrawerOpen(false);
|
|
@@ -771,7 +867,7 @@ export default {
|
|
|
771
867
|
const entry = await getNewMapEntry(type, this.settingsStore.sparcApi);
|
|
772
868
|
this.createNewEntry(entry);
|
|
773
869
|
if (entry.contextCard) {
|
|
774
|
-
EventBus.emit(
|
|
870
|
+
EventBus.emit('contextUpdate', entry.contextCard);
|
|
775
871
|
}
|
|
776
872
|
},
|
|
777
873
|
openSearch: function (facets, query) {
|
|
@@ -784,12 +880,12 @@ export default {
|
|
|
784
880
|
this._facets = facets;
|
|
785
881
|
if (this.$refs && this.$refs.sideBar) {
|
|
786
882
|
this.$refs.sideBar.openSearch(facets, query);
|
|
787
|
-
this.$refs.sideBar.tabClicked({id:
|
|
883
|
+
this.$refs.sideBar.tabClicked({ id: 1, type: 'datasetExplorer' });
|
|
788
884
|
}
|
|
789
885
|
this.startUp = false;
|
|
790
886
|
},
|
|
791
887
|
onFullscreen: function (val) {
|
|
792
|
-
this.$emit(
|
|
888
|
+
this.$emit('onFullscreen', val);
|
|
793
889
|
},
|
|
794
890
|
resetApp: function () {
|
|
795
891
|
this.setState(initialDefaultState());
|
|
@@ -800,39 +896,118 @@ export default {
|
|
|
800
896
|
restoreConnectivityEntries: function (connectivityEntries) {
|
|
801
897
|
const activeFlatmaps = this.getActiveFlatmaps();
|
|
802
898
|
activeFlatmaps.forEach((activeFlatmap) => {
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
899
|
+
if (!activeFlatmap.mapImp) return;
|
|
900
|
+
const featureIds = connectivityEntries.reduce((acc, entry) => {
|
|
901
|
+
// A given flatmap may not contain every restored connectivity id
|
|
902
|
+
// (e.g. it belongs to another species/map); skip those instead of
|
|
903
|
+
// throwing, which would otherwise abort processing for this map
|
|
904
|
+
// entirely and leave its other entries stuck loading forever.
|
|
905
|
+
try {
|
|
906
|
+
const featureId = activeFlatmap.mapImp.modelFeatureIds(entry)[0];
|
|
907
|
+
if (!featureId) return acc;
|
|
908
|
+
const feature = activeFlatmap.mapImp.featureProperties(featureId);
|
|
909
|
+
if (!feature) return acc;
|
|
910
|
+
acc.push({
|
|
911
|
+
resource: [feature.models],
|
|
912
|
+
feature: feature,
|
|
913
|
+
label: feature.label,
|
|
914
|
+
provenanceTaxonomy: feature.taxons,
|
|
915
|
+
alert: feature.alert,
|
|
916
|
+
});
|
|
917
|
+
} catch (_error) {
|
|
918
|
+
// ignore entries that cannot be resolved in this map
|
|
919
|
+
}
|
|
920
|
+
return acc;
|
|
921
|
+
}, []);
|
|
922
|
+
if (featureIds.length) {
|
|
923
|
+
activeFlatmap.checkAndCreatePopups(featureIds, true);
|
|
924
|
+
}
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
// Restore Scaffold-only connectivity entries (e.g. carrying nerve-label)
|
|
928
|
+
// via each Scaffold's own resolved knowledge, since Scaffolds aren't
|
|
929
|
+
// flatmaps and are otherwise never asked to restore their entries.
|
|
930
|
+
const activeScaffolds = this.getActiveScaffolds();
|
|
931
|
+
activeScaffolds.forEach((scaffold) => {
|
|
932
|
+
const matched = (scaffold.nervesKnowledge || []).filter((knowledge) =>
|
|
933
|
+
connectivityEntries.includes(knowledge.id),
|
|
934
|
+
);
|
|
935
|
+
if (matched.length) {
|
|
936
|
+
scaffold.getKnowledgeTooltip({ data: matched, type: scaffold.entry });
|
|
937
|
+
}
|
|
816
938
|
});
|
|
817
939
|
},
|
|
940
|
+
// Maps a sidebar activeTabId to the tab 'type' expected by tabClicked.
|
|
941
|
+
getSidebarTabType: function (tabId) {
|
|
942
|
+
if (tabId === 1) return 'datasetExplorer';
|
|
943
|
+
if (tabId === 3) return 'annotation';
|
|
944
|
+
return 'connectivityExplorer';
|
|
945
|
+
},
|
|
946
|
+
// Called once every restored connectivityEntry has resolved (or we've
|
|
947
|
+
// given up retrying): shows only the restored/filtered connectivities
|
|
948
|
+
// (not the full knowledge list) and opens the sidebar on the tab stored
|
|
949
|
+
// in the permalink state.
|
|
950
|
+
finalizeConnectivityRestore: function (sidebarState) {
|
|
951
|
+
this.restoringConnectivityState = false;
|
|
952
|
+
this.connectivityKnowledge = this.connectivityEntry;
|
|
953
|
+
if (this.connectivityEntry.every((entry) => entry.ready)) {
|
|
954
|
+
this.connectivityHighlight = this.connectivityEntry.map((entry) => entry.id);
|
|
955
|
+
this.connectivityProcessed = true;
|
|
956
|
+
}
|
|
957
|
+
if (this.$refs.sideBar) {
|
|
958
|
+
const tabId = sidebarState?.activeTabId ?? 2;
|
|
959
|
+
this.$refs.sideBar.tabClicked({ id: tabId, type: this.getSidebarTabType(tabId) });
|
|
960
|
+
this.$refs.sideBar.setDrawerOpen(true);
|
|
961
|
+
}
|
|
962
|
+
},
|
|
963
|
+
// Shared completion check for the connectivity restore loop:
|
|
964
|
+
// finalises the restore once every restored entry has resolved,
|
|
965
|
+
// or gives up after enough retry attempts (an id may never be resolvable by any active viewer).
|
|
966
|
+
// Called both from the connectivity-knowledge retry ticks and from openConnectivityInfo,
|
|
967
|
+
// since entries resolve asynchronously through connectivity-info-open rather than the knowledge events.
|
|
968
|
+
checkConnectivityRestoreComplete: function (sidebarState) {
|
|
969
|
+
if (this.sidebarStateRestored || !sidebarState?.connectivityEntries?.length) {
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
// Multiple viewers resolve connectivity knowledge independently,
|
|
973
|
+
// so keep retrying until all restored entries have resolved.
|
|
974
|
+
// A becoming-active flatmap alone is not enough (its knowledge may still be loading),
|
|
975
|
+
// so only finalise on that basis after enough attempts
|
|
976
|
+
// to avoid getting stuck if an id can never be resolved by any viewer.
|
|
977
|
+
const allEntriesReady =
|
|
978
|
+
this.connectivityEntry.length >= sidebarState.connectivityEntries.length &&
|
|
979
|
+
this.connectivityEntry.every((entry) => entry.ready);
|
|
980
|
+
const giveUp = this.restoreAttempts >= 30;
|
|
981
|
+
if (allEntriesReady || giveUp) {
|
|
982
|
+
this.sidebarStateRestored = true;
|
|
983
|
+
this.finalizeConnectivityRestore(sidebarState);
|
|
984
|
+
}
|
|
985
|
+
},
|
|
818
986
|
restoreSidebarState: function (state) {
|
|
819
987
|
// Restore sidebar state only if
|
|
820
988
|
// - there is sidebar state
|
|
821
989
|
// - sidebar component is loaded
|
|
822
|
-
// - connectivity knowledge is loaded
|
|
823
990
|
// - if sidebar state is not restored yet
|
|
824
991
|
const sidebarState = state?.sidebar;
|
|
825
|
-
if (
|
|
992
|
+
if (
|
|
993
|
+
!this.sidebarStateRestored &&
|
|
994
|
+
sidebarState &&
|
|
995
|
+
this.$refs.sideBar &&
|
|
996
|
+
this.connectivityKnowledge?.length
|
|
997
|
+
) {
|
|
826
998
|
if (sidebarState.connectivityEntries?.length) {
|
|
827
999
|
this.restoreConnectivityEntries(sidebarState.connectivityEntries);
|
|
1000
|
+
this.restoreAttempts += 1;
|
|
1001
|
+
this.checkConnectivityRestoreComplete(sidebarState);
|
|
828
1002
|
} else if (sidebarState.annotationEntries?.length && state.annotationId) {
|
|
829
1003
|
// Restore annotation state only if the state has annotationId
|
|
830
1004
|
this.restoreConnectivityEntries(sidebarState.annotationEntries);
|
|
831
1005
|
this.sidebarAnnotationState = true;
|
|
1006
|
+
this.sidebarStateRestored = true;
|
|
832
1007
|
} else {
|
|
833
1008
|
this.$refs.sideBar.setState(sidebarState);
|
|
1009
|
+
this.sidebarStateRestored = true;
|
|
834
1010
|
}
|
|
835
|
-
this.sidebarStateRestored = true;
|
|
836
1011
|
}
|
|
837
1012
|
},
|
|
838
1013
|
setState: function (state) {
|
|
@@ -840,9 +1015,23 @@ export default {
|
|
|
840
1015
|
//Support both old and new permalink.
|
|
841
1016
|
if (state.splitFlow) {
|
|
842
1017
|
this.splitFlowStore.setState(state.splitFlow);
|
|
1018
|
+
} else {
|
|
1019
|
+
this.entries.forEach((entry) => this.splitFlowStore.setIdToPrimaryPane(entry.id));
|
|
843
1020
|
}
|
|
844
|
-
|
|
845
|
-
|
|
1021
|
+
|
|
1022
|
+
// Seed placeholder (not-ready) connectivity entries immediately so the
|
|
1023
|
+
// sidebar shows a loading count right away, instead of either an empty
|
|
1024
|
+
// list or a flash of the full/unfiltered connectivity knowledge while
|
|
1025
|
+
// the active viewers and their connectivity knowledge are still loading.
|
|
1026
|
+
if (state?.sidebar?.connectivityEntries?.length && !this.sidebarStateRestored) {
|
|
1027
|
+
this.restoringConnectivityState = true;
|
|
1028
|
+
this.connectivityEntry = state.sidebar.connectivityEntries.map((id) => ({
|
|
1029
|
+
id,
|
|
1030
|
+
label: '',
|
|
1031
|
+
featureId: [id],
|
|
1032
|
+
ready: false,
|
|
1033
|
+
}));
|
|
1034
|
+
this.connectivityKnowledge = this.connectivityEntry;
|
|
846
1035
|
}
|
|
847
1036
|
|
|
848
1037
|
this.restoreSidebarState(state);
|
|
@@ -857,12 +1046,10 @@ export default {
|
|
|
857
1046
|
const entry = state.entries[i];
|
|
858
1047
|
entry.state = dialogStates[i];
|
|
859
1048
|
//We do not want to serialise the following properties
|
|
860
|
-
if (entry.type ===
|
|
861
|
-
|
|
862
|
-
if (entry.type === "MultiFlatmap" && "uberonId" in entry)
|
|
863
|
-
delete entry.uberonId;
|
|
1049
|
+
if (entry.type === 'Scaffold' && 'viewUrl' in entry) delete entry.viewUrl;
|
|
1050
|
+
if (entry.type === 'MultiFlatmap' && 'uberonId' in entry) delete entry.uberonId;
|
|
864
1051
|
if (anonymousAnnotations === false) {
|
|
865
|
-
if (entry.type ===
|
|
1052
|
+
if (entry.type === 'Scaffold' && entry?.state?.offlineAnnotations) {
|
|
866
1053
|
delete entry.state.offlineAnnotations;
|
|
867
1054
|
} else if (entry?.state?.state?.offlineAnnotations) {
|
|
868
1055
|
delete entry.state.state.offlineAnnotations;
|
|
@@ -882,20 +1069,20 @@ export default {
|
|
|
882
1069
|
this.entriesStore.destroyEntry(index);
|
|
883
1070
|
},
|
|
884
1071
|
resourceSelected: function (result) {
|
|
885
|
-
this.$emit(
|
|
1072
|
+
this.$emit('resource-selected', result);
|
|
886
1073
|
},
|
|
887
|
-
speciesChanged: function (
|
|
1074
|
+
speciesChanged: function (_species) {
|
|
888
1075
|
if (this.$refs.sideBar) {
|
|
889
1076
|
// Use to update the connectivity when switch species
|
|
890
1077
|
// Wait for provenance info with uuid update
|
|
891
1078
|
this.$nextTick(() => {
|
|
892
1079
|
EventBus.emit('species-layout-connectivity-update');
|
|
893
1080
|
this.$refs.sideBar.close();
|
|
894
|
-
})
|
|
1081
|
+
});
|
|
895
1082
|
}
|
|
896
1083
|
},
|
|
897
1084
|
contextUpdate: function (payload) {
|
|
898
|
-
EventBus.emit(
|
|
1085
|
+
EventBus.emit('contextUpdate', payload);
|
|
899
1086
|
},
|
|
900
1087
|
datalinkClicked: function (payload) {
|
|
901
1088
|
// payload is dataset URL
|
|
@@ -904,37 +1091,37 @@ export default {
|
|
|
904
1091
|
const substringB = '?type=dataset';
|
|
905
1092
|
const datasetId = datasetURL.substring(
|
|
906
1093
|
datasetURL.indexOf(substringA) + substringA.length,
|
|
907
|
-
datasetURL.indexOf(substringB)
|
|
1094
|
+
datasetURL.indexOf(substringB),
|
|
908
1095
|
);
|
|
909
1096
|
|
|
910
1097
|
// GA Tagging
|
|
911
1098
|
// Event tracking for map sidebar gallery dataset click
|
|
912
1099
|
Tagging.sendEvent({
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
1100
|
+
event: 'interaction_event',
|
|
1101
|
+
event_name: 'portal_maps_gallery_click',
|
|
1102
|
+
category: datasetURL,
|
|
1103
|
+
location: 'map_sidebar_gallery',
|
|
1104
|
+
dataset_id: datasetId || '',
|
|
918
1105
|
});
|
|
919
1106
|
},
|
|
920
|
-
onAnnotationSubmitted: function(annotation) {
|
|
1107
|
+
onAnnotationSubmitted: function (annotation) {
|
|
921
1108
|
if (this.annotationCallback) {
|
|
922
1109
|
this.annotationCallback(annotation);
|
|
923
1110
|
} else if (this.confirmCommentCallback) {
|
|
924
|
-
this.confirmCommentCallback(annotation)
|
|
1111
|
+
this.confirmCommentCallback(annotation);
|
|
925
1112
|
}
|
|
926
1113
|
},
|
|
927
|
-
onConfirmCreate: function(payload) {
|
|
1114
|
+
onConfirmCreate: function (payload) {
|
|
928
1115
|
if (this.confirmCreateCallback) {
|
|
929
1116
|
this.confirmCreateCallback(payload);
|
|
930
1117
|
}
|
|
931
1118
|
},
|
|
932
|
-
onCancelCreate: function() {
|
|
1119
|
+
onCancelCreate: function () {
|
|
933
1120
|
if (this.cancelCreateCallback) {
|
|
934
1121
|
this.cancelCreateCallback();
|
|
935
1122
|
}
|
|
936
1123
|
},
|
|
937
|
-
onConfirmDelete: function(payload) {
|
|
1124
|
+
onConfirmDelete: function (payload) {
|
|
938
1125
|
if (this.confirmDeleteCallback) {
|
|
939
1126
|
this.confirmDeleteCallback(payload);
|
|
940
1127
|
}
|
|
@@ -954,7 +1141,7 @@ export default {
|
|
|
954
1141
|
this.$refs.dialogToolbar.loadGlobalSettings();
|
|
955
1142
|
},
|
|
956
1143
|
onSidebarTabClosed: function (tab) {
|
|
957
|
-
if (tab.id === 3 && tab.type ===
|
|
1144
|
+
if (tab.id === 3 && tab.type === 'annotation') {
|
|
958
1145
|
EventBus.emit('sidebar-annotation-close');
|
|
959
1146
|
}
|
|
960
1147
|
},
|
|
@@ -979,16 +1166,16 @@ export default {
|
|
|
979
1166
|
this._externalStateSet = false;
|
|
980
1167
|
},
|
|
981
1168
|
mounted: function () {
|
|
982
|
-
EventBus.on(
|
|
1169
|
+
EventBus.on('CreateNewEntry', (newView) => {
|
|
983
1170
|
this.createNewEntry(newView);
|
|
984
1171
|
});
|
|
985
|
-
EventBus.on(
|
|
1172
|
+
EventBus.on('RemoveEntryRequest', (id) => {
|
|
986
1173
|
this.removeEntry(id);
|
|
987
1174
|
});
|
|
988
|
-
EventBus.on(
|
|
1175
|
+
EventBus.on('PopoverActionClick', (payload) => {
|
|
989
1176
|
this.actionClick(payload);
|
|
990
1177
|
});
|
|
991
|
-
EventBus.on('annotation-open', payload => {
|
|
1178
|
+
EventBus.on('annotation-open', (payload) => {
|
|
992
1179
|
this.openAnnotation(payload);
|
|
993
1180
|
});
|
|
994
1181
|
EventBus.on('sidebar-annotation-close', () => {
|
|
@@ -1000,7 +1187,7 @@ export default {
|
|
|
1000
1187
|
// open the anootation tab and return.
|
|
1001
1188
|
if (this.sidebarAnnotationState && viewingMode === 'Annotation') {
|
|
1002
1189
|
this.sidebarAnnotationState = false;
|
|
1003
|
-
this.$refs.sideBar.tabClicked({id: 3, type: 'annotation'});
|
|
1190
|
+
this.$refs.sideBar.tabClicked({ id: 3, type: 'annotation' });
|
|
1004
1191
|
return;
|
|
1005
1192
|
}
|
|
1006
1193
|
|
|
@@ -1008,10 +1195,10 @@ export default {
|
|
|
1008
1195
|
this.createData = {};
|
|
1009
1196
|
|
|
1010
1197
|
if (this.$refs.sideBar) {
|
|
1011
|
-
if (interactiveMode ===
|
|
1012
|
-
this.$refs.sideBar.tabClicked({id:
|
|
1013
|
-
} else if (interactiveMode ===
|
|
1014
|
-
this.$refs.sideBar.tabClicked({id:
|
|
1198
|
+
if (interactiveMode === 'dataset') {
|
|
1199
|
+
this.$refs.sideBar.tabClicked({ id: 1, type: 'datasetExplorer' });
|
|
1200
|
+
} else if (interactiveMode === 'connectivity') {
|
|
1201
|
+
this.$refs.sideBar.tabClicked({ id: 2, type: 'connectivityExplorer' });
|
|
1015
1202
|
}
|
|
1016
1203
|
|
|
1017
1204
|
this.$refs.sideBar.closeConnectivity();
|
|
@@ -1021,21 +1208,21 @@ export default {
|
|
|
1021
1208
|
EventBus.on('update-offline-annotation-enabled', (payload) => {
|
|
1022
1209
|
this.settingsStore.updateOfflineAnnotationEnabled(payload);
|
|
1023
1210
|
});
|
|
1024
|
-
EventBus.on('connectivity-info-open', payload => {
|
|
1211
|
+
EventBus.on('connectivity-info-open', (payload) => {
|
|
1025
1212
|
this.openConnectivityInfo(payload);
|
|
1026
1213
|
});
|
|
1027
|
-
EventBus.on('connectivity-info-close',
|
|
1214
|
+
EventBus.on('connectivity-info-close', (_payload) => {
|
|
1028
1215
|
if (this.$refs.sideBar) {
|
|
1029
1216
|
this.connectivityProcessed = false;
|
|
1030
1217
|
this.$refs.sideBar.resetConnectivitySearch();
|
|
1031
1218
|
}
|
|
1032
1219
|
});
|
|
1033
|
-
EventBus.on('connectivity-error', payload => {
|
|
1220
|
+
EventBus.on('connectivity-error', (payload) => {
|
|
1034
1221
|
if (this.$refs.sideBar) {
|
|
1035
1222
|
this.$refs.sideBar.updateConnectivityError(payload.data);
|
|
1036
1223
|
}
|
|
1037
1224
|
});
|
|
1038
|
-
EventBus.on('neuron-connection-feature-click', payload => {
|
|
1225
|
+
EventBus.on('neuron-connection-feature-click', (payload) => {
|
|
1039
1226
|
if (this.$refs.sideBar) {
|
|
1040
1227
|
const { filters, search } = payload;
|
|
1041
1228
|
this.$refs.sideBar.openConnectivitySearch(filters, search);
|
|
@@ -1043,36 +1230,44 @@ export default {
|
|
|
1043
1230
|
this.$refs.sideBar.setDrawerOpen(true);
|
|
1044
1231
|
}
|
|
1045
1232
|
});
|
|
1046
|
-
EventBus.on(
|
|
1233
|
+
EventBus.on('OpenNewMap', (type) => {
|
|
1047
1234
|
this.updateFlatmapMinimap();
|
|
1048
1235
|
this.openNewMap(type);
|
|
1049
1236
|
});
|
|
1050
|
-
EventBus.on(
|
|
1237
|
+
EventBus.on('startHelp', () => {
|
|
1051
1238
|
if (this.$refs.sideBar) {
|
|
1052
1239
|
this.$refs.sideBar.close();
|
|
1053
1240
|
}
|
|
1054
1241
|
});
|
|
1055
|
-
EventBus.on(
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1242
|
+
EventBus.on('connectivity-knowledge', (payload) => {
|
|
1243
|
+
// Ignore the default/global connectivity dump (emitted when no active
|
|
1244
|
+
// viewer's map/species keys are resolved yet) while restoring a
|
|
1245
|
+
// permalink's sidebar connectivityEntries, otherwise the sidebar would
|
|
1246
|
+
// briefly flash the entire, unfiltered connectivity list.
|
|
1247
|
+
if (!(payload.isDefaultKnowledge && this.restoringConnectivityState)) {
|
|
1248
|
+
this.connectivityKnowledge = payload.data;
|
|
1249
|
+
this.connectivityHighlight = payload.highlight;
|
|
1250
|
+
this.connectivityProcessed = payload.processed;
|
|
1251
|
+
}
|
|
1059
1252
|
|
|
1060
1253
|
// Restore sidebar state if it exists and not restored yet
|
|
1061
1254
|
// after loading connectivity knowledge
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
this.$refs.sideBar.tabClicked({id:
|
|
1255
|
+
if (!this.sidebarStateRestored) {
|
|
1256
|
+
this.restoreSidebarState(this.state);
|
|
1257
|
+
}
|
|
1258
|
+
});
|
|
1259
|
+
EventBus.on('modeUpdate', (payload) => {
|
|
1260
|
+
if (payload === 'dataset') {
|
|
1261
|
+
this.$refs.sideBar.tabClicked({ id: 1, type: 'datasetExplorer' });
|
|
1262
|
+
} else if (payload === 'connectivity') {
|
|
1263
|
+
this.$refs.sideBar.tabClicked({ id: 2, type: 'connectivityExplorer' });
|
|
1069
1264
|
}
|
|
1070
|
-
})
|
|
1071
|
-
EventBus.on(
|
|
1265
|
+
});
|
|
1266
|
+
EventBus.on('connectivity-filter-options', (payload) => {
|
|
1072
1267
|
this.filterOptions = payload;
|
|
1073
|
-
})
|
|
1268
|
+
});
|
|
1074
1269
|
this.$nextTick(() => {
|
|
1075
|
-
if (this.search ===
|
|
1270
|
+
if (this.search === '' && this._facets.length === 0) {
|
|
1076
1271
|
if (this.$refs.sideBar) {
|
|
1077
1272
|
this.$refs.sideBar.close();
|
|
1078
1273
|
}
|
|
@@ -1095,7 +1290,7 @@ export default {
|
|
|
1095
1290
|
FLATMAPAPI_LOCATION: this.settingsStore.flatmapAPI,
|
|
1096
1291
|
};
|
|
1097
1292
|
},
|
|
1098
|
-
entries: function() {
|
|
1293
|
+
entries: function () {
|
|
1099
1294
|
return this.entriesStore.entries;
|
|
1100
1295
|
},
|
|
1101
1296
|
},
|
|
@@ -1139,5 +1334,4 @@ export default {
|
|
|
1139
1334
|
}
|
|
1140
1335
|
}
|
|
1141
1336
|
}
|
|
1142
|
-
|
|
1143
1337
|
</style>
|