@papaemmelab/isabl-web 0.3.9 → 0.3.10

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.
@@ -1943,7 +1943,7 @@ exports = module.exports = __webpack_require__("2350")(false);
1943
1943
 
1944
1944
 
1945
1945
  // module
1946
- exports.push([module.i, ".node-tooltip{background:rgba(0,0,0,.8)}.individual-tree-div{overflow-x:scroll;margin:auto}.individual-tree-div .node{stroke-width:3px}.individual-tree-div .node text{font-size:14px;font-weight:300;fill:#000!important;cursor:pointer}.individual-tree-div .node text.collapse-text{font-size:20px;font-weight:200;cursor:default}.individual-tree-div .node.experiment text{cursor:default}.individual-tree-div .link{fill:none;stroke-width:1.5px;stroke:#aaa}", ""]);
1946
+ exports.push([module.i, ".tree-tooltip.v-tooltip__content{color:#fff;padding:20px;opacity:1;font-size:12px;overflow:hidden}.node-tooltip{background:rgba(0,0,0,.8)}.individual-tree-div{overflow-x:scroll;margin:auto}.individual-tree-div .node{stroke-width:3px}.individual-tree-div .node text{font-size:14px;font-weight:300;fill:#000!important;cursor:pointer}.individual-tree-div .node text.collapse-text{font-size:20px;font-weight:200;cursor:default}.individual-tree-div .node.experiment text{cursor:default}.individual-tree-div .link{fill:none;stroke-width:1.5px;stroke:#aaa}", ""]);
1947
1947
 
1948
1948
  // exports
1949
1949
 
@@ -91168,7 +91168,7 @@ module.exports = overArg;
91168
91168
  /***/ "9224":
91169
91169
  /***/ (function(module) {
91170
91170
 
91171
- module.exports = JSON.parse("{\"name\":\"@papaemmelab/isabl-web\",\"version\":\"0.3.9\",\"scripts\":{\"serve\":\"vue-cli-service serve\",\"lint\":\"vue-cli-service lint\",\"build-lib\":\"vue-cli-service build --target lib --name isabl-web ./src/main.js\",\"build-wc\":\"vue-cli-service build --target wc --name isabl-web ./src/components/*.vue\",\"build-wc-async\":\"vue-cli-service build --target wc-async --name isabl-web ./src/components/*.vue\",\"publish-app\":\"yarn version --patch && yarn build-lib && yarn publish --access public\",\"dev\":\"yarn build-lib --watch\",\"test:unit\":\"vue-cli-service test:unit\",\"test:e2e\":\"vue-cli-service test:e2e\",\"test:travis\":\"yarn test:e2e --headless\",\"test:submissions\":\"node tests/utils/create_test_submission.js\",\"test:report-coverage\":\"nyc report --reporter=text-lcov > coverage.lcov && codecov -t $CODECOV_TOKEN\",\"demo\":\"nodemon demo/demo-app.js\"},\"dependencies\":{\"@mdi/font\":\"^7.0.96\",\"ansi_up\":\"^5\",\"axios\":\"^0.21.1\",\"crossfilter\":\"^1.3.12\",\"crossfilter2\":\"^1.4.7\",\"d3-tip\":\"^0.9.1\",\"dc\":\"3.1.2\",\"detect-csv\":\"^1.1.0\",\"js-md5\":\"^0.7.3\",\"minify-css-string\":\"^1.0.0\",\"moment\":\"^2.22.2\",\"register-service-worker\":\"^1.5.2\",\"v-hotkey\":\"^0.6.0\",\"vue\":\"^2.5.16\",\"vue-clipboard2\":\"^0.2.1\",\"vue-gallery\":\"^1.4.0\",\"vue-highlightjs\":\"^1.3.3\",\"vue-json-excel\":\"^0.2.5\",\"vue-observe-visibility\":\"^0.4.6\",\"vue-router\":\"^3.0.1\",\"vue-upload-component\":\"^2.8.11\",\"vuetify\":\"2.6.10\",\"vuex\":\"^3.0.1\",\"vuex-router-sync\":\"^5.0.0\"},\"devDependencies\":{\"@cypress/code-coverage\":\"^1.10.1\",\"@vue/cli-plugin-babel\":\"^3.3.0\",\"@vue/cli-plugin-e2e-cypress\":\"^3.3.0\",\"@vue/cli-plugin-eslint\":\"^3.3.0\",\"@vue/cli-plugin-pwa\":\"^3.3.0\",\"@vue/cli-plugin-unit-jest\":\"^3.3.0\",\"@vue/cli-service\":\"^3.3.0\",\"@vue/eslint-config-prettier\":\"^3.0.5\",\"@vue/test-utils\":\"^1.0.0-beta.20\",\"axios-mock-adapter\":\"^1.15.0\",\"babel-core\":\"7.0.0-bridge.0\",\"babel-jest\":\"^23.0.1\",\"babel-plugin-istanbul\":\"^5.2.0\",\"codecov\":\"^3.6.1\",\"eslint-plugin-vuetify\":\"^1.1.0\",\"express\":\"^4.16.3\",\"html-webpack-plugin\":\"^3.2.0\",\"istanbul-lib-coverage\":\"^2.0.5\",\"mini-css-extract-plugin\":\"^0.4.2\",\"nyc\":\"^14.1.1\",\"sass\":\"^1.55.0\",\"sass-loader\":\"^7.0.1\",\"vue-template-compiler\":\"^2.5.16\",\"xlsx-populate\":\"^1.19.1\"},\"browserslist\":[\"> 1%\",\"last 2 versions\",\"not ie <= 8\"],\"main\":\"./dist/isabl-web.umd.min.js\",\"license\":\"MIT\",\"files\":[\"dist/isabl-web*.js\"],\"author\":\"Juan S. Medina - Juan E. Arango\",\"description\":\"Isabl Frontend of the MSK Academic License.\",\"bugs\":{\"url\":\"https://github.com/papaemmelab/isabl_web/issues\"},\"homepage\":\"https://github.com/papaemmelab/isabl_web#readme\"}");
91171
+ module.exports = JSON.parse("{\"name\":\"@papaemmelab/isabl-web\",\"version\":\"0.3.10\",\"scripts\":{\"serve\":\"vue-cli-service serve\",\"lint\":\"vue-cli-service lint\",\"build-lib\":\"vue-cli-service build --target lib --name isabl-web ./src/main.js\",\"build-wc\":\"vue-cli-service build --target wc --name isabl-web ./src/components/*.vue\",\"build-wc-async\":\"vue-cli-service build --target wc-async --name isabl-web ./src/components/*.vue\",\"publish-app\":\"yarn version --patch && yarn build-lib && yarn publish --access public\",\"dev\":\"yarn build-lib --watch\",\"test:unit\":\"vue-cli-service test:unit\",\"test:e2e\":\"vue-cli-service test:e2e\",\"test:travis\":\"yarn test:e2e --headless\",\"test:submissions\":\"node tests/utils/create_test_submission.js\",\"test:report-coverage\":\"nyc report --reporter=text-lcov > coverage.lcov && codecov -t $CODECOV_TOKEN\",\"demo\":\"nodemon demo/demo-app.js\"},\"dependencies\":{\"@mdi/font\":\"^7.0.96\",\"ansi_up\":\"^5\",\"axios\":\"^0.21.1\",\"crossfilter\":\"^1.3.12\",\"crossfilter2\":\"^1.4.7\",\"d3-tip\":\"^0.9.1\",\"dc\":\"3.1.2\",\"detect-csv\":\"^1.1.0\",\"js-md5\":\"^0.7.3\",\"minify-css-string\":\"^1.0.0\",\"moment\":\"^2.22.2\",\"register-service-worker\":\"^1.5.2\",\"v-hotkey\":\"^0.6.0\",\"vue\":\"^2.5.16\",\"vue-clipboard2\":\"^0.2.1\",\"vue-gallery\":\"^1.4.0\",\"vue-highlightjs\":\"^1.3.3\",\"vue-json-excel\":\"^0.2.5\",\"vue-observe-visibility\":\"^0.4.6\",\"vue-router\":\"^3.0.1\",\"vue-upload-component\":\"^2.8.11\",\"vuetify\":\"2.6.10\",\"vuex\":\"^3.0.1\",\"vuex-router-sync\":\"^5.0.0\"},\"devDependencies\":{\"@cypress/code-coverage\":\"^1.10.1\",\"@vue/cli-plugin-babel\":\"^3.3.0\",\"@vue/cli-plugin-e2e-cypress\":\"^3.3.0\",\"@vue/cli-plugin-eslint\":\"^3.3.0\",\"@vue/cli-plugin-pwa\":\"^3.3.0\",\"@vue/cli-plugin-unit-jest\":\"^3.3.0\",\"@vue/cli-service\":\"^3.3.0\",\"@vue/eslint-config-prettier\":\"^3.0.5\",\"@vue/test-utils\":\"^1.0.0-beta.20\",\"axios-mock-adapter\":\"^1.15.0\",\"babel-core\":\"7.0.0-bridge.0\",\"babel-jest\":\"^23.0.1\",\"babel-plugin-istanbul\":\"^5.2.0\",\"codecov\":\"^3.6.1\",\"eslint-plugin-vuetify\":\"^1.1.0\",\"express\":\"^4.16.3\",\"html-webpack-plugin\":\"^3.2.0\",\"istanbul-lib-coverage\":\"^2.0.5\",\"mini-css-extract-plugin\":\"^0.4.2\",\"nyc\":\"^14.1.1\",\"sass\":\"^1.55.0\",\"sass-loader\":\"^7.0.1\",\"vue-template-compiler\":\"^2.5.16\",\"xlsx-populate\":\"^1.19.1\"},\"browserslist\":[\"> 1%\",\"last 2 versions\",\"not ie <= 8\"],\"main\":\"./dist/isabl-web.umd.min.js\",\"license\":\"MIT\",\"files\":[\"dist/isabl-web*.js\"],\"author\":\"Juan S. Medina - Juan E. Arango\",\"description\":\"Isabl Frontend of the MSK Academic License.\",\"bugs\":{\"url\":\"https://github.com/papaemmelab/isabl_web/issues\"},\"homepage\":\"https://github.com/papaemmelab/isabl_web#readme\"}");
91172
91172
 
91173
91173
  /***/ }),
91174
91174
 
@@ -253214,8 +253214,8 @@ var RerunButton_component = normalizeComponent(
253214
253214
  )
253215
253215
 
253216
253216
  /* harmony default export */ var RerunButton = (RerunButton_component.exports);
