{"version":3,"file":null,"sources":["../src/utils/colors.js","../src/datasources/Datasource.js","../src/datasources/HTTPDatasource.js","../src/datasources/WebsocketDatasource.js","../src/utils/defaults/linechart.js","../src/svg/components/svgContainer.js","../src/utils/functions.js","../src/utils/screen.js","../src/svg/base/svgAxis.js","../src/svg/components/xAxis.js","../src/svg/components/yAxis.js","../src/svg/components/xyAxes.js","../src/svg/components/lineset.js","../src/svg/components/legend.js","../src/svg/components/areaset.js","../src/svg/components/pointset.js","../src/utils/dataTransformation.js","../src/utils/dataSorting.js","../src/svg/strategy_linechart.js","../src/utils/defaults/barchart.js","../src/svg/components/barset.js","../src/svg/strategy_barchart.js","../src/utils/defaults/streamgraph.js","../src/svg/components/streamset.js","../src/svg/strategy_streamgraph.js","../src/utils/defaults/stackedArea.js","../src/svg/strategy_stackedArea.js","../src/utils/defaults/swimlane.js","../src/svg/components/timeBoxset.js","../src/svg/strategy_swimlane.js","../src/utils/defaults/gauge.js","../src/svg/components/dial.js","../src/svg/components/dialNeedle.js","../src/svg/components/textIndicator.js","../src/svg/strategy_gauge.js","../src/utils/defaults/networkgraph.js","../src/svg/components/nodeset.js","../src/svg/strategy_networkgraph.js","../src/utils/defaults/sunburst.js","../src/svg/components/xRadialAxis.js","../src/svg/components/yRadialAxis.js","../src/svg/components/radialAxes.js","../src/svg/components/sunburstDisk.js","../src/svg/strategy_sunburst.js","../src/utils/defaults/scatterplot.js","../src/svg/strategy_scatterplot.js","../src/svg/SvgStrategy.js","../src/utils/image.js","../src/charts/base/Chart.js","../src/charts/linechart.js","../src/charts/barchart.js","../src/charts/streamgraph.js","../src/charts/stackedArea.js","../src/charts/swimlane.js","../src/charts/gauge.js","../src/charts/scatterplot.js","../src/charts/sunburst.js","../src/charts/networkgraph.js"],"sourcesContent":["import {scaleOrdinal, scaleQuantile} from 'd3';\n\nconst paletteCategory1 = [\n '#e1c8df',\n '#9ecd9d',\n '#acd9d6',\n '#e4e36b',\n '#bfa1c5',\n '#e4d3b8',\n '#facba8',\n '#ced4ea',\n '#acd9d6'\n];\n\nconst paletteCategory2 = [\n '#b6dde2',\n '#6394af',\n '#e4e9ab',\n '#8ea876',\n '#f7dce1',\n '#cc878f',\n '#fadaac',\n '#f29a83',\n '#8d7e9e'\n];\n\nconst paletteCategory3 = [\n '#6b68a9',\n '#8cc590',\n '#b9487d',\n '#bfa1c5',\n '#4e6936',\n '#71bbc3',\n '#484156',\n '#ccaf44',\n '#d0553c'\n];\n\nconst paletteCategory4 = [\n '#f1a30d',\n '#1d4763',\n '#84c7bc',\n '#c1212d',\n '#8fbe46',\n '#076837',\n '#563a2d',\n '#563a2d',\n '#87325d'\n];\n\nconst paletteCategory5 = [\n '#f1a30d',\n '#0c3183',\n '#acd9d6',\n '#c1212d',\n '#8fbe46',\n '#076837',\n '#8a6338',\n '#8d2d84',\n '#f09bbc'\n];\n\nconst paletteCategory6 = [\n '#71bbc3',\n '#1d4763',\n '#8fbe46',\n '#4e6936',\n '#ee8998',\n '#c1212d',\n '#f5af3c',\n '#e95e2e',\n '#634484'\n];\n\nconst paletteCategory7 = [\n '#ea671e',\n '#684592',\n '#84b92a',\n '#cd131c',\n '#3c5ba2',\n '#5baddc',\n '#ffde06',\n '#5db68b',\n '#775e47'\n];\n\nconst paletteCategory8 = [\n '#ebd646',\n '#a50f38',\n '#00a096',\n '#f09bbc',\n '#065b78',\n '#72722a',\n '#005231',\n '#4d4e98',\n '#7c4d25'\n];\n\nconst paletteSequentialYellow = [\n '#fff1c6',\n '#fee5a7',\n '#fcda87',\n '#face64',\n '#f8bf4b',\n '#f6b030',\n '#f4a009',\n '#d28514',\n '#b36c17',\n '#955618',\n '#7a4317',\n '#613214',\n '#49230f'\n];\n\nconst paletteSequentialRedOrange = [\n '#ffecb8',\n '#fbd68b',\n '#f7bf5e',\n '#f3a82f',\n '#df7520',\n '#cd4925',\n '#be0a26',\n '#a81023',\n '#941320',\n '#80141d',\n '#6d1419',\n '#5a1215',\n '#470f0f'\n];\n\nconst paletteSequentialRed = [\n '#fde4d4',\n '#f1c4af',\n '#f7bf5e',\n '#db826a',\n '#d0614d',\n '#c73e36',\n '#be0a26',\n '#a81023',\n '#941320',\n '#80141d',\n '#6d1419',\n '#5a1215',\n '#470f0f'\n];\n\nconst paletteSequentialPink = [\n '#fbe3e3',\n '#f9cfcc',\n '#f0aaa9',\n '#ed7e7e',\n '#ea647b',\n '#e74576',\n '#e41270',\n '#c70f65',\n '#aa105c',\n '#8d1253',\n '#731448',\n '#5a123c',\n '#420e30'\n];\n\nconst paletteSequentialPurplePink = [\n '#f9d8e6',\n '#ebbed7',\n '#dda4c7',\n '#c890bb',\n '#b27daf',\n '#8a4c94',\n '#622181',\n '#622181',\n '#50216b',\n '#472060',\n '#3e1f55',\n '#361e4b',\n '#2d1c41'\n];\n\nconst paletteSequentialPurple = [\n '#f6e8f1',\n '#dcc5de',\n '#c2a3c9',\n '#a980b3',\n '#905e9f',\n '#793f8e',\n '#622181',\n '#592175',\n '#4f216b',\n '#462060',\n '#3d1f55',\n '#351e4b',\n '#2c1c41'\n];\n\nconst paletteSequentialBlue = [\n '#e5f2f9',\n '#d1e5f5',\n '#afd3ed',\n '#91afd7',\n '#738bbf',\n '#3c5a9e',\n '#0c3183',\n '#132a68',\n '#10204c',\n '#0b193b',\n '#06142f',\n '#051228',\n '#061020'\n];\n\nconst paletteSequentialLightBlue = [\n '#eff8fd',\n '#d9eff6',\n '#c2e5ef',\n '#a8dae8',\n '#90cbe4',\n '#76b8e1',\n '#5baddc',\n '#4d96cc',\n '#427ebc',\n '#3a67ab',\n '#324c88',\n '#29366b',\n '#1e2354'\n];\n\nconst paletteSequentialBlueViolet = [\n '#edf7e7',\n '#c8e3d2',\n '#91cdbf',\n '#41b5ab',\n '#218ba4',\n '#145d94',\n '#0c3183',\n '#0d2d76',\n '#0d2a6a',\n '#0e265e',\n '#0d2253',\n '#0c1e47',\n '#0b1a3c'\n];\n\nconst paletteSequentialTurquoise = [\n '#e2ecf6',\n '#cadfe6',\n '#b1d3d6',\n '#94c6c6',\n '#74b9b6',\n '#4caca6',\n '#00a096',\n '#008d89',\n '#007b7c',\n '#006a6f',\n '#005963',\n '#004a57',\n '#063b4c'\n];\n\nconst paletteSequentialLightGreen = [\n '#faf9de',\n '#e9efc3',\n '#d7e4a7',\n '#c5d989',\n '#b1ce6a',\n '#9cc34c',\n '#84b92a',\n '#6fa32b',\n '#5a8f2a',\n '#477c29',\n '#346b27',\n '#205b24',\n '#074d21'\n];\n\nconst paletteSequentialDarkGreen = [\n '#eaf3e5',\n '#c7d5be',\n '#a3ba9a',\n '#80a078',\n '#5c885a',\n '#357442',\n '#00632e',\n '#00592b',\n '#004e27',\n '#004423',\n '#033a1e',\n '#053019',\n '#052613'\n];\n\nconst paletteSequentialGreenBrown = [\n '#f7eccd',\n '#d9cba6',\n '#bcad82',\n '#a29162',\n '#887946',\n '#716330',\n '#5b501f',\n '#51461d',\n '#483d1b',\n '#3f3418',\n '#362b15',\n '#2d2311',\n '#231a0d'\n];\n\nconst paletteSequentialBrown = [\n '#f7eccd',\n '#eed3ab',\n '#e4bb89',\n '#dba269',\n '#ad7446',\n '#834d2c',\n '#5e2f19',\n '#552a18',\n '#4c2516',\n '#432113',\n '#3a1c11',\n '#32180f',\n '#29130b'\n];\n\nconst paletteSequentialGrey = [\n '#e5e8ea',\n '#bdbfc3',\n '#999a9f',\n '#77797f',\n '#595c64',\n '#3e444c',\n '#253038',\n '#20282e',\n '#1a2024',\n '#15181b',\n '#0e1112',\n '#070808',\n '#000000'\n];\n\nconst paletteSequentialVioletCb = [\n '#f4f3f9',\n '#e0dced',\n '#cbc6e0',\n '#b7b0d4',\n '#948cbf',\n '#706baa',\n '#4d4e98',\n '#484889',\n '#42427a',\n '#3d3c6c',\n '#37365e',\n '#313050',\n '#2c2a44'\n];\n\nconst paletteSequentialPinkCb = [\n '#fbe5ee',\n '#f8ccd5',\n '#f4b2bc',\n '#f096a3',\n '#d56976',\n '#bc3f52',\n '#a50f38',\n '#951735',\n '#851b31',\n '#761d2e',\n '#671e2a',\n '#581d26',\n '#4a1c22'\n];\n\nconst paletteSequentialBlueCb = [\n '#eaf6fc',\n '#cfe4f4',\n '#cfe4f4',\n '#91bfe3',\n '#6999bb',\n '#417797',\n '#065b78',\n '#11536b',\n '#174b5f',\n '#194354',\n '#1a3b49',\n '#1a343f',\n '#192d35'\n];\n\nconst paletteSequentialGreenCb = [\n '#fff7d0',\n '#e9e09b',\n '#d1ca62',\n '#b7b623',\n '#9e9e28',\n '#88872a',\n '#72722a',\n '#676726',\n '#5c5c23',\n '#51511f',\n '#47471b',\n '#3d3d17',\n '#333413'\n];\n\nconst paletteSequentialGreenBrownCb = [\n '#f2edde',\n '#d8d1c0',\n '#bfb699',\n '#a09778',\n '#837b5a',\n '#686141',\n '#4f4b2c',\n '#3e3e1f',\n '#2e3313',\n '#292d14',\n '#232613',\n '#1e2012',\n '#191a10'\n];\n\nconst paletteDivergingSpectral1 = [\n '#98141f',\n '#ab332c',\n '#bf5040',\n '#d5705b',\n '#e4a57f',\n '#f3d6a6',\n '#f5f2b8',\n '#cfdbb1',\n '#a4c4a9',\n '#71ada1',\n '#4e868f',\n '#2e637d',\n '#06456c'\n];\n\nconst paletteDivergingSpectral2 = [\n '#d43d4f',\n '#df564b',\n '#eb6d45',\n '#f08e53',\n '#f8b96f',\n '#fee08b',\n '#f5f2b8',\n '#d7e5b1',\n '#b5d7aa',\n '#8ec8a3',\n '#6abda3',\n '#4fa4b5',\n '#3489be'\n];\n\nconst paletteDivergingSpectral3 = [\n '#651035',\n '#ae1143',\n '#c9314b',\n '#dd7257',\n '#eeb27a',\n '#feeb9e',\n '#f5f2b8',\n '#cadfba',\n '#96cabb',\n '#50b4bb',\n '#3eaecc',\n '#206791',\n '#0c2c63'\n];\n\nconst paletteDivergingBrownTurquoise = [\n '#3f3128',\n '#683828',\n '#933624',\n '#d5705b',\n '#db9c5e',\n '#feeb9e',\n '#f5f2b8',\n '#cfdbb1',\n '#a4c4a9',\n '#71ada1',\n '#628f85',\n '#53746d',\n '#475b57'\n];\n\nconst paletteDivergingOrangePink = [\n '#e7511e',\n '#eb6929',\n '#ee7f37',\n '#f29446',\n '#f9c083',\n '#ffe9c3',\n '#ffeee3',\n '#f9cfc1',\n '#f3a9ab',\n '#db6882',\n '#c71360',\n '#891953',\n '#4b1c47'\n];\n\nconst paletteDivergingRedBlue = [\n '#b2172b',\n '#c4443e',\n '#d76a5a',\n '#ed937e',\n '#f4b8a2',\n '#fcdbc7',\n '#efefef',\n '#bfcad5',\n '#8ba7bc',\n '#4d87a5',\n '#3c7ca0',\n '#28729b',\n '#036896'\n];\n\nconst paletteDivergingRedGrey = [\n '#b2172b',\n '#c54532',\n '#da6c3b',\n '#f29446',\n '#f8bc67',\n '#fee08b',\n '#efece5',\n '#c9c5c1',\n '#a5a19f',\n '#808080',\n '#666666',\n '#333333',\n '#000000'\n];\n\nconst paletteDivergingOrangeViolet = [\n '#98141f',\n '#ab332c',\n '#f9bc47',\n '#fdcf66',\n '#fede8d',\n '#ffecb3',\n '#f9eff6',\n '#e8d0e3',\n '#a4c4a9',\n '#a973aa',\n '#834f96',\n '#622181',\n '#402357'\n];\n\nconst paletteDivergingPurpleGreen = [\n '#59194b',\n '#85134b',\n '#c71360',\n '#db6882',\n '#eba7a8',\n '#fce0ca',\n '#faefe1',\n '#dbd9aa',\n '#b9c26e',\n '#94ad31',\n '#728b2b',\n '#546c25',\n '#39521f'\n];\n\nconst paletteDivergingVioletGreen = [\n '#55296e',\n '#75408e',\n '#8a5fa0',\n '#a081b5',\n '#beadcf',\n '#ddd7e7',\n '#eae8ed',\n '#c1d4bc',\n '#93be86',\n '#58a951',\n '#3c853e',\n '#23662f',\n '#084a22'\n];\n\nconst paletteDivergingRedGreen = [\n '#b2172b',\n '#c5403c',\n '#d96453',\n '#ef8972',\n '#f6b49c',\n '#fcdbc7',\n '#f9ebde',\n '#dad6a8',\n '#b9c16d',\n '#94ad31',\n '#728b2b',\n '#546c25',\n '#39521f'\n];\n\nconst paletteDivergingBrownGreen = [\n '#735146',\n '#846454',\n '#977a65',\n '#aa9177',\n '#c2ad91',\n '#dbcaad',\n '#edebd6',\n '#c4d6aa',\n '#94bf7c',\n '#58a951',\n '#3c853e',\n '#23662f',\n '#084a22'\n];\n\nconst paletteDivergingLightBrownTurquoise = [\n '#8b5219',\n '#a46821',\n '#bf812c',\n '#cfa151',\n '#e2c489',\n '#f6e8c3',\n '#f5f1df',\n '#cbdccc',\n '#9cc6b9',\n '#60afa6',\n '#359790',\n '#1d7d75',\n '#00665e'\n];\n\n\nexport function category1() {\n return scaleOrdinal().range(paletteCategory1);\n}\n\nexport function category2() {\n return scaleOrdinal().range(paletteCategory2);\n}\n\nexport function category3() {\n return scaleOrdinal().range(paletteCategory3);\n}\n\nexport function category4() {\n return scaleOrdinal().range(paletteCategory4);\n}\n\nexport function category5() {\n return scaleOrdinal().range(paletteCategory5);\n}\n\nexport function category6() {\n return scaleOrdinal().range(paletteCategory6);\n}\n\nexport function category7() {\n return scaleOrdinal().range(paletteCategory7);\n}\n\nexport function category8() {\n return scaleOrdinal().range(paletteCategory8);\n}\n\nexport function sequentialYellow() {\n return scaleQuantile().range(paletteSequentialYellow);\n}\n\nexport function sequentialRedOrange() {\n return scaleQuantile().range(paletteSequentialRedOrange);\n}\n\nexport function sequentialRed() {\n return scaleQuantile().range(paletteSequentialRed);\n}\n\nexport function sequentialPink() {\n return scaleQuantile().range(paletteSequentialPink);\n}\n\nexport function sequentialPurplePink() {\n return scaleQuantile().range(paletteSequentialPurplePink);\n}\n\nexport function sequentialPurple() {\n return scaleQuantile().range(paletteSequentialPurple);\n}\n\nexport function sequentialBlue() {\n return scaleQuantile().range(paletteSequentialBlue);\n}\n\nexport function sequentialLightBlue() {\n return scaleQuantile().range(paletteSequentialLightBlue);\n}\n\nexport function sequentialBlueViolet() {\n return scaleQuantile().range(paletteSequentialBlueViolet);\n}\n\nexport function sequentialTurquoise() {\n return scaleQuantile().range(paletteSequentialTurquoise);\n}\n\nexport function sequentialLightGreen() {\n return scaleQuantile().range(paletteSequentialLightGreen);\n}\n\nexport function sequentialDarkGreen() {\n return scaleQuantile().range(paletteSequentialDarkGreen);\n}\n\nexport function sequentialGreenBrown() {\n return scaleQuantile().range(paletteSequentialGreenBrown);\n}\n\nexport function sequentialBrown() {\n return scaleQuantile().range(paletteSequentialBrown);\n}\n\nexport function sequentialGrey() {\n return scaleQuantile().range(paletteSequentialGrey);\n}\n\nexport function sequentialVioletCb() {\n return scaleQuantile().range(paletteSequentialVioletCb);\n}\n\nexport function sequentialPinkCb() {\n return scaleQuantile().range(paletteSequentialPinkCb);\n}\n\nexport function sequentialBlueCb() {\n return scaleQuantile().range(paletteSequentialBlueCb);\n}\n\nexport function sequentialGreenCb() {\n return scaleQuantile().range(paletteSequentialGreenCb);\n}\n\nexport function sequentialGreenBrownCb() {\n return scaleQuantile().range(paletteSequentialGreenBrownCb);\n}\n\nexport function diverging_spectral1() {\n return scaleQuantile().range(paletteDivergingSpectral1);\n}\n\nexport function diverging_spectral2() {\n return scaleQuantile().range(paletteDivergingSpectral2);\n}\n\nexport function diverging_spectral3() {\n return scaleQuantile().range(paletteDivergingSpectral3);\n}\n\nexport function diverging_brown_turquoise() {\n return scaleQuantile().range(paletteDivergingBrownTurquoise);\n}\n\nexport function diverging_orange_pink() {\n return scaleQuantile().range(paletteDivergingOrangePink);\n}\n\nexport function diverging_red_blue() {\n return scaleQuantile().range(paletteDivergingRedBlue);\n}\n\nexport function diverging_red_grey() {\n return scaleQuantile().range(paletteDivergingRedGrey);\n}\n\nexport function diverging_orange_violet() {\n return scaleQuantile().range(paletteDivergingOrangeViolet);\n}\n\nexport function diverging_purple_green() {\n return scaleQuantile().range(paletteDivergingPurpleGreen);\n}\n\nexport function diverging_violet_green() {\n return scaleQuantile().range(paletteDivergingVioletGreen);\n}\n\nexport function diverging_red_green() {\n return scaleQuantile().range(paletteDivergingRedGreen);\n}\n\nexport function diverging_brown_green() {\n return scaleQuantile().range(paletteDivergingBrownGreen);\n}\n\nexport function diverging_lightBrown_turquoise() {\n return scaleQuantile().range(paletteDivergingLightBrownTurquoise);\n}","/**\n * \n * A Datasource is the name given to the connection set up to a data endpoint. This class defines the common methods for the datasources,\n * such as start() and stop().\n * \n * @export Default export: Datasource class\n * \n * @class Datasource The Datasource class\n * \n */\nexport default class Datasource {\n /**\n * Creates an instance of Datasource.\n * \n * \n * @memberOf Datasource\n \n */\n constructor() {\n this.filters = [];\n this.properties = [];\n }\n\n /**\n * Starts the stream of data\n * \n * \n * @memberOf Datasource\n */\n start() {\n window.console.log('Starting datasource');\n }\n\n /**\n * \n * If started, this method stops the stream of data\n * \n * @memberOf Datasource\n \n */\n stop() {\n window.console.log('Stopping datasource');\n }\n\n\n property(prop, newProp, cast) {\n this.properties.push({ 'p': prop, 'newP': newProp, cast: cast });\n return this;\n }\n\n\n convert(data) {\n let result = {};\n for (let i in this.properties) {\n let p = this.properties[i].p;\n let value = eval('data.' + this.properties[i].newP);\n // if(this.properties[i].cast){\n // value = new this.properties[i].cast(value);\n // }\n\n result[p] = value;\n }\n return result;\n }\n\n /**\n * Filters the incoming messages. Each data record that do not comply the filter condition will be discarded\n * \n * @param {any} filter A filter condition\n * @returns this Datasource instance\n * \n * @memberOf Datasource\n */\n filter(filter) {\n return this;\n }\n}","import Datasource from './Datasource';\nimport {request} from 'd3';\n\n/**\n * \n * This datasource set up a connection to a http server. \n * @export\n * @class HTTPDatasource\n * @extends {Datasource}\n\n */\nexport default class HTTPDatasource extends Datasource {\n\n /**\n * Creates an instance of HTTPDatasource. This datasource will try to connect to the speficied HTTP endpoint.\n *
\n * var source = {\n * endpoint: 'https://randomuser.me/api';\n * };\n * \n * linechart = new proteic.Linechart(new proteic.HTTPwDatasource(source));\n *\n * \n * If new data is available, this datasource will forward the data records to the chart, which automatically repaint the chart with these new records. \n * @param {any} source An http endpoint. If invalid, this class will throw an Error. \n * \n * @memberOf HTTPDatasource\n \n */\n constructor(source) {\n super();\n this.source = source;\n this.intervalId = -1;\n this.started = false;\n }\n\n /**\n * Configure a dispatcher for this datasource.\n * \n * @param {any} dispatcher A d3 dispatcher. This dispatcher is in charge of receiving and sending events.\n * \n * @memberOf HTTPDatasource\n */\n configure(dispatcher) {\n this.dispatcher = dispatcher;\n }\n\n /**\n * \n * Initialize an HTTP connection\n * \n * @memberOf HTTPDatasource\n \n */\n start() {\n if (!this.started) {\n super.start();\n let pollingTime = this.source.pollingTime;\n let url = this.source.url;\n this._startPolling(url, pollingTime);\n this.started = true;\n }\n }\n\n\n _startPolling(url, time = 1000) {\n let interval = window.setInterval;\n this.intervalId = interval(() => this._startRequest(url), time);\n }\n\n _startRequest(url) {\n\n window.console.log('url', url);\n request(url).get((e, response) => this._handleResponse(response));\n }\n\n _stopPolling() {\n let clearInterval = window.clearInterval;\n clearInterval(this.intervalId);\n }\n\n _handleResponse(xmlHttpRequest) {\n let parseJson = window.JSON.parse;\n if (xmlHttpRequest.readyState === 4 && xmlHttpRequest.status === 200) {\n let response = parseJson(xmlHttpRequest.response);\n this._handleOK(response);\n }\n else {\n this._handleError(xmlHttpRequest);\n }\n }\n\n _handleOK(data) {\n if(this.properties.length > 0 ) {\n data = this.convert(data);\n }\n this.dispatcher.call('onmessage', this, data);\n }\n\n _handleError(data) {\n this.dispatcher.call('onerror', this, data);\n }\n\n /**\n * If started, this method close the HTTP connection.\n * \n * @memberOf HTTPDatasource\n * */\n stop() {\n if (this.started) {\n this._stopPolling();\n this.started = false;\n }\n }\n}","import Datasource from './Datasource';\n\n/**\n * \n * This datasource set up a connection to a websocket server. \n * @export\n * @class WebsocketDatasource\n * @extends {Datasource}\n\n */\nexport default class WebsocketDatasource extends Datasource {\n\n /**\n * Creates an instance of WebsocketDatasource. This datasource will try to connect to the speficied websocket endpoint.\n *
\n * var source = {\n * endpoint: 'ws://192.168.3.32:3000/pathToWebsocketEndpoint';\n * };\n * \n * linechart = new proteic.Linechart(new proteic.WebsocketDatasource(source));\n *\n * \n * If new data is available, this datasource will forward the data records to the chart, which automatically repaint the chart with these new records. \n * @param {any} source A websocket endpoint. If invalid, this class will throw an Error. \n * \n * @memberOf WebsocketDatasource\n \n */\n constructor(source) {\n super();\n this.source = source;\n }\n \n /**\n * Configure a dispatcher for this datasource.\n * \n * @param {any} dispatcher A d3 dispatcher. This dispatcher is in charge of receiving and sending events.\n * \n * @memberOf WebsocketDatasource\n */\n configure(dispatcher) {\n this.dispatcher = dispatcher;\n }\n\n /**\n * \n * Initialize a websocket connection\n * \n * @memberOf WebsocketDatasource\n \n */\n start() {\n super.start();\n this.ws = new window.WebSocket(this.source.endpoint);\n\n this.ws.onopen = (e) => {\n this.dispatcher.call('onopen', this, e);\n };\n this.ws.onerror = (e) => {\n throw new Error('An error occurred trying to reach the websocket server' + e);\n //this.dispatcher.call('onerror', this, e);\n };\n this.ws.onmessage = (e) => {\n var data = JSON.parse(e.data);\n this.dispatcher.call('onmessage', this, data);\n };\n }\n /**\n * If started, this method close the websocket connection.\n * \n * @memberOf WebsocketDatasource\n * */\n stop() {\n super.stop();\n if (this.ws) {\n this.ws.close();\n }\n }\n}","import * as Colors from '../colors';\n\nexport const defaults = {\n selector: '#chart',\n colorScale: Colors.category7(),\n //Area\n areaOpacity: 0.4,\n\n //Axes\n xAxisType: 'linear',\n xAxisFormat: '',\n xAxisLabel: null,\n yAxisType: 'linear',\n yAxisFormat: '',\n yAxisLabel: null,\n //margins\n marginTop: 20,\n marginRight: 250,\n marginBottom: 130,\n marginLeft: 150,\n //markers\n markerShape: 'circle',\n markerSize: 5,\n markerOutlineWidth: 2,\n //Width & height\n width: '100%', // %, auto, or numeric\n height: 250,\n //Events\n onDown(d) {\n },\n onHover(d) {\n },\n onLeave(d) {\n },\n onClick(d) {\n },\n\n maxNumberOfElements: 100, // used by keepDrawing method to reduce the number of elements in the current chart\n};","import {select} from 'd3';\n\nexport class SvgContainer {\n\n constructor(config) {\n this._config = config;\n this.svg = this._initializeSvgContainer(config);\n this.components = Array();\n }\n\n _initializeSvgContainer(config) {\n let selector = config.selector,\n width = config.width + config.marginLeft + config.marginRight,\n height = config.height + config.marginTop + config.marginBottom,\n svg = null;\n\n svg = select(selector)\n .append('svg:svg')\n .attr('width', width)\n .attr('height', height)\n .append('g')\n .attr('class', 'chartContainer')\n .attr('transform', 'translate(' + config.marginLeft + ',' + config.marginTop + ')');\n\n return svg;\n }\n\n\n add(component, render = true) {\n this.components.push(component);\n\n if (render) {\n component.render(this.svg, this._config);\n }\n return this;\n }\n\n transform(translation) {\n this.svg.attr('transform', translation);\n\n }\n}","\nexport function isArray(d) {\n return d && d.constructor === Array && d instanceof Array;\n}\n\nexport function isObject(d) {\n return d && d.constructor === Object && d instanceof Object;\n}\n\nexport function isFunction(func) {\n return func && {}.toString.call(func) === '[object Function]';\n}\n\nexport function isNumeric(n) {\n return !isNaN(parseFloat(n)) && isFinite(n);\n}\n\nexport function isEven(n) {\n return n % 2 === 0;\n}\n\nexport function isPercentage(n) {\n let split = null;\n let number = null;\n if (!n || typeof n !== 'string') {\n return false;\n }\n split = n.split('%');\n number = (+split[0]);\n return split.length === 2 &&\n (number >= 0) &&\n (number <= 100);\n}\n\nexport function keys(array, field) {\n var keys = new Set();\n var element = null;\n\n if (!array || !array.length) {\n return [];\n }\n\n for (let i = 0; i < array.length; i++) {\n element = field ? array[i][field] : array[i];\n if (element) {\n keys.add(element);\n }\n }\n return keys;\n}\n\n\nexport function sortBy(array, o) {\n var _toString = Object.prototype.toString;\n var _parser = (x) => { return x; };\n var _getItem = (x) => {\n return _parser((x !== null && typeof x === 'object' && x[o.prop]) || x);\n };\n\n if (!(array instanceof Array) || !array.length) {\n return [];\n }\n if (_toString.call(o) !== '[object Object]') {\n o = {};\n }\n if (typeof o.parser !== 'function') {\n o.parser = _parser;\n }\n o.desc = o.desc ? -1 : 1;\n return array.sort((a, b) => {\n a = _getItem.call(o, a);\n b = _getItem.call(o, b);\n return o.desc * (a < b ? -1 : +(a > b));\n });\n}\n\nexport function findElement(arr, propName, propValue) {\n for (var i = 0; i < arr.length; i++) {\n if (arr[i][propName] === propValue) {\n return arr[i];\n }\n }\n return null;\n // will return null if not found; you could return a default instead\n}\n\nexport function deg2rad(deg) {\n return deg * Math.PI / 180;\n}","import {\n isNumeric, isPercentage\n} from './functions';\n\nimport { select } from 'd3';\n\nexport function calculateWidth(widthConfig, selector) {\n if (widthConfig === 'auto') {\n return select(selector)\n .node()\n .getBoundingClientRect()\n .width;\n }\n else if (isNumeric(widthConfig)) {\n return widthConfig;\n }\n else if (isPercentage(widthConfig)) {\n let containerWidth, percentage;\n containerWidth = select(selector)\n .node()\n .getBoundingClientRect()\n .width;\n percentage = widthConfig.split('%')[0];\n return Math.round(percentage * containerWidth / 100);\n } else {\n throw Error('Unknow config width value: ' + widthConfig);\n }\n}\n","import { SvgContainer } from '../components/svgContainer';\nimport { calculateWidth } from '../../utils/screen';\n\nexport class SvgAxis {\n /**\n * Creates an instance of SvgAxis.\n * \n * @param {any} context Chart context. It contains data, configuration and chart type\n * \n * @memberOf SvgAxis\n \n */\n constructor(context) {\n this._loadConfig(context.config);\n this.svgContainer = new SvgContainer(this.config);\n }\n\n changeConfigProperty(p, v) {\n this.config[p] = v;\n if (p === 'width' || p === 'height') {\n this.config.needRescaling = true;\n }\n }\n\n rescale(width = this.config.width, height = this.config.height) {\n this.axes.rescale(width, height);\n this.config.needRescaling = false;\n }\n\n /**\n * \n * Load the configuration context. It creates a configuration global from the parameters specified by users.\n * If any parameter is empty, this will be replaced by its default option \n * \n * @param {any} config User configuration\n * @param {any} defaults Defaults values for this chart\n * \n * @memberOf SvgAxis\n \n */\n _loadConfig(config, defaults) {\n this.config = {};\n //Selector\n this.config.selector = config.selector || defaults.selector;\n //Margins \n this.config.marginTop = config.marginTop || defaults.marginTop;\n this.config.marginLeft = config.marginLeft || defaults.marginLeft;\n this.config.marginRight = config.marginRight || defaults.marginRight;\n this.config.marginBottom = config.marginBottom || defaults.marginBottom;\n //Width & height\n this.config.width = config.width\n ? calculateWidth(config.width, this.config.selector) - this.config.marginLeft - this.config.marginRight\n : calculateWidth(defaults.width, this.config.selector) - this.config.marginLeft - this.config.marginRight;\n this.config.height = config.height || defaults.height;\n //Axis\n this.config.xAxisType = config.xAxisType || defaults.xAxisType;\n this.config.xAxisFormat = config.xAxisFormat || defaults.xAxisFormat;\n this.config.xAxisLabel = config.xAxisLabel || defaults.xAxisLabel;\n this.config.yAxisType = config.yAxisType || defaults.yAxisType;\n this.config.yAxisFormat = config.yAxisFormat || defaults.yAxisFormat;\n this.config.yAxisLabel = config.yAxisLabel || defaults.yAxisLabel;\n //Color\n this.config.colorScale = config.colorScale || defaults.colorScale;\n //Events\n this.config.onDown = config.onDown || defaults.onDown;\n this.config.onUp = config.onUp || defaults.onUp;\n this.config.onHover = config.onHover || defaults.onHover;\n this.config.onClick = config.onClick || defaults.onClick;\n this.config.onLeave = config.onLeave || defaults.onLeave;\n }\n\n}","import {isEven} from '../../utils/functions';\nimport {select, scaleTime, scaleLinear, scaleBand, axisBottom, format as d3Format} from 'd3';\n\nexport class XAxis {\n constructor(xAxisType, config) {\n if (config === null) {\n throw new Error('No chart context specified for XAxis');\n }\n\n this.xAxis = this._initializeXAxis(xAxisType, config);\n }\n\n\n rescale(width, height) {\n this.xAxis.scale().range([0, width]);\n }\n\n _initializeXAxis(xAxisType = 'linear', config) {\n let x = null,\n axis = null;\n\n // switch (xAxisType) {\n // case 'time':\n // x = scaleTime().range([0, config.width]);\n // break;\n // case 'linear':\n // x = scaleLinear().range([0, config.width]);\n // break;\n // case 'categorical':\n // x = scaleBand().rangeRound([0, config.width])\n // .padding(0.1)\n // .align(0.5);\n // break;\n // default:\n // throw new Error('Not allowed type for XAxis. Only allowed \"time\", \"linear\" or \"categorical\". Got: ' + xAxisType);\n // }\n\n switch (xAxisType) {\n case 'time':\n x = scaleTime().range([0, config.width]);\n axis = axisBottom(x);\n break;\n case 'linear':\n x = scaleLinear().range([0, config.width]);\n axis = axisBottom(x).tickFormat(d3Format(config.xAxisFormat));\n break;\n case 'categorical':\n x = scaleBand().rangeRound([0, config.width])\n .padding(0.1)\n .align(0.5);\n axis = axisBottom(x);\n break;\n default:\n throw new Error('Not allowed type for XAxis. Only allowed \"time\", \"linear\" or \"categorical\". Got: ' + xAxisType);\n }\n\n return axisBottom(x);\n }\n\n transition(svg, time = 200) {\n svg.selectAll('.x.axis').transition().duration(time).call(this.xAxis).on('end', this.xStyle);\n }\n\n xStyle() {\n select(this).selectAll('g.tick text')\n .style('font', '1.4em Montserrat, sans-serif')\n .style('fill', (d, i) => !isEven(i) || i === 0 ? '#5e6b70' : '#1a2127')\n .style('fill', (d) => '#1a2127')\n\n\n select(this).selectAll(['path', 'line'])\n .attr('stroke', 'gray')\n .attr('stroke-width', .3);\n\n }\n\n /**\n * This function is used when both x and y dial update their domains by x and y max/min values, respectively.\n */\n updateDomainByBBox(b) {\n let x = this.xAxis.scale();\n x.domain([b[0], b[1]]);\n }\n\n /**\n * Used when x domain is caterogial (a set of keys) and y domain is linear.\n */\n updateDomainByKeys(keys, yBbox) {\n let x = this.xAxis.scale();\n x.domain(keys);\n }\n\n render(svg, config) {\n let xAxis = this.xAxis,\n width = config.width,\n height = config.height;\n svg\n .append('g')\n .attr('class', 'x axis')\n .attr('transform', 'translate(0,' + config.height + ')')\n .call(xAxis);\n\n svg\n .append('text')\n .attr('class', 'xaxis-title')\n .attr(\"text-anchor\", \"middle\")\n .attr('x', width / 2)\n .attr('y', height + 40)\n .text(config.xAxisLabel)\n .style('font', '0.8em Montserrat, sans-serif');\n\n this.svg = svg;\n }\n}","import {isEven} from '../../utils/functions';\nimport {select, scaleLinear, scaleBand, axisLeft, format as d3Format} from 'd3';\n\nexport class YAxis {\n constructor(yAxisType, config) {\n if (config === null) {\n throw new Error('No chart context specified for XAxis');\n }\n\n this.yAxis = this._initializeYAxis(yAxisType, config);\n }\n\n rescale(width, height) {\n this.yAxis.tickSizeInner(-width);\n }\n\n _initializeYAxis(yAxisType = 'linear', config) {\n let y = null,\n axis = null;\n switch (yAxisType) {\n case 'linear':\n y = scaleLinear().range([config.height, 0]);\n axis = axisLeft(y).tickFormat(d3Format(config.yAxisFormat));\n break;\n case 'categorical':\n y = scaleBand().rangeRound([config.height, 0])\n .padding(0.1)\n .align(0.5);\n axis = axisLeft(y);\n break;\n default:\n throw new Error('Not allowed type for YAxis. Only allowed \"time\", \"linear\" or \"categorical\". Got: ' + yAxisType);\n }\n\n return axis.tickSizeInner(-config.width)\n .tickSizeOuter(0)\n .tickPadding(20);\n }\n\n // _initializeYAxis(yAxisType = 'linear', config) {\n // let y = null,\n // yAxis = null;\n //\n // switch (yAxisType) {\n // case 'linear':\n // y = scaleLinear().range([config.height, 0]);\n // break;\n // case 'categorical':\n // y = scaleBand().rangeRound([config.height, 0])\n // .padding(0.1)\n // .align(0.5);\n // break;\n // default:\n // throw new Error('Not allowed type for YAxis. Only allowed \"time\", \"linear\" or \"categorical\". Got: ' + yAxisType);\n // }\n // return axisLeft(y)\n // .tickSizeInner(-config.width)\n // .tickSizeOuter(0)\n // .tickPadding(20)\n // .tickFormat((d) => d)\n // .ticks(config.yticks, config.tickLabel);\n // }\n\n transition(svg, time = 200) {\n svg.selectAll('.y.axis').transition().duration(time).call(this.yAxis).on('end', this.yStyle);\n }\n\n yStyle() {\n select(this).selectAll('g.tick text')\n .style('font', '1.4em Montserrat, sans-serif')\n .style('fill', (d, i) => !isEven(i) || i === 0 ? '#5e6b70' : '#1a2127');\n select(this).selectAll('g.tick line')\n .style('stroke', (d, i) => isEven(i) && i !== 0 ? '#5e6b70' : '#dbdad8');\n }\n\n updateDomainByBBox(b) {\n let y = this.yAxis.scale();\n y.domain(b);\n }\n\n updateDomainByKeys(keys) {\n let y = this.yAxis.scale();\n y.domain(keys);\n }\n\n render(svg, config) {\n let yAxis = this.yAxis,\n width = config.width,\n height = config.height;\n svg\n .append('g')\n .attr('class', 'y axis')\n .attr('stroke-dasharray', '1, 5')\n .call(yAxis);\n\n svg\n .append('text')\n .attr('class', 'yaxis-title')\n .attr(\"transform\", \"rotate(-90)\")\n .attr(\"text-anchor\", \"middle\")\n .attr('x', 0 - height / 2)\n .attr('y', 0 - 55)\n .text(config.yAxisLabel)\n .style('font', '0.8em Montserrat, sans-serif');\n\n }\n}","import {XAxis} from './xAxis'\nimport {YAxis} from './yAxis'\n\nexport class XYAxes {\n constructor(xAxisType, yAxisType, config) {\n if (config === null) {\n throw new Error('No chart context specified for XAxis');\n }\n\n this.x = new XAxis(xAxisType, config);\n this.y = new YAxis(yAxisType, config);\n }\n\n transition(svg, time = 200) {\n this.x.transition(svg, time);\n this.y.transition(svg, time);\n }\n\n /**\n * This function is used when both x and y dial update their domains by x and y max/min values, respectively.\n */\n updateDomainByBBox(b) {\n this.x.updateDomainByBBox([b[0], b[1]]);\n this.y.updateDomainByBBox([b[2], b[3]]);\n }\n\n /**\n * Used when x domain is caterogial (a set of keys) and y domain is linear.\n */\n updateDomainByKeysAndBBox(keys, bbox) {\n this.x.updateDomainByKeys(keys);\n this.y.updateDomainByBBox(bbox);\n }\n\n updateDomainByBBoxAndKeys(bbox, keys){\n this.x.updateDomainByBBox(bbox);\n this.y.updateDomainByKeys(keys);\n }\n \n render(svg, config) {\n this.x.render(svg, config);\n this.y.render(svg, config);\n }\n \n rescale(width, height){\n this.x.rescale(width, height);\n this.y.rescale(width, height);\n }\n}","import {line, nest} from 'd3';\n\nexport class Lineset {\n constructor(x, y) {\n this.xAxis = x.xAxis;\n this.yAxis = y.yAxis;\n this.lineGenerator = line()\n .x((d) => this.xAxis.scale()(d.x))\n .y((d) => this.yAxis.scale()(d.y));\n }\n\n update(svg, config, data) {\n let dataSeries = nest().key((d) => d.key).entries(data),\n series = null,\n lines = null,\n colorScale = config.colorScale;\n\n svg.selectAll('g.serie').remove();\n\n series = svg.selectAll('g.serie');\n lines = series\n .data(dataSeries, (d) => d.key)\n .enter()\n .append('g')\n .attr('class', 'serie')\n .attr('stroke', (d, i) => colorScale(i))\n .append('svg:path')\n .style('stroke', (d, i) => colorScale(i))\n .style('stroke-width', 1.3)\n .style('fill', 'none')\n .attr('d', (d) => this.lineGenerator(d.values))\n .attr('class', 'line');\n\n this.svg = svg;\n }\n\n render(svg, config) {\n //Do nothing, since lines render only when new data is received.\n }\n}","import {nest} from 'd3';\n\nexport class Legend {\n constructor() {}\n\n update(svg, config, data) {\n let dataSeries = nest()\n .key((d) => d.key)\n .entries(data),\n legend = null,\n entries = null,\n colorScale = config.colorScale,\n height = config.height,\n width = config.width;\n\n if(dataSeries.length === 1 && dataSeries[0].key === 'undefined'){\n console.warn('Not showing legend, since there is a valid key');\n return;\n }\n \n svg.selectAll('g.legend').remove();\n \n legend = svg.append('g').attr('class', 'legend');\n entries = legend.selectAll('.legend-entry')\n .data(dataSeries, (d) => d.key)\n .enter()\n .append('g')\n .attr('class', 'legend-entry');\n\n\n entries.append('rect')\n .attr('x', width + 10)\n .attr('y', (d, i) => i * 25)\n .attr('height', 20)\n .attr('width', 20)\n .attr('fill', (d, i) => colorScale(i))\n .style('opacity', 0.8);\n\n entries.append('text')\n .attr(\"x\", width + 25 + 10)\n .attr(\"y\", (d, i) => i * 25 + 7)\n .attr(\"dy\", \"0.55em\")\n .text((d) => d.key)\n .style('font', '14px Montserrat, sans-serif');\n\n }\n\n render(svg, config) {\n //Do nothing, since legend render only when new data is received.\n }\n}","import {nest} from 'd3';\n\nexport class Areaset {\n constructor(x, y) {\n this.xAxis = x.xAxis;\n this.yAxis = y.yAxis;\n }\n\n update(svg, config, data) {\n let dataSeries = nest()\n .key((d) => d.key)\n .entries(data);\n\n let series = null\n , areas = null\n , area = config.area\n , colorScale = config.colorScale\n , height = config.height\n , areaOpacity = config.areaOpacity;\n\n let areaGenerator = d3.area()\n .x((d) => this.xAxis.scale()(d.x))\n .y0(height)\n .y1((d) => this.yAxis.scale()(d.y));\n\n svg.selectAll('g.area').remove();\n\n series = svg.selectAll('g.area');\n areas = series\n .data(dataSeries, (d) => d.key)\n .enter()\n .append('g')\n .attr('class', 'area')\n .append('svg:path')\n .style('fill', (d, i) => colorScale(i))\n .style('fill-opacity', areaOpacity)\n .attr('d', (d) => areaGenerator(d.values));\n\n // series\n // .insert('path', ':first-child') //if not :first-child, area overlaps markers.\n // .attr('class', 'area')\n // .data(dataSeries)\n // .style('stroke', (d, i) => colorScale(i))\n // .style('fill', (d, i) => colorScale(i))\n // .style('fill-opacity', areaOpacity)\n // .attr('d', (d) => areaGenerator(d.values));\n\n this.svg = svg;\n }\n\n render(svg, config) {\n //Do nothing, since areas render only when new data is received.\n }\n}","import {nest} from 'd3';\n\nexport class Pointset {\n constructor(x, y) {\n this.xAxis = x.xAxis;\n this.yAxis = y.yAxis;\n }\n update(svg, config, data) {\n let dataSeries = nest()\n .key((d) => d.key)\n .entries(data),\n markers = null,\n markerShape = config.markerShape,\n markerSize = config.markerSize,\n markerOutlineWidth = config.markerOutlineWidth,\n colorScale = config.colorScale,\n points = null,\n series = null;\n\n svg.selectAll('g.points').remove();\n\n series = svg.selectAll('g.points');\n\n switch (markerShape) {\n case 'dot':\n points = series\n .data(dataSeries, (d) => d.key)\n .enter()\n .append('g')\n .attr('class', 'points')\n .style('fill', (d, i) => colorScale(i))\n .selectAll('circle')\n .data((d) => d.values)\n .enter()\n .append('circle')\n .attr('cx', (d) => this.xAxis.scale()(d.x))\n .attr('cy', (d) => this.yAxis.scale()(d.y))\n .attr('r', markerSize)\n .attr('class', 'marker');\n break;\n case 'ring':\n window.console.warn('Deprecated \"circle\" marker shape: use \"dot\" or \"ring\" instead');\n points = series\n .data(dataSeries, (d) => d.key)\n .enter()\n .append('g')\n .attr('class', 'points')\n .style('stroke', (d, i) => colorScale(i))\n .selectAll('circle')\n .data((d, i) => d.values)\n .enter()\n .append('circle')\n .attr('cx', (d) => this.xAxis.scale()(d.x))\n .attr('cy', (d) => this.yAxis.scale()(d.y))\n .attr('r', markerSize)\n .attr('class', 'marker')\n .style('fill', 'white')\n .style('stroke-width', markerOutlineWidth);\n break;\n // Deprecated circle option\n case 'circle':\n window.console.warn('Deprecated \"circle\" marker shape: use \"dot\" or \"ring\" instead');\n points = series\n .data(dataSeries, (d) => d.key)\n .enter()\n .append('g')\n .attr('class', 'points')\n .style('stroke', (d, i) => colorScale(i))\n .selectAll('circle')\n .data((d, i) => d.values)\n .enter()\n .append('circle')\n .attr('cx', (d) => this.xAxis.scale()(d.x))\n .attr('cy', (d) => this.yAxis.scale()(d.y))\n .attr('r', markerSize)\n .attr('class', 'lineMarker')\n .style('fill', 'white')\n .style('stroke-width', markerOutlineWidth);\n break;\n default:\n points = series\n .data(dataSeries, (d) => d.key)\n .enter()\n .append('g')\n .attr('class', 'points')\n .style('stroke', (d, i) => colorScale(i))\n .selectAll('circle')\n .data((d, i) => d.values)\n .enter()\n .append('circle')\n .attr('cx', (d) => this.xAxis.scale()(d.x))\n .attr('cy', (d) => this.yAxis.scale()(d.y))\n .attr('r', markerSize)\n .attr('class', 'lineMarker')\n .style('fill', 'white')\n .style('stroke-width', markerOutlineWidth);\n }\n\n markers = svg.selectAll('g.points circle');\n markers\n .on('mousedown.user', config.onDown)\n .on('mouseup.user', config.onUp)\n .on('mouseleave.user', config.onLeave)\n .on('mouseover.user', config.onHover)\n .on('click.user', config.onClick);\n\n //this.interactiveElements = markers;\n }\n\n render(svg, config) {\n //Do nothing, since points render only when new data is received.\n }\n}","import {nest, timeParse} from 'd3';\n\nexport function simple2stacked(data) {\n return nest().key((d) => d.x).rollup((array) => {\n let r = {};\n for (let i = 0; i < array.length; i++) {\n let object = array[i];\n if (object) {\n r[object.key] = object.y;\n }\n }\n return r;\n }).entries(data);\n}\n\nexport function simple2nested(data, key = 'key') {\n return nest().key((d) => d[key]).entries(data);\n}\n\nexport function nested2simple(data) {\n let array = Array();\n for (let i = 0; i < data.length; i++) {\n for (let j = 0; j < data[i].values.length; j++) {\n let key = data[i].key;\n let x = data[i].values[j].x;\n let y = data[i].values[j].y;\n array.push({ key: key, x: x, y: y });\n }\n }\n return array;\n}\n\nexport function simple2Linked(data) {\n var linkedData = { links: [], nodes: [] };\n data.map((d) => d.key === 'link' ? linkedData.links.push(d) : linkedData.nodes.push(d));\n return linkedData;\n}\n\n\nexport function convertPropretiesToTimeFormat(data, properties, format) {\n data.forEach((d) => {\n properties.map((p) => {\n d[p] = timeParse(format)(d[p]);\n });\n });\n}\n\nexport function convertByXYFormat(data, config) {\n data.forEach((d) => {\n //parse x coordinate\n switch (config.xAxisType) {\n case 'time':\n d.x = timeParse(config.xAxisFormat)(d.x);\n break;\n case 'linear':\n d.x = +d.x;\n break;\n }\n //parse Y coordinate\n switch (config.yAxisType) {\n case 'time':\n d.y = timeParse(config.yAxisFormat)(d.y);\n break;\n case 'linear':\n d.y = +d.y;\n break;\n }\n });\n return data;\n}","export function sortByField (array, field){\n array.sort((e1, e2) => {\n var a = e1[field];\n var b = e2[field];\n return (a < b) ? -1 : (a > b) ? 1 : 0; \n });\n}","import { defaults } from '../utils/defaults/linechart';\nimport { SvgAxis } from './base/svgAxis';\nimport { XYAxes } from './components/xyAxes';\nimport { Lineset } from './components/lineset';\nimport { Legend } from './components/legend';\nimport { Areaset } from './components/areaset';\nimport { Pointset } from './components/pointset';\nimport { convertByXYFormat } from '../utils/dataTransformation';\nimport { sortByField } from '../utils/dataSorting';\nimport {min, max} from 'd3';\n\nexport class SvgLinechartStrategy extends SvgAxis {\n\n constructor(context) {\n super(context);\n\n this.axes = new XYAxes(this.config.xAxisType, 'linear', this.config);\n\n this.lines = new Lineset(this.axes.x, this.axes.y);\n this.legend = new Legend();\n\n //Include components in the chart container\n this.svgContainer\n .add(this.axes)\n .add(this.legend)\n .add(this.lines);\n\n if (this._checkArea(this.config)) {\n this.areas = new Areaset(this.axes.x, this.axes.y);\n this.svgContainer.add(this.areas);\n }\n\n if (this._checkMarkers(this.config)) {\n this.points = new Pointset(this.axes.x, this.axes.y);\n this.svgContainer.add(this.points);\n }\n }\n\n\t/**\n\t * Renders a linechart based on data object\n\t * @param {Object} data Data Object. Contains an array with x and y properties.\n\t * \n\t */\n draw(data) {\n let svg = this.svgContainer.svg,\n config = this.config,\n needRescaling = this.config.needRescaling,\n bbox = null;\n\n //Transform data, if needed\n convertByXYFormat(data, config);\n\n //Sort data\n sortByField(data, 'x');\n\n //rescale, if needed.\n if (needRescaling) {\n this.rescale();\n }\n\n\n bbox = this._getDomainBBox(data);\n\n this.axes.updateDomainByBBox(bbox);\n\n //Create a transition effect for dial rescaling\n this.axes.transition(svg, 200);\n\n // Update legend\n this.legend.update(svg, config, data);\n\n //Now update lines\n this.lines.update(svg, config, data);\n\n if (config.areaOpacity > 0) {\n // Update areas\n this.areas.update(svg, config, data);\n }\n\n if (this._checkMarkers(config)) {\n // Update points\n this.points.update(svg, config, data);\n }\n\n }\n\n _getDomainBBox(data) {\n var minX = min(data, (d) => d.x),\n maxX = max(data, (d) => d.x),\n minY = min(data, (d) => d.y),\n maxY = max(data, (d) => d.y);\n return [minX, maxX, minY, maxY];\n }\n\n\n _checkMarkers(config) {\n return config.markerSize > 0;\n }\n _checkArea(config) {\n return config.areaOpacity > 0;\n }\n\n /**\n * This method adds config options to the chart context.\n * @param {Object} config Config object\n */\n _loadConfig(config) {\n super._loadConfig(config, defaults);\n //Markers\n this.config.markerOutlineWidth = config.markerOutlineWidth || defaults.markerOutlineWidth;\n this.config.markerShape = config.markerShape || defaults.markerShape;\n this.config.markerSize = (typeof config.markerSize === 'undefined' || config.markerSize < 0) ? defaults.markerSize : config.markerSize;\n //Area\n this.config.areaOpacity = (typeof config.areaOpacity === 'undefined' || config.markerSize < 0) ? defaults.areaOpacity : config.areaOpacity;\n return this;\n }\n}","import * as Colors from '../colors';\n\nexport const defaults = {\n selector: '#chart',\n colorScale: Colors.category5(),\n //Stacked\n stacked: true,\n //Axes\n xAxisType: 'linear',\n xAxisFormat: '',\n xAxisLabel: null,\n yAxisType: 'linear',\n yAxisFormat: '',\n yAxisLabel: null,\n //margins\n marginTop: 20,\n marginRight: 250,\n marginBottom: 130,\n marginLeft: 150,\n //width & height\n width: '100%',\n height: 350,\n //Events\n onDown(d) {\n },\n onHover(d) {\n },\n onLeave(d) {\n },\n onClick(d) {\n }\n};","import {simple2nested} from '../../utils/dataTransformation'\nimport {select, map, line, scaleBand} from 'd3';\n\nexport class Barset {\n constructor(xAxis, yAxis) {\n this.xAxis = xAxis;\n this.yAxis = yAxis;\n this.lineGenerator = line()\n .x((d) => xAxis.scale()(d.x))\n .y((d) => yAxis.scale()(d.y));\n }\n\n\n update(svg, config, data, method) {\n let bars = null;\n\n if (method === 'stacked') {\n this._updateStacked(svg, config, data);\n } else {\n this._updateGrouped(svg, config, data);\n }\n bars = svg.selectAll('g.serie rect');\n bars\n .on('mousedown.user', config.onDown)\n .on('mouseup.user', config.onUp)\n .on('mouseleave.user', config.onLeave)\n .on('mouseover.user', config.onHover)\n .on('click.user', config.onClick);\n \n /**\n TODO: Add default events?\n bars\n .on('mousedown.default', config.onDown)\n .on('mouseup.default', config.onUp)\n .on('mouseleave.default', function (){ select(this).transition().duration(150).attr('fill-opacity', 1)})\n .on('mouseover.default', function (){ select(this).transition().duration(150).attr('fill-opacity', 0.9)})\n .on('click.default', config.onClick);\n **/\n\n this.interactiveElements = bars;\n }\n\n _updateStacked(svg, config, dataSeries) {\n this._cleanCurrentSeries(svg);\n\n let colorScale = config.colorScale,\n layer = svg.selectAll('.serie').data(dataSeries),\n layerEnter = layer.enter().append('g'),\n layerMerge = null,\n bar = null,\n barEnter = null,\n barMerge = null,\n x = this.xAxis.scale(),\n y = this.yAxis.scale();\n\n layerMerge = layer.merge(layerEnter)\n .attr('class', 'serie')\n .attr('fill', (d, i) => colorScale(i));\n\n bar = layerMerge.selectAll('rect')\n .data((d) => d);\n\n barEnter = bar.enter().append('rect');\n\n barMerge = bar.merge(barEnter)\n .attr(\"x\", (d) => x(d.data.key))\n .attr(\"y\", (d) => y(d[1]))\n .attr(\"height\", (d) => y(d[0]) - y(d[1]))\n .attr(\"width\", x.bandwidth());\n }\n\n\n _updateGrouped(svg, config, data) {\n this._cleanCurrentSeries(svg);\n\n let keys = map(data, (d) => d.key).keys(),\n colorScale = config.colorScale,\n layer = svg.selectAll('.serie').data(data),\n layerEnter = null,\n layerMerge = null,\n bar = null,\n barEnter = null,\n barMerge = null,\n x = this.xAxis.scale(),\n y = this.yAxis.scale(),\n xGroup = scaleBand().domain(keys).range([0, x.bandwidth()]),\n height = config.height;\n\n data = simple2nested(data, 'x');\n\n layer = svg.selectAll('.serie').data(data);\n\n layerEnter = layer.enter().append('g')\n .attr('transform', (d) => 'translate(' + x(d.key) + ')');\n\n layerMerge = layer.merge(layerEnter)\n .attr('class', 'serie')\n .attr('transform', (d) => 'translate(' + x(d.key) + ')');\n\n bar = layerMerge.selectAll('rect')\n .data((d) => d.values);\n\n barEnter = bar.enter().append('rect');\n\n barMerge = bar.merge(barEnter)\n .attr('width', xGroup.bandwidth())\n .attr(\"x\", (d) => xGroup(d.key))\n .attr('fill', (d, i) => colorScale(i))\n .attr(\"y\", (d) => y(d.y))\n .attr(\"height\", (d) => height - y(d.y));\n\n }\n\n _getKeysFromData(data) {\n let keys = [];\n for (let p in data[0]) {\n if (p !== 'total' && p !== 'key') {\n keys.push(p);\n }\n }\n return keys;\n\n }\n\n _cleanCurrentSeries(svg) {\n svg.selectAll('.serie').remove();\n }\n\n render(svg, config) {\n //Do nothing, since bars render only when new data is received.\n }\n}","import {defaults} from '../utils/defaults/barchart';\nimport {SvgAxis} from './base/svgAxis';\nimport {XYAxes} from './components/xyAxes';\nimport {Barset} from './components/barset';\nimport {Legend} from './components/legend';\nimport {simple2stacked} from '../utils/dataTransformation';\nimport {map, stack as d3Stack, stackOrderNone, max} from 'd3';\n\nexport class SvgBarchartStrategy extends SvgAxis {\n\n constructor(context) {\n super(context);\n\n this.axes = new XYAxes('categorical', 'linear', this.config);\n this.bars = new Barset(this.axes.x.xAxis, this.axes.y.yAxis);\n\n this.legend = new Legend();\n\n this.svgContainer\n .add(this.axes)\n .add(this.bars)\n .add(this.legend);\n\n }\n\n\n\t/**\n\t * Renders a barchart based on data object\n\t * @param {Object} data Data Object. Contains an array with x and y properties.\n\t * \n\t */\n draw(data = this.data) { \n let svg = this.svgContainer.svg,\n config = this.config,\n keys = map(data, (d) => d.key).keys(),\n data4stack = simple2stacked(data),\n data4render = null,\n isStacked = this.config.stacked,\n stack = d3Stack().keys(keys)\n .value((d, k) => d.value[k])\n .order(stackOrderNone),\n yMin = 0,\n yMax = 0,\n method = isStacked ? 'stacked' : 'grouped',\n dataSeries = stack(data4stack),\n needRescaling = this.config.needRescaling;\n\n //rescale, if needed.\n if (needRescaling) {\n this.rescale();\n }\n\n\n yMax = isStacked ?\n max(dataSeries, (serie) => max(serie, (d) => d[1])) :\n max(data, (d) => d.y);\n\n this.axes.updateDomainByKeysAndBBox(map(data, (d) => d.x).keys(), [yMin, yMax]);\n this.axes.transition(svg, 200);\n\n data4render = isStacked ? dataSeries : data;\n\n this.bars.update(svg, config, data4render, method);\n\n this.legend.update(svg, config, data);\n\n this.data = data; // TODO: ? \n\n }\n\n\n transition2Stacked() {\n this.config.stacked = true;\n }\n\n transition2Grouped() {\n this.config.stacked = false;\n }\n\n\t/**\n\t * This method adds config options to the chart context.\n\t * @param {Object} config Config object\n\t */\n _loadConfig(config) {\n super._loadConfig(config, defaults);\n //Stacked\n this.config.stacked = typeof (config.stacked) === 'undefined' ? defaults.stacked : config.stacked; return this;\n }\n}","import * as Colors from '../colors';\n\nexport const defaults = {\n selector: '#chart',\n colorScale: Colors.category4(),\n //Axes\n xAxisType: 'time',\n xAxisFormat: '%y/%m/%d',\n xAxisLabel: null,\n yAxisType: 'categorical',\n yAxisFormat: '',\n yAxisLabel: null,\n //margins\n marginTop: 20,\n marginRight: 250,\n marginBottom: 130,\n marginLeft: 150,\n //Width & height\n width: '100%', // %, auto, or numeric \n height: 250,\n //Events\n onDown(d) {\n },\n onHover(d) {\n },\n onLeave(d) {\n },\n onClick(d) {\n },\n\n maxNumberOfElements: 100, // used by keepDrawing method to reduce the number of elements in the current chart\n};","import {\n timeParse,\n curveCardinal,\n area\n} from 'd3';\n\nexport class Streamset {\n constructor(xAxis, yAxis) {\n this.xAxis = xAxis;\n this.yAxis = yAxis;\n\n this.areaGenerator = area()\n .curve(curveCardinal)\n .x((d) => this.xAxis.scale()((timeParse(this.xDataFormat)(d.data.key)))) // TODO: It seems d3.nest() transform Date object in\n .y0((d) => this.yAxis.scale()(d[0]))\n .y1((d) => this.yAxis.scale()(d[1]))\n }\n\n\n update(svg, config, data) {\n let series = null;\n \n //Update date format, used by areaGenerator function due to a problem when nesting with d3.\n this.xDataFormat = config.xAxisFormat;\n \n svg.selectAll('.serie').remove();\n\n series = svg.selectAll('.serie')\n .data(data)\n .enter()\n .append('g')\n .attr('class', 'serie')\n .style('stroke', (d, i) => config.colorScale(i));\n\n series\n .append('path')\n .attr('class', 'layer')\n .attr('d', this.areaGenerator)\n .style('fill', (d, i) => config.colorScale(i));\n\n\n series.exit().remove();\n \n series\n .attr('opacity', 1)\n .on('mousedown.user', config.onDown)\n .on('mouseup.user', config.onUp)\n .on('mouseleave.user', config.onLeave)\n .on('mouseover.user', config.onHover)\n .on('click.user', config.onClick);\n }\n\n render(svg, config) {\n //Do nothing, since lines render only when new data is received.\n }\n}","import {defaults} from '../utils/defaults/streamgraph';\nimport {SvgAxis} from './base/svgAxis';\nimport {XAxis} from './components/xAxis';\nimport {YAxis} from './components/yAxis';\nimport {Streamset} from './components/streamset';\nimport {Legend} from './components/legend';\nimport {simple2stacked} from '../utils/dataTransformation';\nimport {convertPropretiesToTimeFormat} from '../utils/dataTransformation';\nimport {sortByField} from '../utils/dataSorting';\nimport {min, max, map, stack as d3Stack, stackOrderInsideOut, stackOffsetWiggle} from 'd3';\n\nexport class SvgStreamgraphStrategy extends SvgAxis {\n\n constructor(context) {\n super(context);\n\n this.x = new XAxis('time', this.config);\n this.y = new YAxis('linear', this.config);\n\n this.streams = new Streamset(this.x.xAxis, this.y.yAxis);\n\n this.legend = new Legend();\n\n //Include components in the chart container\n this.svgContainer\n .add(this.x)\n .add(this.y, false) //No render y Axis\n .add(this.legend)\n .add(this.streams);\n }\n draw(data) {\n let svg = this.svgContainer.svg,\n config = this.config,\n bbox = null,\n keys = map(data, (d) => d.key).keys(),\n xDataFormat = this.config.xAxisFormat,\n data4stack = simple2stacked(data),\n stack = d3Stack()\n .keys(keys)\n .value((d, k) => d.value[k])\n .order(stackOrderInsideOut)\n .offset(stackOffsetWiggle),\n dataSeries = stack(data4stack),\n needRescaling = this.config.needRescaling;\n \n convertPropretiesToTimeFormat(data, ['x'], xDataFormat);\n \n //Sort data\n sortByField(data, 'x');\n \n //rescale, if needed.\n if (needRescaling) {\n this.rescale();\n }\n \n bbox = this._getDomainBBox(data, dataSeries);\n\n this.x.updateDomainByBBox([bbox[0], bbox[1]]);\n this.y.updateDomainByBBox([bbox[2], bbox[3]]);\n this.x.transition(svg, 200);\n this.y.transition(svg, 200);\n\n // Update legend\n this.legend.update(svg, config, data);\n\n // Update streams\n this.streams.update(svg, config, dataSeries);\n }\n \n _getDomainBBox(data, dataSeries) {\n let minX = min(data, (d) => new Date(d.x)),\n maxX = max(data, (d) => new Date(d.x)),\n minY = min(dataSeries, (serie) => min(serie, (d) => d[0])),\n maxY = max(dataSeries, (serie) => max(serie, (d) => d[1]));\n\n return [minX, maxX, minY, maxY];\n }\n\n\t/**\n\t * This method adds config options to the chart context.\n\t * @param {Object} config Config object\n\t */\n _loadConfig(config) {\n super._loadConfig(config,defaults);\n return this;\n }\n}","import * as Colors from '../colors';\n\nexport const defaults = {\n selector: '#chart',\n colorScale: Colors.category2(),\n //Axes\n xAxisType: 'time',\n xAxisFormat: '%y/%m/%d',\n xAxisLabel: null,\n yAxisType: 'categorical',\n yAxisFormat: '',\n yAxisLabel: null,\n //margins\n marginTop: 20,\n marginRight: 250,\n marginBottom: 130,\n marginLeft: 150,\n //Width & height\n width: '100%', // %, auto, or numeric \n height: 250,\n //Events\n onDown(d) {\n },\n onHover(d) {\n },\n onLeave(d) {\n },\n onClick(d) {\n },\n maxNumberOfElements: 100, // used by keepDrawing method to reduce the number of elements in the current chart\n};","import { defaults } from '../utils/defaults/stackedArea';\nimport { SvgAxis } from './base/svgAxis';\nimport { XYAxes } from './components/xyAxes';\nimport { Streamset } from './components/streamset';\nimport { Legend } from './components/legend';\nimport { simple2stacked } from '../utils/dataTransformation';\nimport { convertPropretiesToTimeFormat } from '../utils/dataTransformation';\nimport { sortByField } from '../utils/dataSorting';\nimport { min, max, map, stack as d3Stack, stackOrderInsideOut, stackOffNone} from 'd3';\n\nexport class SvgStackedAreaStrategy extends SvgAxis {\n\n constructor(context) {\n super(context);\n\n this.axes = new XYAxes('time', 'linear', this.config);\n\n this.streams = new Streamset(this.axes.x.xAxis, this.axes.y.yAxis);\n\n this.legend = new Legend();\n\n //Include components in the chart container\n this.svgContainer\n .add(this.axes)\n .add(this.legend)\n .add(this.streams);\n }\n\n\n draw(data) {\n let svg = this.svgContainer.svg,\n config = this.config,\n bbox = null,\n keys = map(data, (d) => d.key).keys(),\n data4stack = simple2stacked(data),\n xDataFormat = this.config.xAxisFormat,\n stack = d3Stack()\n .keys(keys)\n .value((d, k) => d.value[k])\n .order(stackOrderInsideOut)\n .offset(stackOffNone),\n dataSeries = stack(data4stack),\n needRescaling = this.config.needRescaling;\n\n //rescale, if needed.\n if (needRescaling) {\n this.rescale();\n }\n\n convertPropretiesToTimeFormat(data, ['x'], xDataFormat);\n\n //Sort data\n sortByField(data, 'x');\n\n bbox = this._getDomainBBox(data, dataSeries);\n\n this.axes.updateDomainByBBox(bbox);\n this.axes.transition(svg, 200);\n\n // Update legend\n this.legend.update(svg, config, data);\n\n // Update streams\n this.streams.update(svg, config, dataSeries);\n }\n\n\n _getDomainBBox(data, dataSeries) {\n let minX = min(data, (d) => (d.x)),\n maxX = max(data, (d) => (d.x)),\n minY = min(dataSeries, (serie) => min(serie, (d) => d[0])),\n maxY = max(dataSeries, (serie) => max(serie, (d) => d[1]));\n\n return [minX, maxX, minY, maxY];\n }\n\n\t/**\n\t * This method adds config options to the chart context.\n\t * @param {Object} config Config object\n\t */\n _loadConfig(config) {\n super._loadConfig(config,defaults);\n }\n}","import * as Colors from '../colors';\n\nexport const defaults = {\n selector: '#chart',\n colorScale: Colors.category3(),\n //Axes\n xAxisType: 'time',\n xAxisFormat: '%y/%m/%d',\n xAxisLabel: null,\n yAxisType: 'categorical',\n yAxisFormat: '%s',\n yAxisLabel: null,\n //margins\n marginTop: 20,\n marginRight: 250,\n marginBottom: 30,\n marginLeft: 50,\n //Width & height\n width: '100%', // %, auto, or numeric \n height: 250,\n //Events\n onDown(d) {\n },\n onHover(d) {\n },\n onLeave(d) {\n },\n onClick(d) {\n }\n};","import {simple2nested} from '../../utils/dataTransformation';\nimport {map, scaleBand, extent, scaleLinear} from 'd3';\n\nexport class TimeBoxset {\n\n constructor(xAxis, yAxis) {\n this.xAxis = xAxis;\n this.yAxis = yAxis;\n\n }\n update(svg, config, data) {\n let colorScale = config.colorScale,\n keys = map(data, (d) => d.key).keys(),\n layer = svg.selectAll('.serie').data(data),\n layerEnter = null,\n layerMerge = null,\n box = null,\n boxEnter = null,\n boxMerge = null,\n extLanes = null,\n yLanes = null,\n yLanesBand = scaleBand().range([0, keys.length + 1]).domain(keys),\n x = this.xAxis.scale(),\n y = this.yAxis.scale();\n\n data = simple2nested(data);\n extLanes = extent(data, (d, i) => i);\n yLanes = scaleLinear().domain([extLanes[0], extLanes[1] + 1]).range([0, config.height]);\n\n layer = svg.selectAll('.serie').data(data);\n layerEnter = layer.enter().append('g');\n\n layerMerge = layer.merge(layerEnter)\n .attr('class', 'serie');\n\n\n box = layerMerge.selectAll('rect')\n .data((d) => d.values);\n\n boxEnter = box.enter().append('rect');\n\n boxMerge = box.merge(boxEnter)\n .attr('width', (d) => x(d.end) - x(d.start))\n .attr('x', (d) => x(d.start))\n .attr('y', (d) => y(d.key))\n .attr('fill', (d) => colorScale(parseInt(yLanesBand(d.key))))\n .attr('height', () => 0.8 * yLanes(1));\n\n box = svg.selectAll('g.serie rect');\n \n box\n .on('mousedown.user', config.onDown)\n .on('mouseup.user', config.onUp)\n .on('mouseleave.user', config.onLeave)\n .on('mouseover.user', config.onHover)\n .on('click.user', config.onClick);\n\n }\n\n render(svg, config) {\n //Do nothing, since lines render only when new data is received.\n }\n}","import {defaults} from '../utils/defaults/swimlane';\nimport {SvgAxis} from './base/svgAxis';\nimport {XYAxes} from './components/xyAxes';\nimport {TimeBoxset} from './components/timeBoxset';\nimport {Legend} from './components/legend';\nimport {convertPropretiesToTimeFormat} from '../utils/dataTransformation';\nimport {min, max, map} from 'd3';\n\nexport class SvgSwimlaneStrategy extends SvgAxis {\n\n constructor(context) {\n super(context);\n this.axes = new XYAxes('time', 'categorical', this.config);\n this.boxs = new TimeBoxset(this.axes.x.xAxis, this.axes.y.yAxis);\n this.legend = new Legend();\n\n this.svgContainer\n .add(this.axes)\n .add(this.boxs)\n .add(this.legend);\n }\n\n draw(data) {\n let svg = this.svgContainer.svg,\n config = this.config,\n dataFormat = this.config.xAxisFormat,\n keys = map(data, (d) => d.key).keys(),\n bbox = null,\n needRescaling = this.config.needRescaling;\n\n convertPropretiesToTimeFormat(data, ['start', 'end'], dataFormat);\n \n //rescale, if needed.\n if (needRescaling) {\n this.rescale();\n }\n \n bbox = this._getBBox(data);\n\n this.axes.updateDomainByBBoxAndKeys(bbox, keys);\n this.axes.transition(svg, 200);\n\n this.boxs.update(svg, config, data);\n this.legend.update(svg, config, data);\n\n }\n \n _getBBox(data) {\n return [\n min(data, (d) => (d.start)),\n max(data, (d) => (d.end))\n ];\n }\n\n\n _loadConfig(config) {\n super._loadConfig(config, defaults); \n return this;\n }\n}","import * as Colors from '../colors';\n\nexport const defaults = {\n selector: '#chart',\n colorScale: Colors.diverging_spectral2(),\n invertColorScale: true,\n minLevel: 0,\n maxLevel: 100,\n minAngle: -90,\n maxAngle: 90,\n ringWidth: 50,\n ringMargin: 20,\n labelInset: 10,\n needleNutRadius: 25,\n needleLenghtRatio: 0.8,\n numericIndicator: true,\n label: 'km/h',\n //margins\n marginTop: 20,\n marginRight: 250,\n marginBottom: 30,\n marginLeft: 50,\n //Width & height\n width: '50%', // %, auto, or numeric\n height: 250,\n ticks: 10, // ticks for y dial.\n};","import {deg2rad} from '../../utils/functions';\nimport {scaleLinear, arc, range} from 'd3';\n\nexport class Dial { // TODO tidy\n constructor(axisType, config) {\n if (config === null) {\n throw new Error('No chart context specified for polarAxis');\n }\n\n this.r = (\n (config.width > config.height) ?\n config.height : config.width\n ) / 2;\n this.translation = (() =>\n 'translate(' + this.r + ',' + this.r + ')'\n );\n config.colorScale.domain([0, 1]);\n\n this.scale = scaleLinear()\n .domain([config.minLevel, config.maxLevel])\n .range([0, 1]);\n\n this.scaleMarks = this.scale.ticks(config.ticks);\n\n this.range = config.maxAngle - config.minAngle;\n\n this.arc = arc()\n .innerRadius(this.r - config.ringWidth - config.ringMargin)\n .outerRadius(this.r - config.ringMargin)\n .startAngle((d, i) => {\n var ratio = d * i;\n return deg2rad(config.minAngle + (ratio * this.range));\n })\n .endAngle((d, i) => {\n var ratio = d * (i + 1);\n return deg2rad(config.minAngle + (ratio * this.range));\n });\n\n this.tickData = range(config.ticks)\n .map(() => 1 / config.ticks);\n }\n\n render(svg, config) {\n let labels = null;\n\n // Append the ring\n let arcs = svg.append('g')\n .attr('class', 'arc')\n .attr('transform', this.translation);\n\n // Append the ring sectors\n let arcPaths = arcs.selectAll('path')\n .data(this.tickData)\n .enter().append('path')\n // ID for textPath linking\n .attr('id', (d, i) => 'sector-' + i)\n .attr('d', this.arc);\n\n // Fill colors\n if (config.invertColorScale) {\n arcPaths.attr('fill', (d, i) => config.colorScale(1 - d * i));\n } else {\n arcPaths.attr('fill', (d, i) => config.colorScale(d * i));\n }\n\n // Apend the scale labels\n labels = svg.append('g')\n .attr('class', 'labels')\n .attr('transform', this.translation);\n\n // // Append scale marker labels\n labels.selectAll('text')\n .data(this.scaleMarks)\n .enter().append('text')\n .attr('transform', (d) => {\n let ratio = this.scale(d);\n let newAngle = config.minAngle + (ratio * this.range);\n return 'rotate(' + newAngle + ') translate(0,' + (config.labelInset - this.r) + ')';\n })\n .text((d) => d)\n .style('text-anchor', 'middle')\n .style('font', '18px Montserrat, sans-serif');\n }\n}","import {deg2rad} from '../../utils/functions';\nimport {scaleLinear, arc, range} from 'd3';\n\nexport class DialNeedle { // TODO tidy\n constructor(axisType, config) {\n if (config === null) {\n throw new Error('No chart context specified for polarAxis');\n }\n\n this.r = (\n (config.width > config.height) ?\n config.height : config.width\n ) / 2;\n\n this.needleLen = config.needleLenghtRatio * (this.r);\n\n this.translation = (() =>\n 'translate(' + this.r + ',' + this.r + ')'\n );\n config.colorScale.domain([0, 1]);\n\n this.scale = scaleLinear()\n .domain([config.minLevel, config.maxLevel])\n .range([0, 1]);\n\n this.angleScale = scaleLinear()\n .domain([config.minLevel, config.maxLevel])\n .range([90 + config.minAngle, 90 + config.maxAngle]);\n\n this.scaleMarks = this.scale.ticks(config.ticks);\n\n this.range = config.maxAngle - config.minAngle;\n\n this.arc = arc()\n .innerRadius(this.r - config.ringWidth - config.ringMargin)\n .outerRadius(this.r - config.ringMargin)\n .startAngle((d, i) => {\n let ratio = d * i;\n return deg2rad(config.minAngle + (ratio * this.range));\n })\n .endAngle((d, i) => {\n let ratio = d * (i + 1);\n return deg2rad(config.minAngle + (ratio * this.range));\n });\n\n this.tickData = range(config.ticks)\n .map(() => 1 / config.ticks);\n }\n\n update(svg, config, data, method) {\n let datum = data[data.length - 1];\n\n this.needle\n .transition()\n .attr('transform', (d) => `translate(${this.r}, ${this.r}) rotate(${this.angleScale(datum.value) - 90})`)\n .attr('d', `M ${0 - config.needleNutRadius} ${0} L ${0} ${0 - this.needleLen} L ${config.needleNutRadius} ${0}`);\n }\n\n render(svg, config) {\n // Update the needle\n this.needle = svg.append('path')\n .attr('class', 'needle')\n .datum(0)\n .attr('transform', (d) => `translate(${this.r}, ${this.r}) rotate(${this.angleScale(d) - 90})`)\n .attr('d', `M ${0 - config.needleNutRadius} ${0} L ${0} ${0 - this.needleLen} L ${config.needleNutRadius} ${0}`)\n .style('fill', '#666666');\n\n // Append needle nut\n svg.append('circle')\n .attr('class', 'needle')\n .attr('transform', this.translation)\n .attr('cx', 0)\n .attr('cy', 0)\n .attr('r', config.needleNutRadius)\n .style('fill', '#666666');\n }\n\n}","export class TextIndicator { // TODO tidy\n constructor(config) {\n if (config === null) {\n throw new Error('No chart context specified for polarAxis');\n }\n\n this.translation = config.textIndicatorTranslation;\n }\n\n update(svg, value, label) {\n svg.select('.value')\n .text(value);\n svg.select('.label')\n .text(label);\n }\n\n render(svg, config) {\n let indicator = svg.append('g')\n .attr('class', 'text-indicator')\n .attr('pointer-events', 'none')\n .style('text-anchor', 'middle')\n .style('alignment-baseline', 'central');\n\n if (this.translation) {\n indicator.attr('transform', this.translation);\n }\n\n indicator.append('text')\n .attr('class', 'value')\n .attr('x', 0)\n .attr('y', 0)\n .attr('pointer-events', 'none')\n .text('0')\n .style('font', '48px Montserrat, sans-serif')\n .style('text-anchor', 'middle');\n\n indicator.append('text')\n .attr('class', 'label')\n .attr('x', 0)\n .attr('y', 0)\n .attr('pointer-events', 'none')\n .text('')\n .style('font', '24px Montserrat, sans-serif')\n .style('transform', 'translate(0, 1.5em')\n .style('text-anchor', 'middle');\n }\n}","import { defaults } from '../utils/defaults/gauge';\nimport { SvgContainer } from './components/svgContainer';\nimport { Dial } from './components/dial';\nimport { DialNeedle } from './components/dialNeedle';\nimport { TextIndicator } from './components/textIndicator';\nimport { calculateWidth } from '../utils/screen';\n\nexport class SvgGaugeStrategy {\n constructor(context) {\n this._loadConfig(context.config);\n this.svgContainer = new SvgContainer(this.config);\n let config = this.config;\n\n this.dial = new Dial('linear', config);\n this.needle = new DialNeedle('linear', config);\n\n this.svgContainer\n .add(this.dial)\n .add(this.needle);\n\n if (config.numericIndicator) {\n let r = (\n (config.width > config.height) ?\n config.height : config.width\n ) / 2;\n let indicatorOffset = r + 75;\n config.textIndicatorTranslation = 'translate(' + r + ',' + indicatorOffset + ')';\n this.textIndicator = new TextIndicator(config);\n this.svgContainer.add(this.textIndicator);\n }\n }\n\n\t/**\n\t * Renders a gauge chart based on data object\n\t * @param {Object} data Data Object. Contains a numeric value.\n\t *\n\t */\n draw(data) {\n let datum = data[data.length - 1],\n svg = this.svgContainer.svg,\n config = this.config;\n\n this.needle.update(svg, config, data);\n if (config.numericIndicator) {\n this.textIndicator.update(svg, datum.value, config.label);\n }\n }\n\n\t/**\n\t * This method adds config options to the chart context.\n\t * @param {Object} config Config object\n\t */\n _loadConfig(config) {\n this.config = {};\n //Selector\n this.config.selector = config.selector || defaults.selector;\n //Margins \n this.config.marginTop = config.marginTop || defaults.marginTop;\n this.config.marginLeft = config.marginLeft || defaults.marginLeft;\n this.config.marginRight = config.marginRight || defaults.marginRight;\n this.config.marginBottom = config.marginBottom || defaults.marginBottom;\n //Width & height\n this.config.width = config.width ?\n calculateWidth(config.width, this.config.selector) - this.config.marginLeft - this.config.marginRight\n : calculateWidth(defaults.width, this.config.selector) - this.config.marginLeft - this.config.marginRight;\n this.config.height = config.height || defaults.height;\n\n this.config.colorScale = config.colorScale || defaults.colorScale;\n this.config.minLevel = config.minLevel || defaults.minLevel;\n this.config.maxLevel = config.maxLevel || defaults.maxLevel;\n this.config.minAngle = config.minAngle || defaults.minAngle;\n this.config.maxAngle = config.maxAngle || defaults.maxAngle;\n this.config.ticks = config.ticks || defaults.ticks;\n this.config.ringWidth = config.ringWidth || defaults.ringWidth;\n this.config.ringMargin = config.ringMargin || defaults.ringMargin;\n this.config.labelInset = config.labelInset || defaults.labelInset;\n this.config.needleNutRadius = config.needleNutRadius || defaults.needleNutRadius;\n this.config.needleLenghtRatio = config.needleLenghtRatio || defaults.needleLenghtRatio;\n this.config.invertColorScale = typeof (config.invertColorScale) === 'undefined' ? defaults.invertColorScale : config.invertColorScale;\n this.config.numericIndicator = typeof (config.numericIndicator) === 'undefined' ? defaults.numericIndicator : config.numericIndicator;\n this.config.label = config.label || defaults.label;\n\n\n return this;\n }\n\n}","export const defaults = {\n selector: '#chart',\n width: '100%', // %, auto, or numeric \n height: 250,\n yAxisLabel: null,\n //margins\n marginTop: 20,\n marginRight: 250,\n marginBottom: 30,\n marginLeft: 50,\n};","import {forceSimulation, forceLink, forceManyBody, forceCenter, drag} from 'd3';\nimport {simple2Linked} from '../../utils/dataTransformation';\nimport {event} from 'd3';\nexport class Nodeset {\n constructor(config) {\n this.config = config;\n var width = config.width,\n height = config.height;\n\n this.simulation = forceSimulation()\n .force(\"link\", forceLink().id((d) => d.id))\n .force(\"charge\", forceManyBody())\n .force(\"center\", forceCenter(width / 2, height / 2));\n\n\n this.dragstarted = (d) => {\n if (!event.active) this.simulation.alphaTarget(0.3).restart();\n d.fx = d.x;\n d.fy = d.y;\n };\n\n this.dragged = (d) => {\n d.fx = event.x;\n d.fy = event.y;\n };\n\n this.dragended = (d) => {\n if (!event.active) this.simulation.alphaTarget(0);\n d.fx = null;\n d.fy = null;\n };\n\n }\n\n update(svg, config, data) {\n data = simple2Linked(data);\n\n var link = svg.append(\"g\")\n .attr(\"class\", \"links\")\n .selectAll(\"line\")\n .data(data.links)\n .enter().append(\"line\")\n .attr(\"stroke-width\", 2)\n .attr(\"stroke\", \"#999\")\n .attr(\"stroke-opacity\", 0.6);\n\n var node = svg.append(\"g\")\n .attr(\"class\", \"nodes\")\n .selectAll(\"circle\")\n .data(data.nodes)\n .enter()\n .append(\"circle\")\n .attr(\"r\", 5)\n .attr(\"fill\", (d) => \"#23436f\")\n .call(drag()\n .on(\"start\", this.dragstarted)\n .on(\"drag\", this.dragged)\n .on(\"end\", this.dragended));\n\n node.append(\"title\")\n .text((d) => d.id);\n\n this.simulation.nodes(data.nodes).on(\"tick\", (e) => this.ticked(link, node));\n\n this.simulation.force(\"link\").links(data.links);\n }\n\n ticked(link, node) {\n link\n .attr(\"x1\", (d) => d.source.x)\n .attr(\"y1\", (d) => d.source.y)\n .attr(\"x2\", (d) => d.target.x)\n .attr(\"y2\", (d) => d.target.y);\n\n node\n .attr(\"cx\", (d) => d.x)\n .attr(\"cy\", (d) => d.y);\n }\n\n render(svg, config) {\n //Do nothing, since lines render only when new data is received.\n }\n}","import {SvgContainer} from './components/svgContainer';\nimport {defaults} from '../utils/defaults/networkgraph';\nimport {calculateWidth} from '../utils/screen';\nimport {Nodeset} from './components/nodeset';\n\nexport class SvgNetworkgraphStrategy {\n\n constructor(context) {\n this._loadConfig(context.config);\n\n this.svgContainer = new SvgContainer(this.config);\n\n this.nodeset = new Nodeset(this.config);\n\n //Include components in the chart container\n this.svgContainer\n .add(this.nodeset);\n }\n\n\t/**\n\t * Renders a linechart based on data object\n\t * @param {Object} data Data Object. Contains an array with x and y properties.\n\t * \n\t */\n draw(data) {\n let svg = this.svgContainer.svg,\n config = this.config;\n\n this.nodeset.update(svg, config, data);\n }\n\n /**\n * This method adds config options to the chart context.\n * @param {Object} config Config object\n */\n _loadConfig(config) {\n this.config = {};\n this.config.selector = config.selector || defaults.selector;\n //Margins \n this.config.marginTop = config.marginTop || defaults.marginTop;\n this.config.marginLeft = config.marginLeft || defaults.marginLeft;\n this.config.marginRight = config.marginRight || defaults.marginRight;\n this.config.marginBottom = config.marginBottom || defaults.marginBottom;\n this.config.width = config.width ? calculateWidth(config.width, this.config.selector) - this.config.marginLeft - this.config.marginRight\n : calculateWidth(defaults.width, this.config.selector) - this.config.marginLeft - this.config.marginRight;\n this.config.height = config.height || defaults.height;\n\n return this;\n }\n}","import * as Colors from '../colors';\n\nexport const defaults = {\n selector: '#chart',\n colorScale: Colors.category8(),\n marginTop: 20,\n marginRight: 20,\n marginBottom: 30,\n marginLeft: 50,\n width: '50%', // %, auto, or numeric\n height: 450,\n tickLabel: '',\n transitionDuration: 300,\n maxNumberOfElements: 5, // used by keepDrawing to reduce the number of elements in the current chart\n sortData: {\n descending: false,\n prop: 'x'\n },\n //Events\n onDown(d) {\n },\n onHover(d) {\n },\n onLeave(d) {\n },\n onClick(d) {\n }\n};","import {scaleLinear} from 'd3';\n\nexport class XRadialAxis {\n\n constructor(config) {\n if (config === null) {\n throw new Error('No chart context specified for XRadialAxis');\n }\n\n this.xRadialAxis = scaleLinear().range([0, 2 * Math.PI]);\n }\n}","import {scaleSqrt} from 'd3';\n\nexport class YRadialAxis {\n\n constructor(config) {\n if (config === null) {\n throw new Error('No chart context specified for XRadialAxis');\n }\n\n let radius = (Math.min(config.width, config.height) / 2) - 10;\n\n this.yRadialAxis = scaleSqrt()\n .range([0, radius]);\n }\n}","import {XRadialAxis} from './xRadialAxis'\nimport {YRadialAxis} from './yRadialAxis'\n//\nexport class RadialAxes {\n constructor(config) {\n if (config === null) {\n throw new Error('No chart context specified for RadialAxis');\n }\n\n this.x = new XRadialAxis(config);\n this.y = new YRadialAxis(config);\n }\n}","import {partition, stratify, arc} from 'd3';\n\n\nexport class SunburstDisk {\n constructor(xRadialAxis, yRadialAxis) {\n this.x = xRadialAxis;\n this.y = yRadialAxis;\n this.arcGen = arc()\n .startAngle((d) => Math.max(0, Math.min(2 * Math.PI, this.x(d.x0))))\n .endAngle((d) => Math.max(0, Math.min(2 * Math.PI, this.x(d.x1))))\n .innerRadius((d) => Math.max(0, this.y(d.y0)))\n .outerRadius((d) => Math.max(0, this.y(d.y1)));\n }\n\n update(svg, config, data) {\n\n // Remove all the paths before redrawing\n this._removePaths(svg);\n\n // Create layout partition\n let root = stratify()\n .id((d) => d.id)\n .parentId((d) => d.parent)\n (data);\n\n root.sum((d) => d.value);\n partition()(root);\n\n // Draw the paths (arcs)\n let paths = svg.selectAll('path')\n .data(root.descendants())\n .enter().append('path')\n .attr('d', this.arcGen)\n .style('fill', (d) => {\n if (!d.parent) {\n return 'white';\n } else {\n return config.colorScale(d.data.label);\n }\n })\n .style('stroke', '#fff')\n .style('stroke-width', '2')\n .style('shape-rendering', 'crispEdge');\n\n paths // TODO extract events to config?\n .on('mouseover.default', (d) => {\n let ancestors = this._getAncestors(d);\n // Fade all the arcs\n if (ancestors.length > 0) {\n svg.selectAll('path')\n .style('opacity', 0.3);\n }\n svg.selectAll('path')\n .filter((node) => ancestors.indexOf(node) >= 0)\n .style('opacity', 1);\n // Hightlight the hovered arc\n svg.select('.text-indicator .label').text(d.data.label);\n svg.select('.text-indicator .value').text(d.value);\n })\n .on('mouseout.default', (d) => {\n svg.selectAll('path').style('opacity', 1);\n svg.select('.text-indicator .label').style('font-weight', 'normal');\n svg.select('.text-indicator .label').text('');\n svg.select('.text-indicator .value').text('');\n })\n ;\n\n paths\n .on('mousedown.user', config.onDown)\n .on('mouseup.user', config.onUp)\n .on('mouseleave.user', config.onLeave)\n .on('mouseover.user', config.onHover)\n .on('click.user', config.onClick);\n\n // ???\n svg.select(self.frameElement).style('height', this.height + 'px');\n }\n\n /**\n * Removes all the paths (arcs). Doing this before each redraw prevents the\n * transition to mess up the arcs.\n * @private\n */\n _removePaths(svg) {\n svg.selectAll('path').remove();\n }\n\n /**\n * From: http://bl.ocks.org/kerryrodden/7090426\n * @param node\n * @returns {Array}\n * @private\n */\n _getAncestors(node) {\n let path = [];\n let current = node;\n while (current.parent) {\n path.unshift(current);\n current = current.parent;\n }\n return path;\n }\n\n render(svg, config) {\n //Do nothing, since disk render only when new data is received.\n }\n}\n","import { defaults } from '../utils/defaults/sunburst';\nimport { SvgContainer } from './components/svgContainer';\nimport { RadialAxes } from './components/radialAxes';\nimport { SunburstDisk } from './components/sunburstDisk';\nimport { TextIndicator } from './components/textIndicator';\nimport { calculateWidth } from '../utils/screen';\n\nexport class SvgSunburstStrategy {\n\n constructor(context) {\n this._loadConfig(context.config);\n\n this.svgContainer = new SvgContainer(this.config);\n let config =\n this.config,\n translation = 'translate(' + config.width / 2 + ',' + (config.height / 2) + ')';\n\n this.svgContainer.transform(translation);\n\n this.axes = new RadialAxes(config);\n\n this.disk = new SunburstDisk(\n this.axes.x.xRadialAxis,\n this.axes.y.yRadialAxis,\n config\n );\n\n this.textIndicator = new TextIndicator(config);\n\n this.svgContainer\n .add(this.disk)\n .add(this.textIndicator);\n }\n\n draw(data) {\n let svg = this.svgContainer.svg,\n config = this.config;\n\n this.disk.update(svg, config, data);\n }\n\n /**\n * This method adds config options to the chart context.\n * @param {Object} config Config object\n */\n _loadConfig(config) {\n this.config = {};\n //Selector\n this.config.selector = config.selector || defaults.selector;\n //Margins \n this.config.marginTop = config.marginTop || defaults.marginTop;\n this.config.marginLeft = config.marginLeft || defaults.marginLeft;\n this.config.marginRight = config.marginRight || defaults.marginRight;\n this.config.marginBottom = config.marginBottom || defaults.marginBottom;\n //Width & height\n this.config.width = config.width ?\n calculateWidth(config.width, this.config.selector) - this.config.marginLeft - this.config.marginRight\n : calculateWidth(defaults.width, this.config.selector) - this.config.marginLeft - this.config.marginRight;\n this.config.height = config.height || defaults.height;\n \n this.config.colorScale = config.colorScale || defaults.colorScale;\n\n //Events\n this.config.onDown = config.onDown || defaults.onDown;\n this.config.onUp = config.onUp || defaults.onUp;\n this.config.onHover = config.onHover || defaults.onHover;\n this.config.onClick = config.onClick || defaults.onClick;\n this.config.onLeave = config.onLeave || defaults.onLeave;\n\n return this;\n }\n}\n","import * as Colors from '../colors';\n\nexport const defaults = {\n selector: '#chart',\n colorScale: Colors.category7(),\n\n //Axes\n xAxisType: 'linear',\n xAxisFormat: '.1f',\n xAxisLabel: 'Sepal length (cm)',\n yAxisType: 'linear',\n yAxisFormat: '.1f',\n yAxisLabel: 'Sepal width (cm)',\n //margins\n marginTop: 20,\n marginRight: 250,\n marginBottom: 130,\n marginLeft: 150,\n //markers\n markerShape: 'dot',\n markerSize: 3,\n //Width & height\n width: '100%', // %, auto, or numeric\n height: 250,\n //Events\n onDown(d) {\n },\n onHover(d) {\n },\n onLeave(d) {\n },\n onClick(d) {\n },\n\n maxNumberOfElements: 100, // used by keepDrawing method to reduce the number of elements in the current chart\n};","import { defaults } from '../utils/defaults/scatterplot';\nimport { SvgAxis } from './base/svgAxis';\nimport { XYAxes } from './components/xyAxes';\nimport { Lineset } from './components/lineset';\nimport { Legend } from './components/legend';\nimport { Areaset } from './components/areaset';\nimport { Pointset } from './components/pointset';\nimport { convertByXYFormat } from '../utils/dataTransformation';\nimport { sortByField } from '../utils/dataSorting';\nimport {min, max} from 'd3';\n\nexport class SvgScatterplotStrategy extends SvgAxis {\n\n constructor(context) {\n super(context);\n this.axes = new XYAxes(this.config.xAxisType, 'linear', this.config);\n this.points = new Pointset(this.axes.x, this.axes.y);\n this.legend = new Legend();\n //Include components in the chart container\n this.svgContainer\n .add(this.axes)\n .add(this.legend)\n .add(this.points);\n }\n\n\t/**\n\t * Renders a scatterplot based on data object\n\t * @param {Object} data Data Object. Contains an array with x and y properties.\n\t * \n\t */\n draw(data) {\n let svg = this.svgContainer.svg,\n config = this.config,\n needRescaling = this.config.needRescaling,\n bbox = null;\n\n // //Transform data, if needed\n convertByXYFormat(data, config);\n\n //Sort data\n sortByField(data, 'x');\n\n //rescale, if needed.\n if (needRescaling) {\n this.rescale();\n }\n\n bbox = this._getDomainBBox(data);\n\n this.axes.updateDomainByBBox(bbox);\n\n //Create a transition effect for dial rescaling\n this.axes.transition(svg, 200);\n\n // Update legend\n this.legend.update(svg, config, data);\n\n // Update points\n this.points.update(svg, config, data);\n }\n\n _getDomainBBox(data) {\n var minX = min(data, (d) => d.x),\n maxX = max(data, (d) => d.x),\n minY = min(data, (d) => d.y),\n maxY = max(data, (d) => d.y);\n return [minX, maxX, minY, maxY];\n }\n\n _checkMarkers(config) {\n return config.markerSize > 0;\n }\n _checkArea(config) {\n return config.areaOpacity > 0;\n }\n\n /**\n * This method adds config options to the chart context.\n * @param {Object} config Config object\n */\n _loadConfig(config) {\n super._loadConfig(config, defaults);\n //Markers\n this.config.markerOutlineWidth = config.markerOutlineWidth || defaults.markerOutlineWidth;\n this.config.markerShape = config.markerShape || defaults.markerShape;\n this.config.markerSize = (typeof config.markerSize === 'undefined' || config.markerSize < 0) ? defaults.markerSize : config.markerSize;\n //Area\n this.config.areaOpacity = (typeof config.areaOpacity === 'undefined' || config.markerSize < 0) ? defaults.areaOpacity : config.areaOpacity;\n return this;\n }\n}","import {SvgLinechartStrategy} from './strategy_linechart';\nimport {SvgBarchartStrategy} from './strategy_barchart';\nimport {SvgStreamgraphStrategy} from './strategy_streamgraph';\nimport {SvgStackedAreaStrategy} from './strategy_stackedArea';\nimport {SvgSwimlaneStrategy} from './strategy_swimlane';\nimport {SvgGaugeStrategy} from './strategy_gauge';\nimport {SvgNetworkgraphStrategy} from './strategy_networkgraph';\nimport {SvgSunburstStrategy} from './strategy_sunburst';\nimport {SvgScatterplotStrategy} from './strategy_scatterplot';\n\n/**\n * SvgStrategy wrapper class\n */\nexport class SvgStrategy {\n constructor(strategy) {\n this.strategy = strategy;\n }\n draw(data) {\n this.strategy.draw(data);\n }\n on(events){\n this.strategy.on(events);\n }\n}\n\nexport const strategies = {\n Barchart(chartContext) {\n return new SvgBarchartStrategy(chartContext);\n },\n Linechart(chartContext) {\n return new SvgLinechartStrategy(chartContext);\n },\n Streamgraph(chartContext) {\n return new SvgStreamgraphStrategy(chartContext);\n },\n Gauge(chartContext) {\n return new SvgGaugeStrategy(chartContext);\n },\n Scatterplot(chartContext) {\n return new SvgScatterplotStrategy(chartContext);\n },\n Sunburst(chartContext) {\n return new SvgSunburstStrategy(chartContext);\n },\n Swimlane(chartContext) {\n return new SvgSwimlaneStrategy(chartContext);\n },\n StackedArea(chartContext) {\n return new SvgStackedAreaStrategy(chartContext);\n },\n Networkgraph(chartContext) {\n return new SvgNetworkgraphStrategy(chartContext);\n }\n};","\nconst doctype = '';\n\nfunction isExternal(url) {\n return url && url.lastIndexOf('http', 0) === 0 && url.lastIndexOf(window.location.host) === -1;\n}\n\nfunction inlineImages(el, callback) {\n let images = el.querySelectorAll('image');\n let left = images.length;\n if (left === 0) {\n callback();\n }\n for (var i = 0; i < images.length; i++) {\n (function (image) {\n var href = image.getAttribute('xlink:href');\n if (href) {\n if (isExternal(href.value)) {\n window.console.warn('Cannot render embedded images linking to external hosts: ' + href.value);\n return;\n }\n }\n let canvas = window.document.createElement('canvas');\n let ctx = canvas.getContext('2d');\n let img = new window.Image();\n href = href || image.getAttribute('href');\n img.src = href;\n img.onload = function () {\n canvas.width = img.width;\n canvas.height = img.height;\n ctx.drawImage(img, 0, 0);\n image.setAttribute('xlink:href', canvas.toDataURL('image/png'));\n left--;\n if (left === 0) {\n callback();\n }\n };\n img.onerror = function () {\n window.console.error('Could not load ' + href);\n left--;\n if (left === 0) {\n callback();\n }\n };\n })(images[i]);\n }\n}\n\nfunction styles(el, selectorRemap) {\n let css = '';\n let sheets = document.styleSheets;\n for (var i = 0; i < sheets.length; i++) {\n if (isExternal(sheets[i].href)) {\n window.console.warn('Cannot include styles from other hosts: ' + sheets[i].href);\n continue;\n }\n let rules = sheets[i].cssRules;\n if (rules !== null) {\n for (var j = 0; j < rules.length; j++) {\n let rule = rules[j];\n if (typeof (rule.style) !== 'undefined') {\n let match = null;\n try {\n match = el.querySelector(rule.selectorText);\n } catch (err) {\n window.console.warn('Invalid CSS selector \"' + rule.selectorText + '\"', err);\n }\n if (match) {\n var selector = selectorRemap ? selectorRemap(rule.selectorText) : rule.selectorText;\n css += selector + ' { ' + rule.style.cssText + ' }\\n';\n } else if (rule.cssText.match(/^@font-face/)) {\n css += rule.cssText + '\\n';\n }\n }\n }\n }\n }\n return css;\n}\n\nexport function svgAsDataUri(el, options, cb) {\n options = options || {};\n options.scale = options.scale || 1;\n var xmlns = 'http://www.w3.org/2000/xmlns/';\n\n inlineImages(el, function () {\n var outer = document.createElement('div');\n var clone = el.cloneNode(true);\n var width, height;\n if (el.tagName === 'svg') {\n width = parseInt(clone.getAttribute('width') || clone.style.width || getComputedStyle(el).getPropertyValue('width'));\n height = parseInt(clone.getAttribute('height') || clone.style.height || getComputedStyle(el).getPropertyValue('height'));\n } else {\n let box = el.getBBox();\n width = box.x + box.width;\n height = box.y + box.height;\n clone.setAttribute('transform', clone.getAttribute('transform').replace(/translate\\(.*?\\)/, ''));\n\n let svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n svg.appendChild(clone);\n clone = svg;\n }\n\n clone.setAttribute('version', '1.1');\n clone.setAttributeNS(xmlns, 'xmlns', 'http://www.w3.org/2000/svg');\n clone.setAttributeNS(xmlns, 'xmlns:xlink', 'http://www.w3.org/1999/xlink');\n clone.setAttribute('width', width * options.scale);\n clone.setAttribute('height', height * options.scale);\n clone.setAttribute('viewBox', '0 0 ' + width + ' ' + height);\n outer.appendChild(clone);\n\n let css = styles(el, options.selectorRemap);\n let s = document.createElement('style');\n s.setAttribute('type', 'text/css');\n s.innerHTML = '';\n let defs = document.createElement('defs');\n defs.appendChild(s);\n clone.insertBefore(defs, clone.firstChild);\n\n let svg = doctype + outer.innerHTML;\n let uri = 'data:image/svg+xml;base64,' + window.btoa(window.unescape(encodeURIComponent(svg)));\n if (cb) {\n cb(uri);\n }\n });\n}\n","import { dispatch } from 'd3';\nimport { SvgStrategy, strategies } from '../../svg/SvgStrategy';\nimport { svgAsDataUri } from '../../utils/image';\n\n/**\n * Base class, which includes common methods for all the charts\n * @export Chart\n * @class Chart\n */\nexport default class Chart {\n /**\n * Non-instanciable Chart. This is the parent class for all the ones (Linechart, Barchart, etc.)\n * \n * @param {any} d Data. This object could be an array of data points or a datasource. Examples:\n *
\n * //With datasource\n * var data = {\n * endpoint: 'ws://192.168.3.32:3000/barchart'\n * };\n * var dataSource = new proteic.WebsocketDatasource(data);\n * \n * barchart = new proteic.Barchart(dataSource);\n * \n * //With data\n * barchart = new proteic.Barchart([{x:\"SP\", y:2},{x:\"FR\", y:6}]);\n *\n * @param {any} config Configuration of the chart.\n * \n * @memberOf Chart\n */\n constructor(d, config) {\n var clazz = this.constructor.name;\n if (clazz === 'Chart') {\n throw new Error(clazz + ' is non-instanciable');\n }\n\n this.events = {};\n\n if (!d && !config) {\n throw new Error('Missing constructor parameters');\n }\n\n let dataFormat = d.constructor.name;\n let nArguments = (d && config) ? 2 : 1;\n\n switch (dataFormat) {\n case 'WebsocketDatasource':\n case 'HTTPDatasource':\n this.datasource = d;\n this.data = [];\n this._configureDatasource();\n break;\n case 'Array':\n this.data = d;\n break;\n default:\n throw TypeError('Wrong data format');\n }\n //if only 1 parameter is specified, take default config. Else, take the second argument as config.\n this.config = (nArguments === 1) ? {} : config;\n\n this._initializeSVGContext();\n }\n\n /**\n * Private method. Initialize the API by dinamically creating methods. It creates N method, one per configuration option\n * \n * @param {any} properties An array that contains the name of the methods\n * \n * @memberOf Chart\n */\n _initializeAPI(properties) {\n let clazz = this.constructor;\n properties.forEach((method) => {\n clazz.prototype[method] = function (value) {\n return this.change(method, value);\n }\n });\n }\n\n /**\n * Return the chart context: data, configuration and type\n * \n * @returns chart Chart context\n * \n * @memberOf Chart\n */\n _getChartContext() {\n return {\n data: this.data,\n config: this.config,\n cType: this.constructor.name\n };\n }\n\n /**\n * Initialize the SVG context, by dinamically creating an