<template>
  <div id="violin" class="chart-wrapper" style="min-height: 270px; max-height: 200px !important"></div>
</template>

<script>
import * as Plotly from 'plotly.js'
import { EventBus } from '../main.js'

export default {
  name: "ValidationController",
  data () {
    return {
      WH: [],
      ResultsValid: [],
    }
  },
  methods: {
    reset () {
      var svg = d3.select("#violin");
      svg.selectAll("*").remove();
    },
    ViolinFun () {
      var svg = d3.select("#violin");
      svg.selectAll("*").remove();
      var chart2

      var data = []
        for (let i=0; i<400; i++){
            if (i < 100){
                data.push({Algorithm:"Accuracy",value:randomIntFromInterval(10,60), category:"#ff7f00"})
                data.push({Algorithm:"Accuracy",value:randomIntFromInterval(10,30), category:"#fdbf6f"})
                data.push({Algorithm:"Accuracy",value:randomIntFromInterval(15,25), category:"#fb9a99"})
                data.push({Algorithm:"Accuracy",value:randomIntFromInterval(35,45), category:"#b15928"})
                data.push({Algorithm:"Accuracy",value:randomIntFromInterval(55,70), category:"#a6cee3"})            
            } else if (i < 200){
                data.push({Algorithm:"Precision",value:randomIntFromInterval(10,60), category:"#ff7f00"})
                data.push({Algorithm:"Precision",value:randomIntFromInterval(40,70), category:"#fdbf6f"})
                data.push({Algorithm:"Precision",value:randomIntFromInterval(60,100), category:"#fb9a99"})
                data.push({Algorithm:"Precision",value:randomIntFromInterval(60,79), category:"#b15928"})
                data.push({Algorithm:"Precision",value:randomIntFromInterval(40,45), category:"#a6cee3"})           
            } else if (i < 300){
                data.push({Algorithm:"Recall",value:randomIntFromInterval(30,40), category:"#ff7f00"})
                data.push({Algorithm:"Recall",value:randomIntFromInterval(30,40), category:"#fdbf6f"})
                data.push({Algorithm:"Recall",value:randomIntFromInterval(12,30), category:"#fb9a99"})
                data.push({Algorithm:"Recall",value:randomIntFromInterval(30,40), category:"#b15928"})    
                data.push({Algorithm:"Recall",value:randomIntFromInterval(30,70), category:"#a6cee3"})   
            } else {
                data.push({Algorithm:"F1-score",value:randomIntFromInterval(20,80), category:"#ff7f00"})
                data.push({Algorithm:"F1-score",value:randomIntFromInterval(30,40), category:"#fdbf6f"}) 
                data.push({Algorithm:"F1-score",value:randomIntFromInterval(50,70), category:"#fb9a99"}) 
                data.push({Algorithm:"F1-score",value:randomIntFromInterval(60,70), category:"#b15928"})  
                data.push({Algorithm:"F1-score",value:randomIntFromInterval(80,100), category:"#a6cee3"})   
            }
            
        }
        function randomIntFromInterval(min, max) { // min and max included 
        return Math.floor(Math.random() * (max - min + 1) + min);
        }

      chart2 = makeDistroChart({
            data:data,
            xName:'Algorithm',
            yName:'value',
            axisLabels: {xAxis: 'Algorithm', yAxis: '# Performance (%) #'},
            selector:"#violin",
            constrainExtremes:true});
        chart2.renderBoxPlot({showBox:false});
        chart2.renderDataPlots({showBeanLines:true,beanWidth:15,showPlot:false,showLines:['median']});
        chart2.renderNotchBoxes({showNotchBox:false});
        chart2.renderViolinPlot({reset:true, width:75, clamp:0, resolution:30, bandwidth:50});

      /**
       * Creates a box plot, violin plot, and or notched box plot
       * @param settings Configuration options for the base plot
       * @param settings.data The data for the plot
       * @param settings.xName The name of the column that should be used for the x groups
       * @param settings.yName The name of the column used for the y values
       * @param {string} settings.selector The selector string for the main chart div
       * @param [settings.axisLabels={}] Defaults to the xName and yName
       * @param [settings.yTicks = 1] 1 = default ticks. 2 =  double, 0.5 = half
       * @param [settings.scale='linear'] 'linear' or 'log' - y scale of the chart
       * @param [settings.chartSize={width:800, height:400}] The height and width of the chart itself (doesn't include the container)
       * @param [settings.margin={top: 15, right: 60, bottom: 40, left: 50}] The margins around the chart (inside the main div)
       * @param [settings.constrainExtremes=false] Should the y scale include outliers?
       * @returns {object} chart A chart object
       */
      function makeDistroChart(settings) {

          var chart = {};

          // Defaults
          chart.settings = {
              data: null,
              xName: null,
              yName: null,
              selector: null,
              axisLables: null,
              yTicks: 1,
              scale: 'linear',
              chartSize: {width: 800, height: 270},
              margin: {top: 15, right: 60, bottom: 50, left: 50},
              constrainExtremes: false,
              color: ['#555','#555','#555','#555','#ffff99']
          };
          for (var setting in settings) {
              chart.settings[setting] = settings[setting]
          }


          function formatAsFloat(d) {
              if (d % 1 !== 0) {
                  return d3.format(".2f")(d);
              } else {
                  return d3.format(".0f")(d);
              }
          }

          function logFormatNumber(d) {
              var x = Math.log(d) / Math.log(10) + 1e-6;
              return Math.abs(x - Math.floor(x)) < 0.6 ? formatAsFloat(d) : "";
          }

          chart.yFormatter = formatAsFloat;

          chart.data = chart.settings.data;

          chart.groupObjs = {}; //The data organized by grouping and sorted as well as any metadata for the groups
          chart.objs = {mainDiv: null, chartDiv: null, g: null, xAxis: null, yAxis: null};
          chart.colorFunct = null;

          /**
           * Takes an array, function, or object mapping and created a color function from it
           * @param {function|[]|object} colorOptions
           * @returns {function} Function to be used to determine chart colors
           */
          function getColorFunct(colorOptions) {
              if (typeof colorOptions == 'function') {
                  return colorOptions
              } else if (Array.isArray(colorOptions)) {
                  //  If an array is provided, map it to the domain
                  var colorMap = {}, cColor = 0;
                  for (var cName in chart.groupObjs) {
                      colorMap[cName] = colorOptions[cColor];
                      cColor = (cColor + 1) % colorOptions.length;
                  }
                  return function (group) {
                      return colorMap[group];
                  }
              } else if (typeof colorOptions == 'object') {
                  // if an object is provided, assume it maps to  the colors
                  return function (group) {
                      return colorOptions[group];
                  }
              } else {
                  return d3.scale.ordinal().range(['#c0c0c0','#c0c0c0','#c0c0c0','#c0c0c0','#ffff99'])
              }
          }

          /**
           * Takes a percentage as returns the values that correspond to that percentage of the group range witdh
           * @param objWidth Percentage of range band
           * @param gName The bin name to use to get the x shift
           * @returns {{left: null, right: null, middle: null}}
           */
          function getObjWidth(objWidth, gName) {
              var objSize = {left: null, right: null, middle: null};
              var width = chart.xScale.rangeBand() * (objWidth / 100);
              var padding = (chart.xScale.rangeBand() - width) / 2;
              var gShift = chart.xScale(gName);
              objSize.middle = chart.xScale.rangeBand() / 2 + gShift;
              objSize.left = padding + gShift;
              objSize.right = objSize.left + width;
              return objSize;
          }

          /**
           * Adds jitter to the  scatter point plot
           * @param doJitter true or false, add jitter to the point
           * @param width percent of the range band to cover with the jitter
           * @returns {number}
           */
          function addJitter(doJitter, width) {
              if (doJitter !== true || width == 0) {
                  return 0
              }
              return Math.floor(Math.random() * width) - width / 2;
          }

          function shallowCopy(oldObj) {
              var newObj = {};
              for (var i in oldObj) {
                  if (oldObj.hasOwnProperty(i)) {
                      newObj[i] = oldObj[i];
                  }
              }
              return newObj;
          }

          /**
           * Closure that creates the tooltip hover function
           * @param groupName Name of the x group
           * @param metrics Object to use to get values for the group
           * @returns {Function} A function that provides the values for the tooltip
           */
          function tooltipHover(groupName, metrics) {
              var tooltipString = "Group: " + groupName;
              tooltipString += "<br\>Max: " + formatAsFloat(metrics.max, 0.1);
              tooltipString += "<br\>Q3: " + formatAsFloat(metrics.quartile3);
              tooltipString += "<br\>Median: " + formatAsFloat(metrics.median);
              tooltipString += "<br\>Q1: " + formatAsFloat(metrics.quartile1);
              tooltipString += "<br\>Min: " + formatAsFloat(metrics.min);
              return function () {
                  chart.objs.tooltip.transition().duration(200).style("opacity", 0.9);
                  chart.objs.tooltip.html(tooltipString)
              };
          }

          /**
           * Parse the data and calculates base values for the plots
           */
          !function prepareData() {
              function calcMetrics(values) {

                  var metrics = { //These are the original non�scaled values
                      max: null,
                      upperOuterFence: null,
                      upperInnerFence: null,
                      quartile3: null,
                      median: null,
                      mean: null,
                      iqr: null,
                      quartile1: null,
                      lowerInnerFence: null,
                      lowerOuterFence: null,
                      min: null
                  };

                  metrics.min = d3.min(values);
                  metrics.quartile1 = d3.quantile(values, 0.25);
                  metrics.median = d3.median(values);
                  metrics.mean = d3.mean(values);
                  metrics.quartile3 = d3.quantile(values, 0.75);
                  metrics.max = d3.max(values);
                  metrics.iqr = metrics.quartile3 - metrics.quartile1;

                  //The inner fences are the closest value to the IQR without going past it (assumes sorted lists)
                  var LIF = metrics.quartile1 - (1.5 * metrics.iqr);
                  var UIF = metrics.quartile3 + (1.5 * metrics.iqr);
                  for (var i = 0; i <= values.length; i++) {
                      if (values[i] < LIF) {
                          continue;
                      }
                      if (!metrics.lowerInnerFence && values[i] >= LIF) {
                          metrics.lowerInnerFence = values[i];
                          continue;
                      }
                      if (values[i] > UIF) {
                          metrics.upperInnerFence = values[i - 1];
                          break;
                      }
                  }


                  metrics.lowerOuterFence = metrics.quartile1 - (3 * metrics.iqr);
                  metrics.upperOuterFence = metrics.quartile3 + (3 * metrics.iqr);
                  if (!metrics.lowerInnerFence) {
                      metrics.lowerInnerFence = metrics.min;
                  }
                  if (!metrics.upperInnerFence) {
                      metrics.upperInnerFence = metrics.max;
                  }
                  return metrics
              }

              var current_x = null;
              var current_y = null;
              var current_row;

              // Group the values
              for (current_row = 0; current_row < chart.data.length; current_row++) {
                  current_x = chart.data[current_row][chart.settings.xName];
                  current_y = chart.data[current_row][chart.settings.yName];

                  if (chart.groupObjs.hasOwnProperty(current_x)) {
                      chart.groupObjs[current_x].values.push(current_y);
                  } else {
                      chart.groupObjs[current_x] = {};
                      chart.groupObjs[current_x].values = [current_y];
                  }
              }

              for (var cName in chart.groupObjs) {
                  chart.groupObjs[cName].values.sort(d3.ascending);
                  chart.groupObjs[cName].metrics = {};
                  chart.groupObjs[cName].metrics = calcMetrics(chart.groupObjs[cName].values);

              }
          }();

          /**
           * Prepare the chart settings and chart div and svg
           */
          !function prepareSettings() {
              //Set base settings
              chart.margin = chart.settings.margin;
              chart.divWidth = chart.settings.chartSize.width;
              chart.divHeight = chart.settings.chartSize.height;
              chart.width = chart.divWidth - chart.margin.left - chart.margin.right;
              chart.height = chart.divHeight - chart.margin.top - chart.margin.bottom;

              if (chart.settings.axisLabels) {
                  chart.xAxisLable = chart.settings.axisLabels.xAxis;
                  chart.yAxisLable = chart.settings.axisLabels.yAxis;
              } else {
                  chart.xAxisLable = chart.settings.xName;
                  chart.yAxisLable = chart.settings.yName;
              }

              if (chart.settings.scale === 'log') {
                  chart.yScale = d3.scale.log();
                  chart.yFormatter = logFormatNumber;
              } else {
                  chart.yScale = d3.scale.linear();
              }

              if (chart.settings.constrainExtremes === true) {
                  var fences = [];
                  for (var cName in chart.groupObjs) {
                      fences.push(chart.groupObjs[cName].metrics.lowerInnerFence);
                      fences.push(chart.groupObjs[cName].metrics.upperInnerFence);
                  }
                  chart.range = d3.extent(fences);

              } else {
                  chart.range = d3.extent(chart.data, function (d) {return d[chart.settings.yName];});
              }

              chart.colorFunct = getColorFunct(chart.settings.colors);

              // Build Scale functions
              chart.yScale.range([chart.height, 0]).domain(chart.range).nice().clamp(true);
              chart.xScale = d3.scale.ordinal().domain(Object.keys(chart.groupObjs)).rangeBands([0, chart.width]);

              //Build Axes Functions
              chart.objs.yAxis = d3.svg.axis()
                  .scale(chart.yScale)
                  .orient("left")
                  .tickFormat(chart.yFormatter)
                  .outerTickSize(0)
                  .innerTickSize(-chart.width + (chart.margin.right + chart.margin.left));
              chart.objs.yAxis.ticks(chart.objs.yAxis.ticks()*chart.settings.yTicks);
              chart.objs.xAxis = d3.svg.axis().scale(chart.xScale).orient("bottom").tickSize(5);
          }();

          /**
           * Updates the chart based on the current settings and window size
           * @returns {*}
           */
          chart.update = function () {
              // Update chart size based on view port size
              chart.width = parseInt(chart.objs.chartDiv.style("width"), 10) - (chart.margin.left + chart.margin.right) + 30;
              chart.height = parseInt(chart.objs.chartDiv.style("height"), 10) - (chart.margin.top + chart.margin.bottom) - 20;

              // Update scale functions
              chart.xScale.rangeBands([0, chart.width]);
              chart.yScale.range([chart.height, 0]);

              // Update the yDomain if the Violin plot clamp is set to -1 meaning it will extend the violins to make nice points
              if (chart.violinPlots && chart.violinPlots.options.show == true && chart.violinPlots.options._yDomainVP != null) {
                  chart.yScale.domain(chart.violinPlots.options._yDomainVP).nice().clamp(true);
              } else {
                  chart.yScale.domain(chart.range).nice().clamp(true);
              }

              //Update axes
              chart.objs.g.select('.x.axis').attr("transform", "translate(0," + chart.height + ")").call(chart.objs.xAxis)
                  .selectAll("text")
                  .attr("y", 5)
                  .attr("x", 30)
                  .attr("transform", "rotate(0)")
                  .style("text-anchor", "end");
              chart.objs.g.select('.x.axis .label').attr("x", chart.width / 2);
              chart.objs.g.select('.y.axis').call(chart.objs.yAxis.innerTickSize(-chart.width));
              chart.objs.g.select('.y.axis .label').attr("x", -chart.height / 2);
              chart.objs.chartDiv.select('svg').attr("width", chart.width + (chart.margin.left + chart.margin.right)).attr("height", chart.height + (chart.margin.top + chart.margin.bottom));

              return chart;
          };

          /**
           * Prepare the chart html elements
           */
          !function prepareChart() {
              // Build main div and chart div
              chart.objs.mainDiv = d3.select(chart.settings.selector)
                  .style("max-width", chart.divWidth + "px");
              // Add all the divs to make it centered and responsive
              chart.objs.mainDiv.append("div")
                  .attr("class", "inner-wrapper")
                  .append("div").attr("class", "outer-box")
                  .append("div").attr("class", "inner-box");
              // Capture the inner div for the chart (where the chart actually is)
              chart.selector = chart.settings.selector + " .inner-box";
              chart.objs.chartDiv = d3.select(chart.selector);
              d3.select(window).on('resize.' + chart.selector, chart.update);

              // Create the svg
              chart.objs.g = chart.objs.chartDiv.append("svg")
                  .attr("class", "chart-area")
                  .attr("width", chart.width + (chart.margin.left + chart.margin.right))
                  .attr("height", 270 + (chart.margin.top + chart.margin.bottom))
                  .append("g")
                  .attr("transform", "translate(" + chart.margin.left + "," + chart.margin.top + ")");

              // Create axes
              chart.objs.axes = chart.objs.g.append("g").attr("class", "axis");
              chart.objs.axes.append("g")
                  .attr("class", "x axis")
                  .attr("transform", "translate(0," + chart.height + ")")
                  .call(chart.objs.xAxis);
              chart.objs.axes.append("g")
                  .attr("class", "y axis")
                  .call(chart.objs.yAxis)
                  .append("text")
                  .attr("class", "label")
                  .attr("transform", "rotate(-90)")
                  .attr("y", -42)
                  .attr("x", -chart.height / 2)
                  .attr("dy", ".71em")
                  .style("text-anchor", "middle")
                  .text(chart.yAxisLable);

              // Create tooltip div
              chart.objs.tooltip = chart.objs.mainDiv.append('div').attr('class', 'tooltip');
              for (var cName in chart.groupObjs) {
                  chart.groupObjs[cName].g = chart.objs.g.append("g").attr("class", "group");
                  chart.groupObjs[cName].g.on("mouseover", function () {
                      chart.objs.tooltip
                          .style("display", null)
                          .style("left", (d3.event.pageX) + "px")
                          .style("top", (d3.event.pageY - 28) + "px");
                  }).on("mouseout", function () {
                      chart.objs.tooltip.style("display", "none");
                  }).on("mousemove", tooltipHover(cName, chart.groupObjs[cName].metrics))
              }
              chart.update();
          }();

          /**
           * Render a violin plot on the current chart
           * @param options
           * @param [options.showViolinPlot=true] True or False, show the violin plot
           * @param [options.resolution=100 default]
           * @param [options.bandwidth=10 default] May need higher bandwidth for larger data sets
           * @param [options.width=50] The max percent of the group rangeBand that the violin can be
           * @param [options.interpolation=''] How to render the violin
           * @param [options.clamp=0 default]
           *   0 = keep data within chart min and max, clamp once data = 0. May extend beyond data set min and max
           *   1 = clamp at min and max of data set. Possibly no tails
           *  -1 = extend chart axis to make room for data to interpolate to 0. May extend axis and data set min and max
           * @param [options.colors=chart default] The color mapping for the violin plot
           * @returns {*} The chart object
           */
          chart.renderViolinPlot = function (options) {
              chart.violinPlots = {};

              var defaultOptions = {
                  show: true,
                  showViolinPlot: true,
                  resolution: 100,
                  bandwidth: 20,
                  width: 50,
                  interpolation: 'cardinal',
                  clamp: 1,
                  colors: chart.colorFunct,
                  _yDomainVP: null // If the Violin plot is set to close all violin plots, it may need to extend the domain, that extended domain is stored here
              };
              chart.violinPlots.options = shallowCopy(defaultOptions);
              for (var option in options) {
                  chart.violinPlots.options[option] = options[option]
              }
              var vOpts = chart.violinPlots.options;

              // Create violin plot objects
              for (var cName in chart.groupObjs) {
                  chart.groupObjs[cName].violin = {};
                  chart.groupObjs[cName].violin.objs = {};
              }

              /**
               * Take a new set of options and redraw the violin
               * @param updateOptions
               */
              chart.violinPlots.change = function (updateOptions) {
                  if (updateOptions) {
                      for (var key in updateOptions) {
                          vOpts[key] = updateOptions[key]
                      }
                  }

                  for (var cName in chart.groupObjs) {
                      chart.groupObjs[cName].violin.objs.g.remove()
                  }

                  chart.violinPlots.prepareViolin();
                  chart.violinPlots.update();
              };

              chart.violinPlots.reset = function () {
                  chart.violinPlots.change(defaultOptions)
              };
              chart.violinPlots.show = function (opts) {
                  if (opts !== undefined) {
                      opts.show = true;
                      if (opts.reset) {
                          chart.violinPlots.reset()
                      }
                  } else {
                      opts = {show: true};
                  }
                  chart.violinPlots.change(opts);

              };

              chart.violinPlots.hide = function (opts) {
                  if (opts !== undefined) {
                      opts.show = false;
                      if (opts.reset) {
                          chart.violinPlots.reset()
                      }
                  } else {
                      opts = {show: false};
                  }
                  chart.violinPlots.change(opts);

              };

              /**
               * Update the violin obj values
               */
              chart.violinPlots.update = function () {
                  var cName, cViolinPlot;

                  for (cName in chart.groupObjs) {
                      cViolinPlot = chart.groupObjs[cName].violin;

                      // Build the violins sideways, so use the yScale for the xScale and make a new yScale
                      var xVScale = chart.yScale.copy();


                      // Create the Kernel Density Estimator Function
                      cViolinPlot.kde = kernelDensityEstimator(eKernel(vOpts.bandwidth), xVScale.ticks(vOpts.resolution));
                      cViolinPlot.kdedata = cViolinPlot.kde(chart.groupObjs[cName].values);

                      var interpolateMax = chart.groupObjs[cName].metrics.max,
                          interpolateMin = chart.groupObjs[cName].metrics.min;

                      if (vOpts.clamp == 0 || vOpts.clamp == -1) { //
                          // When clamp is 0, calculate the min and max that is needed to bring the violin plot to a point
                          // interpolateMax = the Minimum value greater than the max where y = 0
                          interpolateMax = d3.min(cViolinPlot.kdedata.filter(function (d) {
                              return (d.x > chart.groupObjs[cName].metrics.max && d.y == 0)
                          }), function (d) {
                              return d.x;
                          });
                          // interpolateMin = the Maximum value less than the min where y = 0
                          interpolateMin = d3.max(cViolinPlot.kdedata.filter(function (d) {
                              return (d.x < chart.groupObjs[cName].metrics.min && d.y == 0)
                          }), function (d) {
                              return d.x;
                          });
                          // If clamp is -1 we need to extend the axises so that the violins come to a point
                          if (vOpts.clamp == -1) {
                              kdeTester = eKernelTest(eKernel(vOpts.bandwidth), chart.groupObjs[cName].values);
                              if (!interpolateMax) {
                                  var interMaxY = kdeTester(chart.groupObjs[cName].metrics.max);
                                  var interMaxX = chart.groupObjs[cName].metrics.max;
                                  var count = 25; // Arbitrary limit to make sure we don't get an infinite loop
                                  while (count > 0 && interMaxY != 0) {
                                      interMaxY = kdeTester(interMaxX);
                                      interMaxX += 1;
                                      count -= 1;
                                  }
                                  interpolateMax = interMaxX;
                              }
                              if (!interpolateMin) {
                                  var interMinY = kdeTester(chart.groupObjs[cName].metrics.min);
                                  var interMinX = chart.groupObjs[cName].metrics.min;
                                  var count = 25;  // Arbitrary limit to make sure we don't get an infinite loop
                                  while (count > 0 && interMinY != 0) {
                                      interMinY = kdeTester(interMinX);
                                      interMinX -= 1;
                                      count -= 1;
                                  }
                                  interpolateMin = interMinX;
                              }

                          }
                          // Check to see if the new values are outside the existing chart range
                          //   If they are assign them to the master _yDomainVP
                          if (!vOpts._yDomainVP) vOpts._yDomainVP = chart.range.slice(0);
                          if (interpolateMin && interpolateMin < vOpts._yDomainVP[0]) {
                              vOpts._yDomainVP[0] = interpolateMin;
                          }
                          if (interpolateMax && interpolateMax > vOpts._yDomainVP[1]) {
                              vOpts._yDomainVP[1] = interpolateMax;
                          }


                      }


                      if (vOpts.showViolinPlot) {
                          chart.update();
                          xVScale = chart.yScale.copy();

                          // Need to recalculate the KDE because the xVScale changed
                          cViolinPlot.kde = kernelDensityEstimator(eKernel(vOpts.bandwidth), xVScale.ticks(vOpts.resolution));
                          cViolinPlot.kdedata = cViolinPlot.kde(chart.groupObjs[cName].values);
                      }

                      cViolinPlot.kdedata = cViolinPlot.kdedata
                          .filter(function (d) {
                              return (!interpolateMin || d.x >= interpolateMin)
                          })
                          .filter(function (d) {
                              return (!interpolateMax || d.x <= interpolateMax)
                          });
                  }
                  for (cName in chart.groupObjs) {
                      cViolinPlot = chart.groupObjs[cName].violin;

                      // Get the violin width
                      var objBounds = getObjWidth(vOpts.width, cName);
                      var width = (objBounds.right - objBounds.left) / 2;

                      var yVScale = d3.scale.linear()
                          .range([width, 0])
                          .domain([0, d3.max(cViolinPlot.kdedata, function (d) {return d.y;})])
                          .clamp(true);

                      var area = d3.svg.area()
                          .interpolate(vOpts.interpolation)
                          .x(function (d) {return xVScale(d.x);})
                          .y0(width)
                          .y1(function (d) {return yVScale(d.y);});

                      var line = d3.svg.line()
                          .interpolate(vOpts.interpolation)
                          .x(function (d) {return xVScale(d.x);})
                          .y(function (d) {return yVScale(d.y)});

                      if (cViolinPlot.objs.left.area) {
                          cViolinPlot.objs.left.area
                              .datum(cViolinPlot.kdedata)
                              .attr("d", area);
                          cViolinPlot.objs.left.line
                              .datum(cViolinPlot.kdedata)
                              .attr("d", line);

                          cViolinPlot.objs.right.area
                              .datum(cViolinPlot.kdedata)
                              .attr("d", area);
                          cViolinPlot.objs.right.line
                              .datum(cViolinPlot.kdedata)
                              .attr("d", line);
                      }

                      // Rotate the violins
                      cViolinPlot.objs.left.g.attr("transform", "rotate(90,0,0)   translate(0,-" + objBounds.left + ")  scale(1,-1)");
                      cViolinPlot.objs.right.g.attr("transform", "rotate(90,0,0)  translate(0,-" + objBounds.right + ")");
                  }
              };

              /**
              * Create the svg elements for the violin plot
              */
              chart.violinPlots.prepareViolin = function () {
                  var cName, cViolinPlot;

                  if (vOpts.colors) {
                      chart.violinPlots.color = getColorFunct(vOpts.colors);
                  } else {
                      chart.violinPlots.color = chart.colorFunct
                  }

                  if (vOpts.show == false) {return}

                  for (cName in chart.groupObjs) {
                      cViolinPlot = chart.groupObjs[cName].violin;

                      cViolinPlot.objs.g = chart.groupObjs[cName].g.append("g").attr("class", "violin-plot");
                      cViolinPlot.objs.left = {area: null, line: null, g: null};
                      cViolinPlot.objs.right = {area: null, line: null, g: null};

                      cViolinPlot.objs.left.g = cViolinPlot.objs.g.append("g");
                      cViolinPlot.objs.right.g = cViolinPlot.objs.g.append("g");

                      if (vOpts.showViolinPlot !== false) {
                          //Area
                          cViolinPlot.objs.left.area = cViolinPlot.objs.left.g.append("path")
                              .attr("class", "area")
                              .style("fill", chart.violinPlots.color(cName));
                          cViolinPlot.objs.right.area = cViolinPlot.objs.right.g.append("path")
                              .attr("class", "area")
                              .style("fill", chart.violinPlots.color(cName));

                          //Lines
                          cViolinPlot.objs.left.line = cViolinPlot.objs.left.g.append("path")
                              .attr("class", "line")
                              .attr("fill", 'none')
                              .style("stroke", chart.violinPlots.color(cName));
                          cViolinPlot.objs.right.line = cViolinPlot.objs.right.g.append("path")
                              .attr("class", "line")
                              .attr("fill", 'none')
                              .style("stroke", chart.violinPlots.color(cName));
                      }

                  }

              };


              function kernelDensityEstimator(kernel, x) {
                  return function (sample) {
                      return x.map(function (x) {
                          return {x:x, y:d3.mean(sample, function (v) {return kernel(x - v);})};
                      });
                  };
              }

              function eKernel(scale) {
                  return function (u) {
                      return Math.abs(u /= scale) <= 1 ? .75 * (1 - u * u) / scale : 0;
                  };
              }

              // Used to find the roots for adjusting violin axis
              // Given an array, find the value for a single point, even if it is not in the domain
              function eKernelTest(kernel, array) {
                  return function (testX) {
                      return d3.mean(array, function (v) {return kernel(testX - v);})
                  }
              }

              chart.violinPlots.prepareViolin();

              d3.select(window).on('resize.' + chart.selector + '.violinPlot', chart.violinPlots.update);
              chart.violinPlots.update();
              return chart;
          };

          /**
          * Render a box plot on the current chart
          * @param options
          * @param [options.show=true] Toggle the whole plot on and off
          * @param [options.showBox=true] Show the box part of the box plot
          * @param [options.showWhiskers=true] Show the whiskers
          * @param [options.showMedian=true] Show the median line
          * @param [options.showMean=false] Show the mean line
          * @param [options.medianCSize=3] The size of the circle on the median
          * @param [options.showOutliers=true] Plot outliers
          * @param [options.boxwidth=30] The max percent of the group rangeBand that the box can be
          * @param [options.lineWidth=boxWidth] The max percent of the group rangeBand that the line can be
          * @param [options.outlierScatter=false] Spread out the outliers so they don't all overlap (in development)
          * @param [options.outlierCSize=2] Size of the outliers
          * @param [options.colors=chart default] The color mapping for the box plot
          * @returns {*} The chart object
          */
          chart.renderBoxPlot = function (options) {
              chart.boxPlots = {};

              // Defaults
              var defaultOptions = {
                  show: true,
                  showBox: true,
                  showWhiskers: true,
                  showMedian: true,
                  showMean: false,
                  medianCSize: 3.5,
                  showOutliers: true,
                  boxWidth: 30,
                  lineWidth: null,
                  scatterOutliers: false,
                  outlierCSize: 2.5,
                  colors: chart.colorFunct
              };
              chart.boxPlots.options = shallowCopy(defaultOptions);
              for (var option in options) {
                  chart.boxPlots.options[option] = options[option]
              }
              var bOpts = chart.boxPlots.options;

              //Create box plot objects
              for (var cName in chart.groupObjs) {
                  chart.groupObjs[cName].boxPlot = {};
                  chart.groupObjs[cName].boxPlot.objs = {};
              }


              /**
              * Calculates all the outlier points for each group
              */
              !function calcAllOutliers() {

                  /**
                  * Create lists of the outliers for each content group
                  * @param cGroup The object to modify
                  * @return null Modifies the object in place
                  */
                  function calcOutliers(cGroup) {
                      var cExtremes = [];
                      var cOutliers = [];
                      var cOut, idx;
                      for (idx = 0; idx <= cGroup.values.length; idx++) {
                          cOut = {value: cGroup.values[idx]};

                          if (cOut.value < cGroup.metrics.lowerInnerFence) {
                              if (cOut.value < cGroup.metrics.lowerOuterFence) {
                                  cExtremes.push(cOut);
                              } else {
                                  cOutliers.push(cOut);
                              }
                          } else if (cOut.value > cGroup.metrics.upperInnerFence) {
                              if (cOut.value > cGroup.metrics.upperOuterFence) {
                                  cExtremes.push(cOut);
                              } else {
                                  cOutliers.push(cOut);
                              }
                          }
                      }
                      cGroup.boxPlot.objs.outliers = cOutliers;
                      cGroup.boxPlot.objs.extremes = cExtremes;
                  }

                  for (var cName in chart.groupObjs) {
                      calcOutliers(chart.groupObjs[cName]);
                  }
              }();

              /**
              * Take updated options and redraw the box plot
              * @param updateOptions
              */
              chart.boxPlots.change = function (updateOptions) {
                  if (updateOptions) {
                      for (var key in updateOptions) {
                          bOpts[key] = updateOptions[key]
                      }
                  }

                  for (var cName in chart.groupObjs) {
                      chart.groupObjs[cName].boxPlot.objs.g.remove()
                  }
                  chart.boxPlots.prepareBoxPlot();
                  chart.boxPlots.update()
              };

              chart.boxPlots.reset = function () {
                  chart.boxPlots.change(defaultOptions)
              };
              chart.boxPlots.show = function (opts) {
                  if (opts !== undefined) {
                      opts.show = true;
                      if (opts.reset) {
                          chart.boxPlots.reset()
                      }
                  } else {
                      opts = {show: true};
                  }
                  chart.boxPlots.change(opts)

              };
              chart.boxPlots.hide = function (opts) {
                  if (opts !== undefined) {
                      opts.show = false;
                      if (opts.reset) {
                          chart.boxPlots.reset()
                      }
                  } else {
                      opts = {show: false};
                  }
                  chart.boxPlots.change(opts)
              };

              /**
              * Update the box plot obj values
              */
              chart.boxPlots.update = function () {
                  var cName, cBoxPlot;

                  for (cName in chart.groupObjs) {
                      cBoxPlot = chart.groupObjs[cName].boxPlot;

                      // Get the box width
                      var objBounds = getObjWidth(bOpts.boxWidth, cName);
                      var width = (objBounds.right - objBounds.left);

                      var sMetrics = {}; //temp var for scaled (plottable) metric values
                      for (var attr in chart.groupObjs[cName].metrics) {
                          sMetrics[attr] = null;
                          sMetrics[attr] = chart.yScale(chart.groupObjs[cName].metrics[attr]);
                      }

                      // Box
                      if (cBoxPlot.objs.box) {
                          cBoxPlot.objs.box
                              .attr("x", objBounds.left)
                              .attr('width', width)
                              .attr("y", sMetrics.quartile3)
                              .attr("rx", 1)
                              .attr("ry", 1)
                              .attr("height", -sMetrics.quartile3 + sMetrics.quartile1)
                      }

                      // Lines
                      var lineBounds = null;
                      if (bOpts.lineWidth) {
                          lineBounds = getObjWidth(bOpts.lineWidth, cName)
                      } else {
                          lineBounds = objBounds
                      }
                      // --Whiskers
                      if (cBoxPlot.objs.upperWhisker) {
                          cBoxPlot.objs.upperWhisker.fence
                              .attr("x1", lineBounds.left)
                              .attr("x2", lineBounds.right)
                              .attr('y1', sMetrics.upperInnerFence)
                              .attr("y2", sMetrics.upperInnerFence);
                          cBoxPlot.objs.upperWhisker.line
                              .attr("x1", lineBounds.middle)
                              .attr("x2", lineBounds.middle)
                              .attr('y1', sMetrics.quartile3)
                              .attr("y2", sMetrics.upperInnerFence);

                          cBoxPlot.objs.lowerWhisker.fence
                              .attr("x1", lineBounds.left)
                              .attr("x2", lineBounds.right)
                              .attr('y1', sMetrics.lowerInnerFence)
                              .attr("y2", sMetrics.lowerInnerFence);
                          cBoxPlot.objs.lowerWhisker.line
                              .attr("x1", lineBounds.middle)
                              .attr("x2", lineBounds.middle)
                              .attr('y1', sMetrics.quartile1)
                              .attr("y2", sMetrics.lowerInnerFence);
                      }

                      // --Median
                      if (cBoxPlot.objs.median) {
                          cBoxPlot.objs.median.line
                              .attr("x1", lineBounds.left)
                              .attr("x2", lineBounds.right)
                              .attr('y1', sMetrics.median)
                              .attr("y2", sMetrics.median);
                          cBoxPlot.objs.median.circle
                              .attr("cx", lineBounds.middle)
                              .attr("cy", sMetrics.median)
                      }

                      // --Mean
                      if (cBoxPlot.objs.mean) {
                          cBoxPlot.objs.mean.line
                              .attr("x1", lineBounds.left)
                              .attr("x2", lineBounds.right)
                              .attr('y1', sMetrics.mean)
                              .attr("y2", sMetrics.mean);
                          cBoxPlot.objs.mean.circle
                              .attr("cx", lineBounds.middle)
                              .attr("cy", sMetrics.mean);
                      }

                      // Outliers

                      var pt;
                      if (cBoxPlot.objs.outliers) {
                          for (pt in cBoxPlot.objs.outliers) {
                              cBoxPlot.objs.outliers[pt].point
                                  .attr("cx", objBounds.middle + addJitter(bOpts.scatterOutliers, width))
                                  .attr("cy", chart.yScale(cBoxPlot.objs.outliers[pt].value));
                          }
                      }
                      if (cBoxPlot.objs.extremes) {
                          for (pt in cBoxPlot.objs.extremes) {
                              cBoxPlot.objs.extremes[pt].point
                                  .attr("cx", objBounds.middle + addJitter(bOpts.scatterOutliers, width))
                                  .attr("cy", chart.yScale(cBoxPlot.objs.extremes[pt].value));
                          }
                      }
                  }
              };

              /**
              * Create the svg elements for the box plot
              */
              chart.boxPlots.prepareBoxPlot = function () {
                  var cName, cBoxPlot;

                  if (bOpts.colors) {
                      chart.boxPlots.colorFunct = getColorFunct(bOpts.colors);
                  } else {
                      chart.boxPlots.colorFunct = chart.colorFunct
                  }

                  if (bOpts.show == false) {
                      return
                  }

                  for (cName in chart.groupObjs) {
                      cBoxPlot = chart.groupObjs[cName].boxPlot;

                      cBoxPlot.objs.g = chart.groupObjs[cName].g.append("g").attr("class", "box-plot");

                      //Plot Box (default show)
                      if (bOpts.showBox) {
                          cBoxPlot.objs.box = cBoxPlot.objs.g.append("rect")
                              .attr("class", "box")
                              .style("fill", chart.boxPlots.colorFunct(cName))
                              .style("stroke", chart.boxPlots.colorFunct(cName));
                          //A stroke is added to the box with the group color, it is
                          // hidden by default and can be shown through css with stroke-width
                      }

                      //Plot Median (default show)
                      if (bOpts.showMedian) {
                          cBoxPlot.objs.median = {line: null, circle: null};
                          cBoxPlot.objs.median.line = cBoxPlot.objs.g.append("line")
                              .attr("class", "median");
                          cBoxPlot.objs.median.circle = cBoxPlot.objs.g.append("circle")
                              .attr("class", "median")
                              .attr('r', bOpts.medianCSize)
                              .style("fill", chart.boxPlots.colorFunct(cName));
                      }

                      // Plot Mean (default no plot)
                      if (bOpts.showMean) {
                          cBoxPlot.objs.mean = {line: null, circle: null};
                          cBoxPlot.objs.mean.line = cBoxPlot.objs.g.append("line")
                              .attr("class", "mean");
                          cBoxPlot.objs.mean.circle = cBoxPlot.objs.g.append("circle")
                              .attr("class", "mean")
                              .attr('r', bOpts.medianCSize)
                              .style("fill", chart.boxPlots.colorFunct(cName));
                      }

                      // Plot Whiskers (default show)
                      if (bOpts.showWhiskers) {
                          cBoxPlot.objs.upperWhisker = {fence: null, line: null};
                          cBoxPlot.objs.lowerWhisker = {fence: null, line: null};
                          cBoxPlot.objs.upperWhisker.fence = cBoxPlot.objs.g.append("line")
                              .attr("class", "upper whisker")
                              .style("stroke", chart.boxPlots.colorFunct(cName));
                          cBoxPlot.objs.upperWhisker.line = cBoxPlot.objs.g.append("line")
                              .attr("class", "upper whisker")
                              .style("stroke", chart.boxPlots.colorFunct(cName));

                          cBoxPlot.objs.lowerWhisker.fence = cBoxPlot.objs.g.append("line")
                              .attr("class", "lower whisker")
                              .style("stroke", chart.boxPlots.colorFunct(cName));
                          cBoxPlot.objs.lowerWhisker.line = cBoxPlot.objs.g.append("line")
                              .attr("class", "lower whisker")
                              .style("stroke", chart.boxPlots.colorFunct(cName));
                      }

                      // Plot outliers (default show)
                      if (bOpts.showOutliers) {
                          if (!cBoxPlot.objs.outliers) calcAllOutliers();
                          var pt;
                          if (cBoxPlot.objs.outliers.length) {
                              var outDiv = cBoxPlot.objs.g.append("g").attr("class", "boxplot outliers");
                              for (pt in cBoxPlot.objs.outliers) {
                                  cBoxPlot.objs.outliers[pt].point = outDiv.append("circle")
                                      .attr("class", "outlier")
                                      .attr('r', bOpts.outlierCSize)
                                      .style("fill", chart.boxPlots.colorFunct(cName));
                              }
                          }

                          if (cBoxPlot.objs.extremes.length) {
                              var extDiv = cBoxPlot.objs.g.append("g").attr("class", "boxplot extremes");
                              for (pt in cBoxPlot.objs.extremes) {
                                  cBoxPlot.objs.extremes[pt].point = extDiv.append("circle")
                                      .attr("class", "extreme")
                                      .attr('r', bOpts.outlierCSize)
                                      .style("stroke", chart.boxPlots.colorFunct(cName));
                              }
                          }
                      }


                  }
              };
              chart.boxPlots.prepareBoxPlot();

              d3.select(window).on('resize.' + chart.selector + '.boxPlot', chart.boxPlots.update);
              chart.boxPlots.update();
              return chart;

          };

          /**
          * Render a notched box on the current chart
          * @param options
          * @param [options.show=true] Toggle the whole plot on and off
          * @param [options.showNotchBox=true] Show the notch box
          * @param [options.showLines=false] Show lines at the confidence intervals
          * @param [options.boxWidth=35] The width of the widest part of the box
          * @param [options.medianWidth=20] The width of the narrowist part of the box
          * @param [options.lineWidth=50] The width of the confidence interval lines
          * @param [options.notchStyle=null] null=traditional style, 'box' cuts out the whole notch in right angles
          * @param [options.colors=chart default] The color mapping for the notch boxes
          * @returns {*} The chart object
          */
          chart.renderNotchBoxes = function (options) {
              chart.notchBoxes = {};

              //Defaults
              var defaultOptions = {
                  show: true,
                  showNotchBox: true,
                  showLines: false,
                  boxWidth: 35,
                  medianWidth: 20,
                  lineWidth: 75,
                  notchStyle: null,
                  colors: null
              };
              chart.notchBoxes.options = shallowCopy(defaultOptions);
              for (var option in options) {
                  chart.notchBoxes.options[option] = options[option]
              }
              var nOpts = chart.notchBoxes.options;

              //Create notch objects
              for (var cName in chart.groupObjs) {
                  chart.groupObjs[cName].notchBox = {};
                  chart.groupObjs[cName].notchBox.objs = {};
              }

              /**
              * Makes the svg path string for a notched box
              * @param cNotch Current notch box object
              * @param notchBounds objBound object
              * @returns {string} A string in the proper format for a svg polygon
              */
              function makeNotchBox(cNotch, notchBounds) {
                  var scaledValues = [];
                  if (nOpts.notchStyle == 'box') {
                      scaledValues = [
                          [notchBounds.boxLeft, chart.yScale(cNotch.metrics.quartile1)],
                          [notchBounds.boxLeft, chart.yScale(cNotch.metrics.lowerNotch)],
                          [notchBounds.medianLeft, chart.yScale(cNotch.metrics.lowerNotch)],
                          [notchBounds.medianLeft, chart.yScale(cNotch.metrics.median)],
                          [notchBounds.medianLeft, chart.yScale(cNotch.metrics.upperNotch)],
                          [notchBounds.boxLeft, chart.yScale(cNotch.metrics.upperNotch)],
                          [notchBounds.boxLeft, chart.yScale(cNotch.metrics.quartile3)],
                          [notchBounds.boxRight, chart.yScale(cNotch.metrics.quartile3)],
                          [notchBounds.boxRight, chart.yScale(cNotch.metrics.upperNotch)],
                          [notchBounds.medianRight, chart.yScale(cNotch.metrics.upperNotch)],
                          [notchBounds.medianRight, chart.yScale(cNotch.metrics.median)],
                          [notchBounds.medianRight, chart.yScale(cNotch.metrics.lowerNotch)],
                          [notchBounds.boxRight, chart.yScale(cNotch.metrics.lowerNotch)],
                          [notchBounds.boxRight, chart.yScale(cNotch.metrics.quartile1)]
                      ];
                  } else {
                      scaledValues = [
                          [notchBounds.boxLeft, chart.yScale(cNotch.metrics.quartile1)],
                          [notchBounds.boxLeft, chart.yScale(cNotch.metrics.lowerNotch)],
                          [notchBounds.medianLeft, chart.yScale(cNotch.metrics.median)],
                          [notchBounds.boxLeft, chart.yScale(cNotch.metrics.upperNotch)],
                          [notchBounds.boxLeft, chart.yScale(cNotch.metrics.quartile3)],
                          [notchBounds.boxRight, chart.yScale(cNotch.metrics.quartile3)],
                          [notchBounds.boxRight, chart.yScale(cNotch.metrics.upperNotch)],
                          [notchBounds.medianRight, chart.yScale(cNotch.metrics.median)],
                          [notchBounds.boxRight, chart.yScale(cNotch.metrics.lowerNotch)],
                          [notchBounds.boxRight, chart.yScale(cNotch.metrics.quartile1)]
                      ];
                  }
                  return scaledValues.map(function (d) {
                      return [d[0], d[1]].join(",");
                  }).join(" ");
              }

              /**
              * Calculate the confidence intervals
              */
              !function calcNotches() {
                  var cNotch, modifier;
                  for (var cName in chart.groupObjs) {
                      cNotch = chart.groupObjs[cName];
                      modifier = (1.57 * (cNotch.metrics.iqr / Math.sqrt(cNotch.values.length)));
                      cNotch.metrics.upperNotch = cNotch.metrics.median + modifier;
                      cNotch.metrics.lowerNotch = cNotch.metrics.median - modifier;
                  }
              }();

              /**
              * Take a new set of options and redraw the notch boxes
              * @param updateOptions
              */
              chart.notchBoxes.change = function (updateOptions) {
                  if (updateOptions) {
                      for (var key in updateOptions) {
                          nOpts[key] = updateOptions[key]
                      }
                  }

                  for (var cName in chart.groupObjs) {
                      chart.groupObjs[cName].notchBox.objs.g.remove()
                  }
                  chart.notchBoxes.prepareNotchBoxes();
                  chart.notchBoxes.update();
              };

              chart.notchBoxes.reset = function () {
                  chart.notchBoxes.change(defaultOptions)
              };
              chart.notchBoxes.show = function (opts) {
                  if (opts !== undefined) {
                      opts.show = true;
                      if (opts.reset) {
                          chart.notchBoxes.reset()
                      }
                  } else {
                      opts = {show: true};
                  }
                  chart.notchBoxes.change(opts)
              };
              chart.notchBoxes.hide = function (opts) {
                  if (opts !== undefined) {
                      opts.show = false;
                      if (opts.reset) {
                          chart.notchBoxes.reset()
                      }
                  } else {
                      opts = {show: false};
                  }
                  chart.notchBoxes.change(opts)
              };

              /**
              * Update the notch box obj values
              */
              chart.notchBoxes.update = function () {
                  var cName, cGroup;

                  for (cName in chart.groupObjs) {
                      cGroup = chart.groupObjs[cName];

                      // Get the box size
                      var boxBounds = getObjWidth(nOpts.boxWidth, cName);
                      var medianBounds = getObjWidth(nOpts.medianWidth, cName);

                      var notchBounds = {
                          boxLeft: boxBounds.left,
                          boxRight: boxBounds.right,
                          middle: boxBounds.middle,
                          medianLeft: medianBounds.left,
                          medianRight: medianBounds.right
                      };

                      // Notch Box
                      if (cGroup.notchBox.objs.notch) {
                          cGroup.notchBox.objs.notch
                              .attr("points", makeNotchBox(cGroup, notchBounds));
                      }
                      if (cGroup.notchBox.objs.upperLine) {
                          var lineBounds = null;
                          if (nOpts.lineWidth) {
                              lineBounds = getObjWidth(nOpts.lineWidth, cName)
                          } else {
                              lineBounds = objBounds
                          }

                          var confidenceLines = {
                              upper: chart.yScale(cGroup.metrics.upperNotch),
                              lower: chart.yScale(cGroup.metrics.lowerNotch)
                          };
                          cGroup.notchBox.objs.upperLine
                              .attr("x1", lineBounds.left)
                              .attr("x2", lineBounds.right)
                              .attr('y1', confidenceLines.upper)
                              .attr("y2", confidenceLines.upper);
                          cGroup.notchBox.objs.lowerLine
                              .attr("x1", lineBounds.left)
                              .attr("x2", lineBounds.right)
                              .attr('y1', confidenceLines.lower)
                              .attr("y2", confidenceLines.lower);
                      }
                  }
              };

              /**
              * Create the svg elements for the notch boxes
              */
              chart.notchBoxes.prepareNotchBoxes = function () {
                  var cName, cNotch;

                  if (nOpts && nOpts.colors) {
                      chart.notchBoxes.colorFunct = getColorFunct(nOpts.colors);
                  } else {
                      chart.notchBoxes.colorFunct = chart.colorFunct
                  }

                  if (nOpts.show == false) {
                      return
                  }

                  for (cName in chart.groupObjs) {
                      cNotch = chart.groupObjs[cName].notchBox;

                      cNotch.objs.g = chart.groupObjs[cName].g.append("g").attr("class", "notch-plot");
                      // Plot Box (default show)
                      if (nOpts.showNotchBox) {
                          cNotch.objs.notch = cNotch.objs.g.append("polygon")
                              .attr("class", "notch")
                              .style("fill", chart.notchBoxes.colorFunct(cName))
                              .style("stroke", chart.notchBoxes.colorFunct(cName));
                          //A stroke is added to the notch with the group color, it is
                          // hidden by default and can be shown through css with stroke-width
                      }

                      //Plot Confidence Lines (default hide)
                      if (nOpts.showLines) {
                          cNotch.objs.upperLine = cNotch.objs.g.append("line")
                              .attr("class", "upper confidence line")
                              .style("stroke", chart.notchBoxes.colorFunct(cName));

                          cNotch.objs.lowerLine = cNotch.objs.g.append("line")
                              .attr("class", "lower confidence line")
                              .style("stroke", chart.notchBoxes.colorFunct(cName));
                      }
                  }
              };
              chart.notchBoxes.prepareNotchBoxes();

              d3.select(window).on('resize.' + chart.selector + '.notchBox', chart.notchBoxes.update);
              chart.notchBoxes.update();
              return chart;
          };

          /**
          * Render a raw data in various forms
          * @param options
          * @param [options.show=true] Toggle the whole plot on and off
          * @param [options.showPlot=false] True or false, show points
          * @param [options.plotType='none'] Options: no scatter = (false or 'none'); scatter points= (true or [amount=% of width (default=10)]); beeswarm points = ('beeswarm')
          * @param [options.pointSize=6] Diameter of the circle in pizels (not the radius)
          * @param [options.showLines=['median']] Can equal any of the metrics lines
          * @param [options.showbeanLines=false] Options: no lines = false
          * @param [options.beanWidth=20] % width
          * @param [options.colors=chart default]
          * @returns {*} The chart object
          *
          */
          chart.renderDataPlots = function (options) {
              chart.dataPlots = {};


              //Defaults
              var defaultOptions = {
                  show: true,
                  showPlot: false,
                  plotType: 'none',
                  pointSize: 6,
                  showLines: false,//['median'],
                  showBeanLines: false,
                  beanWidth: 20,
                  colors: null
              };   

              chart.dataPlots.options = shallowCopy(defaultOptions);
              for (var option in options) {
                  chart.dataPlots.options[option] = options[option]
              }
              var dOpts = chart.dataPlots.options;

              //Create notch objects
              for (var cName in chart.groupObjs) {
                  chart.groupObjs[cName].dataPlots = {};
                  chart.groupObjs[cName].dataPlots.objs = {};
              }
              // The lines don't fit into a group bucket so they live under the dataPlot object
              chart.dataPlots.objs = {};

              /**
              * Take updated options and redraw the data plots
              * @param updateOptions
              */
              chart.dataPlots.change = function (updateOptions) {
                  if (updateOptions) {
                      for (var key in updateOptions) {
                          dOpts[key] = updateOptions[key]
                      }
                  }

                  chart.dataPlots.objs.g.remove();
                  for (var cName in chart.groupObjs) {
                      chart.groupObjs[cName].dataPlots.objs.g.remove()
                  }
                  chart.dataPlots.preparePlots();
                  chart.dataPlots.update()
              };

              chart.dataPlots.reset = function () {
                  chart.dataPlots.change(defaultOptions)
              };
              chart.dataPlots.show = function (opts) {
                  if (opts !== undefined) {
                      opts.show = true;
                      if (opts.reset) {
                          chart.dataPlots.reset()
                      }
                  } else {
                      opts = {show: true};
                  }
                  chart.dataPlots.change(opts)
              };
              chart.dataPlots.hide = function (opts) {
                  if (opts !== undefined) {
                      opts.show = false;
                      if (opts.reset) {
                          chart.dataPlots.reset()
                      }
                  } else {
                      opts = {show: false};
                  }
                  chart.dataPlots.change(opts)
              };

              /**
              * Update the data plot obj values
              */
              chart.dataPlots.update = function () {
                  var cName, cGroup, cPlot;

                  // Metrics lines
                  if (chart.dataPlots.objs.g) {
                      var halfBand = chart.xScale.rangeBand() / 2; // find the middle of each band
                      for (var cMetric in chart.dataPlots.objs.lines) {
                          chart.dataPlots.objs.lines[cMetric].line
                              .x(function (d) {
                                  return chart.xScale(d.x) + halfBand
                              });
                          chart.dataPlots.objs.lines[cMetric].g
                              .datum(chart.dataPlots.objs.lines[cMetric].values)
                              .attr('d', chart.dataPlots.objs.lines[cMetric].line);
                      }
                  }


                  for (cName in chart.groupObjs) {
                      cGroup = chart.groupObjs[cName];
                      cPlot = cGroup.dataPlots;

                      if (cPlot.objs.points) {
                          if (dOpts.plotType == 'beeswarm') {
                              var swarmBounds = getObjWidth(100, cName);
                              var yPtScale = chart.yScale.copy()
                                  .range([Math.floor(chart.yScale.range()[0] / dOpts.pointSize), 0])
                                  .interpolate(d3.interpolateRound)
                                  .domain(chart.yScale.domain());
                              var maxWidth = Math.floor(chart.xScale.rangeBand() / dOpts.pointSize);
                              var ptsObj = {};
                              var cYBucket = null;
                              //  Bucket points
                              for (var pt = 0; pt < cGroup.values.length; pt++) {
                                  cYBucket = yPtScale(cGroup.values[pt]);
                                  if (ptsObj.hasOwnProperty(cYBucket) !== true) {
                                      ptsObj[cYBucket] = [];
                                  }
                                  ptsObj[cYBucket].push(cPlot.objs.points.pts[pt]
                                      .attr("cx", swarmBounds.middle)
                                      .attr("cy", yPtScale(cGroup.values[pt]) * dOpts.pointSize));
                              }
                              //  Plot buckets
                              var rightMax = Math.min(swarmBounds.right - dOpts.pointSize);
                              for (var row in ptsObj) {
                                  var leftMin = swarmBounds.left + (Math.max((maxWidth - ptsObj[row].length) / 2, 0) * dOpts.pointSize);
                                  var col = 0;
                                  for (pt in ptsObj[row]) {
                                      ptsObj[row][pt].attr("cx", Math.min(leftMin + col * dOpts.pointSize, rightMax) + dOpts.pointSize / 2);
                                      col++
                                  }
                              }
                          } else { // For scatter points and points with no scatter
                              var plotBounds = null,
                                  scatterWidth = 0,
                                  width = 0;
                              if (dOpts.plotType == 'scatter' || typeof dOpts.plotType == 'number') {
                                  //Default scatter percentage is 20% of box width
                                  scatterWidth = typeof dOpts.plotType == 'number' ? dOpts.plotType : 20;
                              }

                              plotBounds = getObjWidth(scatterWidth, cName);
                              width = plotBounds.right - plotBounds.left;

                              for (var pt = 0; pt < cGroup.values.length; pt++) {
                                  cPlot.objs.points.pts[pt]
                                      .attr("cx", plotBounds.middle + addJitter(true, width))
                                      .attr("cy", chart.yScale(cGroup.values[pt]));
                              }
                          }
                      }


                      if (cPlot.objs.bean) {
                          var beanBounds = getObjWidth(dOpts.beanWidth, cName);
                          for (var pt = 0; pt < cGroup.values.length; pt++) {
                              cPlot.objs.bean.lines[pt]
                                  .attr("x1", beanBounds.left)
                                  .attr("x2", beanBounds.right)
                                  .attr('y1', chart.yScale(cGroup.values[pt]))
                                  .attr("y2", chart.yScale(cGroup.values[pt]));
                          }
                      }
                  }
              };

              /**
              * Create the svg elements for the data plots
              */
              chart.dataPlots.preparePlots = function () {
                  var cName, cPlot;

                  if (dOpts && dOpts.colors) {
                      chart.dataPlots.colorFunct = getColorFunct(dOpts.colors);
                  } else {
                      chart.dataPlots.colorFunct = chart.colorFunct
                  }

                  if (dOpts.show == false) {
                      return
                  }

                  // Metrics lines
                  chart.dataPlots.objs.g = chart.objs.g.append("g").attr("class", "metrics-lines");
                  if (dOpts.showLines && dOpts.showLines.length > 0) {
                      chart.dataPlots.objs.lines = {};
                      var cMetric;
                      for (var line in dOpts.showLines) {
                          cMetric = dOpts.showLines[line];
                          chart.dataPlots.objs.lines[cMetric] = {};
                          chart.dataPlots.objs.lines[cMetric].values = [];
                          for (var cGroup in chart.groupObjs) {
                              chart.dataPlots.objs.lines[cMetric].values.push({
                                  x: cGroup,
                                  y: chart.groupObjs[cGroup].metrics[cMetric]
                              })
                          }
                          chart.dataPlots.objs.lines[cMetric].line = d3.svg.line()
                              .interpolate("cardinal")
                              .y(function (d) {
                                  return chart.yScale(d.y)
                              });
                          chart.dataPlots.objs.lines[cMetric].g = chart.dataPlots.objs.g.append("path")
                              .attr("class", "line " + cMetric)
                              .attr("data-metric", cMetric)
                              .style("fill", 'none')
                              .style("stroke", chart.colorFunct(cMetric));
                      }

                  }

                  // mention active models number
                  var loopValue = 0

                  for (cName in chart.groupObjs) {

                      cPlot = chart.groupObjs[cName].dataPlots;
                      cPlot.objs.g = chart.groupObjs[cName].g.append("g").attr("class", "data-plot");

                      // Points Plot
                      if (dOpts.showPlot) {
                          cPlot.objs.points = {g: null, pts: []};
                          cPlot.objs.points.g = cPlot.objs.g.append("g").attr("class", "points-plot");
                          for (var pt = 0; pt < chart.groupObjs[cName].values.length; pt++) {
                              cPlot.objs.points.pts.push(cPlot.objs.points.g.append("circle")
                                  .attr("class", "point")
                                  .attr('r', dOpts.pointSize / 2)// Options is diameter, r takes radius so divide by 2
                                  .style("fill", chart.dataPlots.colorFunct(cName)));
                          }
                      }


                      // Bean lines
                      if (dOpts.showBeanLines) {
                          cPlot.objs.bean = {g: null, lines: []};
                          cPlot.objs.bean.g = cPlot.objs.g.append("g").attr("class", "bean-plot");
                          for (var pt = 0; pt < chart.groupObjs[cName].values.length; pt++) {
                              cPlot.objs.bean.lines.push(cPlot.objs.bean.g.append("line")
                                  .attr("class", "bean line")
                                  .style("stroke-width", '1')
                                  .style("stroke", function () { return chart.data[pt+loopValue].category; }));
                          }
                      }

                      loopValue = loopValue + 500
                  }

              };
              chart.dataPlots.preparePlots();

              d3.select(window).on('resize.' + chart.selector + '.dataPlot', chart.dataPlots.update);
              chart.dataPlots.update();
              return chart;
          };

          return chart;
      }
    }
  },
  mounted () {
    EventBus.$on('callValidation', data => { this.ResultsValid = data })
    EventBus.$on('callValidation', this.ViolinFun)

    EventBus.$on('Responsive', data => {
    this.WH = data})
    EventBus.$on('ResponsiveandChange', data => {
    this.WH = data})

    // reset the views
    EventBus.$on('resetViews', this.reset)
  }
}