253217
- // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"d220a428-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/analyses/ResultsGallery.vue?vue&type=template&id=6a3b6186&
253218
- var ResultsGalleryvue_type_template_id_6a3b6186_render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.showDialog)?_c('v-dialog',{directives:[{name:"hotkey",rawName:"v-hotkey",value:(_vm.keymap),expression:"keymap"}],staticClass:"gallery-dialog",attrs:{"fullscreen":_vm.isResultModalFullScreen,"scrollable":""},model:{value:(_vm.showDialog),callback:function ($$v) {_vm.showDialog=$$v},expression:"showDialog"}},[_c('v-card',{staticClass:"gallery-card",attrs:{"flat":""}},[_c('v-card-title',{staticClass:"pa-2"},[_c('v-row',[_c('v-col',{attrs:{"cols":"6"}},[_c('h3',{staticClass:"font-weight-thin primary--text"},[_vm._v("\n "+_vm._s(_vm.result.verboseName)+"\n ")])]),_c('v-col',{staticClass:"d-flex justify-end align-center panel-slot-actions",attrs:{"cols":"6"}},[(_vm.result.frontendType === 'tsv-file' && _vm.tsvHeaders.length)?_c('v-tooltip',{attrs:{"top":""},scopedSlots:_vm._u([{key:"activator",fn:function(ref){
253217
+ // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"d220a428-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/analyses/ResultsGallery.vue?vue&type=template&id=185780e8&
253218
+ var ResultsGalleryvue_type_template_id_185780e8_render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.showDialog)?_c('v-dialog',{directives:[{name:"hotkey",rawName:"v-hotkey",value:(_vm.keymap),expression:"keymap"}],staticClass:"gallery-dialog",attrs:{"fullscreen":_vm.isResultModalFullScreen,"scrollable":""},model:{value:(_vm.showDialog),callback:function ($$v) {_vm.showDialog=$$v},expression:"showDialog"}},[_c('v-card',{staticClass:"gallery-card",attrs:{"flat":""}},[_c('v-card-title',{staticClass:"pa-2"},[_c('v-row',[_c('v-col',{attrs:{"cols":"6"}},[_c('h3',{staticClass:"font-weight-thin primary--text"},[_vm._v("\n "+_vm._s(_vm.result.verboseName)+"\n ")])]),_c('v-col',{staticClass:"d-flex justify-end align-center panel-slot-actions",attrs:{"cols":"6"}},[(_vm.result.frontendType === 'tsv-file' && _vm.tsvHeaders.length)?_c('v-tooltip',{attrs:{"top":""},scopedSlots:_vm._u([{key:"activator",fn:function(ref){
253219
253219
  var on = ref.on;
253220
253220
  return [_c('v-icon',_vm._g({staticClass:"primary--text ml-4",attrs:{"small":""},on:{"click":function($event){_vm.displayRawText = !_vm.displayRawText}}},on),[_vm._v("\n "+_vm._s(_vm.displayRawText ? 'table_chart' : 'text_fields')+"\n ")])]}}],null,false,578865119)},[_vm._v("\n "+_vm._s(_vm.displayRawText ? 'Display Tabulated Content' : 'Display Raw Content')+"\n ")]):_vm._e(),(_vm.result.download)?_c('v-tooltip',{attrs:{"top":""},scopedSlots:_vm._u([{key:"activator",fn:function(ref){
253221
253221
  var on = ref.on;
@@ -253232,10 +253232,10 @@ var on = ref.on;
253232
253232
  return [_c('v-icon',_vm._g({staticClass:"primary--text ml-4",attrs:{"small":""},on:{"click":_vm.hideDialog}},on),[_vm._v("\n close\n ")])]}}],null,false,804201733)},[_vm._v("\n Close file\n ")])],1)],1)],1),_c('v-row',{staticClass:"pa-0 ma-0 gallery-content",staticStyle:{"height":"85vh"}},[(_vm.loading)?_c('v-progress-linear',{staticClass:"panel-loader",attrs:{"indeterminate":"","height":"2","color":"primary"}}):_vm._e(),(_vm.resultType === 'raw' && _vm.resultKey === 'command_script')?_c('pre',{directives:[{name:"highlightjs",rawName:"v-highlightjs",value:(_vm.content),expression:"content"}],ref:"raw",staticClass:"v-markup pre-wrap"},[_vm._v(" "),_c('code',{staticClass:"bash"}),_vm._v("\n ")]):(_vm.isAnsi)?_c('code',{ref:"raw",staticClass:"v-markup"},[_c('pre',{domProps:{"innerHTML":_vm._s(_vm.content)}})]):(_vm.resultType === 'raw')?_c('code',{ref:"raw",staticClass:"v-markup"},[_c('pre',[_vm._v(" "+_vm._s(_vm.content)+"\n "),(_vm.content && !_vm.streamOptions.end)?_c('span',{staticClass:"stream-message grey--text lighten-2"},[_vm._v("\n ... Loading more rows\n ")]):_vm._e(),_vm._v("\n "),(!_vm.loading && _vm.streamOptions.end)?_c('span',{staticClass:"stream-message grey--text"},[_vm._v("\n *** End of file. ***\n ")]):_vm._e(),_vm._v("\n ")])]):(_vm.resultType === 'table')?_c('div',{ref:"table",staticClass:"tsv-table"},[_c('v-data-table',{attrs:{"headers":_vm.tsvHeaders,"items":_vm.tsvItems,"items-per-page":_vm.tsvItems.length,"hide-default-footer":"","multi-sort":"","fixed-header":"","dense":""},scopedSlots:_vm._u([{key:"body",fn:function(ref){
253233
253233
  var items = ref.items;
253234
253234
  return [_c('tbody',_vm._l((items),function(item){return _c('tr',{key:item.name},_vm._l((_vm.tsvHeaders),function(header,index){return _c('td',{key:index + header.value,class:[header.value === 'index' ? 'line-column text' : ''],domProps:{"innerHTML":_vm._s(_vm.parseTsvCell(_vm.$get(item, header.value)))}})}),0)}),0)]}}],null,false,3434796515)}),_c('div',{staticClass:"text-center stream-message"},[(_vm.content && !_vm.streamOptions.end)?_c('span',[_vm._v("... Loading more rows")]):_vm._e(),(!_vm.loading && _vm.streamOptions.end)?_c('span',[_vm._v("End of file.")]):_vm._e()])],1):(_vm.resultType === 'image')?_c('img',{staticClass:"results-image",attrs:{"src":_vm.$store.getters.getBlobUrl(_vm.result.downloadUrl),"alt":_vm.result.verboseName}}):(['html', 'pdf'].includes(_vm.resultType))?_c('iframe',{staticClass:"results-iframe",staticStyle:{"width":"100%","height":"100%","min-height":"80vh"},attrs:{"src":_vm.$store.getters.getBlobUrl(_vm.result.downloadUrl, ''),"frameborder":"0"}}):(_vm.resultType === 'igv_bam')?_c('iframe',{staticClass:"results-iframe",staticStyle:{"width":"100%","height":"100%","min-height":"80vh"},attrs:{"src":_vm.result.downloadUrl,"frameborder":"0"}}):(_vm.resultType === 'number')?_c('code',{staticClass:"v-markup"},[_c('span',{attrs:{"size":"100px"}},[_vm._v(_vm._s(_vm._f("round")(_vm.result.value,3)))])]):(['string', 'url-link'].includes(_vm.resultType))?_c('code',{staticClass:"v-markup"},[_c('span',{attrs:{"size":"100px"}},[_vm._v(_vm._s(_vm.result.value))])]):_vm._e()],1)],1)],1):_vm._e()}
253235
- var ResultsGalleryvue_type_template_id_6a3b6186_staticRenderFns = []
253235
+ var ResultsGalleryvue_type_template_id_185780e8_staticRenderFns = []
253236
253236
 
253237
253237
 
253238
- // CONCATENATED MODULE: ./src/components/analyses/ResultsGallery.vue?vue&type=template&id=6a3b6186&
253238
+ // CONCATENATED MODULE: ./src/components/analyses/ResultsGallery.vue?vue&type=template&id=185780e8&
253239
253239
 
253240
253240
  // EXTERNAL MODULE: ./node_modules/detect-csv/index.js
253241
253241
  var detect_csv = __webpack_require__("0f34");
@@ -253244,7 +253244,7 @@ var detect_csv_default = /*#__PURE__*/__webpack_require__.n(detect_csv);
253244
253244
  // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/thread-loader/dist/cjs.js!./node_modules/@vue/cli-plugin-babel/node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/analyses/ResultsGallery.vue?vue&type=script&lang=js&
253245
253245
  var cov_d7x60homs = function () {
253246
253246
  var path = "/Users/juanes/papaemmelab/isabl_web/src/components/analyses/ResultsGallery.vue";
253247
- var hash = "22f2dbdd387a60567b7780d3d04bdf3435e3d749";
253247
+ var hash = "fe09864f43de2d4dae5821f382f528b5c4317436";
253248
253248
  var global = new Function("return this")();
253249
253249
  var gcv = "__coverage__";
253250
253250
  var coverageData = {
@@ -254027,7 +254027,7 @@ var cov_d7x60homs = function () {
254027
254027
  },
254028
254028
  end: {
254029
254029
  line: 491,
254030
- column: 34
254030
+ column: 41
254031
254031
  }
254032
254032
  },
254033
254033
  "78": {
@@ -254037,7 +254037,7 @@ var cov_d7x60homs = function () {
254037
254037
  },
254038
254038
  end: {
254039
254039
  line: 492,
254040
- column: 71
254040
+ column: 77
254041
254041
  }
254042
254042
  },
254043
254043
  "79": {
@@ -254959,7 +254959,7 @@ var cov_d7x60homs = function () {
254959
254959
  },
254960
254960
  end: {
254961
254961
  line: 491,
254962
- column: 34
254962
+ column: 41
254963
254963
  }
254964
254964
  },
254965
254965
  line: 491
@@ -254983,7 +254983,7 @@ var cov_d7x60homs = function () {
254983
254983
  },
254984
254984
  end: {
254985
254985
  line: 492,
254986
- column: 71
254986
+ column: 77
254987
254987
  }
254988
254988
  },
254989
254989
  line: 492
@@ -256233,7 +256233,7 @@ var cov_d7x60homs = function () {
256233
256233
  },
256234
256234
  end: {
256235
256235
  line: 492,
256236
- column: 72
256236
+ column: 78
256237
256237
  }
256238
256238
  }, {
256239
256239
  start: {
@@ -256288,7 +256288,7 @@ var cov_d7x60homs = function () {
256288
256288
  },
256289
256289
  end: {
256290
256290
  line: 492,
256291
- column: 71
256291
+ column: 77
256292
256292
  }
256293
256293
  },
256294
256294
  type: "binary-expr",
@@ -256299,16 +256299,16 @@ var cov_d7x60homs = function () {
256299
256299
  },
256300
256300
  end: {
256301
256301
  line: 492,
256302
- column: 45
256302
+ column: 48
256303
256303
  }
256304
256304
  }, {
256305
256305
  start: {
256306
256306
  line: 492,
256307
- column: 49
256307
+ column: 52
256308
256308
  },
256309
256309
  end: {
256310
256310
  line: 492,
256311
- column: 71
256311
+ column: 77
256312
256312
  }
256313
256313
  }],
256314
256314
  line: 492
@@ -256849,10 +256849,10 @@ var cov_d7x60homs = function () {
256849
256849
  mappings: ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiRA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA",
256850
256850
  file: "ResultsGallery.vue",
256851
256851
  sourceRoot: "src/components/analyses",
256852
- sourcesContent: ["<template>\n <v-dialog\n v-hotkey=\"keymap\"\n v-if=\"showDialog\"\n v-model=\"showDialog\"\n :fullscreen=\"isResultModalFullScreen\"\n scrollable\n class=\"gallery-dialog\"\n >\n <!-- style=\"transition: 0.3s\" -->\n <v-card flat class=\"gallery-card\">\n <v-card-title class=\"pa-2\">\n <v-row>\n <v-col cols=\"6\">\n <h3 class=\"font-weight-thin primary--text\">\n {{ result.verboseName }}\n </h3>\n </v-col>\n\n <v-col cols=\"6\" class=\"d-flex justify-end align-center panel-slot-actions\">\n\n <!-- toggle raw text -->\n <v-tooltip\n v-if=\"result.frontendType === 'tsv-file' && tsvHeaders.length\"\n top\n >\n <template v-slot:activator=\"{ on }\">\n <v-icon\n class=\"primary--text ml-4\"\n small\n @click=\"displayRawText = !displayRawText\"\n v-on=\"on\"\n >\n {{ displayRawText ? 'table_chart' : 'text_fields' }}\n </v-icon>\n </template>\n {{ displayRawText ? 'Display Tabulated Content' : 'Display Raw Content' }}\n </v-tooltip>\n\n <!-- download result -->\n <v-tooltip\n v-if=\"result.download\"\n top\n >\n <template v-slot:activator=\"{ on }\">\n <v-icon\n class=\"primary--text ml-4\"\n small\n @click=\"result.download\"\n v-on=\"on\"\n >\n {{ result.downloadIcon }}\n </v-icon>\n </template>\n {{ result.downloadIcon === 'open_in_new' ? 'Open in new tab' : 'Download Result' }}\n </v-tooltip>\n\n <!-- copy to clipboard -->\n <v-tooltip\n v-if=\"result.copyToClipBoard\"\n top\n >\n <template v-slot:activator=\"{ on }\">\n <v-icon\n class=\"primary--text ml-4\"\n small\n @click=\"result.copyToClipBoard\"\n v-on=\"on\"\n >\n file_copy\n </v-icon>\n </template>\n Copy to clipboard!\n </v-tooltip>\n\n <!-- external link -->\n <v-tooltip\n v-if=\"result.openExternalLink\"\n top\n >\n <template v-slot:activator=\"{ on }\">\n <v-icon\n class=\"primary--text ml-4\"\n small\n @click=\"result.openExternalLink\"\n v-on=\"on\"\n >\n info\n </v-icon>\n </template>\n Learn more!\n </v-tooltip>\n\n <!-- show from bottom -->\n <v-tooltip\n v-if=\"['raw'].includes(resultType)\"\n top\n >\n <template v-slot:activator=\"{ on }\">\n <v-icon\n class=\"primary--text ml-4\"\n small\n @click=\"reverseContent\"\n v-on=\"on\"\n >\n {{ streamOptions.reverse ? 'vertical_align_bottom' : 'vertical_align_top'}}\n </v-icon>\n </template>\n <span>\n Show content from {{ streamOptions.reverse ? 'top' : 'bottom' }}\n </span>\n </v-tooltip>\n\n <!-- show fullscreen -->\n <v-tooltip\n top\n >\n <template v-slot:activator=\"{ on }\">\n <v-icon\n class=\"primary--text ml-4\"\n small\n @click=\"toggleFullScreen\"\n v-on=\"on\"\n >\n <!-- @click=\"(isFullScreen = !isFullScreen)\" -->\n {{ isResultModalFullScreen ? 'fullscreen_exit' : 'fullscreen'}}\n </v-icon>\n </template>\n <span>\n Toggle Full Screen\n </span>\n </v-tooltip>\n\n <!-- close dialog -->\n <v-tooltip top>\n <template v-slot:activator=\"{ on }\">\n <v-icon\n class=\"primary--text ml-4\"\n small\n @click=\"hideDialog\"\n v-on=\"on\"\n >\n close\n </v-icon>\n </template>\n Close file\n </v-tooltip>\n </v-col>\n </v-row>\n </v-card-title>\n\n <v-row class=\"pa-0 ma-0 gallery-content\" style=\"height: 85vh\">\n <v-progress-linear\n v-if=\"loading\"\n class=\"panel-loader\"\n indeterminate\n height=\"2\"\n color=\"primary\"\n />\n\n <pre\n v-highlightjs=\"content\"\n v-if=\"resultType === 'raw' && resultKey === 'command_script'\"\n ref=\"raw\"\n class=\"v-markup pre-wrap\"\n >\n <code class=\"bash\" />\n </pre>\n\n <code\n v-else-if=\"isAnsi\"\n ref=\"raw\"\n class=\"v-markup\"\n >\n <pre v-html=\"content\" />\n </code>\n\n <code\n v-else-if=\"resultType === 'raw'\"\n ref=\"raw\"\n class=\"v-markup\"\n >\n <pre>\n {{ content }}\n <span v-if=\"content && !streamOptions.end\" class=\"stream-message grey--text lighten-2\">\n ... Loading more rows\n </span>\n <span v-if=\"!loading && streamOptions.end\" class=\"stream-message grey--text\">\n *** End of file. ***\n </span>\n </pre>\n </code>\n\n <div\n v-else-if=\"resultType === 'table'\"\n ref=\"table\"\n class=\"tsv-table\"\n >\n <v-data-table\n :headers=\"tsvHeaders\"\n :items=\"tsvItems\"\n :items-per-page=\"tsvItems.length\"\n hide-default-footer\n multi-sort\n fixed-header\n dense\n >\n <template v-slot:body=\"{ items }\">\n <tbody>\n <tr\n v-for=\"item in items\"\n :key=\"item.name\"\n >\n <td\n v-for=\"(header, index) in tsvHeaders\"\n :key=\"index + header.value\"\n :class=\"[header.value === 'index' ? 'line-column text' : '']\"\n v-html=\"parseTsvCell($get(item, header.value))\"\n />\n </tr>\n </tbody>\n </template>\n </v-data-table>\n\n <div class=\"text-center stream-message\">\n <span v-if=\"content && !streamOptions.end\">... Loading more rows</span>\n <span v-if=\"!loading && streamOptions.end\">End of file.</span>\n </div>\n </div>\n\n <img\n v-else-if=\"resultType === 'image'\"\n :src=\"$store.getters.getBlobUrl(result.downloadUrl)\"\n :alt=\"result.verboseName\"\n class=\"results-image\"\n >\n\n <iframe\n v-else-if=\"['html', 'pdf'].includes(resultType)\"\n :src=\"$store.getters.getBlobUrl(result.downloadUrl, '')\"\n frameborder=\"0\"\n class=\"results-iframe\"\n style=\"width: 100%; height: 100%; min-height: 80vh;\"\n />\n\n <iframe\n v-else-if=\"resultType === 'igv_bam'\"\n :src=\"result.downloadUrl\"\n frameborder=\"0\"\n class=\"results-iframe\"\n style=\"width: 100%; height: 100%; min-height: 80vh;\"\n />\n\n <code\n v-else-if=\"resultType === 'number'\"\n class=\"v-markup\"\n >\n <span size=\"100px\">{{ result.value | round(3) }}</span>\n </code>\n\n <code\n v-else-if=\"['string', 'url-link'].includes(resultType)\"\n class=\"v-markup\"\n >\n <span size=\"100px\">{{ result.value }}</span>\n </code>\n </v-row>\n\n </v-card>\n </v-dialog>\n</template>\n\n<script>\nimport { loadResult } from '@/utils/api'\nimport { HIDE_PANEL, SHOW_PANEL } from '@/store/actions/panels'\nimport { TOGGLE_RESULT_FULLSCREEN } from '@/store/actions/user'\nimport { GET_FILE } from '@/store/actions/files'\nimport { mapGetters } from 'vuex'\nimport detect from 'detect-csv'\n\nexport default {\n name: 'ResultsGallery',\n props: {\n resultIndex: {\n type: Number,\n },\n results: {\n type: Array,\n },\n },\n data() {\n return {\n content: '',\n displayRawText: false,\n isBottomScrolled: false,\n loading: false,\n showDialog: false,\n tsvItems: [],\n tsvHeaders: [],\n streamOptions: {\n lines: 200,\n offset: 0,\n reverse: false,\n previous: '',\n end: false,\n },\n }\n },\n computed: {\n ...mapGetters(['resultKey', 'isResultModalFullScreen']),\n keymap() {\n return {\n right: this.increaseIndex,\n left: this.decreaseIndex,\n esc: this.hideDialog,\n }\n },\n result() {\n return this.results[this.resultIndex] || {}\n },\n isAnsi() {\n return this.result && this.result.frontendType === 'ansi'\n },\n resultType() {\n if (this.result.frontendType === 'ansi') {\n return 'raw'\n } else if (this.result.frontendType === 'text-file') {\n return 'raw'\n } else if (this.result.frontendType === 'tsv-file') {\n if (\n !this.displayRawText &&\n this.tsvItems.length &&\n this.tsvHeaders.length\n ) {\n return 'table'\n } else {\n return 'raw'\n }\n } else {\n return this.result.frontendType\n }\n },\n },\n watch: {\n showDialog(value) {\n if (!value) {\n this.$store.dispatch(HIDE_PANEL, 'result')\n this.$emit('display-result', null)\n }\n },\n resultIndex(value) {\n this.showDialog = Number.isInteger(value) && value < this.results.length\n if (this.showDialog && ['raw', 'table'].includes(this.resultType)) {\n this.resetStreamEvents()\n this.loadFileContents()\n }\n },\n resultKey: {\n handler() {\n this.updateResultIndex()\n },\n immediate: true,\n },\n result(value) {\n if (value && value.resultKey) {\n this.$store.dispatch(SHOW_PANEL, {\n result: value.resultKey,\n })\n if (['html', 'pdf'].includes(value.frontendType)) {\n this.$store.dispatch(GET_FILE, value.downloadUrl)\n }\n // Track result viewed\n window.analytics.track('Result Previewed', value.analytics)\n }\n this.updateResultIndex()\n },\n isBottomScrolled(value) {\n if (value) {\n this.loadFileContents()\n }\n },\n displayRawText() {\n this.addScrollListener()\n },\n },\n methods: {\n resetStreamEvents() {\n // Reset content\n this.content = ''\n this.streamOptions = {\n lines: 200,\n offset: 0,\n reverse: false,\n previous: '',\n end: false,\n }\n // Remove listeners\n this.$nextTick(() => {\n if (this.$refs.raw) {\n this.$refs.raw.removeEventListener('scroll', this.isBottomVisible)\n }\n if (this.$refs.table) {\n this.$refs.table.removeEventListener('scroll', this.isBottomVisible)\n }\n })\n },\n addScrollListener() {\n this.$nextTick(() => {\n this.isBottomVisible()\n if (!this.isBottomScrolled) {\n if (this.$refs.raw) {\n this.$refs.raw.addEventListener('scroll', this.isBottomVisible)\n }\n if (this.$refs.table) {\n this.$refs.table.addEventListener('scroll', this.isBottomVisible)\n }\n }\n })\n },\n updateResultIndex() {\n if (this.resultKey !== this.result.resultKey) {\n this.results.forEach((result, index) => {\n if (result.resultKey === this.resultKey) {\n this.showDialog = true\n this.$emit('display-result', index)\n }\n })\n }\n },\n hideDialog() {\n if (this.showDialog) {\n this.showDialog = false\n }\n },\n increaseIndex() {\n if (this.showDialog) {\n if (this.resultIndex < this.results.length - 1) {\n this.$emit('display-result', this.resultIndex + 1)\n this.content = ''\n } else {\n this.$emit('display-result', 0)\n this.content = ''\n }\n }\n },\n decreaseIndex() {\n if (this.showDialog) {\n if (this.resultIndex > 0) {\n this.$emit('display-result', this.resultIndex - 1)\n } else {\n this.$emit('display-result', this.results.length - 1)\n }\n }\n },\n async loadFileContents() {\n const { lines, offset, reverse, end, previous } = this.streamOptions\n if (!end) {\n this.loading = true\n let content = await loadResult(\n this.result.analysis.pk,\n this.result.resultKey,\n lines,\n offset,\n reverse\n )\n this.loading = false\n\n if (content && previous !== content) {\n this.streamOptions.previous = content\n this.streamOptions.offset += lines\n this.content += content\n this.setTsvItems(this.content)\n this.addScrollListener()\n } else {\n this.streamOptions.end = true\n this.setTsvItems(this.content)\n }\n\n }\n },\n setTsvItems(content) {\n const isTsv = this.result.frontendType === 'tsv-file'\n const detectHandler = (\n detect(content.slice(0, 1000)) || { newline: '\\n', delimiter: '\\t'}\n )\n\n // csv to list of lists\n const dataRows =\n content && isTsv\n ? this.csvToArray(content.trim(), detectHandler.delimiter)\n .filter((row) => row)\n .filter((row) => row[0] !== '#' || row.includes('#CHROM'))\n : []\n\n // Headers\n this.tsvHeaders = dataRows.length\n ? dataRows[0].map((header, index) => {\n return {\n text: header,\n value: `index${index}`,\n sortable: this.streamOptions.end,\n }\n })\n : []\n\n this.tsvHeaders.unshift({\n text: 1,\n value: 'index',\n sortable: false,\n align: 'end',\n class: 'line-column text'\n })\n\n // Content rows\n this.tsvItems = dataRows.length\n ? dataRows.slice(1)\n .map((row, index) => {\n let dictRow = { index: index + 2 }\n row.forEach((value, index) => {\n dictRow[`index${index}`] = value\n })\n return dictRow\n })\n : []\n\n if (!isTsv || (isTsv && !this.tsvHeaders.length)) {\n this.displayRawText = true\n }\n },\n reverseContent() {\n const reverse = this.streamOptions.reverse\n this.resetStreamEvents()\n this.streamOptions.reverse = !reverse\n this.loadFileContents()\n },\n isBottomVisible() {\n const divElement = this.$refs[this.resultType]\n const scrollTop = divElement.scrollTop\n const visible = divElement.clientHeight\n const pageHeight = divElement.scrollHeight\n const bottomOfPage = visible + scrollTop >= pageHeight\n this.isBottomScrolled = bottomOfPage || pageHeight < visible\n },\n toggleFullScreen() {\n this.$store.dispatch(TOGGLE_RESULT_FULLSCREEN, {\n value: !this.isResultModalFullScreen\n })\n },\n parseTsvCell(value) {\n let pattern = /^((http|https|ftp):\\/\\/)/\n if (pattern.test(value)) {\n return `<a href=\"${value}\" target=\"_blank\">Open link</a>`\n } else {\n return value\n }\n },\n\n csvToArray(strData, strDelimiter=\",\") {\n // See: https://stackoverflow.com/a/1293163/3949081\n const objPattern = new RegExp(\n (\n // Delimiters.\n \"(\\\\\" + strDelimiter + \"|\\\\r?\\\\n|\\\\r|^)\" +\n // Quoted fields.\n \"(?:\\\"([^\\\"]*(?:\\\"\\\"[^\\\"]*)*)\\\"|\" +\n // Standard fields.\n \"([^\\\"\\\\\" + strDelimiter + \"\\\\r\\\\n]*))\"\n ),\n \"gi\"\n )\n const arrData = [[]]\n let arrMatches = null\n\n while (arrMatches = objPattern.exec(strData)) {\n // If not delimiter, an end of row has been reached\n const strMatchedDelimiter = arrMatches[1]\n if (\n strMatchedDelimiter.length &&\n strMatchedDelimiter !== strDelimiter\n ){\n arrData.push([])\n }\n\n let strMatchedValue\n if (arrMatches[2]){\n // Quoted value, Unescape any double quotes.\n strMatchedValue = arrMatches[2].replace(\n new RegExp( \"\\\"\\\"\", \"g\" ),\n \"\\\"\"\n )\n } else {\n // Non-quoted value.\n strMatchedValue = arrMatches[3]\n }\n arrData[arrData.length - 1].push(strMatchedValue)\n }\n return arrData\n }\n },\n}\n</script>\n\n<style lang=\"scss\">\n.tsv-table {\n max-width: 100%;\n max-height: 100%;\n margin: 0px auto;\n overflow: auto;\n}\n.v-dialog {\n overflow-y: visible !important; // truly important for iframe performance, dont remove!\n &:not(.v-dialog--fullscreen) {\n max-height: 100% !important;\n max-width: calc(100% - 48px);\n }\n iframe.results-iframe {\n height: 100% !important;\n }\n &.gallery-dialog {\n height: calc(100vh - 60px);\n }\n}\n.results-image {\n max-width: 100%;\n max-height: 85vh;\n margin: auto;\n max-height: 100%;\n width: auto;\n}\n.gallery-card {\n ::-webkit-scrollbar {\n width: 0px;\n height: 0px;\n }\n}\n.v-application .gallery-card {\n .gallery-content {\n background: var(--v-background-base);\n }\n\n pre {\n white-space: pre-line;\n &.pre-wrap {\n white-space: pre-wrap;\n }\n code {\n font-size: 12px;\n font-weight: 600;\n }\n }\n\n &.theme--dark code {\n background-color: rgba(0,0,0,.05);\n }\n\n code.v-markup,\n pre.v-markup {\n padding: 10px;\n width: 100%;\n height: 100%;\n text-align: start;\n box-shadow: none;\n text-shadow: none;\n display: flex;\n position: relative;\n overflow-x: auto;\n overflow-y: auto;\n margin: 0px;\n top: 0px;\n color: rgb(255, 255, 255);\n border-radius: 0px;\n background: rgb(0, 0, 0);\n font-size: 12px;\n font-weight: 600;\n\n code.hljs {\n font-size: 12px;\n padding: 0px;\n\n .hljs-string,\n .hljs-bullet,\n .hljs-subst,\n .hljs-title,\n .hljs-section,\n .hljs-emphasis,\n .hljs-type,\n .hljs-built_in,\n .hljs-builtin-name,\n .hljs-selector-attr,\n .hljs-selector-pseudo,\n .hljs-addition,\n .hljs-variable,\n .hljs-template-tag,\n .hljs-template-variable {\n color: var(--v-warning-base);\n }\n }\n }\n\n .tsv-table {\n border: thin solid rgba(0,0,0,.12);\n margin-left: 0;\n\n .theme--dark {\n thead tr th {\n background: var(--v-surface-darken1);\n span {\n color: #9e9e9e;\n }\n }\n tbody tr td {\n background: var(--v-background-base);\n span {\n color: #9e9e9e;\n }\n }\n }\n\n thead tr {\n th {\n background: var(--v-background-base);\n max-width: 250px;\n white-space: nowrap;\n span {\n font-size: 12px;\n font-family: Roboto;\n color: #333333;\n }\n }\n }\n tbody tr {\n td {\n font-size: 12px;\n font-family: Roboto;\n overflow-x: auto;\n white-space: nowrap;\n max-width: 250px;\n }\n }\n\n thead tr th, tbody tr td {\n &.line-column {\n background: var(--v-surface-darken1);\n width: 50px;\n text-align: right;\n font-weight: normal;\n // Make line-column sticky\n position: sticky !important;\n left: 0;\n z-index: 9998;\n }\n }\n\n thead tr th .line-column {\n z-index: 9999;\n }\n }\n\n\n .stream-message {\n font-size: 12px;\n font-style: italic;\n margin: 20px 0px;\n }\n}\n</style>\n"]
256852
+ sourcesContent: ["<template>\n <v-dialog\n v-hotkey=\"keymap\"\n v-if=\"showDialog\"\n v-model=\"showDialog\"\n :fullscreen=\"isResultModalFullScreen\"\n scrollable\n class=\"gallery-dialog\"\n >\n <!-- style=\"transition: 0.3s\" -->\n <v-card flat class=\"gallery-card\">\n <v-card-title class=\"pa-2\">\n <v-row>\n <v-col cols=\"6\">\n <h3 class=\"font-weight-thin primary--text\">\n {{ result.verboseName }}\n </h3>\n </v-col>\n\n <v-col cols=\"6\" class=\"d-flex justify-end align-center panel-slot-actions\">\n\n <!-- toggle raw text -->\n <v-tooltip\n v-if=\"result.frontendType === 'tsv-file' && tsvHeaders.length\"\n top\n >\n <template v-slot:activator=\"{ on }\">\n <v-icon\n class=\"primary--text ml-4\"\n small\n @click=\"displayRawText = !displayRawText\"\n v-on=\"on\"\n >\n {{ displayRawText ? 'table_chart' : 'text_fields' }}\n </v-icon>\n </template>\n {{ displayRawText ? 'Display Tabulated Content' : 'Display Raw Content' }}\n </v-tooltip>\n\n <!-- download result -->\n <v-tooltip\n v-if=\"result.download\"\n top\n >\n <template v-slot:activator=\"{ on }\">\n <v-icon\n class=\"primary--text ml-4\"\n small\n @click=\"result.download\"\n v-on=\"on\"\n >\n {{ result.downloadIcon }}\n </v-icon>\n </template>\n {{ result.downloadIcon === 'open_in_new' ? 'Open in new tab' : 'Download Result' }}\n </v-tooltip>\n\n <!-- copy to clipboard -->\n <v-tooltip\n v-if=\"result.copyToClipBoard\"\n top\n >\n <template v-slot:activator=\"{ on }\">\n <v-icon\n class=\"primary--text ml-4\"\n small\n @click=\"result.copyToClipBoard\"\n v-on=\"on\"\n >\n file_copy\n </v-icon>\n </template>\n Copy to clipboard!\n </v-tooltip>\n\n <!-- external link -->\n <v-tooltip\n v-if=\"result.openExternalLink\"\n top\n >\n <template v-slot:activator=\"{ on }\">\n <v-icon\n class=\"primary--text ml-4\"\n small\n @click=\"result.openExternalLink\"\n v-on=\"on\"\n >\n info\n </v-icon>\n </template>\n Learn more!\n </v-tooltip>\n\n <!-- show from bottom -->\n <v-tooltip\n v-if=\"['raw'].includes(resultType)\"\n top\n >\n <template v-slot:activator=\"{ on }\">\n <v-icon\n class=\"primary--text ml-4\"\n small\n @click=\"reverseContent\"\n v-on=\"on\"\n >\n {{ streamOptions.reverse ? 'vertical_align_bottom' : 'vertical_align_top'}}\n </v-icon>\n </template>\n <span>\n Show content from {{ streamOptions.reverse ? 'top' : 'bottom' }}\n </span>\n </v-tooltip>\n\n <!-- show fullscreen -->\n <v-tooltip\n top\n >\n <template v-slot:activator=\"{ on }\">\n <v-icon\n class=\"primary--text ml-4\"\n small\n @click=\"toggleFullScreen\"\n v-on=\"on\"\n >\n <!-- @click=\"(isFullScreen = !isFullScreen)\" -->\n {{ isResultModalFullScreen ? 'fullscreen_exit' : 'fullscreen'}}\n </v-icon>\n </template>\n <span>\n Toggle Full Screen\n </span>\n </v-tooltip>\n\n <!-- close dialog -->\n <v-tooltip top>\n <template v-slot:activator=\"{ on }\">\n <v-icon\n class=\"primary--text ml-4\"\n small\n @click=\"hideDialog\"\n v-on=\"on\"\n >\n close\n </v-icon>\n </template>\n Close file\n </v-tooltip>\n </v-col>\n </v-row>\n </v-card-title>\n\n <v-row class=\"pa-0 ma-0 gallery-content\" style=\"height: 85vh\">\n <v-progress-linear\n v-if=\"loading\"\n class=\"panel-loader\"\n indeterminate\n height=\"2\"\n color=\"primary\"\n />\n\n <pre\n v-highlightjs=\"content\"\n v-if=\"resultType === 'raw' && resultKey === 'command_script'\"\n ref=\"raw\"\n class=\"v-markup pre-wrap\"\n >\n <code class=\"bash\" />\n </pre>\n\n <code\n v-else-if=\"isAnsi\"\n ref=\"raw\"\n class=\"v-markup\"\n >\n <pre v-html=\"content\" />\n </code>\n\n <code\n v-else-if=\"resultType === 'raw'\"\n ref=\"raw\"\n class=\"v-markup\"\n >\n <pre>\n {{ content }}\n <span v-if=\"content && !streamOptions.end\" class=\"stream-message grey--text lighten-2\">\n ... Loading more rows\n </span>\n <span v-if=\"!loading && streamOptions.end\" class=\"stream-message grey--text\">\n *** End of file. ***\n </span>\n </pre>\n </code>\n\n <div\n v-else-if=\"resultType === 'table'\"\n ref=\"table\"\n class=\"tsv-table\"\n >\n <v-data-table\n :headers=\"tsvHeaders\"\n :items=\"tsvItems\"\n :items-per-page=\"tsvItems.length\"\n hide-default-footer\n multi-sort\n fixed-header\n dense\n >\n <template v-slot:body=\"{ items }\">\n <tbody>\n <tr\n v-for=\"item in items\"\n :key=\"item.name\"\n >\n <td\n v-for=\"(header, index) in tsvHeaders\"\n :key=\"index + header.value\"\n :class=\"[header.value === 'index' ? 'line-column text' : '']\"\n v-html=\"parseTsvCell($get(item, header.value))\"\n />\n </tr>\n </tbody>\n </template>\n </v-data-table>\n\n <div class=\"text-center stream-message\">\n <span v-if=\"content && !streamOptions.end\">... Loading more rows</span>\n <span v-if=\"!loading && streamOptions.end\">End of file.</span>\n </div>\n </div>\n\n <img\n v-else-if=\"resultType === 'image'\"\n :src=\"$store.getters.getBlobUrl(result.downloadUrl)\"\n :alt=\"result.verboseName\"\n class=\"results-image\"\n >\n\n <iframe\n v-else-if=\"['html', 'pdf'].includes(resultType)\"\n :src=\"$store.getters.getBlobUrl(result.downloadUrl, '')\"\n frameborder=\"0\"\n class=\"results-iframe\"\n style=\"width: 100%; height: 100%; min-height: 80vh;\"\n />\n\n <iframe\n v-else-if=\"resultType === 'igv_bam'\"\n :src=\"result.downloadUrl\"\n frameborder=\"0\"\n class=\"results-iframe\"\n style=\"width: 100%; height: 100%; min-height: 80vh;\"\n />\n\n <code\n v-else-if=\"resultType === 'number'\"\n class=\"v-markup\"\n >\n <span size=\"100px\">{{ result.value | round(3) }}</span>\n </code>\n\n <code\n v-else-if=\"['string', 'url-link'].includes(resultType)\"\n class=\"v-markup\"\n >\n <span size=\"100px\">{{ result.value }}</span>\n </code>\n </v-row>\n\n </v-card>\n </v-dialog>\n</template>\n\n<script>\nimport { loadResult } from '@/utils/api'\nimport { HIDE_PANEL, SHOW_PANEL } from '@/store/actions/panels'\nimport { TOGGLE_RESULT_FULLSCREEN } from '@/store/actions/user'\nimport { GET_FILE } from '@/store/actions/files'\nimport { mapGetters } from 'vuex'\nimport detect from 'detect-csv'\n\nexport default {\n name: 'ResultsGallery',\n props: {\n resultIndex: {\n type: Number,\n },\n results: {\n type: Array,\n },\n },\n data() {\n return {\n content: '',\n displayRawText: false,\n isBottomScrolled: false,\n loading: false,\n showDialog: false,\n tsvItems: [],\n tsvHeaders: [],\n streamOptions: {\n lines: 200,\n offset: 0,\n reverse: false,\n previous: '',\n end: false,\n },\n }\n },\n computed: {\n ...mapGetters(['resultKey', 'isResultModalFullScreen']),\n keymap() {\n return {\n right: this.increaseIndex,\n left: this.decreaseIndex,\n esc: this.hideDialog,\n }\n },\n result() {\n return this.results[this.resultIndex] || {}\n },\n isAnsi() {\n return this.result && this.result.frontendType === 'ansi'\n },\n resultType() {\n if (this.result.frontendType === 'ansi') {\n return 'raw'\n } else if (this.result.frontendType === 'text-file') {\n return 'raw'\n } else if (this.result.frontendType === 'tsv-file') {\n if (\n !this.displayRawText &&\n this.tsvItems.length &&\n this.tsvHeaders.length\n ) {\n return 'table'\n } else {\n return 'raw'\n }\n } else {\n return this.result.frontendType\n }\n },\n },\n watch: {\n showDialog(value) {\n if (!value) {\n this.$store.dispatch(HIDE_PANEL, 'result')\n this.$emit('display-result', null)\n }\n },\n resultIndex(value) {\n this.showDialog = Number.isInteger(value) && value < this.results.length\n if (this.showDialog && ['raw', 'table'].includes(this.resultType)) {\n this.resetStreamEvents()\n this.loadFileContents()\n }\n },\n resultKey: {\n handler() {\n this.updateResultIndex()\n },\n immediate: true,\n },\n result(value) {\n if (value && value.resultKey) {\n this.$store.dispatch(SHOW_PANEL, {\n result: value.resultKey,\n })\n if (['html', 'pdf'].includes(value.frontendType)) {\n this.$store.dispatch(GET_FILE, value.downloadUrl)\n }\n // Track result viewed\n window.analytics.track('Result Previewed', value.analytics)\n }\n this.updateResultIndex()\n },\n isBottomScrolled(value) {\n if (value) {\n this.loadFileContents()\n }\n },\n displayRawText() {\n this.addScrollListener()\n },\n },\n methods: {\n resetStreamEvents() {\n // Reset content\n this.content = ''\n this.streamOptions = {\n lines: 200,\n offset: 0,\n reverse: false,\n previous: '',\n end: false,\n }\n // Remove listeners\n this.$nextTick(() => {\n if (this.$refs.raw) {\n this.$refs.raw.removeEventListener('scroll', this.isBottomVisible)\n }\n if (this.$refs.table) {\n this.$refs.table.removeEventListener('scroll', this.isBottomVisible)\n }\n })\n },\n addScrollListener() {\n this.$nextTick(() => {\n this.isBottomVisible()\n if (!this.isBottomScrolled) {\n if (this.$refs.raw) {\n this.$refs.raw.addEventListener('scroll', this.isBottomVisible)\n }\n if (this.$refs.table) {\n this.$refs.table.addEventListener('scroll', this.isBottomVisible)\n }\n }\n })\n },\n updateResultIndex() {\n if (this.resultKey !== this.result.resultKey) {\n this.results.forEach((result, index) => {\n if (result.resultKey === this.resultKey) {\n this.showDialog = true\n this.$emit('display-result', index)\n }\n })\n }\n },\n hideDialog() {\n if (this.showDialog) {\n this.showDialog = false\n }\n },\n increaseIndex() {\n if (this.showDialog) {\n if (this.resultIndex < this.results.length - 1) {\n this.$emit('display-result', this.resultIndex + 1)\n this.content = ''\n } else {\n this.$emit('display-result', 0)\n this.content = ''\n }\n }\n },\n decreaseIndex() {\n if (this.showDialog) {\n if (this.resultIndex > 0) {\n this.$emit('display-result', this.resultIndex - 1)\n } else {\n this.$emit('display-result', this.results.length - 1)\n }\n }\n },\n async loadFileContents() {\n const { lines, offset, reverse, end, previous } = this.streamOptions\n if (!end) {\n this.loading = true\n let content = await loadResult(\n this.result.analysis.pk,\n this.result.resultKey,\n lines,\n offset,\n reverse\n )\n this.loading = false\n\n if (content && previous !== content) {\n this.streamOptions.previous = content\n this.streamOptions.offset += lines\n this.content += content\n this.setTsvItems(this.content)\n this.addScrollListener()\n } else {\n this.streamOptions.end = true\n this.setTsvItems(this.content)\n }\n\n }\n },\n setTsvItems(content) {\n const isTsv = this.result.frontendType === 'tsv-file'\n const detectHandler = (\n detect(content.slice(0, 1000)) || { newline: '\\n', delimiter: '\\t'}\n )\n\n // csv to list of lists\n const dataRows =\n content && isTsv\n ? this.csvToArray(content.trim(), detectHandler.delimiter)\n .filter((row) => row.length)\n .filter((row) => row[0][0] !== '#' || row[0].includes('#CHROM'))\n : []\n\n // Headers\n this.tsvHeaders = dataRows.length\n ? dataRows[0].map((header, index) => {\n return {\n text: header,\n value: `index${index}`,\n sortable: this.streamOptions.end,\n }\n })\n : []\n\n this.tsvHeaders.unshift({\n text: 1,\n value: 'index',\n sortable: false,\n align: 'end',\n class: 'line-column text'\n })\n\n // Content rows\n this.tsvItems = dataRows.length\n ? dataRows.slice(1)\n .map((row, index) => {\n let dictRow = { index: index + 2 }\n row.forEach((value, index) => {\n dictRow[`index${index}`] = value\n })\n return dictRow\n })\n : []\n\n if (!isTsv || (isTsv && !this.tsvHeaders.length)) {\n this.displayRawText = true\n }\n },\n reverseContent() {\n const reverse = this.streamOptions.reverse\n this.resetStreamEvents()\n this.streamOptions.reverse = !reverse\n this.loadFileContents()\n },\n isBottomVisible() {\n const divElement = this.$refs[this.resultType]\n const scrollTop = divElement.scrollTop\n const visible = divElement.clientHeight\n const pageHeight = divElement.scrollHeight\n const bottomOfPage = visible + scrollTop >= pageHeight\n this.isBottomScrolled = bottomOfPage || pageHeight < visible\n },\n toggleFullScreen() {\n this.$store.dispatch(TOGGLE_RESULT_FULLSCREEN, {\n value: !this.isResultModalFullScreen\n })\n },\n parseTsvCell(value) {\n let pattern = /^((http|https|ftp):\\/\\/)/\n if (pattern.test(value)) {\n return `<a href=\"${value}\" target=\"_blank\">Open link</a>`\n } else {\n return value\n }\n },\n\n csvToArray(strData, strDelimiter=\",\") {\n // See: https://stackoverflow.com/a/1293163/3949081\n const objPattern = new RegExp(\n (\n // Delimiters.\n \"(\\\\\" + strDelimiter + \"|\\\\r?\\\\n|\\\\r|^)\" +\n // Quoted fields.\n \"(?:\\\"([^\\\"]*(?:\\\"\\\"[^\\\"]*)*)\\\"|\" +\n // Standard fields.\n \"([^\\\"\\\\\" + strDelimiter + \"\\\\r\\\\n]*))\"\n ),\n \"gi\"\n )\n const arrData = [[]]\n let arrMatches = null\n\n while (arrMatches = objPattern.exec(strData)) {\n // If not delimiter, an end of row has been reached\n const strMatchedDelimiter = arrMatches[1]\n if (\n strMatchedDelimiter.length &&\n strMatchedDelimiter !== strDelimiter\n ){\n arrData.push([])\n }\n\n let strMatchedValue\n if (arrMatches[2]){\n // Quoted value, Unescape any double quotes.\n strMatchedValue = arrMatches[2].replace(\n new RegExp( \"\\\"\\\"\", \"g\" ),\n \"\\\"\"\n )\n } else {\n // Non-quoted value.\n strMatchedValue = arrMatches[3]\n }\n arrData[arrData.length - 1].push(strMatchedValue)\n }\n return arrData\n }\n },\n}\n</script>\n\n<style lang=\"scss\">\n.tsv-table {\n max-width: 100%;\n max-height: 100%;\n margin: 0px auto;\n overflow: auto;\n}\n.v-dialog {\n overflow-y: visible !important; // truly important for iframe performance, dont remove!\n &:not(.v-dialog--fullscreen) {\n max-height: 100% !important;\n max-width: calc(100% - 48px);\n }\n iframe.results-iframe {\n height: 100% !important;\n }\n &.gallery-dialog {\n height: calc(100vh - 60px);\n }\n}\n.results-image {\n max-width: 100%;\n max-height: 85vh;\n margin: auto;\n max-height: 100%;\n width: auto;\n}\n.gallery-card {\n ::-webkit-scrollbar {\n width: 0px;\n height: 0px;\n }\n}\n.v-application .gallery-card {\n .gallery-content {\n background: var(--v-background-base);\n }\n\n pre {\n white-space: pre-line;\n &.pre-wrap {\n white-space: pre-wrap;\n }\n code {\n font-size: 12px;\n font-weight: 600;\n }\n }\n\n &.theme--dark code {\n background-color: rgba(0,0,0,.05);\n }\n\n code.v-markup,\n pre.v-markup {\n padding: 10px;\n width: 100%;\n height: 100%;\n text-align: start;\n box-shadow: none;\n text-shadow: none;\n display: flex;\n position: relative;\n overflow-x: auto;\n overflow-y: auto;\n margin: 0px;\n top: 0px;\n color: rgb(255, 255, 255);\n border-radius: 0px;\n background: rgb(0, 0, 0);\n font-size: 12px;\n font-weight: 600;\n\n code.hljs {\n font-size: 12px;\n padding: 0px;\n\n .hljs-string,\n .hljs-bullet,\n .hljs-subst,\n .hljs-title,\n .hljs-section,\n .hljs-emphasis,\n .hljs-type,\n .hljs-built_in,\n .hljs-builtin-name,\n .hljs-selector-attr,\n .hljs-selector-pseudo,\n .hljs-addition,\n .hljs-variable,\n .hljs-template-tag,\n .hljs-template-variable {\n color: var(--v-warning-base);\n }\n }\n }\n\n .tsv-table {\n border: thin solid rgba(0,0,0,.12);\n margin-left: 0;\n\n .theme--dark {\n thead tr th {\n background: var(--v-surface-darken1);\n span {\n color: #9e9e9e;\n }\n }\n tbody tr td {\n background: var(--v-background-base);\n span {\n color: #9e9e9e;\n }\n }\n }\n\n thead tr {\n th {\n background: var(--v-background-base);\n max-width: 250px;\n white-space: nowrap;\n span {\n font-size: 12px;\n font-family: Roboto;\n color: #333333;\n }\n }\n }\n tbody tr {\n td {\n font-size: 12px;\n font-family: Roboto;\n overflow-x: auto;\n white-space: nowrap;\n max-width: 250px;\n }\n }\n\n thead tr th, tbody tr td {\n &.line-column {\n background: var(--v-surface-darken1);\n width: 50px;\n text-align: right;\n font-weight: normal;\n // Make line-column sticky\n position: sticky !important;\n left: 0;\n z-index: 9998;\n }\n }\n\n thead tr th .line-column {\n z-index: 9999;\n }\n }\n\n\n .stream-message {\n font-size: 12px;\n font-style: italic;\n margin: 20px 0px;\n }\n}\n</style>\n"]
256853
256853
  },
256854
256854
  _coverageSchema: "43e27e138ebf9cfc5966b082cf9a028302ed4184",
256855
- hash: "22f2dbdd387a60567b7780d3d04bdf3435e3d749"
256855
+ hash: "fe09864f43de2d4dae5821f382f528b5c4317436"
256856
256856
  };
256857
256857
  var coverage = global[gcv] || (global[gcv] = {});
256858
256858
 
@@ -257585,11 +257585,11 @@ function ResultsGalleryvue_type_script_lang_js_defineProperty(obj, key, value) {
257585
257585
  var dataRows = (cov_d7x60homs.s[76]++, (cov_d7x60homs.b[32][0]++, content) && (cov_d7x60homs.b[32][1]++, isTsv) ? (cov_d7x60homs.b[31][0]++, this.csvToArray(content.trim(), detectHandler.delimiter).filter(function (row) {
257586
257586
  cov_d7x60homs.f[22]++;
257587
257587
  cov_d7x60homs.s[77]++;
257588
- return row;
257588
+ return row.length;
257589
257589
  }).filter(function (row) {
257590
257590
  cov_d7x60homs.f[23]++;
257591
257591
  cov_d7x60homs.s[78]++;
257592
- return (cov_d7x60homs.b[33][0]++, row[0] !== '#') || (cov_d7x60homs.b[33][1]++, row.includes('#CHROM'));
257592
+ return (cov_d7x60homs.b[33][0]++, row[0][0] !== '#') || (cov_d7x60homs.b[33][1]++, row[0].includes('#CHROM'));
257593
257593
  })) : (cov_d7x60homs.b[31][1]++, [])); // Headers
257594
257594
 
257595
257595
  cov_d7x60homs.s[79]++;
@@ -257743,8 +257743,8 @@ var ResultsGalleryvue_type_style_index_0_lang_scss_ = __webpack_require__("3942"
257743
257743
 
257744
257744
  var ResultsGallery_component = normalizeComponent(
257745
257745
  analyses_ResultsGalleryvue_type_script_lang_js_,
257746
- ResultsGalleryvue_type_template_id_6a3b6186_render,
257747
- ResultsGalleryvue_type_template_id_6a3b6186_staticRenderFns,
257746
+ ResultsGalleryvue_type_template_id_185780e8_render,
257747
+ ResultsGalleryvue_type_template_id_185780e8_staticRenderFns,
257748
257748
  false,
257749
257749
  null,
257750
257750
  null,
@@ -264731,12 +264731,12 @@ var ProjectPanelvue_type_template_id_7c1bc440_staticRenderFns = []
264731
264731
 
264732
264732
  // CONCATENATED MODULE: ./src/components/projects/ProjectPanel.vue?vue&type=template&id=7c1bc440&
264733
264733
 
264734
- // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"d220a428-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/dashboard/BaseDashboard.vue?vue&type=template&id=191c4226&
264735
- var BaseDashboardvue_type_template_id_191c4226_render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{ref:"dashboard"},[_c('v-card',{staticClass:"isabl-base-card",attrs:{"flat":""}},[_c('dc-graph',{attrs:{"model":_vm.model,"params":_vm.params,"items":_vm.items,"figures":_vm.figures,"loading":_vm.loading,"max-records":_vm.maxRecords,"total-items":_vm.totalItems,"is-embedded":_vm.isEmbedded},on:{"set-max-records":_vm.setMaxRecords,"set-options":_vm.setOptions}})],1)],1)}
264736
- var BaseDashboardvue_type_template_id_191c4226_staticRenderFns = []
264734
+ // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"d220a428-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/dashboard/BaseDashboard.vue?vue&type=template&id=2fd2fca6&
264735
+ var BaseDashboardvue_type_template_id_2fd2fca6_render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{ref:"dashboard"},[_c('v-card',{staticClass:"isabl-base-card",attrs:{"flat":""}},[_c('dc-graph',{attrs:{"model":_vm.model,"params":_vm.params,"items":_vm.items,"figures":_vm.figures,"loading":_vm.loading,"max-records":_vm.maxRecords,"total-items":_vm.totalItems,"is-embedded":_vm.isEmbedded},on:{"set-max-records":_vm.setMaxRecords,"set-options":_vm.setOptions}})],1)],1)}
264736
+ var BaseDashboardvue_type_template_id_2fd2fca6_staticRenderFns = []
264737
264737
 
264738
264738
 
264739
- // CONCATENATED MODULE: ./src/components/dashboard/BaseDashboard.vue?vue&type=template&id=191c4226&
264739
+ // CONCATENATED MODULE: ./src/components/dashboard/BaseDashboard.vue?vue&type=template&id=2fd2fca6&
264740
264740
 
264741
264741
  // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"d220a428-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/dashboard/DcGraph.vue?vue&type=template&id=7d68029b&
264742
264742
  var DcGraphvue_type_template_id_7d68029b_render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{ref:"main",class:['main-dc', !_vm.isEmbedded ? 'px-2' : '']},[_c('div',{directives:[{name:"show",rawName:"v-show",value:(_vm.loading),expression:"loading"}],staticClass:"overlay"},[_c('v-progress-circular',{staticClass:"loading",attrs:{"width":4,"size":40,"indeterminate":"","color":"primary"}}),_c('div',{staticClass:"mt-6"},[_vm._v("loading records...")])],1),_c('transition',{attrs:{"name":"slide-fade"}},[_c('data-table',{directives:[{name:"show",rawName:"v-show",value:(_vm.showPrefilterTable),expression:"showPrefilterTable"}],attrs:{"model":_vm.model,"params":_vm.params,"open-records-in-new-tab":!_vm.isEmbedded,"title":"Import Into dashboard","info-text":"Search and filter the records you may want to import into the interactive dashboard.","card-classes":"elevation-0 pa-0 mb-12"},on:{"fetch-data":_vm.setOptions}})],1),_c('div',{directives:[{name:"observe-visibility",rawName:"v-observe-visibility",value:(_vm.onVisible),expression:"onVisible"}],staticClass:"dashboard-graphs dc-data-count",attrs:{"id":("dc-data-count-" + _vm.uuid)}},[_c('v-container',{attrs:{"fluid":""}},[_c('v-row',{staticClass:"pb-6 justify-space-between"},[_c('v-col',{staticClass:"pa-0",attrs:{"cols":"4"}},[_c('h2',{staticClass:"isabl-section-title text-left"},[_vm._v(_vm._s(_vm._f("capitalize")(_vm.title || 'Interactive Dashboard')))])]),_c('v-col',{staticClass:"text-center pa-0",attrs:{"cols":"4"}},[_c('span',{staticClass:"filter-count primary--text"}),_vm._v(" selected out of\n "),_c('span',{staticClass:"total-count primary--text"}),(_vm.totalItems && _vm.totalItems > _vm.maxRecords)?_c('span',{staticClass:"total-items"},[_vm._v("\n /\n "),_c('span',{staticClass:"primary--text"},[_c('b',[_vm._v(_vm._s(_vm._f("formatNumber")(_vm.totalItems)))])])]):_vm._e(),_vm._v("\n records\n\n "),(_vm.totalItems && _vm.totalItems > _vm.maxRecords)?_c('v-tooltip',{attrs:{"top":""},scopedSlots:_vm._u([{key:"activator",fn:function(ref){
@@ -291366,7 +291366,7 @@ var DcGraph_component = normalizeComponent(
291366
291366
  // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/thread-loader/dist/cjs.js!./node_modules/@vue/cli-plugin-babel/node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/dashboard/BaseDashboard.vue?vue&type=script&lang=js&
291367
291367
  var cov_21s3whnekh = function () {
291368
291368
  var path = "/Users/juanes/papaemmelab/isabl_web/src/components/dashboard/BaseDashboard.vue";
291369
- var hash = "0b4078169eae16bef6c925f7ea045fdcabd12d44";
291369
+ var hash = "80378eaf505b52746695623a345007e2d0c5f9ea";
291370
291370
  var global = new Function("return this")();
291371
291371
  var gcv = "__coverage__";
291372
291372
  var coverageData = {
@@ -292258,10 +292258,10 @@ var cov_21s3whnekh = function () {
292258
292258
  mappings: ";;;;;;;;;;;;;;;;;;;;AAoBA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA",
292259
292259
  file: "BaseDashboard.vue",
292260
292260
  sourceRoot: "src/components/dashboard",
292261
- sourcesContent: ["<template>\n <div ref=\"dashboard\">\n <v-card flat class=\"isabl-base-card\">\n <dc-graph\n :model=\"model\"\n :params=\"params\"\n :items=\"items\"\n :figures=\"figures\"\n :loading=\"loading\"\n :max-records=\"maxRecords\"\n :total-items=\"totalItems\"\n :is-embedded=\"isEmbedded\"\n @set-max-records=\"setMaxRecords\"\n @set-options=\"setOptions\"\n />\n </v-card>\n </div>\n</template>\n\n<script>\nimport BasePanel from '@/components/base/BasePanel.vue'\nimport BaseSectionTitle from '@/components/base/BaseSectionTitle.vue'\nimport DataTable from '@/components/table/DataTable.vue'\nimport DcGraph from '@/components/dashboard/DcGraph.vue'\nimport { fetchListRecords } from '@/utils/api'\nimport { SHOW_ALERT } from '@/store/actions/alert'\n\nexport default {\n name: 'BaseDashboard',\n components: {\n BasePanel,\n BaseSectionTitle,\n DataTable,\n DcGraph\n },\n props: {\n params: {\n type: Object,\n default: () => ({})\n },\n isEmbedded: {\n type: Boolean,\n default: true\n },\n model: {\n type: String,\n required: true\n },\n figures: {\n type: Array,\n required: true\n },\n requiredFields: {\n type: Array,\n default: () => []\n }\n },\n data() {\n return {\n items: [],\n loading: false,\n maxRecords: 500,\n options: {},\n totalItems: null\n }\n },\n computed: {\n queryFields() {\n const fields = this.figures.map(figure => figure.queryFields)\n // flat and remove duplicates\n return [...new Set(fields.flat())]\n },\n fields() {\n return [\n ...new Set([\n 'pk',\n 'created',\n 'modified',\n 'analytics',\n ...this.requiredFields,\n ...this.queryFields\n ])\n ]\n .filter(i => !!i)\n .map(i => i.replace(/\\./g, '__'))\n }\n },\n created() {\n this.loading = true\n },\n methods: {\n setOptions(options) {\n delete options.offset\n this.options = options\n this.fetchData()\n },\n setMaxRecords(value) {\n if (value !== this.maxRecords) {\n this.maxRecords = value\n this.fetchData()\n }\n },\n fetchData() {\n this.loading = true\n\n const fields = this.fields.map(i => i.replace(/\\./g, '__'))\n fetchListRecords(this.model, {\n ...this.options,\n ...this.params,\n limit: this.maxRecords,\n distinct: true,\n fields,\n 'fields!': ''\n })\n .then(response => {\n this.items = response.results.map(r => {\n const result = {\n ...r,\n ...r.analytics\n }\n delete result.analytics\n return result\n })\n this.totalItems = response.count\n })\n .catch(error => {\n let errorMessage = `Search failed: `\n if (error.response) {\n const errorMessages = Object.values(error.response.data).flat()\n errorMessage += errorMessages.length ? errorMessages[0] : '...'\n }\n this.$store.dispatch(SHOW_ALERT, {\n error: errorMessage\n })\n })\n .finally(() => (this.loading = false))\n }\n }\n}\n</script>\n<style lang=\"scss\">\n.dashboard .panels {\n max-width: 95%;\n max-width: 1800px;\n padding-bottom: 100px;\n}\n</style>\n"]
292261
+ sourcesContent: ["<template>\n <div ref=\"dashboard\">\n <v-card flat class=\"isabl-base-card\">\n <dc-graph\n :model=\"model\"\n :params=\"params\"\n :items=\"items\"\n :figures=\"figures\"\n :loading=\"loading\"\n :max-records=\"maxRecords\"\n :total-items=\"totalItems\"\n :is-embedded=\"isEmbedded\"\n @set-max-records=\"setMaxRecords\"\n @set-options=\"setOptions\"\n />\n </v-card>\n </div>\n</template>\n\n<script>\n import BasePanel from '@/components/base/BasePanel.vue'\n import BaseSectionTitle from '@/components/base/BaseSectionTitle.vue'\n import DataTable from '@/components/table/DataTable.vue'\n import DcGraph from '@/components/dashboard/DcGraph.vue'\n import { fetchListRecords } from '@/utils/api'\n import { SHOW_ALERT } from '@/store/actions/alert'\n\n export default {\n name: 'BaseDashboard',\n components: {\n BasePanel,\n BaseSectionTitle,\n DataTable,\n DcGraph\n },\n props: {\n params: {\n type: Object,\n default: () => ({})\n },\n isEmbedded: {\n type: Boolean,\n default: true\n },\n model: {\n type: String,\n required: true\n },\n figures: {\n type: Array,\n required: true\n },\n requiredFields: {\n type: Array,\n default: () => []\n }\n },\n data() {\n return {\n items: [],\n loading: false,\n maxRecords: 500,\n options: {},\n totalItems: null\n }\n },\n computed: {\n queryFields() {\n const fields = this.figures.map(figure => figure.queryFields)\n // flat and remove duplicates\n return [...new Set(fields.flat())]\n },\n fields() {\n return [\n ...new Set([\n 'pk',\n 'created',\n 'modified',\n 'analytics',\n ...this.requiredFields,\n ...this.queryFields\n ])\n ]\n .filter(i => !!i)\n .map(i => i.replace(/\\./g, '__'))\n }\n },\n created() {\n this.loading = true\n },\n methods: {\n setOptions(options) {\n delete options.offset\n this.options = options\n this.fetchData()\n },\n setMaxRecords(value) {\n if (value !== this.maxRecords) {\n this.maxRecords = value\n this.fetchData()\n }\n },\n fetchData() {\n this.loading = true\n\n const fields = this.fields.map(i => i.replace(/\\./g, '__'))\n fetchListRecords(this.model, {\n ...this.options,\n ...this.params,\n limit: this.maxRecords,\n distinct: true,\n fields,\n 'fields!': ''\n })\n .then(response => {\n this.items = response.results.map(r => {\n const result = {\n ...r,\n ...r.analytics\n }\n delete result.analytics\n return result\n })\n this.totalItems = response.count\n })\n .catch(error => {\n let errorMessage = `Search failed: `\n if (error.response) {\n const errorMessages = Object.values(error.response.data).flat()\n errorMessage += errorMessages.length ? errorMessages[0] : '...'\n }\n this.$store.dispatch(SHOW_ALERT, {\n error: errorMessage\n })\n })\n .finally(() => (this.loading = false))\n }\n }\n }\n</script>\n<style lang=\"scss\">\n.dashboard .panels {\n max-width: 95%;\n max-width: 1800px;\n padding-bottom: 100px;\n}\n</style>\n"]
292262
292262
  },
292263
292263
  _coverageSchema: "43e27e138ebf9cfc5966b082cf9a028302ed4184",
292264
- hash: "0b4078169eae16bef6c925f7ea045fdcabd12d44"
292264
+ hash: "80378eaf505b52746695623a345007e2d0c5f9ea"
292265
292265
  };
292266
292266
  var coverage = global[gcv] || (global[gcv] = {});
292267
292267
 
@@ -292492,8 +292492,8 @@ var BaseDashboardvue_type_style_index_0_lang_scss_ = __webpack_require__("f624")
292492
292492
 
292493
292493
  var BaseDashboard_component = normalizeComponent(
292494
292494
  dashboard_BaseDashboardvue_type_script_lang_js_,
292495
- BaseDashboardvue_type_template_id_191c4226_render,
292496
- BaseDashboardvue_type_template_id_191c4226_staticRenderFns,
292495
+ BaseDashboardvue_type_template_id_2fd2fca6_render,
292496
+ BaseDashboardvue_type_template_id_2fd2fca6_staticRenderFns,
292497
292497
  false,
292498
292498
  null,
292499
292499
  null,
@@ -298468,18 +298468,18 @@ var BioModelPanelvue_type_template_id_b52cff22_staticRenderFns = []
298468
298468
 
298469
298469
  // CONCATENATED MODULE: ./src/components/bioModels/BioModelPanel.vue?vue&type=template&id=b52cff22&
298470
298470
 
298471
- // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"d220a428-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/bioModels/BioModelTree.vue?vue&type=template&id=8ae628c2&
298472
- var BioModelTreevue_type_template_id_8ae628c2_render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('v-card',{staticClass:"isabl-base-card",attrs:{"flat":"","data-test":"biomodel-tree"}},[_c('base-section-title',{attrs:{"show-more":_vm.showTree},on:{"toggle-show-more":function($event){_vm.showTree = !_vm.showTree}},scopedSlots:_vm._u([{key:"title",fn:function(){return [_vm._v("Samples Tree")]},proxy:true},{key:"actions",fn:function(){return [_c('div',[(_vm.isBigTree)?_c('v-btn',{staticClass:"table-action-btn pull-right ma-0",attrs:{"fab":"","outlined":"","color":"primary"},on:{"click":function($event){_vm.showFullHeight = !_vm.showFullHeight
298471
+ // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js?{"cacheDirectory":"node_modules/.cache/vue-loader","cacheIdentifier":"d220a428-vue-loader-template"}!./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/bioModels/BioModelTree.vue?vue&type=template&id=2b110930&
298472
+ var BioModelTreevue_type_template_id_2b110930_render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('v-card',{staticClass:"isabl-base-card",attrs:{"flat":"","data-test":"biomodel-tree"}},[_c('base-section-title',{attrs:{"show-more":_vm.showTree},on:{"toggle-show-more":function($event){_vm.showTree = !_vm.showTree}},scopedSlots:_vm._u([{key:"title",fn:function(){return [_vm._v("Samples Tree")]},proxy:true},{key:"actions",fn:function(){return [_c('div',[(_vm.isBigTree)?_c('v-btn',{staticClass:"table-action-btn pull-right ma-0",attrs:{"fab":"","outlined":"","color":"primary"},on:{"click":function($event){_vm.showFullHeight = !_vm.showFullHeight
298473
298473
  _vm.scrollToCurrentNode()}}},[_c('v-icon',{attrs:{"color":"primary"}},[_vm._v(_vm._s(_vm.showFullHeight ? 'expand_less' : 'expand_more'))])],1):_vm._e()],1)]},proxy:true}])}),_c('v-row',{directives:[{name:"show",rawName:"v-show",value:(_vm.showTree),expression:"showTree"}],staticClass:"individual-tree-div ma-0",style:(_vm.showFullHeight ? '' : 'max-height: 300px;'),attrs:{"id":"tree-div"}},[_c('v-col',{staticClass:"individual-tree text-center pa-0 ma-0"})],1)],1)}
298474
- var BioModelTreevue_type_template_id_8ae628c2_staticRenderFns = []
298474
+ var BioModelTreevue_type_template_id_2b110930_staticRenderFns = []
298475
298475
 
298476
298476
 
298477
- // CONCATENATED MODULE: ./src/components/bioModels/BioModelTree.vue?vue&type=template&id=8ae628c2&
298477
+ // CONCATENATED MODULE: ./src/components/bioModels/BioModelTree.vue?vue&type=template&id=2b110930&
298478
298478
 
298479
298479
  // CONCATENATED MODULE: ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/thread-loader/dist/cjs.js!./node_modules/@vue/cli-plugin-babel/node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/bioModels/BioModelTree.vue?vue&type=script&lang=js&
298480
298480
  var cov_2hmzqbstvs = function () {
298481
298481
  var path = "/Users/juanes/papaemmelab/isabl_web/src/components/bioModels/BioModelTree.vue";
298482
- var hash = "5f37394f4c641e085634b4de8a6e95016123fe5c";
298482
+ var hash = "1e089864fe6792f2d8555180af07f2e5d7f516a5";
298483
298483
  var global = new Function("return this")();
298484
298484
  var gcv = "__coverage__";
298485
298485
  var coverageData = {
@@ -303764,10 +303764,10 @@ var cov_2hmzqbstvs = function () {
303764
303764
  mappings: ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA",
303765
303765
  file: "BioModelTree.vue",
303766
303766
  sourceRoot: "src/components/bioModels",
303767
- sourcesContent: ["<template>\n <v-card\n flat\n class=\"isabl-base-card\"\n data-test=\"biomodel-tree\"\n >\n <base-section-title\n :show-more=\"showTree\"\n @toggle-show-more=\"showTree = !showTree\"\n >\n <template v-slot:title>Samples Tree</template>\n <template v-slot:actions>\n <div>\n <v-btn\n v-if=\"isBigTree\"\n class=\"table-action-btn pull-right ma-0\"\n fab\n outlined\n color=\"primary\"\n @click=\"\n showFullHeight = !showFullHeight\n scrollToCurrentNode()\n \"\n >\n <v-icon color=\"primary\">{{ showFullHeight ? 'expand_less' : 'expand_more' }}</v-icon>\n </v-btn>\n </div>\n </template>\n </base-section-title>\n\n <!-- Graph -->\n <v-row\n v-show=\"showTree\"\n id=\"tree-div\"\n :style=\"showFullHeight ? '' : 'max-height: 300px;'\"\n class=\"individual-tree-div ma-0\"\n >\n <v-col class=\"individual-tree text-center pa-0 ma-0\" />\n </v-row>\n\n </v-card>\n</template>\n\n<script>\n/*\n Vue implementation of D3 Tree from:\n https://beta.observablehq.com/@asktree/interactive-tree-diagram-d3v4-v5\n*/\n\nimport { fetchIndividualTree } from '@/utils/api'\nimport { mapGetters } from 'vuex'\nimport { SHOW_ALERT } from '@/store/actions/alert'\nimport { SHOW_PANEL } from '@/store/actions/panels'\nimport BasePanel from '@/components/base/BasePanel.vue'\nimport BaseSectionTitle from '@/components/base/BaseSectionTitle'\nimport {\n tree as d3Tree,\n event as d3Event,\n select as d3Select,\n hierarchy as d3Hierarchy,\n linkHorizontal as d3linkHorizontal\n} from 'd3'\n\nexport default {\n name: 'BioModelTree',\n components: {\n BasePanel,\n BaseSectionTitle\n },\n data() {\n const { treeIcons } = this.$settings\n return {\n loading: false,\n duration: 0,\n data: null,\n root: null,\n tree: null,\n graph: null,\n species: null,\n dataNodes: null,\n showTree: true,\n isBigTree: false,\n showFullHeight: true,\n skipToScrollNode: false,\n currentNode: null,\n nodeSize: Object.keys(treeIcons).length ? 15 : 5,\n margin: { top: 20, bottom: 20, right: 0, left: 105 },\n diagonal: d3linkHorizontal()\n .x(d => d.y)\n .y(d => d.x),\n treeIcons\n }\n },\n computed: {\n ...mapGetters({\n currentModel: 'bioModelId',\n individual: 'individualId'\n })\n },\n watch: {\n individual(value, oldValue) {\n if (value !== oldValue) {\n this.fetchData()\n }\n },\n currentModel() {\n this.data && this.update(this.data)\n }\n },\n mounted() {\n this.fetchData()\n },\n methods: {\n buildTree() {\n let dx = Object.keys(this.treeIcons).length ? this.nodeSize + 4 : 18\n let dy = 0\n let iconSize = this.nodeSize\n\n // set width\n this.width = 540 - this.margin.right - this.margin.left\n this.tree = d3Tree()\n .nodeSize([dx, dy])\n .separation((a, b) => (a.parent == b.parent ? 1.75 : 2.5))\n\n // clear div\n let div = d3Select(this.$el).select('.individual-tree')\n div.selectAll('svg').remove()\n\n // create svg\n this.svg = div\n .append('svg')\n .attr('width', this.width)\n .attr('height', dx)\n .attr('viewBox', [-this.margin.left, -this.margin.top, this.width, dx])\n .style('font', '10px sans-serif')\n .style('user-select', 'none')\n\n // create filters\n this.svg\n .append('svg:filter')\n .attr('id', 'invertColor')\n .append('feColorMatrix')\n .attr('in', 'SourceGraphic')\n .attr('type', 'matrix')\n .attr('values', '-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0')\n\n // create image definitions\n const defs = this.svg.append('svg:defs')\n Object.keys(this.treeIcons).forEach(model => {\n const pattern = defs\n .append('svg:pattern')\n .attr('id', `icon-${model}`)\n .attr('width', iconSize)\n .attr('height', iconSize)\n .attr('patternUnits', 'objectBoundingBox')\n\n pattern\n .append('rect')\n .attr('width', '100%')\n .attr('height', '100%')\n .attr('fill', 'var(--v-primary-base)')\n\n pattern\n .append('svg:image')\n .attr('xlink:href', this.treeIcons[model])\n .attr('width', iconSize)\n .attr('height', iconSize)\n .attr('x', iconSize / 2)\n .attr('y', iconSize / 2)\n .attr('filter', 'url(#invertColor)')\n })\n\n // create links\n this.gLink = this.svg\n .append('g')\n .attr('fill', 'none')\n .attr('stroke', '#555')\n .attr('stroke-opacity', 0.4)\n .attr('stroke-width', 1.5)\n\n // create tooltip\n this.tooltip = d3Select('body')\n .append('div')\n .style('position', 'absolute')\n .style('z-index', '9999')\n .style('visibility', 'hidden')\n\n // create nodes\n this.gNode = this.svg.append('g')\n\n // build root\n this.root = d3Hierarchy(this.data, d => d.children)\n this.root.x0 = dy\n this.root.y0 = 0\n this.root.descendants().forEach((d, i) => {\n d.id = i\n d.collapsed = false\n d._children = d.children\n\n // collapse samples with too many experiments\n if (d.depth !== 0 && d.children ? d.children.length > 10 : false) {\n d.children = null\n d.collapsed = true\n }\n\n // by default hide tree if too many samples\n if (d.depth === 0 && d.children && d.children.length > 10) {\n this.showFullHeight = false\n this.isBigTree = true\n }\n })\n\n this.update(this.root)\n },\n async fetchData() {\n if (this.individual) {\n this.loading = true\n let individual = await fetchIndividualTree(this.individual)\n this.species = individual.species\n\n // Format Individual Tree\n let individualNode = {}\n individualNode.id = individual.system_id\n individualNode.uuid = individual.uuid\n individualNode.name = individual.system_id\n individualNode.parent = null\n individualNode.individual = individual\n individualNode.class = 'individual'\n if (individual.species === 'MOUSE') {\n individualNode.img = 'mouse'\n } else if (individual.species === 'HUMAN') {\n if (individual.gender === 'FEMALE') {\n individualNode.img = 'female'\n } else {\n individualNode.img = 'male'\n }\n } else {\n individualNode.img = 'alien'\n }\n individualNode.children = individual.sample_set.map(sample => {\n // Samples Children\n let sampleNode = {}\n sampleNode.id = sample.system_id\n sampleNode.uuid = sample.uuid\n sampleNode.name = sample.system_id.split('_')\n sampleNode.name = sampleNode.name[sampleNode.name.length - 1]\n sampleNode.parent = individual.system_id\n sampleNode.sample = sample\n sampleNode.individual = individual\n sampleNode.class = 'sample'\n\n if (sample.category === 'TUMOR') {\n sampleNode.img = 'tumor'\n } else {\n sampleNode.img = 'normal'\n }\n\n let aliquots_ids = [\n ...new Set(sample.experiment_set.map(e => e.aliquot_id))\n ]\n\n sampleNode.children = aliquots_ids\n .map(aliquot_id => {\n // Aliquot children\n let aliquotNode = {}\n aliquotNode.aliquot_id = aliquot_id\n aliquotNode.name = ''\n aliquotNode.parent = sample.system_id\n aliquotNode.sample = sample\n aliquotNode.sampleNode = sampleNode\n aliquotNode.individual = individual\n aliquotNode.class = 'aliquot'\n aliquotNode.img = 'aliquot'\n aliquotNode.children = []\n\n sample.experiment_set.map(experiment => {\n // Experiment Children\n if (experiment.aliquot_id === aliquot_id) {\n let experimentNode = {}\n experimentNode.parent = sample.system_id\n experimentNode.sample = sample\n experimentNode.individual = individual\n experimentNode.aliquot_id = experiment.aliquot_id\n experimentNode.id = experiment.system_id\n experimentNode.uuid = experiment.uuid\n experimentNode.class = 'experiment'\n experimentNode.experiment = experiment\n\n let name = experiment.system_id.split('_')\n experimentNode.name = name[name.length - 1]\n\n if (experiment.custom_fields.is_pdx) {\n experimentNode.img = 'mouse'\n } else {\n if (experiment.technique.category === 'IMG') {\n experimentNode.img = 'image'\n } else if (experiment.technique.category === 'RNA') {\n experimentNode.img = 'rna'\n } else if (experiment.technique.category === 'DNA') {\n experimentNode.img = 'dna'\n } else {\n experimentNode.img = 'dna'\n }\n }\n\n // legacy leukgen ID\n if (experiment.system_id.split('-').length === 7) {\n aliquotNode.name = experiment.system_id.split('-')[4]\n experimentNode.name = experiment.system_id\n .split('-')\n .slice(5, 7)\n .join('-')\n } else {\n aliquotNode.name = experiment.system_id.split('_')\n aliquotNode.name = parseInt(\n aliquotNode.name[aliquotNode.name.length - 2]\n )\n }\n aliquotNode.children.push(experimentNode)\n }\n })\n return aliquotNode\n })\n .sort((a, b) => a.name - b.name)\n return sampleNode\n })\n\n this.data = individualNode\n this.dataNodes = individualNode.children.reduce(\n (totalNodes, nodeInChildren) => {\n return totalNodes + nodeInChildren.children.length\n },\n 0\n )\n\n this.buildTree()\n this.loading = false\n }\n },\n update(source) {\n const duration = d3Event && d3Event.altKey ? 2500 : 250\n const nodes = this.root.descendants().reverse()\n const links = this.root.links()\n\n // Compute the new tree layout.\n this.tree(this.root)\n\n let left = this.root\n let right = this.root\n this.root.eachBefore(node => {\n if (node.x < left.x) left = node\n if (node.x > right.x) right = node\n })\n\n const height = right.x - left.x + this.margin.top + this.margin.bottom\n\n this.svg\n .transition()\n .duration(duration)\n .attr('height', height)\n .attr('viewBox', [\n -this.margin.left,\n left.x - this.margin.top,\n this.width,\n height\n ])\n .tween(\n 'resize',\n window.ResizeObserver ? null : () => () => this.svg.dispatch('toggle')\n )\n\n // Normalize for fixed-depth.\n nodes.forEach(d => {\n d.y = {\n individual: 0,\n sample: 150,\n aliquot: 225,\n experiment: 300\n }[d.data.class]\n })\n\n // ****************** Nodes section ***************************\n\n // Update the nodes...\n let node = this.gNode.selectAll('g').data(nodes, d => d.id)\n\n // Enter any new modes at the parent's previous position.\n let nodeEnter = node\n .enter()\n .append('g')\n .attr('class', d => `node ${d.data.class}`)\n .attr('transform', () => `translate(${source.y0},${source.x0})`)\n\n const circles = nodeEnter\n .append('circle')\n .attr('cursor', d => (d.data.class !== 'aliquot' ? 'pointer' : null))\n .attr('class', 'node shades--text')\n\n circles.style('fill', d =>\n this.treeIcons[d.data.img] ? `url(#icon-${d.data.img})` : ''\n )\n\n nodeEnter\n .on('click', this.changeModel)\n // tooltip, see https://stackoverflow.com/questions/10805184\n .on('mouseover', d => {\n if (d.data.class === 'aliquot' && d.parent.children.length < 2) return\n\n let leftPaddings = { experiment: 300, individual: 120, sample: 200 }\n this.tooltip\n .html(this.getTooltip(d))\n .style('visibility', 'visible')\n .attr('transform', `translate(${d.y},${d.x})`)\n .style('top', `${d3Event.pageY + 20}px`)\n .style('left', `${d3Event.pageX - leftPaddings[d.data.class]}px`)\n })\n .on('mouseout', () => this.tooltip.style('visibility', 'hidden'))\n\n // Add labels for the nodes\n nodeEnter\n .append('text')\n .on('click', this.onTextClick)\n .attr('dy', '.35em')\n .attr('x', -13)\n .attr('text-anchor', d => (d.children || d._children ? 'end' : 'end'))\n .text(d =>\n d.data.class === 'aliquot' && d.parent.children.length < 2\n ? ''\n : d.data.name\n )\n\n // Add collapse text to indicate node can be opened\n nodeEnter\n .append('text')\n .attr('dy', 0)\n .attr('x', 26)\n .text('...')\n .attr('class', 'collapse-text')\n\n // UPDATE\n let nodeUpdate = nodeEnter.merge(node)\n\n // Transition to the proper position for the node\n nodeUpdate\n .transition()\n .duration(this.duration)\n .attr('transform', d => `translate(${d.y},${d.x})`)\n\n // Update the node attributes and style\n nodeUpdate\n .select('.node')\n .attr('r', d => (d.data.class !== 'aliquot' ? this.nodeSize : 5))\n .attr('cx', this.nodeSize / 2)\n .attr('cy', 0)\n .attr('data-intercom-target', d => `tree-node-circle-${d.data.id}`)\n .attr('fill', d =>\n d._children && !d.children ? 'gray' : 'var(--v-primary-base)'\n )\n .attr('opacity', d =>\n d.data.class === 'aliquot' && d.parent.children.length < 2 ? 0 : 1\n )\n .style('stroke', d => {\n if (\n d.data.id === this.currentModel ||\n d.data.uuid === this.currentModel\n ) {\n this.currentNode = d\n if (!this.skipToScrollNode) {\n this.scrollToCurrentNode()\n }\n this.skipToScrollNode = false\n return 'var(--v-secondary-base)'\n }\n return 'transparent'\n })\n\n // Only show dots for collapsed and collapsible nodes\n nodeUpdate\n .select('text.collapse-text')\n .attr('visibility', d =>\n d.collapsed && d._children ? 'visible' : 'hidden'\n )\n\n // Remove any exiting nodes\n let nodeExit = node\n .exit()\n .transition()\n .duration(this.duration)\n .attr('transform', `translate(${source.y},${source.x})`)\n .remove()\n\n // On exit reduce the node circles size to 0\n nodeExit.select('.node').attr('r', 1e-6)\n\n // On exit reduce the opacity of text labels\n nodeExit.select('text').style('fill-opacity', 1e-6)\n\n // ================ links section ================\n\n // Update the links\n let link = this.gLink.selectAll('path.link').data(links, d => d.id)\n\n // Enter any new links at the parent's previous position.\n let linkEnter = link\n .enter()\n .insert('path', 'g')\n .attr('class', 'link')\n\n // UPDATE\n let linkUpdate = linkEnter.merge(link)\n\n // Transition back to the parent element position\n linkUpdate\n .transition()\n .duration(this.duration)\n .attr('d', this.diagonal)\n\n // Remove any exiting links\n link\n .exit()\n .transition()\n .duration(this.duration)\n .remove()\n\n // Store the old positions for transition.\n nodes.forEach(d => {\n d.x0 = d.x\n d.y0 = d.y\n })\n\n this.$emit('update-size')\n },\n onTextClick(d) {\n this.collapseBranch(d)\n if (!d._children && d.data.id) {\n this.$copyText(d.data.id).then(\n () => {\n this.$store.dispatch(SHOW_ALERT, {\n message: 'Copied System ID to clipboard!'\n })\n },\n () => {\n this.$store.dispatch(SHOW_ALERT, {\n error: 'System ID could not be copied to clipboard'\n })\n }\n )\n }\n },\n collapseBranch(d) {\n d.children = d.children ? null : d._children\n d.collapsed = !d.collapsed\n this.update(d)\n },\n changeModel(d) {\n if (d.data.id) {\n this.skipToScrollNode = true // don't scroll to node when click\n\n // Track individual tree usage\n window.analytics.track('Browsed Individual Tree', {\n model: d.data.class,\n record: d.data.id\n })\n\n this.$store.dispatch(SHOW_PANEL, {\n bioModel: d.data.id\n })\n }\n },\n scrollToCurrentNode() {\n this.$nextTick(() => {\n if (!this.showFullHeight)\n setTimeout(() => {\n document.getElementById('tree-div').scrollTop =\n this.currentNode.x0 + 395 // 395 is a function of the max height\n }, 100) // this delay was determined kind of experimentally\n })\n },\n getTooltip(d) {\n const keyValue = (key, verboseName) => {\n let value = this.$get(d.data, key, null)\n return value === null\n ? ''\n : `\n <div class=\"layout row mb-1\" style=\"max-width: 100%\"\">\n <div class=\"flex xs5\" style=\"width: 100%\">${verboseName}</div>\n <div class=\"flex xs7\" style=\"width: 100%\"><b>${value}</b></div>\n </div>\n `\n }\n return `\n <div class=\"v-tooltip__content\" style=\"width: 350px;\">\n ${keyValue('experiment.system_id', 'System ID')}\n ${keyValue('experiment.identifier', 'Experiment ID')}\n ${keyValue('aliquot_id', 'Aliquot ID')}\n ${keyValue('sample.identifier', 'Sample ID')}\n ${keyValue('individual.identifier', 'Individual ID')}\n ${keyValue('experiment.technique.method', 'Method')}\n ${keyValue('experiment.technique.name', 'Technique Name')}\n ${keyValue('sample.disease.acronym', 'Disease')}\n ${keyValue('individual.species', 'Species')}\n ${keyValue('individual.center.acronym', 'Center')}\n </div>\n `\n }\n }\n}\n</script>\n\n<style lang=\"scss\">\n.node-tooltip {\n background: rgba($color: #000000, $alpha: 0.8);\n}\n.individual-tree-div {\n overflow-x: scroll;\n margin: auto;\n .node {\n stroke-width: 3px;\n text {\n font-size: 14px;\n font-weight: 300;\n fill: black !important;\n cursor: pointer;\n &.collapse-text {\n font-size: 20px;\n font-weight: 200;\n cursor: default;\n }\n }\n &.experiment text {\n cursor: default;\n }\n }\n .link {\n fill: none;\n stroke-width: 1.5px;\n stroke: #aaa;\n }\n}\n</style>\n"]
303767
+ sourcesContent: ["<template>\n <v-card\n flat\n class=\"isabl-base-card\"\n data-test=\"biomodel-tree\"\n >\n <base-section-title\n :show-more=\"showTree\"\n @toggle-show-more=\"showTree = !showTree\"\n >\n <template v-slot:title>Samples Tree</template>\n <template v-slot:actions>\n <div>\n <v-btn\n v-if=\"isBigTree\"\n class=\"table-action-btn pull-right ma-0\"\n fab\n outlined\n color=\"primary\"\n @click=\"\n showFullHeight = !showFullHeight\n scrollToCurrentNode()\n \"\n >\n <v-icon color=\"primary\">{{ showFullHeight ? 'expand_less' : 'expand_more' }}</v-icon>\n </v-btn>\n </div>\n </template>\n </base-section-title>\n\n <!-- Graph -->\n <v-row\n v-show=\"showTree\"\n id=\"tree-div\"\n :style=\"showFullHeight ? '' : 'max-height: 300px;'\"\n class=\"individual-tree-div ma-0\"\n >\n <v-col class=\"individual-tree text-center pa-0 ma-0\" />\n </v-row>\n\n </v-card>\n</template>\n\n<script>\n/*\n Vue implementation of D3 Tree from:\n https://beta.observablehq.com/@asktree/interactive-tree-diagram-d3v4-v5\n*/\n\nimport { fetchIndividualTree } from '@/utils/api'\nimport { mapGetters } from 'vuex'\nimport { SHOW_ALERT } from '@/store/actions/alert'\nimport { SHOW_PANEL } from '@/store/actions/panels'\nimport BasePanel from '@/components/base/BasePanel.vue'\nimport BaseSectionTitle from '@/components/base/BaseSectionTitle'\nimport {\n tree as d3Tree,\n event as d3Event,\n select as d3Select,\n hierarchy as d3Hierarchy,\n linkHorizontal as d3linkHorizontal\n} from 'd3'\n\nexport default {\n name: 'BioModelTree',\n components: {\n BasePanel,\n BaseSectionTitle\n },\n data() {\n const { treeIcons } = this.$settings\n return {\n loading: false,\n duration: 0,\n data: null,\n root: null,\n tree: null,\n graph: null,\n species: null,\n dataNodes: null,\n showTree: true,\n isBigTree: false,\n showFullHeight: true,\n skipToScrollNode: false,\n currentNode: null,\n nodeSize: Object.keys(treeIcons).length ? 15 : 5,\n margin: { top: 20, bottom: 20, right: 0, left: 105 },\n diagonal: d3linkHorizontal()\n .x(d => d.y)\n .y(d => d.x),\n treeIcons\n }\n },\n computed: {\n ...mapGetters({\n currentModel: 'bioModelId',\n individual: 'individualId'\n })\n },\n watch: {\n individual(value, oldValue) {\n if (value !== oldValue) {\n this.fetchData()\n }\n },\n currentModel() {\n this.data && this.update(this.data)\n }\n },\n mounted() {\n this.fetchData()\n },\n methods: {\n buildTree() {\n let dx = Object.keys(this.treeIcons).length ? this.nodeSize + 4 : 18\n let dy = 0\n let iconSize = this.nodeSize\n\n // set width\n this.width = 540 - this.margin.right - this.margin.left\n this.tree = d3Tree()\n .nodeSize([dx, dy])\n .separation((a, b) => (a.parent == b.parent ? 1.75 : 2.5))\n\n // clear div\n let div = d3Select(this.$el).select('.individual-tree')\n div.selectAll('svg').remove()\n\n // create svg\n this.svg = div\n .append('svg')\n .attr('width', this.width)\n .attr('height', dx)\n .attr('viewBox', [-this.margin.left, -this.margin.top, this.width, dx])\n .style('font', '10px sans-serif')\n .style('user-select', 'none')\n\n // create filters\n this.svg\n .append('svg:filter')\n .attr('id', 'invertColor')\n .append('feColorMatrix')\n .attr('in', 'SourceGraphic')\n .attr('type', 'matrix')\n .attr('values', '-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0')\n\n // create image definitions\n const defs = this.svg.append('svg:defs')\n Object.keys(this.treeIcons).forEach(model => {\n const pattern = defs\n .append('svg:pattern')\n .attr('id', `icon-${model}`)\n .attr('width', iconSize)\n .attr('height', iconSize)\n .attr('patternUnits', 'objectBoundingBox')\n\n pattern\n .append('rect')\n .attr('width', '100%')\n .attr('height', '100%')\n .attr('fill', 'var(--v-primary-base)')\n\n pattern\n .append('svg:image')\n .attr('xlink:href', this.treeIcons[model])\n .attr('width', iconSize)\n .attr('height', iconSize)\n .attr('x', iconSize / 2)\n .attr('y', iconSize / 2)\n .attr('filter', 'url(#invertColor)')\n })\n\n // create links\n this.gLink = this.svg\n .append('g')\n .attr('fill', 'none')\n .attr('stroke', '#555')\n .attr('stroke-opacity', 0.4)\n .attr('stroke-width', 1.5)\n\n // create tooltip\n this.tooltip = d3Select('body')\n .append('div')\n .style('position', 'absolute')\n .style('z-index', '9999')\n .style('visibility', 'hidden')\n\n // create nodes\n this.gNode = this.svg.append('g')\n\n // build root\n this.root = d3Hierarchy(this.data, d => d.children)\n this.root.x0 = dy\n this.root.y0 = 0\n this.root.descendants().forEach((d, i) => {\n d.id = i\n d.collapsed = false\n d._children = d.children\n\n // collapse samples with too many experiments\n if (d.depth !== 0 && d.children ? d.children.length > 10 : false) {\n d.children = null\n d.collapsed = true\n }\n\n // by default hide tree if too many samples\n if (d.depth === 0 && d.children && d.children.length > 10) {\n this.showFullHeight = false\n this.isBigTree = true\n }\n })\n\n this.update(this.root)\n },\n async fetchData() {\n if (this.individual) {\n this.loading = true\n let individual = await fetchIndividualTree(this.individual)\n this.species = individual.species\n\n // Format Individual Tree\n let individualNode = {}\n individualNode.id = individual.system_id\n individualNode.uuid = individual.uuid\n individualNode.name = individual.system_id\n individualNode.parent = null\n individualNode.individual = individual\n individualNode.class = 'individual'\n if (individual.species === 'MOUSE') {\n individualNode.img = 'mouse'\n } else if (individual.species === 'HUMAN') {\n if (individual.gender === 'FEMALE') {\n individualNode.img = 'female'\n } else {\n individualNode.img = 'male'\n }\n } else {\n individualNode.img = 'alien'\n }\n individualNode.children = individual.sample_set.map(sample => {\n // Samples Children\n let sampleNode = {}\n sampleNode.id = sample.system_id\n sampleNode.uuid = sample.uuid\n sampleNode.name = sample.system_id.split('_')\n sampleNode.name = sampleNode.name[sampleNode.name.length - 1]\n sampleNode.parent = individual.system_id\n sampleNode.sample = sample\n sampleNode.individual = individual\n sampleNode.class = 'sample'\n\n if (sample.category === 'TUMOR') {\n sampleNode.img = 'tumor'\n } else {\n sampleNode.img = 'normal'\n }\n\n let aliquots_ids = [\n ...new Set(sample.experiment_set.map(e => e.aliquot_id))\n ]\n\n sampleNode.children = aliquots_ids\n .map(aliquot_id => {\n // Aliquot children\n let aliquotNode = {}\n aliquotNode.aliquot_id = aliquot_id\n aliquotNode.name = ''\n aliquotNode.parent = sample.system_id\n aliquotNode.sample = sample\n aliquotNode.sampleNode = sampleNode\n aliquotNode.individual = individual\n aliquotNode.class = 'aliquot'\n aliquotNode.img = 'aliquot'\n aliquotNode.children = []\n\n sample.experiment_set.map(experiment => {\n // Experiment Children\n if (experiment.aliquot_id === aliquot_id) {\n let experimentNode = {}\n experimentNode.parent = sample.system_id\n experimentNode.sample = sample\n experimentNode.individual = individual\n experimentNode.aliquot_id = experiment.aliquot_id\n experimentNode.id = experiment.system_id\n experimentNode.uuid = experiment.uuid\n experimentNode.class = 'experiment'\n experimentNode.experiment = experiment\n\n let name = experiment.system_id.split('_')\n experimentNode.name = name[name.length - 1]\n\n if (experiment.custom_fields.is_pdx) {\n experimentNode.img = 'mouse'\n } else {\n if (experiment.technique.category === 'IMG') {\n experimentNode.img = 'image'\n } else if (experiment.technique.category === 'RNA') {\n experimentNode.img = 'rna'\n } else if (experiment.technique.category === 'DNA') {\n experimentNode.img = 'dna'\n } else {\n experimentNode.img = 'dna'\n }\n }\n\n // legacy leukgen ID\n if (experiment.system_id.split('-').length === 7) {\n aliquotNode.name = experiment.system_id.split('-')[4]\n experimentNode.name = experiment.system_id\n .split('-')\n .slice(5, 7)\n .join('-')\n } else {\n aliquotNode.name = experiment.system_id.split('_')\n aliquotNode.name = parseInt(\n aliquotNode.name[aliquotNode.name.length - 2]\n )\n }\n aliquotNode.children.push(experimentNode)\n }\n })\n return aliquotNode\n })\n .sort((a, b) => a.name - b.name)\n return sampleNode\n })\n\n this.data = individualNode\n this.dataNodes = individualNode.children.reduce(\n (totalNodes, nodeInChildren) => {\n return totalNodes + nodeInChildren.children.length\n },\n 0\n )\n\n this.buildTree()\n this.loading = false\n }\n },\n update(source) {\n const duration = d3Event && d3Event.altKey ? 2500 : 250\n const nodes = this.root.descendants().reverse()\n const links = this.root.links()\n\n // Compute the new tree layout.\n this.tree(this.root)\n\n let left = this.root\n let right = this.root\n this.root.eachBefore(node => {\n if (node.x < left.x) left = node\n if (node.x > right.x) right = node\n })\n\n const height = right.x - left.x + this.margin.top + this.margin.bottom\n\n this.svg\n .transition()\n .duration(duration)\n .attr('height', height)\n .attr('viewBox', [\n -this.margin.left,\n left.x - this.margin.top,\n this.width,\n height\n ])\n .tween(\n 'resize',\n window.ResizeObserver ? null : () => () => this.svg.dispatch('toggle')\n )\n\n // Normalize for fixed-depth.\n nodes.forEach(d => {\n d.y = {\n individual: 0,\n sample: 150,\n aliquot: 225,\n experiment: 300\n }[d.data.class]\n })\n\n // ****************** Nodes section ***************************\n\n // Update the nodes...\n let node = this.gNode.selectAll('g').data(nodes, d => d.id)\n\n // Enter any new modes at the parent's previous position.\n let nodeEnter = node\n .enter()\n .append('g')\n .attr('class', d => `node ${d.data.class}`)\n .attr('transform', () => `translate(${source.y0},${source.x0})`)\n\n const circles = nodeEnter\n .append('circle')\n .attr('cursor', d => (d.data.class !== 'aliquot' ? 'pointer' : null))\n .attr('class', 'node shades--text')\n\n circles.style('fill', d =>\n this.treeIcons[d.data.img] ? `url(#icon-${d.data.img})` : ''\n )\n\n nodeEnter\n .on('click', this.changeModel)\n // tooltip, see https://stackoverflow.com/questions/10805184\n .on('mouseover', d => {\n if (d.data.class === 'aliquot' && d.parent.children.length < 2) return\n\n let leftPaddings = { experiment: 300, individual: 120, sample: 200 }\n this.tooltip\n .html(this.getTooltip(d))\n .style('visibility', 'visible')\n .attr('transform', `translate(${d.y},${d.x})`)\n .style('top', `${d3Event.pageY + 20}px`)\n .style('left', `${d3Event.pageX - leftPaddings[d.data.class]}px`)\n })\n .on('mouseout', () => this.tooltip.style('visibility', 'hidden'))\n\n // Add labels for the nodes\n nodeEnter\n .append('text')\n .on('click', this.onTextClick)\n .attr('dy', '.35em')\n .attr('x', -13)\n .attr('text-anchor', d => (d.children || d._children ? 'end' : 'end'))\n .text(d =>\n d.data.class === 'aliquot' && d.parent.children.length < 2\n ? ''\n : d.data.name\n )\n\n // Add collapse text to indicate node can be opened\n nodeEnter\n .append('text')\n .attr('dy', 0)\n .attr('x', 26)\n .text('...')\n .attr('class', 'collapse-text')\n\n // UPDATE\n let nodeUpdate = nodeEnter.merge(node)\n\n // Transition to the proper position for the node\n nodeUpdate\n .transition()\n .duration(this.duration)\n .attr('transform', d => `translate(${d.y},${d.x})`)\n\n // Update the node attributes and style\n nodeUpdate\n .select('.node')\n .attr('r', d => (d.data.class !== 'aliquot' ? this.nodeSize : 5))\n .attr('cx', this.nodeSize / 2)\n .attr('cy', 0)\n .attr('data-intercom-target', d => `tree-node-circle-${d.data.id}`)\n .attr('fill', d =>\n d._children && !d.children ? 'gray' : 'var(--v-primary-base)'\n )\n .attr('opacity', d =>\n d.data.class === 'aliquot' && d.parent.children.length < 2 ? 0 : 1\n )\n .style('stroke', d => {\n if (\n d.data.id === this.currentModel ||\n d.data.uuid === this.currentModel\n ) {\n this.currentNode = d\n if (!this.skipToScrollNode) {\n this.scrollToCurrentNode()\n }\n this.skipToScrollNode = false\n return 'var(--v-secondary-base)'\n }\n return 'transparent'\n })\n\n // Only show dots for collapsed and collapsible nodes\n nodeUpdate\n .select('text.collapse-text')\n .attr('visibility', d =>\n d.collapsed && d._children ? 'visible' : 'hidden'\n )\n\n // Remove any exiting nodes\n let nodeExit = node\n .exit()\n .transition()\n .duration(this.duration)\n .attr('transform', `translate(${source.y},${source.x})`)\n .remove()\n\n // On exit reduce the node circles size to 0\n nodeExit.select('.node').attr('r', 1e-6)\n\n // On exit reduce the opacity of text labels\n nodeExit.select('text').style('fill-opacity', 1e-6)\n\n // ================ links section ================\n\n // Update the links\n let link = this.gLink.selectAll('path.link').data(links, d => d.id)\n\n // Enter any new links at the parent's previous position.\n let linkEnter = link\n .enter()\n .insert('path', 'g')\n .attr('class', 'link')\n\n // UPDATE\n let linkUpdate = linkEnter.merge(link)\n\n // Transition back to the parent element position\n linkUpdate\n .transition()\n .duration(this.duration)\n .attr('d', this.diagonal)\n\n // Remove any exiting links\n link\n .exit()\n .transition()\n .duration(this.duration)\n .remove()\n\n // Store the old positions for transition.\n nodes.forEach(d => {\n d.x0 = d.x\n d.y0 = d.y\n })\n\n this.$emit('update-size')\n },\n onTextClick(d) {\n this.collapseBranch(d)\n if (!d._children && d.data.id) {\n this.$copyText(d.data.id).then(\n () => {\n this.$store.dispatch(SHOW_ALERT, {\n message: 'Copied System ID to clipboard!'\n })\n },\n () => {\n this.$store.dispatch(SHOW_ALERT, {\n error: 'System ID could not be copied to clipboard'\n })\n }\n )\n }\n },\n collapseBranch(d) {\n d.children = d.children ? null : d._children\n d.collapsed = !d.collapsed\n this.update(d)\n },\n changeModel(d) {\n if (d.data.id) {\n this.skipToScrollNode = true // don't scroll to node when click\n\n // Track individual tree usage\n window.analytics.track('Browsed Individual Tree', {\n model: d.data.class,\n record: d.data.id\n })\n\n this.$store.dispatch(SHOW_PANEL, {\n bioModel: d.data.id\n })\n }\n },\n scrollToCurrentNode() {\n this.$nextTick(() => {\n if (!this.showFullHeight)\n setTimeout(() => {\n document.getElementById('tree-div').scrollTop =\n this.currentNode.x0 + 395 // 395 is a function of the max height\n }, 100) // this delay was determined kind of experimentally\n })\n },\n getTooltip(d) {\n const keyValue = (key, verboseName) => {\n let value = this.$get(d.data, key, null)\n return value === null\n ? ''\n : `\n <div class=\"layout row ma-3\" style=\"max-width: 100%\"\">\n <div class=\"flex xs5\" style=\"width: 100%\">${verboseName}</div>\n <div class=\"flex xs7\" style=\"width: 100%\"><b>${value}</b></div>\n </div>\n `\n }\n return `\n <div class=\"v-tooltip__content tree-tooltip\" style=\"min-width: 350px;\">\n ${keyValue('experiment.system_id', 'System ID')}\n ${keyValue('experiment.identifier', 'Experiment ID')}\n ${keyValue('aliquot_id', 'Aliquot ID')}\n ${keyValue('sample.identifier', 'Sample ID')}\n ${keyValue('individual.identifier', 'Individual ID')}\n ${keyValue('experiment.technique.method', 'Method')}\n ${keyValue('experiment.technique.name', 'Technique Name')}\n ${keyValue('sample.disease.acronym', 'Disease')}\n ${keyValue('individual.species', 'Species')}\n ${keyValue('individual.center.acronym', 'Center')}\n </div>\n `\n }\n }\n}\n</script>\n\n<style lang=\"scss\">\n.tree-tooltip.v-tooltip__content {\n color: white;\n padding: 20px;\n opacity: 1;\n font-size: 12px;\n overflow: hidden;\n}\n.node-tooltip {\n background: rgba($color: #000000, $alpha: 0.8);\n}\n.individual-tree-div {\n overflow-x: scroll;\n margin: auto;\n .node {\n stroke-width: 3px;\n text {\n font-size: 14px;\n font-weight: 300;\n fill: black !important;\n cursor: pointer;\n &.collapse-text {\n font-size: 20px;\n font-weight: 200;\n cursor: default;\n }\n }\n &.experiment text {\n cursor: default;\n }\n }\n .link {\n fill: none;\n stroke-width: 1.5px;\n stroke: #aaa;\n }\n}\n</style>\n"]
303768
303768
  },
303769
303769
  _coverageSchema: "43e27e138ebf9cfc5966b082cf9a028302ed4184",
303770
- hash: "5f37394f4c641e085634b4de8a6e95016123fe5c"
303770
+ hash: "1e089864fe6792f2d8555180af07f2e5d7f516a5"
303771
303771
  };
303772
303772
  var coverage = global[gcv] || (global[gcv] = {});
303773
303773
 
@@ -304613,11 +304613,11 @@ function BioModelTreevue_type_script_lang_js_defineProperty(obj, key, value) { i
304613
304613
  cov_2hmzqbstvs.f[50]++;
304614
304614
  var value = (cov_2hmzqbstvs.s[204]++, _this5.$get(d.data, key, null));
304615
304615
  cov_2hmzqbstvs.s[205]++;
304616
- return value === null ? (cov_2hmzqbstvs.b[49][0]++, '') : (cov_2hmzqbstvs.b[49][1]++, "\n <div class=\"layout row mb-1\" style=\"max-width: 100%\"\">\n <div class=\"flex xs5\" style=\"width: 100%\">".concat(verboseName, "</div>\n <div class=\"flex xs7\" style=\"width: 100%\"><b>").concat(value, "</b></div>\n </div>\n "));
304616
+ return value === null ? (cov_2hmzqbstvs.b[49][0]++, '') : (cov_2hmzqbstvs.b[49][1]++, "\n <div class=\"layout row ma-3\" style=\"max-width: 100%\"\">\n <div class=\"flex xs5\" style=\"width: 100%\">".concat(verboseName, "</div>\n <div class=\"flex xs7\" style=\"width: 100%\"><b>").concat(value, "</b></div>\n </div>\n "));
304617
304617
  };
304618
304618
 
304619
304619
  cov_2hmzqbstvs.s[206]++;
304620
- return "\n <div class=\"v-tooltip__content\" style=\"width: 350px;\">\n ".concat(keyValue('experiment.system_id', 'System ID'), "\n ").concat(keyValue('experiment.identifier', 'Experiment ID'), "\n ").concat(keyValue('aliquot_id', 'Aliquot ID'), "\n ").concat(keyValue('sample.identifier', 'Sample ID'), "\n ").concat(keyValue('individual.identifier', 'Individual ID'), "\n ").concat(keyValue('experiment.technique.method', 'Method'), "\n ").concat(keyValue('experiment.technique.name', 'Technique Name'), "\n ").concat(keyValue('sample.disease.acronym', 'Disease'), "\n ").concat(keyValue('individual.species', 'Species'), "\n ").concat(keyValue('individual.center.acronym', 'Center'), "\n </div>\n ");
304620
+ return "\n <div class=\"v-tooltip__content tree-tooltip\" style=\"min-width: 350px;\">\n ".concat(keyValue('experiment.system_id', 'System ID'), "\n ").concat(keyValue('experiment.identifier', 'Experiment ID'), "\n ").concat(keyValue('aliquot_id', 'Aliquot ID'), "\n ").concat(keyValue('sample.identifier', 'Sample ID'), "\n ").concat(keyValue('individual.identifier', 'Individual ID'), "\n ").concat(keyValue('experiment.technique.method', 'Method'), "\n ").concat(keyValue('experiment.technique.name', 'Technique Name'), "\n ").concat(keyValue('sample.disease.acronym', 'Disease'), "\n ").concat(keyValue('individual.species', 'Species'), "\n ").concat(keyValue('individual.center.acronym', 'Center'), "\n </div>\n ");
304621
304621
  }
304622
304622
  }
304623
304623
  });
@@ -304637,8 +304637,8 @@ var BioModelTreevue_type_style_index_0_lang_scss_ = __webpack_require__("34ec");
304637
304637
 
304638
304638
  var BioModelTree_component = normalizeComponent(
304639
304639
  bioModels_BioModelTreevue_type_script_lang_js_,
304640
- BioModelTreevue_type_template_id_8ae628c2_render,
304641
- BioModelTreevue_type_template_id_8ae628c2_staticRenderFns,
304640
+ BioModelTreevue_type_template_id_2b110930_render,
304641
+ BioModelTreevue_type_template_id_2b110930_staticRenderFns,
304642
304642
  false,
304643
304643
  null,
304644
304644
  null,