@abi-software/flatmapvuer 0.3.2 → 0.3.3

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.
@@ -1,519 +1,519 @@
1
- <template>
2
- <div class="tooltip-container">
3
- <el-main v-if="content" class="main" v-loading="loading">
4
- <div class="block" v-if="content.title">
5
- <span class="title">{{capitalise(content.title)}}</span>
6
- </div>
7
- <div class="block" v-else>
8
- <span class="title">{{content.featureId}}</span>
9
- </div>
10
- <div class="content-container scrollbar">
11
- <!-- Currently we don't show the pubmed viewer, will remove once we are certain it won't be used -->
12
- <pubmed-viewer v-if="content.featureIds" v-show="false" class="block" :entry="content" @pubmedSearchUrl="pubmedSearchUrlUpdate"/>
13
- {{content.paths}}
14
- <div v-if="this.origins" class="block">
15
- <div>
16
- <span class="attribute-title">Origin</span>
17
- <el-popover
18
- width="250"
19
- trigger="hover"
20
- :append-to-body=false
21
- popper-class="popover-origin-help"
22
- >
23
- <i slot="reference" class="el-icon-warning-outline info"/>
24
- <span style="word-break: keep-all;">
25
- <i>Origin</i> {{originDescription}}
26
- </span>
27
- </el-popover>
28
- </div>
29
- <div v-for="origin in origins" class="attribute-content" :key="origin">
30
- {{ capitalise(origin) }}
31
- </div>
32
- <el-button v-show="originsWithDatasets.length > 0" class="button" @click="openDendrites">
33
- Explore origin data
34
- </el-button>
35
- </div>
36
- <div v-if="this.components" class="block">
37
- <div class="attribute-title">Components</div>
38
- <div v-for="component in components" class="attribute-content" :key="component">
39
- {{ capitalise(component) }}
40
- </div>
41
- </div>
42
- <div v-if="this.destinations" class="block">
43
- <div>
44
- <span class="attribute-title">Destination</span>
45
- <el-popover
46
- width="250"
47
- trigger="hover"
48
- :append-to-body=false
49
- popper-class="popover-origin-help"
50
- >
51
- <i slot="reference" class="el-icon-warning-outline info"/>
52
- <span style="word-break: keep-all;">
53
- <i>Destination</i> is where the axons terminate
54
- </span>
55
- </el-popover>
56
- </div>
57
- <div v-for="destination in destinations" class="attribute-content" :key="destination">
58
- {{ capitalise(destination) }}
59
- </div>
60
- <el-button v-show="destinationsWithDatasets.length > 0" class="button" @click="openAxons">
61
- Explore destination data
62
- </el-button>
63
- </div>
64
-
65
- <!-- We will serach on components until we can search on neurons -->
66
- <el-button v-show="components.length > 0" class="button" @click="openAll">
67
- Search for data on components
68
- </el-button>
69
-
70
- <!-- Disable neuron search until it is ready -->
71
- <!-- <el-button v-for="action in content.actions" round :key="action.title"
72
- class="button" @click="resourceSelected(action)">
73
- <i v-if="action.title === 'Search for datasets' || action.title === 'View Dataset' " class="el-icon-coin"></i>
74
- {{action.title}}
75
- </el-button> -->
76
- <el-button v-if="pubmedSearchUrl" class="button" icon="el-icon-notebook-2" @click="openUrl(pubmedSearchUrl)">
77
- Open publications in pubmed
78
- </el-button>
79
- </div>
80
- </el-main>
81
- </div>
82
- </template>
83
-
84
-
85
- <script>
86
- /* eslint-disable no-alert, no-console */
87
- import Vue from "vue";
88
- import {
89
- Button,
90
- Container,
91
- Header,
92
- Icon,
93
- Main
94
- } from "element-ui";
95
- import lang from "element-ui/lib/locale/lang/en";
96
- import locale from "element-ui/lib/locale";
97
- locale.use(lang);
98
- Vue.use(Button);
99
- Vue.use(Container);
100
- Vue.use(Header);
101
- Vue.use(Icon);
102
- Vue.use(Main);
103
-
104
- // pubmedviewer is currently not in use, but still under review so not ready to delete yet
105
- import PubmedViewer from './PubmedViewer.vue'
106
- import EventBus from './EventBus'
107
-
108
- const titleCase = (str) => {
109
- return str.replace(/\w\S*/g, (t) => { return t.charAt(0).toUpperCase() + t.substr(1).toLowerCase() });
110
- }
111
-
112
- const capitalise = function(str){
113
- if (str)
114
- return str.charAt(0).toUpperCase() + str.slice(1)
115
- return ""
116
- }
117
-
118
- export default {
119
- components: { PubmedViewer },
120
- name: "Tooltip",
121
- props: {
122
- visible: {
123
- type: Boolean,
124
- default: false
125
- },
126
- content: {
127
- type: Object,
128
- default: undefined
129
- },
130
- },
131
- data: function() {
132
- return {
133
- activeSpecies: undefined,
134
- appendToBody: false,
135
- pubmedSearchUrl: '',
136
- loading: false,
137
- destinations: [],
138
- origins: [],
139
- components: [],
140
- destinationsWithDatasets: [],
141
- originsWithDatasets: [],
142
- originDescriptions: {
143
- 'motor': 'is the location of the initial cell body of the circuit',
144
- 'sensory': 'is the location of the initial cell body in the PNS circuit'
145
- },
146
- componentsWithDatasets: [],
147
- uberons: [{id: undefined, name: undefined}]
148
- };
149
- },
150
- inject: ['sparcAPI', 'flatmapAPI'],
151
- watch: {
152
- 'content.featureIds': {
153
- handler: function(){
154
- this.pathwayQuery(this.content.featureIds)
155
- }
156
- }
157
- },
158
- mounted: function(){
159
- this.getOrganCuries()
160
- },
161
- computed: {
162
- originDescription: function(){
163
- if(this.content && this.content.title && this.content.title.toLowerCase().includes('motor')){
164
- return this.originDescriptions.motor
165
- } else {
166
- return this.originDescriptions.sensory
167
- }
168
- }
169
- },
170
- methods: {
171
- resourceSelected: function(action) {
172
- this.$emit("resource-selected", action);
173
- },
174
- titleCase: function(title){
175
- return titleCase(title)
176
- },
177
- capitalise: function(text){
178
- return capitalise(text)
179
- },
180
- onClose: function() {
181
- this.$emit("onClose")
182
- },
183
- openUrl: function(url){
184
- window.open(url, '_blank')
185
- },
186
- openAll: function(){
187
- EventBus.$emit('onActionClick', {type:'Facets', labels: this.components})
188
- },
189
- openAxons: function(){
190
- EventBus.$emit('onActionClick', {type:'Facets', labels: this.destinationsWithDatasets.map(a=>a.name)})
191
- },
192
- openDendrites: function(){
193
- EventBus.$emit('onActionClick', {type:'Facets', labels: this.originsWithDatasets.map(a=>a.name)})
194
- },
195
- pubmedSearchUrlUpdate: function (val){
196
- this.pubmedSearchUrl = val
197
- },
198
- findComponents: function(connectivity){
199
- let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
200
- let nodes = [...new Set(dnodes)] // remove duplicates
201
-
202
- let found = []
203
- let terminal = false
204
- nodes.forEach(node=>{
205
- let n = node.flat() // Find all terms on the node
206
- terminal = false
207
-
208
- // Check if the node is an destination or origin (note that they are labelled dendrite and axon as opposed to origin and destination)
209
- n.forEach(s=>{
210
- if(connectivity.axons.includes(s)){
211
- terminal = true
212
- }
213
- if(connectivity.dendrites.includes(s)){
214
- terminal = true
215
- }
216
- })
217
- if (!terminal){
218
- found.push(node)
219
- }
220
- })
221
-
222
- // remove duplicates
223
- let foundUnique = [...new Set(found.map(n=>n[0]))]
224
- return foundUnique
225
- },
226
- getOrganCuries: function(){
227
- fetch(`${this.sparcAPI}get-organ-curies/`)
228
- .then(response=>response.json())
229
- .then(data=>{
230
- this.uberons = data.uberon.array
231
- })
232
- },
233
- buildConnectivitySqlStatement: function(keastIds) {
234
- let sql = 'select knowledge from knowledge where entity in ('
235
- if (keastIds.length === 1) {
236
- sql += `'${keastIds[0]}')`
237
- } else if (keastIds.length > 1) {
238
- for (let i in keastIds) {
239
- sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} `
240
- }
241
- }
242
- return sql
243
- },
244
- buildLabelSqlStatement: function(uberons) {
245
- let sql = 'select entity, label from labels where entity in ('
246
- if (uberons.length === 1) {
247
- sql += `'${uberons[0]}')`
248
- } else if (uberons.length > 1) {
249
- for (let i in uberons) {
250
- sql += `'${uberons[i]}'${i >= uberons.length - 1 ? ')' : ','} `
251
- }
252
- }
253
- return sql
254
- },
255
- createLabelLookup: function(uberons) {
256
- return new Promise(resolve=> {
257
- let uberonMap = {}
258
- const data = { sql: this.buildLabelSqlStatement(uberons)}
259
- fetch(`${this.flatmapAPI}knowledge/query/`, {
260
- method: 'POST',
261
- headers: {
262
- 'Content-Type': 'application/json',
263
- },
264
- body: JSON.stringify(data),
265
- })
266
- .then(response => response.json())
267
- .then(payload => {
268
- const entity = payload.keys.indexOf("entity");
269
- const label = payload.keys.indexOf("label");
270
- if (entity > -1 && label > -1) {
271
- payload.values.forEach(pair => {
272
- uberonMap[pair[entity]] = pair[label];
273
- });
274
- }
275
- resolve(uberonMap)
276
- })
277
- })
278
- },
279
- pathwayQuery: function(keastIds){
280
- this.destinations = []
281
- this.origins = []
282
- this.components = []
283
- this.loading = true
284
- if (!keastIds || keastIds.length == 0) return
285
- const data = { sql: this.buildConnectivitySqlStatement(keastIds)};
286
- fetch(`${this.flatmapAPI}knowledge/query/`, {
287
- method: 'POST',
288
- headers: {
289
- 'Content-Type': 'application/json',
290
- },
291
- body: JSON.stringify(data),
292
- })
293
- .then(response => response.json())
294
- .then(data => {
295
- let connectivity = JSON.parse(data.values[0][0])
296
- let components = this.findComponents(connectivity)
297
-
298
- // Create list of ids to get labels for
299
- let conIds = connectivity.axons.concat(connectivity.dendrites.concat(components))
300
- this.createLabelLookup(conIds).then(lookUp=>{
301
- this.destinations = connectivity.axons.map(a=>lookUp[a])
302
- this.origins = connectivity.dendrites.map(d=>lookUp[d])
303
- this.components = components.map(c=>lookUp[c])
304
- })
305
-
306
- // Filter for the anatomy which is annotated on datasets
307
- this.destinationsWithDatasets = this.uberons.filter(ub => connectivity.axons.indexOf(ub.id) !== -1)
308
- this.originsWithDatasets = this.uberons.filter(ub => connectivity.dendrites.indexOf(ub.id) !== -1)
309
- this.componentsWithDatasets = this.uberons.filter(ub => connectivity.dendrites.indexOf(ub.id) !== -1)
310
- this.loading = false
311
- })
312
- .catch((error) => {
313
- console.error('Error:', error);
314
- })
315
- }
316
- }
317
- };
318
- </script>
319
-
320
- <style scoped lang="scss">
321
- @import "~element-ui/packages/theme-chalk/src/button";
322
- @import "~element-ui/packages/theme-chalk/src/container";
323
- @import "~element-ui/packages/theme-chalk/src/header";
324
- @import "~element-ui/packages/theme-chalk/src/main";
325
-
326
- .tooltip-container {
327
- text-align:justify;
328
- border-radius: 4px;
329
- box-shadow: 0 1px 2px rgba(0,0,0,.1);
330
- pointer-events: auto;
331
- background: #fff;
332
- border: 1px solid $app-primary-color;
333
- display: flex;
334
- justify-content: center;
335
- align-items: center;
336
- }
337
-
338
- .display {
339
- width: 44px;
340
- word-break: normal;
341
- }
342
-
343
- .title {
344
- text-align: left;
345
- width: 16em;
346
- line-height: 1.5em !important;
347
- font-size: 1em;
348
- font-family: Helvetica;
349
- font-weight: 500;
350
- /* font-weight: bold; */
351
- padding-bottom: 8px;
352
- }
353
-
354
- .block {
355
- margin-bottom: 1.5em;
356
- }
357
-
358
- .pub {
359
- width: 16rem;
360
- }
361
-
362
- .icon {
363
- right: 0px;
364
- position: absolute;
365
- top: 10px;
366
- }
367
-
368
- .icon:hover {
369
- cursor: pointer;
370
- }
371
-
372
- .popover-origin-help {
373
- text-transform: none !important; // need to overide the tooltip text transform
374
- }
375
-
376
- .info{
377
- transform: rotate(180deg);
378
- color: #8300bf;
379
- margin-left: 8px;
380
- }
381
-
382
- .main {
383
- font-size: 14px;
384
- text-align: left;
385
- line-height: 1.5em;
386
- font-family: Helvetica;
387
- font-weight: 400;
388
- /* outline: thin red solid; */
389
- padding: 1em !important;
390
- overflow: hidden;
391
- min-width: 16rem;
392
- }
393
-
394
- .title{
395
- font-size: 18px;
396
- font-weight: 500;
397
- font-weight: bold;
398
- padding-bottom: 8px;
399
- color: rgb(131, 0, 191);
400
-
401
- }
402
-
403
- .attribute-title{
404
- font-size: 16px;
405
- font-weight: 600;
406
- /* font-weight: bold; */
407
- text-transform: uppercase;
408
- }
409
-
410
- .attribute-content{
411
- font-size: 14px;
412
- font-weight: 500;
413
- }
414
-
415
- .popover-container {
416
- height: 100%;
417
- width: 100%;
418
- }
419
-
420
- .main {
421
- .el-button.is-round{
422
- border-radius: 4px;
423
- padding: 9px 20px 10px 20px;
424
- display: flex;
425
- height: 36px;
426
- }
427
- }
428
-
429
- .button {
430
- margin-left: 0px !important;
431
- margin-top: 0px !important;
432
- font-size: 14px !important;
433
- background-color: $app-primary-color;
434
- color: #fff;
435
- &+.button {
436
- margin-top: 10px !important;
437
- }
438
- &:hover {
439
- color: #fff !important;
440
- background: #ac76c5 !important;
441
- border: 1px solid #ac76c5 !important;
442
- }
443
- }
444
-
445
- .tooltip-container{
446
- &::after, &::before {
447
- content: '';
448
- display: block;
449
- position: absolute;
450
- width: 0;
451
- height: 0;
452
- border-style: solid;
453
- flex-shrink: 0;
454
- }
455
- }
456
-
457
- .mapboxgl-popup-anchor-bottom {
458
- .tooltip-container {
459
- &::after, &::before {
460
- top: 100%;
461
- border-width: 12px;
462
- }
463
- &::after {
464
- margin-top:-1px;
465
- border-color: rgb(255, 255, 255) transparent transparent transparent ;
466
- }
467
- &::before {
468
- margin: 0 auto;
469
- border-color: $app-primary-color transparent transparent transparent ;
470
- }
471
- }
472
- }
473
-
474
- .mapboxgl-popup-anchor-top {
475
- .tooltip-container {
476
- &::after, &::before {
477
- top: -24px;
478
- border-width: 12px;
479
- }
480
- &::after {
481
- margin-top: 1px;
482
- border-color: transparent transparent rgb(255, 255, 255) transparent ;
483
- }
484
- &::before {
485
- margin: 0 auto;
486
- border-color: transparent transparent $app-primary-color transparent ;
487
- }
488
- }
489
- }
490
-
491
- .content-container {
492
- overflow-y: scroll;
493
- scrollbar-width: thin !important;
494
- height: 200px;
495
- }
496
-
497
- .scrollbar::-webkit-scrollbar-track {
498
- border-radius: 10px;
499
- background-color: #f5f5f5;
500
- }
501
-
502
- .scrollbar::-webkit-scrollbar {
503
- width: 12px;
504
- right: -12px;
505
- background-color: #f5f5f5;
506
- }
507
-
508
- .scrollbar::-webkit-scrollbar-thumb {
509
- border-radius: 4px;
510
- box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.06);
511
- background-color: #979797;
512
- }
513
-
514
-
515
- /* Fix for chrome bug where under triangle pops up above one on top of it */
516
- .selector:not(*:root), .tooltip-container::after{
517
- top: 99.4%;
518
- }
519
- </style>
1
+ <template>
2
+ <div class="tooltip-container">
3
+ <el-main v-if="content" class="main" v-loading="loading">
4
+ <div class="block" v-if="content.title">
5
+ <span class="title">{{capitalise(content.title)}}</span>
6
+ </div>
7
+ <div class="block" v-else>
8
+ <span class="title">{{content.featureId}}</span>
9
+ </div>
10
+ <div class="content-container scrollbar">
11
+ <!-- Currently we don't show the pubmed viewer, will remove once we are certain it won't be used -->
12
+ <pubmed-viewer v-if="content.featureIds" v-show="false" class="block" :entry="content" @pubmedSearchUrl="pubmedSearchUrlUpdate"/>
13
+ {{content.paths}}
14
+ <div v-if="this.origins" class="block">
15
+ <div>
16
+ <span class="attribute-title">Origin</span>
17
+ <el-popover
18
+ width="250"
19
+ trigger="hover"
20
+ :append-to-body=false
21
+ popper-class="popover-origin-help"
22
+ >
23
+ <i slot="reference" class="el-icon-warning-outline info"/>
24
+ <span style="word-break: keep-all;">
25
+ <i>Origin</i> {{originDescription}}
26
+ </span>
27
+ </el-popover>
28
+ </div>
29
+ <div v-for="origin in origins" class="attribute-content" :key="origin">
30
+ {{ capitalise(origin) }}
31
+ </div>
32
+ <el-button v-show="originsWithDatasets.length > 0" class="button" @click="openDendrites">
33
+ Explore origin data
34
+ </el-button>
35
+ </div>
36
+ <div v-if="this.components" class="block">
37
+ <div class="attribute-title">Components</div>
38
+ <div v-for="component in components" class="attribute-content" :key="component">
39
+ {{ capitalise(component) }}
40
+ </div>
41
+ </div>
42
+ <div v-if="this.destinations" class="block">
43
+ <div>
44
+ <span class="attribute-title">Destination</span>
45
+ <el-popover
46
+ width="250"
47
+ trigger="hover"
48
+ :append-to-body=false
49
+ popper-class="popover-origin-help"
50
+ >
51
+ <i slot="reference" class="el-icon-warning-outline info"/>
52
+ <span style="word-break: keep-all;">
53
+ <i>Destination</i> is where the axons terminate
54
+ </span>
55
+ </el-popover>
56
+ </div>
57
+ <div v-for="destination in destinations" class="attribute-content" :key="destination">
58
+ {{ capitalise(destination) }}
59
+ </div>
60
+ <el-button v-show="destinationsWithDatasets.length > 0" class="button" @click="openAxons">
61
+ Explore destination data
62
+ </el-button>
63
+ </div>
64
+
65
+ <!-- We will serach on components until we can search on neurons -->
66
+ <el-button v-show="components.length > 0" class="button" @click="openAll">
67
+ Search for data on components
68
+ </el-button>
69
+
70
+ <!-- Disable neuron search until it is ready -->
71
+ <!-- <el-button v-for="action in content.actions" round :key="action.title"
72
+ class="button" @click="resourceSelected(action)">
73
+ <i v-if="action.title === 'Search for datasets' || action.title === 'View Dataset' " class="el-icon-coin"></i>
74
+ {{action.title}}
75
+ </el-button> -->
76
+ <el-button v-if="pubmedSearchUrl" class="button" icon="el-icon-notebook-2" @click="openUrl(pubmedSearchUrl)">
77
+ Open publications in pubmed
78
+ </el-button>
79
+ </div>
80
+ </el-main>
81
+ </div>
82
+ </template>
83
+
84
+
85
+ <script>
86
+ /* eslint-disable no-alert, no-console */
87
+ import Vue from "vue";
88
+ import {
89
+ Button,
90
+ Container,
91
+ Header,
92
+ Icon,
93
+ Main
94
+ } from "element-ui";
95
+ import lang from "element-ui/lib/locale/lang/en";
96
+ import locale from "element-ui/lib/locale";
97
+ locale.use(lang);
98
+ Vue.use(Button);
99
+ Vue.use(Container);
100
+ Vue.use(Header);
101
+ Vue.use(Icon);
102
+ Vue.use(Main);
103
+
104
+ // pubmedviewer is currently not in use, but still under review so not ready to delete yet
105
+ import PubmedViewer from './PubmedViewer.vue'
106
+ import EventBus from './EventBus'
107
+
108
+ const titleCase = (str) => {
109
+ return str.replace(/\w\S*/g, (t) => { return t.charAt(0).toUpperCase() + t.substr(1).toLowerCase() });
110
+ }
111
+
112
+ const capitalise = function(str){
113
+ if (str)
114
+ return str.charAt(0).toUpperCase() + str.slice(1)
115
+ return ""
116
+ }
117
+
118
+ export default {
119
+ components: { PubmedViewer },
120
+ name: "Tooltip",
121
+ props: {
122
+ visible: {
123
+ type: Boolean,
124
+ default: false
125
+ },
126
+ content: {
127
+ type: Object,
128
+ default: undefined
129
+ },
130
+ },
131
+ data: function() {
132
+ return {
133
+ activeSpecies: undefined,
134
+ appendToBody: false,
135
+ pubmedSearchUrl: '',
136
+ loading: false,
137
+ destinations: [],
138
+ origins: [],
139
+ components: [],
140
+ destinationsWithDatasets: [],
141
+ originsWithDatasets: [],
142
+ originDescriptions: {
143
+ 'motor': 'is the location of the initial cell body of the circuit',
144
+ 'sensory': 'is the location of the initial cell body in the PNS circuit'
145
+ },
146
+ componentsWithDatasets: [],
147
+ uberons: [{id: undefined, name: undefined}]
148
+ };
149
+ },
150
+ inject: ['sparcAPI', 'flatmapAPI'],
151
+ watch: {
152
+ 'content.featureIds': {
153
+ handler: function(){
154
+ this.pathwayQuery(this.content.featureIds)
155
+ }
156
+ }
157
+ },
158
+ mounted: function(){
159
+ this.getOrganCuries()
160
+ },
161
+ computed: {
162
+ originDescription: function(){
163
+ if(this.content && this.content.title && this.content.title.toLowerCase().includes('motor')){
164
+ return this.originDescriptions.motor
165
+ } else {
166
+ return this.originDescriptions.sensory
167
+ }
168
+ }
169
+ },
170
+ methods: {
171
+ resourceSelected: function(action) {
172
+ this.$emit("resource-selected", action);
173
+ },
174
+ titleCase: function(title){
175
+ return titleCase(title)
176
+ },
177
+ capitalise: function(text){
178
+ return capitalise(text)
179
+ },
180
+ onClose: function() {
181
+ this.$emit("onClose")
182
+ },
183
+ openUrl: function(url){
184
+ window.open(url, '_blank')
185
+ },
186
+ openAll: function(){
187
+ EventBus.$emit('onActionClick', {type:'Facets', labels: this.components})
188
+ },
189
+ openAxons: function(){
190
+ EventBus.$emit('onActionClick', {type:'Facets', labels: this.destinationsWithDatasets.map(a=>a.name)})
191
+ },
192
+ openDendrites: function(){
193
+ EventBus.$emit('onActionClick', {type:'Facets', labels: this.originsWithDatasets.map(a=>a.name)})
194
+ },
195
+ pubmedSearchUrlUpdate: function (val){
196
+ this.pubmedSearchUrl = val
197
+ },
198
+ findComponents: function(connectivity){
199
+ let dnodes = connectivity.connectivity.flat() // get nodes from edgelist
200
+ let nodes = [...new Set(dnodes)] // remove duplicates
201
+
202
+ let found = []
203
+ let terminal = false
204
+ nodes.forEach(node=>{
205
+ let n = node.flat() // Find all terms on the node
206
+ terminal = false
207
+
208
+ // Check if the node is an destination or origin (note that they are labelled dendrite and axon as opposed to origin and destination)
209
+ n.forEach(s=>{
210
+ if(connectivity.axons.includes(s)){
211
+ terminal = true
212
+ }
213
+ if(connectivity.dendrites.includes(s)){
214
+ terminal = true
215
+ }
216
+ })
217
+ if (!terminal){
218
+ found.push(node)
219
+ }
220
+ })
221
+
222
+ // remove duplicates
223
+ let foundUnique = [...new Set(found.map(n=>n[0]))]
224
+ return foundUnique
225
+ },
226
+ getOrganCuries: function(){
227
+ fetch(`${this.sparcAPI}get-organ-curies/`)
228
+ .then(response=>response.json())
229
+ .then(data=>{
230
+ this.uberons = data.uberon.array
231
+ })
232
+ },
233
+ buildConnectivitySqlStatement: function(keastIds) {
234
+ let sql = 'select knowledge from knowledge where entity in ('
235
+ if (keastIds.length === 1) {
236
+ sql += `'${keastIds[0]}')`
237
+ } else if (keastIds.length > 1) {
238
+ for (let i in keastIds) {
239
+ sql += `'${keastIds[i]}'${i >= keastIds.length - 1 ? ')' : ','} `
240
+ }
241
+ }
242
+ return sql
243
+ },
244
+ buildLabelSqlStatement: function(uberons) {
245
+ let sql = 'select entity, label from labels where entity in ('
246
+ if (uberons.length === 1) {
247
+ sql += `'${uberons[0]}')`
248
+ } else if (uberons.length > 1) {
249
+ for (let i in uberons) {
250
+ sql += `'${uberons[i]}'${i >= uberons.length - 1 ? ')' : ','} `
251
+ }
252
+ }
253
+ return sql
254
+ },
255
+ createLabelLookup: function(uberons) {
256
+ return new Promise(resolve=> {
257
+ let uberonMap = {}
258
+ const data = { sql: this.buildLabelSqlStatement(uberons)}
259
+ fetch(`${this.flatmapAPI}knowledge/query/`, {
260
+ method: 'POST',
261
+ headers: {
262
+ 'Content-Type': 'application/json',
263
+ },
264
+ body: JSON.stringify(data),
265
+ })
266
+ .then(response => response.json())
267
+ .then(payload => {
268
+ const entity = payload.keys.indexOf("entity");
269
+ const label = payload.keys.indexOf("label");
270
+ if (entity > -1 && label > -1) {
271
+ payload.values.forEach(pair => {
272
+ uberonMap[pair[entity]] = pair[label];
273
+ });
274
+ }
275
+ resolve(uberonMap)
276
+ })
277
+ })
278
+ },
279
+ pathwayQuery: function(keastIds){
280
+ this.destinations = []
281
+ this.origins = []
282
+ this.components = []
283
+ this.loading = true
284
+ if (!keastIds || keastIds.length == 0) return
285
+ const data = { sql: this.buildConnectivitySqlStatement(keastIds)};
286
+ fetch(`${this.flatmapAPI}knowledge/query/`, {
287
+ method: 'POST',
288
+ headers: {
289
+ 'Content-Type': 'application/json',
290
+ },
291
+ body: JSON.stringify(data),
292
+ })
293
+ .then(response => response.json())
294
+ .then(data => {
295
+ let connectivity = JSON.parse(data.values[0][0])
296
+ let components = this.findComponents(connectivity)
297
+
298
+ // Create list of ids to get labels for
299
+ let conIds = connectivity.axons.concat(connectivity.dendrites.concat(components))
300
+ this.createLabelLookup(conIds).then(lookUp=>{
301
+ this.destinations = connectivity.axons.map(a=>lookUp[a])
302
+ this.origins = connectivity.dendrites.map(d=>lookUp[d])
303
+ this.components = components.map(c=>lookUp[c])
304
+ })
305
+
306
+ // Filter for the anatomy which is annotated on datasets
307
+ this.destinationsWithDatasets = this.uberons.filter(ub => connectivity.axons.indexOf(ub.id) !== -1)
308
+ this.originsWithDatasets = this.uberons.filter(ub => connectivity.dendrites.indexOf(ub.id) !== -1)
309
+ this.componentsWithDatasets = this.uberons.filter(ub => connectivity.dendrites.indexOf(ub.id) !== -1)
310
+ this.loading = false
311
+ })
312
+ .catch((error) => {
313
+ console.error('Error:', error);
314
+ })
315
+ }
316
+ }
317
+ };
318
+ </script>
319
+
320
+ <style scoped lang="scss">
321
+ @import "~element-ui/packages/theme-chalk/src/button";
322
+ @import "~element-ui/packages/theme-chalk/src/container";
323
+ @import "~element-ui/packages/theme-chalk/src/header";
324
+ @import "~element-ui/packages/theme-chalk/src/main";
325
+
326
+ .tooltip-container {
327
+ text-align:justify;
328
+ border-radius: 4px;
329
+ box-shadow: 0 1px 2px rgba(0,0,0,.1);
330
+ pointer-events: auto;
331
+ background: #fff;
332
+ border: 1px solid $app-primary-color;
333
+ display: flex;
334
+ justify-content: center;
335
+ align-items: center;
336
+ }
337
+
338
+ .display {
339
+ width: 44px;
340
+ word-break: normal;
341
+ }
342
+
343
+ .title {
344
+ text-align: left;
345
+ width: 16em;
346
+ line-height: 1.5em !important;
347
+ font-size: 1em;
348
+ font-family: Helvetica;
349
+ font-weight: 500;
350
+ /* font-weight: bold; */
351
+ padding-bottom: 8px;
352
+ }
353
+
354
+ .block {
355
+ margin-bottom: 1.5em;
356
+ }
357
+
358
+ .pub {
359
+ width: 16rem;
360
+ }
361
+
362
+ .icon {
363
+ right: 0px;
364
+ position: absolute;
365
+ top: 10px;
366
+ }
367
+
368
+ .icon:hover {
369
+ cursor: pointer;
370
+ }
371
+
372
+ .popover-origin-help {
373
+ text-transform: none !important; // need to overide the tooltip text transform
374
+ }
375
+
376
+ .info{
377
+ transform: rotate(180deg);
378
+ color: #8300bf;
379
+ margin-left: 8px;
380
+ }
381
+
382
+ .main {
383
+ font-size: 14px;
384
+ text-align: left;
385
+ line-height: 1.5em;
386
+ font-family: Helvetica;
387
+ font-weight: 400;
388
+ /* outline: thin red solid; */
389
+ padding: 1em !important;
390
+ overflow: hidden;
391
+ min-width: 16rem;
392
+ }
393
+
394
+ .title{
395
+ font-size: 18px;
396
+ font-weight: 500;
397
+ font-weight: bold;
398
+ padding-bottom: 8px;
399
+ color: rgb(131, 0, 191);
400
+
401
+ }
402
+
403
+ .attribute-title{
404
+ font-size: 16px;
405
+ font-weight: 600;
406
+ /* font-weight: bold; */
407
+ text-transform: uppercase;
408
+ }
409
+
410
+ .attribute-content{
411
+ font-size: 14px;
412
+ font-weight: 500;
413
+ }
414
+
415
+ .popover-container {
416
+ height: 100%;
417
+ width: 100%;
418
+ }
419
+
420
+ .main {
421
+ .el-button.is-round{
422
+ border-radius: 4px;
423
+ padding: 9px 20px 10px 20px;
424
+ display: flex;
425
+ height: 36px;
426
+ }
427
+ }
428
+
429
+ .button {
430
+ margin-left: 0px !important;
431
+ margin-top: 0px !important;
432
+ font-size: 14px !important;
433
+ background-color: $app-primary-color;
434
+ color: #fff;
435
+ &+.button {
436
+ margin-top: 10px !important;
437
+ }
438
+ &:hover {
439
+ color: #fff !important;
440
+ background: #ac76c5 !important;
441
+ border: 1px solid #ac76c5 !important;
442
+ }
443
+ }
444
+
445
+ .tooltip-container{
446
+ &::after, &::before {
447
+ content: '';
448
+ display: block;
449
+ position: absolute;
450
+ width: 0;
451
+ height: 0;
452
+ border-style: solid;
453
+ flex-shrink: 0;
454
+ }
455
+ }
456
+
457
+ .mapboxgl-popup-anchor-bottom {
458
+ .tooltip-container {
459
+ &::after, &::before {
460
+ top: 100%;
461
+ border-width: 12px;
462
+ }
463
+ &::after {
464
+ margin-top:-1px;
465
+ border-color: rgb(255, 255, 255) transparent transparent transparent ;
466
+ }
467
+ &::before {
468
+ margin: 0 auto;
469
+ border-color: $app-primary-color transparent transparent transparent ;
470
+ }
471
+ }
472
+ }
473
+
474
+ .mapboxgl-popup-anchor-top {
475
+ .tooltip-container {
476
+ &::after, &::before {
477
+ top: -24px;
478
+ border-width: 12px;
479
+ }
480
+ &::after {
481
+ margin-top: 1px;
482
+ border-color: transparent transparent rgb(255, 255, 255) transparent ;
483
+ }
484
+ &::before {
485
+ margin: 0 auto;
486
+ border-color: transparent transparent $app-primary-color transparent ;
487
+ }
488
+ }
489
+ }
490
+
491
+ .content-container {
492
+ overflow-y: scroll;
493
+ scrollbar-width: thin !important;
494
+ height: 200px;
495
+ }
496
+
497
+ .scrollbar::-webkit-scrollbar-track {
498
+ border-radius: 10px;
499
+ background-color: #f5f5f5;
500
+ }
501
+
502
+ .scrollbar::-webkit-scrollbar {
503
+ width: 12px;
504
+ right: -12px;
505
+ background-color: #f5f5f5;
506
+ }
507
+
508
+ .scrollbar::-webkit-scrollbar-thumb {
509
+ border-radius: 4px;
510
+ box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.06);
511
+ background-color: #979797;
512
+ }
513
+
514
+
515
+ /* Fix for chrome bug where under triangle pops up above one on top of it */
516
+ .selector:not(*:root), .tooltip-container::after{
517
+ top: 99.4%;
518
+ }
519
+ </style>