</script>

<style>
/*Primary Chart*/

.chart-wrapper .inner-wrapper {
    position: relative;
    padding-bottom: 50%; /*Overwritten by the JS*/
    width: 100%;
}
.chart-wrapper .outer-box {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
}

.chart-wrapper text {
  font-family: sans-serif;
  font-size: 18.5px;
}

.chart-wrapper .axis path,
.chart-wrapper .axis line {
    fill: none;
    stroke: #888;
    stroke-width: 2px;
    shape-rendering: crispEdges;
}

.chart-wrapper .y.axis .tick line {
    stroke: lightgrey;
    opacity: 0.6;
    stroke-dasharray: 2,1;
    stroke-width: 1;
    shape-rendering: crispEdges;

}

.chart-wrapper .x.axis .domain {
  display: none;
}

.axis text {
  font-size: 16px !important;
}

.chart-wrapper div.tooltip {
    position: absolute;
    text-align: left;
    padding: 3px;
    font: 12px sans-serif;
    background: lightcyan;
    border: 0px;
    border-radius: 1px;
    pointer-events: none;
    opacity: 0.7;
}

.chart-wrapper .box-plot circle.median {
    /*the script makes the circles the same color as the box, you can override this in the js*/
    fill: white !important;
}

.chart-wrapper .box-plot .mean {
    stroke: white;
    stroke-dasharray: 2,1;
    stroke-width: 1px;
}

@media (max-width:500px){
    .chart-wrapper .box-plot circle {display: none;}
}

/*Violin Plot*/

.chart-wrapper .violin-plot .area {
    shape-rendering: geometricPrecision;
    opacity: 0.4;
}

.chart-wrapper .violin-plot .line {
    fill: none;
    stroke-width: 2px;
    shape-rendering: geometricPrecision;
}

/*Notch Plot*/
.chart-wrapper .notch-plot .notch {
    fill-opacity: 0.4;
    stroke-width: 2;
}

/* Point Plots*/
.chart-wrapper .points-plot .point {
    stroke: black;
    stroke-width: 1px;
}

.chart-wrapper .metrics-lines {
    stroke-width: 4px;
}

/* Non-Chart Styles for demo*/
.chart-options  {
    min-width: 200px;
    font-size: 18.5px;
    font-family: sans-serif;
}
.chart-options button {
    margin: 3px;
    padding: 3px;
    font-size: 18.5px;
}
.chart-options p {
    display: inline;
}
@media (max-width:500px){
    .chart-options p {display: block;}
}
</style>