diff --git a/backend/routes/action.js b/backend/routes/action.js index 08aefa68..9f2d11fb 100644 --- a/backend/routes/action.js +++ b/backend/routes/action.js @@ -181,15 +181,14 @@ router.get('/:id', checkAuth2, (req, res, next) => { //} //await getMainHierarchyOfPages(pages, hierarchyOfPages); - //console.log('final hierarchy'); - //console.log(hierarchyOfPages); + // console.log('final hierarchy'); + // console.log(hierarchyOfPages); res.status(200).json({ message: 'Action was found', action: actionResult[0], hierarchyOfPages: hierarchyOfPages }) - } - else{ + } else { res.status(200).json({ message: 'Action was found', action: actionResult[0], @@ -218,12 +217,41 @@ router.get('/:id', checkAuth2, (req, res, next) => { path: 'hasPages' } }) - .then(result => { + .then(async result => { if (result[0].published === true) { - res.status(200).json({ - message: 'Action was found', - action: result[0] - }) + let hierarchyOfPages = []; + let pages=result[0].hasPageSet.hasPages; + if(pages.length!=0){ + for(let i=0;i {{ showSubmitButton ? 'Choose another csv' : 'Choose csv' }} insert_drive_file - + Submit {{ fileToUpload.name }} send - + close - + - + Your csv contains too many lines diff --git a/src/app/app-engine/apps/crispr/crispr.component.ts b/src/app/app-engine/apps/crispr/crispr.component.ts index c2b4044b..961f4653 100644 --- a/src/app/app-engine/apps/crispr/crispr.component.ts +++ b/src/app/app-engine/apps/crispr/crispr.component.ts @@ -76,77 +76,80 @@ export class CrisprComponent { submitFile( sequence?: string ) { - let reader = new FileReader(); - reader.readAsText( this.fileToUpload ); + if ( !this.waitingForResponse ) { + this.progressBarValue = 0; + let reader = new FileReader(); + reader.readAsText( this.fileToUpload ); - reader.onload = () => { + reader.onload = () => { - const csvRecordsArray = (reader.result).split(/\r\n|\n/); + const csvRecordsArray = (reader.result).split(/\r\n|\n/); - if ( csvRecordsArray.length > 20001 ) { - this.csvContainsTooManyLines = true; - } + if ( csvRecordsArray.length > 20001 ) { + this.csvContainsTooManyLines = true; + } - this.sequencesArray = []; + this.sequencesArray = []; - for ( const sequenceEntry of csvRecordsArray ) { - this.sequencesArray.push( { - sequence: sequenceEntry.split( ',')[ 0 ], - positions: sequenceEntry.split( ',')[ 1 ] - } ); - } + for ( const sequenceEntry of csvRecordsArray ) { + this.sequencesArray.push( { + sequence: sequenceEntry.split( ',')[ 0 ], + positions: sequenceEntry.split( ',')[ 1 ] + } ); + } - this.sequencesArray.shift(); + this.sequencesArray.shift(); - let arrayForTable = []; + let arrayForTable = []; - if ( this.sequencesArray.length > 100 ) { - arrayForTable = this.sequencesArray.slice( 0, 100 ); - } else { - arrayForTable = this.sequencesArray; - } + if ( this.sequencesArray.length > 100 ) { + arrayForTable = this.sequencesArray.slice( 0, 100 ); + } else { + arrayForTable = this.sequencesArray; + } - this.dataSource = new MatTableDataSource( arrayForTable ); + this.dataSource = new MatTableDataSource( arrayForTable ); - }; + }; - reader.onerror = function () { - console.log('error is occured while reading file!'); - }; + reader.onerror = function () { + console.log('error is occured while reading file!'); + }; - this.errorMessage = undefined; + this.errorMessage = undefined; - this.selectedSequences = [ sequence ]; + this.selectedSequences = [ sequence ]; - this.form.set( 'selectedBaseEditor', this.selectedBaseEditor ); - this.form.set( 'selectedPredictionType', this.selectedPredictionType ); - this.form.set( 'selectedSequences', this.selectedAction === 'plot' ? this.selectedSequences.toString() : undefined ); - this.form.set( 'selectedAction', this.selectedAction ); + this.form.set( 'selectedBaseEditor', this.selectedBaseEditor ); + this.form.set( 'selectedPredictionType', this.selectedPredictionType ); + this.form.set( 'selectedSequences', this.selectedAction === 'plot' ? this.selectedSequences.toString() : undefined ); + this.form.set( 'selectedAction', this.selectedAction ); - this.submittedBaseEditor = this.selectedBaseEditor; - this.submittedPredictionType = this.selectedPredictionType; + this.submittedBaseEditor = this.selectedBaseEditor; + this.submittedPredictionType = this.selectedPredictionType; - this.waitingForResponse = true; + this.waitingForResponse = true; - this.updateProgressBar(); + this.updateProgressBar(); - console.log( this.sessionHash ); - this.http.post('http://172.23.39.73:4321', this.form, { responseType: 'blob' }) - .subscribe((val) => { - if ( this.selectedAction === 'plot' ) { - const blob = new Blob([ val as any ], { type: 'application/pdf' }); - const url = URL.createObjectURL(blob); - this.pathToFile = this.sanitizer.bypassSecurityTrustResourceUrl( url ); - this.fileHasChanged = false; - this.waitingForResponse = false; - } else if ( this.selectedAction === 'download' ) { - saveAs(val, 'predictions_' + this.selectedBaseEditor + '_' + this.selectedPredictionType + '.zip'); + console.log( this.sessionHash ); + this.http.post('http://172.23.39.73:4321', this.form, { responseType: 'blob' }) + .subscribe((val) => { + if ( this.selectedAction === 'plot' ) { + const blob = new Blob([ val as any ], { type: 'application/pdf' }); + const url = URL.createObjectURL(blob); + this.pathToFile = this.sanitizer.bypassSecurityTrustResourceUrl( url ); + this.fileHasChanged = false; + this.waitingForResponse = false; + } else if ( this.selectedAction === 'download' ) { + saveAs(val, 'predictions_' + this.selectedBaseEditor + '_' + this.selectedPredictionType + '.zip'); + this.waitingForResponse = false; + } + }, error => { + this.errorMessage = error.message; this.waitingForResponse = false; - } - }, error => { - this.errorMessage = error.message; - this.waitingForResponse = false; - }); + }); + } } /** Whether the number of selected elements matches the total number of rows. */ diff --git a/src/app/app-engine/apps/d3js/bar-chart/bar-chart.component.html b/src/app/app-engine/apps/d3js/bar-chart/bar-chart.component.html index 7d33475f..53524b4d 100644 --- a/src/app/app-engine/apps/d3js/bar-chart/bar-chart.component.html +++ b/src/app/app-engine/apps/d3js/bar-chart/bar-chart.component.html @@ -1,23 +1,56 @@
-
-
- - - +
+
+
+
+
Chart width
+ + + +
+
+ +
+
-
- - +
+
+
+
+
+ + + + +
+
+
+
+
 
+ + + + +
+
+
@@ -33,7 +66,7 @@ [(ngModel)]="this.isSorted" (change)="reDrawBarChart()" > - Sort by value + Sorted by value
diff --git a/src/app/app-engine/apps/d3js/bar-chart/bar-chart.component.scss b/src/app/app-engine/apps/d3js/bar-chart/bar-chart.component.scss index 7804d7b5..f5d7d9fa 100644 --- a/src/app/app-engine/apps/d3js/bar-chart/bar-chart.component.scss +++ b/src/app/app-engine/apps/d3js/bar-chart/bar-chart.component.scss @@ -4,12 +4,10 @@ overflow: hidden; } -.scaleBarChartInput { - -} - -.scaleBarChartSlider { - +.selectBarChartRange { + margin: 10px 0 0 30px; + width: 128px; + overflow: hidden; } .bar { @@ -49,8 +47,5 @@ z-index: 4; } -.barChartTooltipValue { - -} diff --git a/src/app/app-engine/apps/d3js/bar-chart/bar-chart.component.ts b/src/app/app-engine/apps/d3js/bar-chart/bar-chart.component.ts index 79bf92e7..83640403 100644 --- a/src/app/app-engine/apps/d3js/bar-chart/bar-chart.component.ts +++ b/src/app/app-engine/apps/d3js/bar-chart/bar-chart.component.ts @@ -75,10 +75,17 @@ export class BarChartComponent implements AfterViewChecked { */ alreadyInitialised = false; - imageWidth = 350; - newImageWidth = 0; + chartWidth: any = 350; + newChartWidth = 0; isSorted = false; + showRange = false; + rangeLabel: string; + rangeLowest: any; + newRangeLowest: any; + rangeHighest: any; + newRangeHighest: any; + private posX: number; private posY: number; /** @@ -111,8 +118,6 @@ export class BarChartComponent implements AfterViewChecked { } drawBarChart() { - // this.g = undefined; - // this.svgChart = undefined; this.initSvg(); this.initAxis(); this.drawAxis(); @@ -120,20 +125,24 @@ export class BarChartComponent implements AfterViewChecked { } private initSvg() { - if (this.newImageWidth !== 0) { - this.imageWidth = this.newImageWidth; + if (this.data.metadata.rangeLabel) { + this.showRange = true; + this.rangeLabel = this.data.metadata.rangeLabel; + } + if (this.newChartWidth !== 0) { + this.chartWidth = this.newChartWidth; } else { - if (this.data.data.length * 25 > this.imageWidth) { - this.imageWidth = this.data.data.length * 25; + if (this.data.data.length * 25 > this.chartWidth) { + this.chartWidth = this.data.data.length * 25; } else { - this.newImageWidth = this.imageWidth; + this.newChartWidth = this.chartWidth; } } this.svgChart = d3.select('#barChartChart_' + this.numberOfInitialisedComponent) .append('svg') - .attr('width', this.imageWidth) + .attr('width', this.chartWidth) .attr('height', 350); - this.width = this.imageWidth - this.margin.left - this.margin.right; + this.width = this.chartWidth - this.margin.left - this.margin.right; this.height = 350 - this.margin.top - this.margin.bottom; this.g = this.svgChart.append('g') .attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')'); @@ -151,12 +160,25 @@ export class BarChartComponent implements AfterViewChecked { * Initialize the components for the axis. */ private initAxis() { + if (this.data.metadata.rangeLabel) { + // CURRENTLY, NO LOWEST/HIGHEST ARE SHOWN BECAUSE THERE'S AN NGMODEL FOR NEWRANGELOWEST, ECT. WHICH IS STILL FALSE!!! + this.rangeLowest = d3Array.min(this.data.data, (d) => d3Array.min(d.ranges, (r) => r.point)); + this.rangeHighest = d3Array.max(this.data.data, (d) => d3Array.max(d.ranges, (r) => r.point)); + // console.log('lowest', d3Array.min(this.data.data, (d) => d3Array.min(d.ranges, (z) => z.range))); + // console.log('highest', d3Array.max(this.data.data, (d) => d3Array.max(d.ranges, (z) => z.range))); + + // Check if not default range + // If not default range, re-build this.data.data: "value" has to be total of all ranges.value within given range + // Similar to chartWidth/newChartWidth + } if (this.isSorted === true) { // Sort by value this.data.data.sort((a: any, b: any) => b.value - a.value); + } else { + this.data.data.sort((a: any, b: any) => a.value - b.value); } - this.x = d3Scale.scaleBand().range([0, this.imageWidth - this.margin.left - this.margin.right]) + this.x = d3Scale.scaleBand().range([0, this.chartWidth - this.margin.left - this.margin.right]) .paddingInner(0.1) .paddingOuter(0.1) .align(0.5); @@ -181,7 +203,7 @@ export class BarChartComponent implements AfterViewChecked { .attr('class', 'axis axis--y') .call(d3Axis.axisLeft(this.y).ticks(10)); - if (this.data.metadata) { + if (this.data.metadata.axes) { this.gYaxis.append('g') .append('text') .attr('transform', 'rotate(-90)') @@ -191,15 +213,16 @@ export class BarChartComponent implements AfterViewChecked { .attr('fill', 'black') .attr('font-weight', 'bold') .attr('text-anchor', 'middle') - .text(this.data.metadata.yAxis); + .text(this.data.metadata.axes.y); this.g.append('g') .append('text') - .attr('transform', 'translate(' + ((this.imageWidth - this.margin.left - this.margin.right) / 2) + ',' + (this.height + this.margin.top + 10) + ')') + // tslint:disable-next-line:max-line-length + .attr('transform', 'translate(' + ((this.chartWidth - this.margin.left - this.margin.right) / 2) + ',' + (this.height + this.margin.top + 10) + ')') .attr('fill', 'black') .attr('font-weight', 'bold') .style('text-anchor', 'middle') - .text(this.data.metadata.xAxis); + .text(this.data.metadata.axes.x); } } diff --git a/src/app/app-engine/apps/d3js/radial-barchart/radial-barchart.component.ts b/src/app/app-engine/apps/d3js/radial-barchart/radial-barchart.component.ts index 49a38986..16572621 100644 --- a/src/app/app-engine/apps/d3js/radial-barchart/radial-barchart.component.ts +++ b/src/app/app-engine/apps/d3js/radial-barchart/radial-barchart.component.ts @@ -8,6 +8,7 @@ import * as d3Shape from 'd3-shape'; import * as d3Interpolate from 'd3-interpolate'; import { STATISTICS } from './STATISTICS'; +import * as d3ScaleChromatic from 'd3-scale-chromatic'; @Component({ selector: 'app-radial-barchart', @@ -51,21 +52,22 @@ export class RadialBarchartComponent implements AfterViewChecked { this.alreadyInitialised = true; setTimeout(() => { // this.formatNumber = d3Format.format('s'); - this.color = d3Scale.scaleOrdinal() - .range([ - '#8dd3c7', - '#ffffb3', - '#bebada', - '#fb8072', - '#80b1d3', - '#fdb462', - '#b3de69', - '#fccde5', - '#d9d9d9', - '#bc80bd', - '#ccebc5', - '#ffed6f' - ]); + // this.color = d3Scale.scaleOrdinal() + // .range([ + // '#8dd3c7', + // '#ffffb3', + // '#bebada', + // '#fb8072', + // '#80b1d3', + // '#fdb462', + // '#b3de69', + // '#fccde5', + // '#d9d9d9', + // '#bc80bd', + // '#ccebc5', + // '#ffed6f' + // ]); + this.color = d3Scale.scaleOrdinal(d3ScaleChromatic.schemePaired); this.initSvg(); this.initAxis(); }, 100); diff --git a/src/app/app-engine/apps/d3js/stacked-bar-chart/stacked-bar-chart.component.html b/src/app/app-engine/apps/d3js/stacked-bar-chart/stacked-bar-chart.component.html index 19402fb4..10050cb0 100644 --- a/src/app/app-engine/apps/d3js/stacked-bar-chart/stacked-bar-chart.component.html +++ b/src/app/app-engine/apps/d3js/stacked-bar-chart/stacked-bar-chart.component.html @@ -1,3 +1,42 @@ -
-
+
+
+ + + +
+
+ + +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+ Sorted by value +
diff --git a/src/app/app-engine/apps/d3js/stacked-bar-chart/stacked-bar-chart.component.scss b/src/app/app-engine/apps/d3js/stacked-bar-chart/stacked-bar-chart.component.scss index eb7b98da..8af407c6 100644 --- a/src/app/app-engine/apps/d3js/stacked-bar-chart/stacked-bar-chart.component.scss +++ b/src/app/app-engine/apps/d3js/stacked-bar-chart/stacked-bar-chart.component.scss @@ -1,3 +1,59 @@ -.axis .domain { +:host /deep/ .scaleStackedBarChartWidth { + margin: 10px 0 0 30px; + width: 128px; + overflow: hidden; +} + +:host /deep/ .scaleStackedBarChartInput { + +} + +:host /deep/ .scaleStackedBarChartSlider { + +} + +:host /deep/ rect { + stroke-width: 2; +} +:host /deep/ rect.disabled { + fill: transparent !important; +} + +:host /deep/ .legend_container { + position: relative; +} + +:host /deep/ .legend { + position: relative; +} + +:host /deep/ .chart_container { + position: relative; +} + +:host /deep/ .yaxis { + position: absolute; + float: left; + z-index: 3; +} + +:host /deep/ .chart { + position: relative; + overflow-x: auto; +} + +:host /deep/ .sortChart { + margin:0 0 0 30px; +} + +/* tooltip */ +:host /deep/ .stackedBarChartTooltip { + position: fixed; display: none; + text-align: center; + z-index: 4; +} + +:host /deep/ .stackedBarChartTooltipLabel { + font-weight: bold; } diff --git a/src/app/app-engine/apps/d3js/stacked-bar-chart/stacked-bar-chart.component.ts b/src/app/app-engine/apps/d3js/stacked-bar-chart/stacked-bar-chart.component.ts index 6683fa69..81f92f3e 100644 --- a/src/app/app-engine/apps/d3js/stacked-bar-chart/stacked-bar-chart.component.ts +++ b/src/app/app-engine/apps/d3js/stacked-bar-chart/stacked-bar-chart.component.ts @@ -1,149 +1,320 @@ -import {AfterViewChecked, Component, Input, OnInit, ViewEncapsulation} from '@angular/core'; - +import {AfterViewChecked, Component, Input} from '@angular/core'; import * as d3 from 'd3-selection'; import * as d3Scale from 'd3-scale'; -import * as d3Shape from 'd3-shape'; -import * as d3Axis from 'd3-axis'; import * as d3Array from 'd3-array'; - -export interface Margin { - top: number; - right: number; - bottom: number; - left: number; -} +import * as d3ScaleChromatic from 'd3-scale-chromatic'; +import * as d3Axis from 'd3-axis'; +import * as d3Shape from 'd3-shape'; @Component({ selector: 'app-stacked-bar-chart', - encapsulation: ViewEncapsulation.None, templateUrl: './stacked-bar-chart.component.html', styleUrls: ['./stacked-bar-chart.component.scss'] }) + export class StackedBarChartComponent implements AfterViewChecked { @Input() initialised = false; @Input() numberOfInitialisedComponent: number; @Input() data: any; + title = 'Grouped Bar Chart'; alreadyInitialised = false; + width: any; + newWidth: number; + private posX: number; + private posY: number; + chartWidthFactor = 100; + titleYaxis: string; + isSorted = false; - title = 'Stacked Bar Chart'; - - private margin: Margin; - - private width: number; - private height: number; - - private svg: any; // TODO replace all `any` by the right type - private x: any; - private y: any; - private z: any; - private g: any; - - constructor() {} + constructor() { + } ngAfterViewChecked() { - // console.log( this.data ); - if ( this.initialised && !this.alreadyInitialised && this.data !== undefined && this.data.data.length > 0 ) { - this.alreadyInitialised = true; - setTimeout(() => { - this.initMargins(); - this.initSvg(); - this.drawChart(this.data.data); - }, 300); + // console.log( this.numberOfInitialisedComponent, this.data ); + if ( this.initialised && !this.alreadyInitialised && this.data && this.data.data ) { + if (typeof this.data === 'string' && IsJsonString(this.data) && JSON.parse(this.data).length > 0) { + const help = this.data; + this.data = {}; + this.data.data = JSON.parse(help); + this.alreadyInitialised = true; + setTimeout(() => { + // console.log( this.data ); + this.drawD3(this.data.data, 0); + }, 500); + } else if (typeof this.data !== 'string') { + this.alreadyInitialised = true; + setTimeout(() => { + // console.log(this.data); + this.drawD3(this.data.data, 0); + }, 500); + } } } - generateComponentDivClass() { - return 'stackedBarChart' + this.numberOfInitialisedComponent; - } + drawD3(data: Array, width: number) { + // console.log(width); + if (width === 0) { + width = this.data.data.length * this.chartWidthFactor; + } - private initMargins() { - this.margin = {top: 20, right: 20, bottom: 30, left: 40}; - } + this.width = width; + this.newWidth = width; - private initSvg() { - this.svg = d3.select('.' + this.generateComponentDivClass()) - .append('svg') - .attr('width', 400) // Change here for size of the bars - .attr('height', 500); - - this.width = +this.svg.attr('width') - this.margin.left - this.margin.right; - this.height = +this.svg.attr('height') - this.margin.top - this.margin.bottom; - this.g = this.svg.append('g').attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')'); - - this.x = d3Scale.scaleBand() - .rangeRound([0, this.width]) - .paddingInner(0.05) - .align(0.1); - this.y = d3Scale.scaleLinear() - .rangeRound([this.height, 0]); - this.z = d3Scale.scaleOrdinal() - .range(['#98abc5', '#8a89a6', '#7b6888', '#6b486b', '#a05d56', '#d0743c', '#ff8c00']); - } + // Remove current chart elements if already there + d3.select('#chart_' + this.numberOfInitialisedComponent).select('svg').remove(); + d3.select('#yaxis_' + this.numberOfInitialisedComponent).select('svg').remove(); + d3.select('#legend_' + this.numberOfInitialisedComponent).select('svg').remove(); - private drawChart(data: any[]) { - let keys = Object.getOwnPropertyNames(data[0]).slice(1); + // getting all the key names for the legend + const keys = Object.keys(data[0]).slice(1); data = data.map(v => { v.total = keys.map(key => v[key]).reduce((a, b) => a + b, 0); return v; }); - data.sort((a: any, b: any) => b.total - a.total); - this.x.domain(data.map((d: any) => d.xValue)); - this.y.domain([0, d3Array.max(data, (d: any) => d.total)]).nice(); - this.z.domain(keys); + if (this.isSorted === true) { + data.sort((a: any, b: any) => b.total - a.total); + } - this.g.append('g') - .selectAll('g') - .data(d3Shape.stack().keys(keys)(data)) - .enter().append('g') - .attr('fill', d => this.z(d.key)) - .selectAll('rect') - .data(d => d) - .enter().append('rect') - .attr('x', d => this.x(d.data.xValue)) - .attr('y', d => this.y(d[1])) - .attr('height', d => this.y(d[0]) - this.y(d[1])) - .attr('width', this.x.bandwidth()); - - this.g.append('g') - .attr('class', 'axis') - .attr('transform', 'translate(0,' + this.height + ')') - .call(d3Axis.axisBottom(this.x)); + // setting size of and spacing between legend squares + const legendRectSize = 25; + const legendSpacing = 6; + + // setting svg chart dimensions + // this.width = width !== undefined ? width : this.data.data.length * 100; + const height = 350; + + // setting margins + const margin = { + top: 20, + right: 20, + bottom: 50, + left: 40 + }; + + // setting a d3.js color scheme for the legend + const color = d3Scale.scaleOrdinal(d3ScaleChromatic.schemePaired); + + // creating the yaxis + const svgYaxis = d3.select('#yaxis_' + this.numberOfInitialisedComponent) + .append('svg') + .attr('width', 50) + .attr('height', height + margin.top + margin.bottom) + .append('g') + .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); // translate along x-axis and y-axis + + // creating the chart + const svgChart = d3.select('#chart_' + this.numberOfInitialisedComponent) + .append('svg') // appending an element + .attr('width', +width + +margin.left + +margin.right) + .attr('height', height + margin.top + margin.bottom) + .append('g') + .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); - this.g.append('g') + // creating the legend + const svgLegend = d3.select('#legend_' + this.numberOfInitialisedComponent) + .append('svg') // appending an element + .attr('width', 650 + margin.left + margin.right) // setting its width + .attr('height', ( keys.length * (legendRectSize + legendSpacing)) + margin.top + margin.bottom) // setting its height + .append('g') + .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')'); + + // scale for the x-axis + const x = d3Scale.scaleBand() + .domain(data.map((d) => { + return d.label; + })) // returns array of all the labels for the x-axis (["Verse 1", "Verse 2", ...]) + .range([0, width]) + .paddingInner(0.1) + .paddingOuter(0.5) + .align(0.5); + + // scale for the y-axis + const y = d3Scale.scaleLinear() + .domain([0, d3Array.max(data, (d: any) => d.total)]) + .range([height, 0]) + .nice(); // nicing the scale (ending on round values) + + function drawStacks(currentKeys) { + d3.selectAll('.gStack').remove(); + svgChart.append('g') + .attr('class', 'gStack') + .selectAll('g') + .data(d3Shape.stack().keys(currentKeys)(data)) + .enter() + .append('g') + .attr('fill', (d) => { + return color(d.key); + }) + .selectAll('rect') + .data(d => d) + .enter() + .append('rect') + .attr('class', 'barPart') + .attr('x', d => x(d.data.label)) + .attr('y', d => y(d[1])) + .attr('width', x.bandwidth()) + .attr('height', d => y(d[0]) - y(d[1])); + } + drawStacks(keys); + + svgChart.append('g') .attr('class', 'axis') - .call(d3Axis.axisLeft(this.y).ticks(null, 's')) - .append('text') - .attr('x', 2) - .attr('y', this.y(this.y.ticks().pop()) + 0.5) - .attr('dy', '0.32em') - .attr('fill', '#000') - .attr('font-weight', 'bold') - .attr('text-anchor', 'start') - .text('Population'); - - let legend = this.g.append('g') - .attr('font-family', 'sans-serif') - .attr('font-size', 10) - .attr('text-anchor', 'end') + .attr('transform', 'translate(0,' + height + ')') + .call(d3Axis.axisBottom(x)); + + svgYaxis.append('g') + .attr('class', 'y') + .call(d3Axis.axisLeft(y).ticks(null, 's')); + + if (this.data.metadata) { + svgYaxis.append('g') + .append('text') + .attr('transform', 'rotate(-90)') + .attr('y', 0 - (margin.left + 3)) + .attr('x', 0 - (height / 2)) + .attr('dy', '1em') + .attr('fill', 'black') + .attr('font-weight', 'bold') + .attr('text-anchor', 'middle') + .text(this.data.metadata.yAxis); + + svgChart.append('g') + .append('text') + .attr('transform', 'translate(' + width / 2 + ',' + (height + margin.top + 20) + ')') + .attr('fill', 'black') + .attr('font-weight', 'bold') + .style('text-anchor', 'middle') + .text(this.data.metadata.xAxis); + } + + // define tooltip + function doTooltip( component, posX, posY ) { + const tooltip = d3.select('#chart_' + component) + .append('div') + .attr('class', 'stackedBarChartTooltip') + .attr('id', 'stackedBarChartTooltip_' + component); + + tooltip.append('div') + .attr('class', 'stackedBarChartTooltipLabel') + .attr('id', 'stackedBarChartTooltipLabel_' + component); + + tooltip.append('div') + .attr('class', 'stackedBarChartTooltipCount') + .attr('id', 'stackedBarChartTooltipCount_' + component); + + const barPart = svgChart.selectAll('.barPart'); + + barPart.on('mouseover', (d) => { + // tooltip.select('#stackedBarChartTooltipLabel_' + this.numberOfInitialisedComponent).html(); + tooltip.select('#stackedBarChartTooltipCount_' + component).html(d[1] - d[0]); + tooltip.style('display', 'block'); + + onmousemove = (e) => { + posX = e.clientX + 20; + posY = e.clientY - 20; + tooltip.style('left', (posX) + 'px') + .style('top', (posY) + 'px'); + }; + + onmouseout = (e) => { + tooltip.style('display', 'none'); + }; + }); + + } + doTooltip(this.numberOfInitialisedComponent, this.posX, this.posY); + + const legend = svgLegend.append('g') .selectAll('g') - .data(keys.slice().reverse()) - .enter().append('g') - .attr('transform', (d, i) => 'translate(0,' + i * 20 + ')'); + .data(keys.slice()) + .enter() + .append('g') + .attr('transform', (d, i) => { + const thisHeight = legendRectSize + legendSpacing; + const vert = i * thisHeight; + return 'translate(' + -120 + ',' + vert + ')'; + }); legend.append('rect') - .attr('x', this.width - 19) - .attr('width', 19) - .attr('height', 19) - .attr('fill', this.z); + .attr('x', 150) + .attr('width', legendRectSize) + .attr('height', legendRectSize) + .attr('fill', color) + .attr('stroke', color) + .attr('stroke-width', 2) + .attr('cursor', 'pointer') + .on('click', (d) => { + update(d, this.numberOfInitialisedComponent, this.posX, this.posY); + }); legend.append('text') - .attr('x', this.width - 24) - .attr('y', 9.5) - .attr('dy', '0.32em') - .text(d => d); + .attr('x', 200) + .attr('y', legendRectSize - legendSpacing) + .text((d) => { + return d; + }); + + let filtered = []; + + function update(d, component, posX, posY) { + // update the array to filter the chart by: + console.log('d is just the legend label', d); + // add the clicked key if not included: + if (filtered.indexOf(d) === -1) { + filtered.push(d); + // if all bars are un-checked, reset: + if (filtered.length === keys.length) { + filtered = []; + } + } else { + filtered.splice(filtered.indexOf(d), 1); + } + + const newKeys = []; + keys.forEach(function (d) { + if (filtered.indexOf(d) === -1) { + newKeys.push(d); + } + }); + console.log(newKeys); + drawStacks(newKeys); + doTooltip(component, posX, posY); + + // update legend: + legend.selectAll('rect') + .transition() + .attr('fill', function (d) { + if (filtered.length) { + if (filtered.indexOf(d) === -1) { + return color(d); + } else { + return 'white'; + } + } else { + return color(d); + } + }) + .duration(100); + } // end of update() + + // Always sort data back by label + data.sort((a: any, b: any) => a.label - b.label); + console.log(data); + // ...and remove the 'total' key + data.map((d) => { + delete d.total; + }); } +} +function IsJsonString(str) { + try { + JSON.parse(str); + } catch (e) { + return false; + } + return true; } diff --git a/src/app/app-engine/apps/grouped-bar-chart-v2/grouped-bar-chart-v2.component.html b/src/app/app-engine/apps/grouped-bar-chart-v2/grouped-bar-chart-v2.component.html index 375fe907..039007f0 100644 --- a/src/app/app-engine/apps/grouped-bar-chart-v2/grouped-bar-chart-v2.component.html +++ b/src/app/app-engine/apps/grouped-bar-chart-v2/grouped-bar-chart-v2.component.html @@ -39,6 +39,6 @@ [(ngModel)]="this.isSorted" (change)="drawD3(this.data.data, this.newWidth)" > - Sort by value + Sorted by value
diff --git a/src/app/app-engine/apps/grouped-bar-chart-v2/grouped-bar-chart-v2.component.ts b/src/app/app-engine/apps/grouped-bar-chart-v2/grouped-bar-chart-v2.component.ts index a6ef9d03..3dc3c6ab 100644 --- a/src/app/app-engine/apps/grouped-bar-chart-v2/grouped-bar-chart-v2.component.ts +++ b/src/app/app-engine/apps/grouped-bar-chart-v2/grouped-bar-chart-v2.component.ts @@ -16,7 +16,7 @@ export class GroupedBarChartV2Component implements AfterViewChecked { @Input() data: any; title = 'Grouped Bar Chart'; alreadyInitialised = false; - width: number; + width: any; newWidth: number; private posX: number; private posY: number; @@ -76,7 +76,6 @@ export class GroupedBarChartV2Component implements AfterViewChecked { d3.select('#chart_' + this.numberOfInitialisedComponent).select('svg').remove(); d3.select('#yaxis_' + this.numberOfInitialisedComponent).select('svg').remove(); d3.select('#legend_' + this.numberOfInitialisedComponent).select('svg').remove(); - // console.log('Initialized component: ' + this.numberOfInitialisedComponent); // getting all the key names for the legend const keys = Object.keys(data[0]).slice(1); diff --git a/src/app/app-engine/apps/textlist-viewer/textlist-viewer.component.ts b/src/app/app-engine/apps/textlist-viewer/textlist-viewer.component.ts index 4e35a550..b574969d 100644 --- a/src/app/app-engine/apps/textlist-viewer/textlist-viewer.component.ts +++ b/src/app/app-engine/apps/textlist-viewer/textlist-viewer.component.ts @@ -1,6 +1,7 @@ -import {Component, OnInit, Input, OnChanges } from '@angular/core'; +import {Component, OnInit, Input, OnChanges, HostListener} from '@angular/core'; import {DomSanitizer, SafeHtml} from '@angular/platform-browser'; import {HttpClient} from '@angular/common/http'; +import {ActivatedRoute, Router} from '@angular/router'; @Component({ selector: 'app-textlist-viewer', @@ -11,12 +12,59 @@ export class TextlistViewerComponent implements OnChanges { @Input() textToDisplay; displayArray: boolean; safeHtml: SafeHtml; - + paramObj: any; constructor( private domSanitizer: DomSanitizer, - private http: HttpClient + private http: HttpClient, + public router: Router, + private route: ActivatedRoute ) { } + + @HostListener('click', ['$event']) + public onClick(event) { + // Handle same-page links with extra parameters through microservice-generated HTML + // Indicate links with pageID and 1-n custom parameters: + // e.g. /page?page=5f96f0f4a65e56001e21a3ee&verse-label=verse-001¶m2=two + if (event.target.tagName === 'A') { + // Create object of all current parameters + this.route.queryParamMap + .subscribe((params) => { + this.paramObj = { ...params.keys, ...params }; + } + ); + // Get current pageID + const currentPageID = this.paramObj.params.page; + // Get page ID in microservice-generated link + // It matches everything in the given href after 'page=' and before the next '&' + const targetPageID = event.target.href.match(/(?<=page=)(.*?)(?=&)/)[1]; + + // Check if it's a same-page link + if ( currentPageID === targetPageID ) { + // Get the complete custom parameter string after the given page ID + & (1-n) + const targetParamsString = event.target.href.split(targetPageID + '&')[1]; + // Split the query parameter string at '&' to get each parameter (["param1=one", "param2=two"]) + const targetParams = targetParamsString.split('&'); + + const addedParams = {}; + let i; + for (i = 0; i < targetParams.length; i++) { + const thisParam = targetParams[i].split('='); + addedParams[thisParam[0]] = thisParam[1]; + } + this.router.navigate( ['/page'], { + queryParams: addedParams, + queryParamsHandling: 'merge' + } ); + event.preventDefault(); + } else { + return; + } + } else { + return; + } + } + ngOnChanges() { if ( this.textToDisplay && this.textToDisplay.search( 'http' ) !== -1 ) { this.http.get( this.textToDisplay, { responseType: 'text' } ) diff --git a/src/app/app-engine/page/page/page.component.ts b/src/app/app-engine/page/page/page.component.ts index 1adaab27..befac0b9 100644 --- a/src/app/app-engine/page/page/page.component.ts +++ b/src/app/app-engine/page/page/page.component.ts @@ -544,6 +544,7 @@ export class PageComponent implements OnInit, AfterViewChecked { * This function is used to navigate to another page belonging to the current pageSet * */ navigateToOtherView(page: any) { + console.log( page ); this.router.navigate( [ 'page' ], { queryParams: { 'actionID': this.actionID, @@ -575,6 +576,7 @@ export class PageComponent implements OnInit, AfterViewChecked { this.pagesOfThisActtion[this.pagesOfThisActtion.length] = page; this.alreadyLoaded = true; } + console.log( this.pagesOfThisActtion, this.subPagesOfPage ); if ( goToPage ) { this.selectedPageIndex = this.pagesOfThisActtion.length - 1; this.router.navigate( [ 'page' ], {