@abi-software/mapintegratedvuer 1.1.0-beta.1 → 1.1.0-beta.2

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.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abi-software/mapintegratedvuer",
3
- "version": "1.1.0-beta.1",
3
+ "version": "1.1.0-beta.2",
4
4
  "license": "Apache-2.0",
5
5
  "scripts": {
6
6
  "serve": "vite --host --force",
@@ -49,9 +49,9 @@
49
49
  ],
50
50
  "dependencies": {
51
51
  "@abi-software/flatmapvuer": "^1.1.0-beta.2",
52
- "@abi-software/map-side-bar": "2.0.1",
52
+ "@abi-software/map-side-bar": "^2.2.0",
53
53
  "@abi-software/plotvuer": "1.0.0",
54
- "@abi-software/scaffoldvuer": "^1.1.0-beta.0",
54
+ "@abi-software/scaffoldvuer": "^1.1.0-beta.1",
55
55
  "@abi-software/simulationvuer": "1.0.0",
56
56
  "@abi-software/svg-sprite": "1.0.0",
57
57
  "@element-plus/icons-vue": "^2.3.1",
@@ -22,15 +22,6 @@
22
22
  </div>
23
23
  </div>
24
24
  <el-row class="icon-group">
25
- <div v-show="contextCardEntry && contextCardVisible" class="hide" @click="contextCardVisible = false">
26
- Hide information
27
- <el-icon><el-icon-arrow-up /></el-icon>
28
- </div>
29
- <div v-show="contextCardEntry && !contextCardVisible" class="hide" @click="contextCardVisible = true">
30
- Show information
31
- <el-icon><el-icon-arrow-down /></el-icon>
32
- </div>
33
-
34
25
  <el-popover
35
26
  placement="bottom"
36
27
  :teleported="false"
@@ -58,12 +49,16 @@
58
49
  />
59
50
  </template>
60
51
  <template #reference>
61
- <el-icon
62
- class="info-icon"
63
- @click="contextCardVisible = !contextCardVisible"
64
- v-show="contextCardEntry">
65
- <el-icon-info-filled/>
66
- </el-icon>
52
+ <div v-show="contextCardEntry">
53
+ <div v-show="contextCardVisible" class="hide" @click="contextCardVisible = false">
54
+ Hide information
55
+ <el-icon><el-icon-arrow-up /></el-icon>
56
+ </div>
57
+ <div v-show="!contextCardVisible" class="hide" @click="contextCardVisible = true">
58
+ Show information
59
+ <el-icon><el-icon-arrow-down /></el-icon>
60
+ </div>
61
+ </div>
67
62
  </template>
68
63
  </el-popover>
69
64
  <el-popover class="tooltip" content="Close and remove" placement="bottom-end" :show-after="helpDelay"
@@ -19,6 +19,7 @@
19
19
 
20
20
  <script>
21
21
  /* eslint-disable no-alert, no-console */
22
+ import Tagging from '../services/tagging.js';
22
23
  import SplitFlow from './SplitFlow.vue';
23
24
  import EventBus from './EventBus';
24
25
  import { mapStores } from 'pinia';
@@ -26,6 +27,7 @@ import { useSettingsStore } from '../stores/settings';
26
27
  import { findSpeciesKey } from './scripts/utilities.js';
27
28
  import { MapSvgSpriteColor} from '@abi-software/svg-sprite';
28
29
  import { initialState } from "./scripts/utilities.js";
30
+ import RetrieveContextCardMixin from "../mixins/RetrieveContextCardMixin.js"
29
31
  import {
30
32
  ElLoading as Loading
31
33
  } from "element-plus";
@@ -40,6 +42,7 @@ export default {
40
42
  Loading,
41
43
  SplitFlow,
42
44
  },
45
+ mixins: [RetrieveContextCardMixin],
43
46
  props: {
44
47
  /**
45
48
  * A link (URL) to share.
@@ -158,7 +161,7 @@ export default {
158
161
  * instead change the current entry by setting the state.
159
162
  * @arg state
160
163
  */
161
- setCurrentEntry: function(state) {
164
+ setCurrentEntry: async function(state) {
162
165
  if (state && state.type) {
163
166
  if (state.type === "Scaffold" && state.url) {
164
167
  //State for scaffold containing the following items:
@@ -167,7 +170,7 @@ export default {
167
170
  // resource - the url to metadata
168
171
  // state - state to restore (viewport)
169
172
  // viewUrl - relative path of the view file to metadata
170
- const newView = {
173
+ let newView = {
171
174
  type: state.type,
172
175
  label: state.label,
173
176
  region: state.region,
@@ -175,6 +178,9 @@ export default {
175
178
  state: state.state,
176
179
  viewUrl: state.viewUrl
177
180
  };
181
+ // Add content from scicrunch for the context card
182
+ const contextCardInfo = await this.retrieveContextCardFromUrl(state.url);
183
+ newView = {...newView, ...contextCardInfo};
178
184
  this.$refs.flow.createNewEntry(newView);
179
185
  } else if (state.type === "MultiFlatmap") {
180
186
  //State for scaffold containing the following items:
@@ -246,6 +252,15 @@ export default {
246
252
  * This event emit when the component is mounted.
247
253
  */
248
254
  this.$emit("isReady");
255
+
256
+ // GA Tagging
257
+ // Page view tracking for maps' buttons click on portal
258
+ // category: AC | FC | WholeBody
259
+ Tagging.sendEvent({
260
+ 'event': 'interaction_event',
261
+ 'event_name': 'portal_maps_page_view',
262
+ 'category': this.startingMap
263
+ });
249
264
  },
250
265
  },
251
266
  computed: {
@@ -28,7 +28,9 @@
28
28
  @actionClick="actionClick"
29
29
  @tabClicked="tabClicked"
30
30
  @search-changed="searchChanged($event)"
31
+ @hover-changed="hoverChanged($event)"
31
32
  @contextUpdate="contextUpdate($event)"
33
+ @datalink-clicked="datalinkClicked($event)"
32
34
  />
33
35
  <SplitDialog
34
36
  :entries="entries"
@@ -42,12 +44,18 @@
42
44
 
43
45
  <script>
44
46
  /* eslint-disable no-alert, no-console */
47
+ import Tagging from '../services/tagging.js';
45
48
  import DialogToolbarContent from "./DialogToolbarContent.vue";
46
49
  import EventBus from "./EventBus";
47
50
  import SplitDialog from "./SplitDialog.vue";
48
51
  // import contextCards from './context-cards'
49
52
  import { SideBar } from "@abi-software/map-side-bar";
50
- import { capitalise, getNewMapEntry, initialDefaultState } from "./scripts/utilities.js";
53
+ import {
54
+ capitalise,
55
+ getNewMapEntry,
56
+ initialDefaultState,
57
+ intersectArrays,
58
+ } from "./scripts/utilities.js";
51
59
  import { mapStores } from 'pinia';
52
60
  import { useEntriesStore } from '../stores/entries';
53
61
  import { useSettingsStore } from '../stores/settings';
@@ -85,6 +93,9 @@ export default {
85
93
  startUp: true,
86
94
  search: '',
87
95
  activeDockedId : 1,
96
+ filterTriggered: false,
97
+ availableFacets: [],
98
+ hoveredMarkerDelay: undefined,
88
99
  }
89
100
  },
90
101
  watch: {
@@ -109,12 +120,34 @@ export default {
109
120
  this.openSearch([action.filter], action.label);
110
121
  } else {
111
122
  this.openSearch([], action.term);
123
+ // GA Tagging
124
+ // Event tracking for map action search/filter data
125
+ const eventName = action.featuredDataset
126
+ ? 'portal_maps_featured_dataset_search'
127
+ : 'portal_maps_action_search';
128
+ Tagging.sendEvent({
129
+ 'event': 'interaction_event',
130
+ 'event_name': eventName,
131
+ 'category': action.term || 'filter',
132
+ 'location': 'map_location_pin'
133
+ });
134
+ this.filterTriggered = true;
112
135
  }
113
136
  } else if (action.type == "URL") {
114
137
  window.open(action.resource, "_blank");
115
138
  } else if (action.type == "Facet") {
116
139
  if (this.$refs.sideBar) {
117
140
  this.$refs.sideBar.addFilter(action);
141
+ const { facet } = action;
142
+ // GA Tagging
143
+ // Event tracking for map action search/filter data
144
+ Tagging.sendEvent({
145
+ 'event': 'interaction_event',
146
+ 'event_name': 'portal_maps_action_filter',
147
+ 'category': facet || 'filter',
148
+ 'location': 'map_location_pin'
149
+ });
150
+ this.filterTriggered = true;
118
151
  }
119
152
  } else if (action.type == "Facets") {
120
153
  const facets = [];
@@ -140,12 +173,51 @@ export default {
140
173
  );
141
174
  if (this.$refs.sideBar) {
142
175
  this.$refs.sideBar.openSearch(facets, "");
176
+
177
+ const filterValuesArray = intersectArrays(this.availableFacets, action.labels);
178
+ const filterValues = filterValuesArray.join(', ');
179
+ // GA Tagging
180
+ // Event tracking for map action search/filter data
181
+ Tagging.sendEvent({
182
+ 'event': 'interaction_event',
183
+ 'event_name': 'portal_maps_action_filter',
184
+ 'category': filterValues || 'filter',
185
+ 'location': 'map_popup_button'
186
+ });
143
187
  }
144
188
  } else {
189
+ this.trackGalleryClick(action);
145
190
  this.createNewEntry(action);
146
191
  }
147
192
  }
148
193
  },
194
+ trackGalleryClick: function (action) {
195
+ const categoryValues = [];
196
+ const { label, type, datasetId, discoverId, resource } = action;
197
+ let filePath = '';
198
+ let id = datasetId ? datasetId : discoverId;
199
+ if (label) categoryValues.push(label);
200
+ if (type) categoryValues.push(type);
201
+ if (datasetId) categoryValues.push('(' + id + ')');
202
+ if (resource) {
203
+ if (type === "Plot") {
204
+ filePath = resource.dataSource.url;
205
+ } else {
206
+ filePath = typeof resource === 'string' ? resource : resource.share_link;
207
+ }
208
+ }
209
+
210
+ // GA Tagging
211
+ // Event tracking for map sidebar gallery click
212
+ Tagging.sendEvent({
213
+ 'event': 'interaction_event',
214
+ 'event_name': 'portal_maps_gallery_click',
215
+ 'category': categoryValues.join(' '),
216
+ 'location': 'map_sidebar_gallery',
217
+ 'dataset_id': id ? id + '' : '', // change to string format
218
+ 'file_path': filePath,
219
+ });
220
+ },
149
221
  onDisplaySearch: function (payload) {
150
222
  let searchFound = false;
151
223
  //Search all active viewers when global callback is on
@@ -159,6 +231,15 @@ export default {
159
231
  });
160
232
  }
161
233
  this.$refs.dialogToolbar.setFailedSearch(searchFound ? undefined : payload.term);
234
+
235
+ // GA Tagging
236
+ // Event tracking for map on display search
237
+ Tagging.sendEvent({
238
+ 'event': 'interaction_event',
239
+ 'event_name': 'portal_maps_display_search',
240
+ 'category': payload.term,
241
+ 'location': 'map_toolbar'
242
+ });
162
243
  },
163
244
  fetchSuggestions: function(payload) {
164
245
  const suggestions = [];
@@ -174,17 +255,61 @@ export default {
174
255
  }
175
256
  payload.data.cb(suggestions);
176
257
  },
258
+ hoverChanged: function (data) {
259
+ const hoverEntries = data && data.anatomy ? data.anatomy : []
260
+ this.settingsStore.updateHoveredMarkers(hoverEntries);
261
+ if (!hoverEntries.length) {
262
+ this.hoveredMarkerDelay = setTimeout(() => {
263
+ EventBus.emit("markerUpdate");
264
+ }, 3000)
265
+ } else {
266
+ clearTimeout(this.hoveredMarkerDelay)
267
+ EventBus.emit("markerUpdate");
268
+ }
269
+ },
177
270
  searchChanged: function (data) {
178
271
  if (data && data.type == "query-update") {
179
272
  this.search = data.value;
273
+ if (this.search && !this.filterTriggered) {
274
+ // GA Tagging
275
+ // Event tracking for map action search/filter data
276
+ Tagging.sendEvent({
277
+ 'event': 'interaction_event',
278
+ 'event_name': 'portal_maps_action_search',
279
+ 'category': this.search,
280
+ 'location': 'map_sidebar_search'
281
+ });
282
+ }
283
+ this.filterTriggered = false; // reset for next action
180
284
  }
181
285
  if (data && data.type == "filter-update") {
182
286
  this.settingsStore.updateFacets(data.value);
287
+
288
+ // Remove filter event from maps' popup
289
+ if (!this.filterTriggered) {
290
+ const { value } = data;
291
+ const filterValuesArray = value.filter((val) =>
292
+ val.facet && val.facet.toLowerCase() !== 'show all'
293
+ ).map((val) => val.facet);
294
+ const filterValues = filterValuesArray.join(', ');
295
+
296
+ // GA Tagging
297
+ // Event tracking for map action search/filter data
298
+ Tagging.sendEvent({
299
+ 'event': 'interaction_event',
300
+ 'event_name': 'portal_maps_action_filter',
301
+ 'category': filterValues || 'filter',
302
+ 'location': 'map_sidebar_filter'
303
+ });
304
+ }
305
+ this.filterTriggered = false; // reset for next action
183
306
  }
184
307
  if (data && data.type == "available-facets") {
185
308
  this.settingsStore.updateFacetLabels(data.value.labels);
186
309
  this.settingsStore.updateMarkers(data.value.uberons);
187
310
  EventBus.emit("markerUpdate");
311
+
312
+ this.availableFacets = data.value.labels
188
313
  }
189
314
  },
190
315
  getNewEntryId: function() {
@@ -323,7 +448,27 @@ export default {
323
448
  },
324
449
  contextUpdate: function (payload) {
325
450
  EventBus.emit("contextUpdate", payload);
326
- }
451
+ },
452
+ datalinkClicked: function (payload) {
453
+ // payload is dataset URL
454
+ const datasetURL = payload || '';
455
+ const substringA = 'datasets/';
456
+ const substringB = '?type=dataset';
457
+ const datasetId = datasetURL.substring(
458
+ datasetURL.indexOf(substringA) + substringA.length,
459
+ datasetURL.indexOf(substringB)
460
+ );
461
+
462
+ // GA Tagging
463
+ // Event tracking for map sidebar gallery dataset click
464
+ Tagging.sendEvent({
465
+ 'event': 'interaction_event',
466
+ 'event_name': 'portal_maps_gallery_click',
467
+ 'category': datasetURL,
468
+ 'location': 'map_sidebar_gallery',
469
+ 'dataset_id': datasetId || ''
470
+ });
471
+ },
327
472
  },
328
473
  created: function () {
329
474
  this._facets = [];
@@ -162,12 +162,36 @@ const getBodyScaffoldInfo = async (sparcApi, species) => {
162
162
  return {url, datasetInfo};
163
163
  }
164
164
 
165
- export {
165
+ // Array intersection
166
+ const intersectArrays = (arr1, arr2) => {
167
+ return arr1.filter((x) => arr2.includes(x));
168
+ };
169
+
170
+ // Not using URLSearchParams to avoid encoding spaces
171
+ const transformObjToString = (obj) => {
172
+ return Object.keys(obj)
173
+ .map((key) => `${key}=${obj[key]}`)
174
+ .join('&');
175
+ };
176
+
177
+ const transformStringToObj = (str) => {
178
+ const params = new URLSearchParams(str);
179
+ const obj = {};
180
+ for (const [key, value] of params) {
181
+ obj[key] = value;
182
+ }
183
+ return obj;
184
+ };
185
+
186
+ export {
166
187
  availableSpecies,
167
188
  capitalise,
168
189
  findSpeciesKey,
169
190
  initialState,
170
191
  initialDefaultState,
171
192
  getBodyScaffoldInfo,
172
- getNewMapEntry
193
+ getNewMapEntry,
194
+ intersectArrays,
195
+ transformObjToString,
196
+ transformStringToObj,
173
197
  }
@@ -17,6 +17,7 @@
17
17
  :flatmapAPI="flatmapAPI"
18
18
  :sparcAPI="apiLocation"
19
19
  @open-map="openMap"
20
+ @pathway-selection-changed="onPathwaySelectionChanged"
20
21
  />
21
22
  </template>
22
23
 
@@ -26,6 +27,7 @@ import { FlatmapVuer } from "@abi-software/flatmapvuer";
26
27
  import EventBus from "../EventBus";
27
28
  import ContentMixin from "../../mixins/ContentMixin";
28
29
  import DynamicMarkerMixin from "../../mixins/DynamicMarkerMixin";
30
+ import { transformObjToString } from '../scripts/utilities';
29
31
  import "@abi-software/flatmapvuer/dist/style.css";
30
32
 
31
33
  export default {
@@ -49,7 +51,24 @@ export default {
49
51
  },
50
52
  flatmaprResourceSelected: function (type, resource) {
51
53
  this.resourceSelected(
52
- type, resource, (this.$refs.flatmap.viewingMode === "Exploration"));
54
+ type, resource, (this.$refs.map.viewingMode === "Exploration"));
55
+ if (resource.eventType === 'click' && resource.feature.type === 'feature') {
56
+ const eventData = {
57
+ label: resource.label || '',
58
+ id: resource.feature.id || '',
59
+ featureId: resource.feature.featureId || '',
60
+ taxonomy: resource.taxonomy || '',
61
+ resources: resource.resource.join(', ')
62
+ };
63
+ const paramString = transformObjToString(eventData);
64
+ // `transformStringToObj` function can be used to change it back to object
65
+ Tagging.sendEvent({
66
+ 'event': 'interaction_event',
67
+ 'event_name': 'portal_maps_connectivity',
68
+ 'category': paramString,
69
+ "location": type + ' ' + this.$refs.map.viewingMode
70
+ });
71
+ }
53
72
  },
54
73
  flatmapReadyCall: function (flatmap) {
55
74
  let provClone = {id: this.entry.id, prov: this.getFlatmapImp().provenance}; //create clone of provenance and add id
@@ -60,6 +79,17 @@ export default {
60
79
  this.flatmapReadyForMarkerUpdates(flatmap);
61
80
  }
62
81
  },
82
+ onPathwaySelectionChanged: function (data) {
83
+ const { label, property, checked, selectionsTitle } = data;
84
+ // GA Tagging
85
+ // Event tracking for maps' pathway selection change
86
+ Tagging.sendEvent({
87
+ 'event': 'interaction_event',
88
+ 'event_name': 'portal_maps_pathway_change',
89
+ 'category': label + ' [' + property + '] ' + checked,
90
+ 'location': selectionsTitle
91
+ });
92
+ },
63
93
  highlightFeatures: function(info) {
64
94
  let name = info.name;
65
95
  const flatmap = this.$refs.flatmap.mapImp;
@@ -17,16 +17,22 @@
17
17
  :sparcAPI="apiLocation"
18
18
  @pan-zoom-callback="flatmapPanZoomCallback"
19
19
  @open-map="openMap"
20
+ @pathway-selection-changed="onPathwaySelectionChanged"
21
+ @open-pubmed-url="onOpenPubmedUrl"
20
22
  />
21
23
  </template>
22
24
 
23
25
  <script>
24
26
  /* eslint-disable no-alert, no-console */
25
- import { availableSpecies } from "../scripts/utilities.js";
27
+ import Tagging from '../../services/tagging.js';
26
28
  import { MultiFlatmapVuer } from "@abi-software/flatmapvuer";
27
29
  import ContentMixin from "../../mixins/ContentMixin";
28
30
  import EventBus from "../EventBus";
29
- import { getBodyScaffoldInfo } from "../scripts/utilities";
31
+ import {
32
+ availableSpecies,
33
+ getBodyScaffoldInfo,
34
+ transformObjToString
35
+ } from "../scripts/utilities";
30
36
  import DyncamicMarkerMixin from "../../mixins/DynamicMarkerMixin";
31
37
 
32
38
  import "@abi-software/flatmapvuer/dist/style.css";
@@ -159,6 +165,45 @@ export default {
159
165
  flatmaprResourceSelected: function (type, resource) {
160
166
  const map = this.$refs.multiflatmap.getCurrentFlatmap();
161
167
  this.resourceSelected(type, resource, (map.viewingMode === "Exploration"));
168
+
169
+ if (resource.eventType === 'click' && resource.feature.type === 'feature') {
170
+ const eventData = {
171
+ label: resource.label || '',
172
+ id: resource.feature.id || '',
173
+ featureId: resource.feature.featureId || '',
174
+ taxonomy: resource.taxonomy || '',
175
+ resources: resource.resource.join(', ')
176
+ };
177
+ const paramString = transformObjToString(eventData);
178
+ // `transformStringToObj` function can be used to change it back to object
179
+ Tagging.sendEvent({
180
+ 'event': 'interaction_event',
181
+ 'event_name': 'portal_maps_connectivity',
182
+ 'category': paramString,
183
+ "location": type + ' ' + map.viewingMode
184
+ });
185
+ }
186
+ },
187
+ onPathwaySelectionChanged: function (data) {
188
+ const { label, property, checked, selectionsTitle } = data;
189
+ // GA Tagging
190
+ // Event tracking for maps' pathway selection change
191
+ Tagging.sendEvent({
192
+ 'event': 'interaction_event',
193
+ 'event_name': 'portal_maps_pathway_change',
194
+ 'category': label + ' [' + property + '] ' + checked,
195
+ 'location': selectionsTitle
196
+ });
197
+ },
198
+ onOpenPubmedUrl: function (url) {
199
+ // GA Tagging
200
+ // Event tracking for open pubmed url from popup
201
+ Tagging.sendEvent({
202
+ 'event': 'interaction_event',
203
+ 'event_name': 'portal_maps_pubmed_url',
204
+ 'file_path': url,
205
+ 'location': 'map_popup_button',
206
+ });
162
207
  },
163
208
  /**
164
209
  * Handle sync pan zoom event
@@ -243,6 +288,14 @@ export default {
243
288
  await this.toggleSyncMode();
244
289
  }
245
290
  this.updateProvCard();
291
+
292
+ // GA Tagging
293
+ // Event tracking for maps' species change
294
+ Tagging.sendEvent({
295
+ 'event': 'interaction_event',
296
+ 'event_name': 'portal_maps_species_change',
297
+ 'category': this.activeSpecies
298
+ });
246
299
  },
247
300
  multiFlatmapReady: function (flatmap) {
248
301
  if (flatmap) {
@@ -368,6 +421,12 @@ export default {
368
421
  cursor: pointer !important;
369
422
  z-index: 2;
370
423
  }
424
+ &.hovered {
425
+ div {
426
+ scale: 2;
427
+ transform: translate(0px, -5px);
428
+ }
429
+ }
371
430
  &.highlight-marker {
372
431
  visibility: visible !important;
373
432
  cursor: pointer !important;
@@ -9,6 +9,7 @@ import markerZoomLevels from "../components/markerZoomLevelsHardCoded.js";
9
9
  import { mapStores } from 'pinia';
10
10
  import { useSettingsStore } from '../stores/settings';
11
11
  import { useSplitFlowStore } from '../stores/splitFlow';
12
+ import Tagging from '../services/tagging.js';
12
13
 
13
14
  function capitalise(text) {
14
15
  return text[0].toUpperCase() + text.substring(1)
@@ -52,10 +53,22 @@ export default {
52
53
  openMap: function (type) {
53
54
  if (type === "SYNC") {
54
55
  this.toggleSyncMode();
56
+ this.trackOpenMap('toggle_map_sync_mode');
55
57
  } else {
56
58
  EventBus.emit("OpenNewMap", type);
59
+ this.trackOpenMap(`open_new_${type}_map`);
57
60
  }
58
61
  },
62
+ trackOpenMap: function (category) {
63
+ // GA Tagging
64
+ // Open map tracking
65
+ Tagging.sendEvent({
66
+ 'event': 'interaction_event',
67
+ 'event_name': 'portal_maps_open_map',
68
+ 'category': category,
69
+ 'location': 'open_new_map'
70
+ });
71
+ },
59
72
  updateWithViewUrl: function() {
60
73
  return;
61
74
  },
@@ -113,6 +126,7 @@ export default {
113
126
  term: this.settingsStore.featuredMarkerDoi(
114
127
  resource.feature.id
115
128
  ),
129
+ featuredDataset: true,
116
130
  };
117
131
  } else if (hardcodedAnnotation.filter(h => h.keyword).length > 0) {
118
132
  // if it matches our stored keywords, it is a keyword search
@@ -42,9 +42,19 @@ export default {
42
42
 
43
43
  if (flatmapImp) {
44
44
  let markers = this.settingsStore.markers;
45
+ let hoveredMarkers = this.settingsStore.hoveredMarkers
45
46
  markers = removeDuplicates(markers);
47
+ hoveredMarkers = removeDuplicates(hoveredMarkers);
46
48
  flatmapImp.clearMarkers();
47
- markers.forEach(id => flatmapImp.addMarker(id, {className: "standard-marker"}))
49
+ markers.forEach(id => {
50
+ let markerClass = "standard-marker"
51
+ let markerCluster = true
52
+ if (hoveredMarkers.includes(id)) {
53
+ markerClass += " hovered" // Space-separated CSS class names
54
+ markerCluster = false // Disable cluster when related dataset is hovered
55
+ }
56
+ flatmapImp.addMarker(id, { className: markerClass, cluster: markerCluster })
57
+ })
48
58
  if (this.entry.type === "MultiFlatmap") {
49
59
  this.restoreFeaturedMarkers(flatmapImp);
50
60
  }
@@ -0,0 +1,83 @@
1
+
2
+ /* eslint-disable no-alert, no-console */
3
+ export default {
4
+ // Note that the setting store is included in MapContent.vue
5
+ methods: {
6
+ retrieveContextCardFromUrl: async function (url) {
7
+ // split the url to get the datasetId
8
+ const [datasetId, basePath, scaffoldPath, s3uri] = this.splitInfoFromUrl(url);
9
+
10
+ // get the context file from scicrunch
11
+ const sciResults = await this.getContextFileFromScicrunch(datasetId, scaffoldPath);
12
+ if (!sciResults.success){
13
+ return {} // return empty object if no context file is found (the empty object will be added to the entry)
14
+ }
15
+
16
+ // return the context file
17
+ const fullPath = basePath + sciResults.contextFile + s3uri;
18
+ return {
19
+ s3uri: sciResults.s3uri,
20
+ contextCardUrl: fullPath,
21
+ }
22
+ },
23
+ splitInfoFromUrl: function (url) {
24
+ // example url: "https://mapcore-demo.org/current/sparc-api-v2/s3-resource/221/3/files/derivative/Scaffolds/mouse_colon_metadata.json",
25
+ // find the part after 's3-resource'
26
+ let s3path = url.split('s3-resource')[1];
27
+ let basePath = url.split('files/')[0] + 'files/' // This gives us the base path for our relative path we will get from scicrunch
28
+ let scaffoldPath = url.split('files/')[1].split('?')[0] // This gives us the relative path to the file we want to get from scicrunch
29
+ let s3uri = '?' + url.split('?')[1] // This gives us the uri needed to get the file from s3
30
+
31
+ // split the url by '/'
32
+ const parts = s3path.split('/');
33
+ // remove the first part
34
+ parts.shift();
35
+ // return the datasetId which is the first part
36
+ const datasetId = parts[0];
37
+
38
+ return [datasetId, basePath, scaffoldPath, s3uri];
39
+ },
40
+ getContextFileFromScicrunch: async function (datasetId, scaffoldPath) {
41
+ // get the context file from scicrunch
42
+ let results = await fetch(`${this.settingsStore.sparcApi}/dataset_info/using_multiple_discoverIds/?discoverIds=${datasetId}`)
43
+ .then(response => response.json())
44
+ .then(data => {
45
+ // get the context info from the response
46
+ if (data.numberOfHits === 1) { // check if there is only one hit (We don't want to use the data if there are multiple hits)
47
+ const contextFile = data.results[0]['abi-contextual-information']
48
+
49
+ // check if there is only one context file and if so return it
50
+ if ( contextFile && contextFile.length === 1) {
51
+ return {
52
+ success: true,
53
+ contextFile: contextFile[0],
54
+ s3uri: data.results[0]['s3uri']
55
+ }
56
+ }
57
+
58
+ // If there are multiple context files, find the one that matches the scaffold path
59
+ else if (contextFile && contextFile.length > 1) {
60
+ let search = this.findContextInforForFilePath(data.results[0]['abi-context-file'], scaffoldPath);
61
+ if (search) {
62
+ return {
63
+ success: true,
64
+ contextFile: search,
65
+ s3uri: data.results[0]['s3uri']
66
+ }
67
+ }
68
+ }
69
+ }
70
+ return {success: false};
71
+ }).catch(error => {
72
+ console.error('Error:', error);
73
+ return {success: false};
74
+ });
75
+ return results;
76
+ },
77
+ findContextInforForFilePath: function (dataciteInfo, filePath) {
78
+ // find the context file that matches the scaffold path
79
+ let result = dataciteInfo.find((info) => info.datacite.isDerivedFrom.path.includes(filePath))
80
+ return result?.dataset?.path
81
+ }
82
+ }
83
+ }
@@ -0,0 +1,28 @@
1
+ export default {
2
+ sendEvent: function(data) {
3
+ const taggingData = {
4
+ event: data.event || '',
5
+ event_name: data.event_name || '',
6
+ files: data.files || '',
7
+ file_name: data.file_name || '',
8
+ file_path: data.file_path || '',
9
+ file_type: data.file_type || '',
10
+ category: data.category || '',
11
+ dataset_id: data.dataset_id || '',
12
+ version_id: data.version_id || '',
13
+ doi: data.doi || '',
14
+ citation_type: data.citation_type || '',
15
+ location: data.location || ''
16
+ };
17
+
18
+ // set debugging mode
19
+ if (localStorage.getItem('debugTagging') === 'yes') {
20
+ console.table(taggingData);
21
+ }
22
+
23
+ // push to dataLayer for GTM
24
+ if (typeof dataLayer !== 'undefined') {
25
+ dataLayer.push(taggingData);
26
+ }
27
+ }
28
+ }
@@ -17,6 +17,7 @@ export const useSettingsStore = defineStore('settings', {
17
17
  facets: { species: [], gender: [], organ: [] },
18
18
  facetLabels: [],
19
19
  markers: [],
20
+ hoveredMarkers: [],
20
21
  featuredMarkers: [],
21
22
  featuredMarkerIdentifiers: [],
22
23
  featuredMarkerDois: [],
@@ -67,6 +68,9 @@ export const useSettingsStore = defineStore('settings', {
67
68
  updateMarkers(markers) {
68
69
  this.markers = markers;
69
70
  },
71
+ updateHoveredMarkers(markers) {
72
+ this.hoveredMarkers = markers;
73
+ },
70
74
  updateFeatured(datasetIdentifiers) {
71
75
  this.featuredMarkerIdentifiers = new Array(datasetIdentifiers.length);
72
76
  this.featuredMarkers = new Array(datasetIdentifiers.length);