/*!
 * Ark Graphs v1.0.0-0 (2015-10-08)
 * http://ark.genesys.com
 * Copyright (c) 2015 Ark Team at Genesys; License: MIT
 */

'use strict';

// TODO: This Angular module should be use from external package
// Probably included into underscore.js Nexus package
angular.module('underscore', []).factory('_', function() {
  return window._;
});

angular.module('ark.graphs', [
    'underscore',
    'ark.graphs.common',
    'ark.graphs.bar',
    'ark.graphs.bubble-graph',
    'ark.graphs.spark-line',
    //'ark.graphs.line-graph',
    'ark.graphs.gauge',
    'ark.graphs.donut',
    // 'ark.graphs.pie',
    // 'ark.graphs.multi-widget',
    'ark.graphs.multi-line-graph',
    'ark.graphs.multiBar',
    'ark.graphs.verticalMultiBar',
    'ark.graphs.simpleLine',
    'ark.graphs.standardDeviation'
  ])
  .run(['$rootScope', function($rootScope) {
    $rootScope.safeApply = function(fn) {
      var phase = this.$root.$$phase;
      if (phase === '$apply' || phase === '$digest') {
        if (fn && (typeof(fn) === 'function')) {
          fn();
        }
      } else {
        this.$apply(fn);
      }
    };
    angular.element(window).on('resize', function() {
      $rootScope.safeApply();
    });
  }]);


'use strict';

angular.module('ark.graphs.common', []);

'use strict';

angular.module('ark.graphs.bar', ['ark.graphs.common']);

'use strict';

angular.module('ark.graphs.bubble-graph', ['ark.graphs.common']);

'use strict';

angular.module('ark.graphs.donut', ['ark.graphs.common']);

'use strict';

angular.module('ark.graphs.gauge', ['ark.graphs.common']);

'use strict';
angular.module('ark.graphs.multiBar', ['ark.graphs.common', 'nvd3']);

'use strict';

angular.module('ark.graphs.multi-line-graph', ['ark.graphs.common']);

'use strict';

angular.module('ark.graphs.simpleLine', ['ark.graphs.common', 'nvd3']);

'use strict';

angular.module('ark.graphs.spark-line', ['ark.graphs.common']);

'use strict';

angular.module('ark.graphs.standardDeviation', ['ark.graphs.common', 'nvd3']);

'use strict';

angular.module('ark.graphs.verticalMultiBar', ['ark.graphs.common', 'nvd3']);

'use strict';

angular.module('ark.graphs.common')
  .service('ark.graphs.arc-service', ['ark.graphs.d3',
    function(d3) {
      var ArcService = function() {};

      ArcService.prototype.polarToCartesian = function(centerX, centerY, radius, angleInDegrees) {
        var angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;
        return {
          x: centerX + (radius * Math.cos(angleInRadians)),
          y: centerY + (radius * Math.sin(angleInRadians))
        };
      };

      ArcService.prototype.toRadians = function(degrees) {
        return (degrees / 180) * Math.PI;
      };

      ArcService.prototype.toDegrees = function(radians) {
        return (radians * 180) / Math.PI;
      };

      ArcService.prototype.describeArc = function(x, y, radius, startAngle, endAngle) {
        var start = this.polarToCartesian(x, y, radius, endAngle);
        var end = this.polarToCartesian(x, y, radius, startAngle);
        var arcSweep = ((endAngle - startAngle) <= 180) ? 0 : 1;
        return [
          'M', start.x, start.y,
          'A', radius, radius, 0, arcSweep, 0, end.x, end.y
        ].join(' ');
      };

      ArcService.prototype.computeArc = function(startAngle, endAngle, value, max, width, height, radius) {
        return this.describeArc(
          width / 2,
          height / 2,
          radius,
          startAngle,
          endAngle * (value / max)
        );
      };

      ArcService.prototype.computeRotation = function(angle, width, height) {
        return 'rotate(' + angle + ',' + width / 2 + ',' + height / 2 + ')';
      };

      ArcService.prototype.translate = function(width, height) {
        return 'translate(' + width / 2 + ',' + height / 2 + ')';
      };

      ArcService.prototype.d3Arc = function(radius, strokeWidth) {
        return d3.svg.arc()
          .outerRadius(radius)
          .innerRadius(radius - strokeWidth);
      };

      ArcService.prototype.d3Pie = function(amplitude, padAngle, sorting) {
        var sort = null;
        if (sorting === 'ascending') {
          sort = d3.ascending;
        } else if (sorting === 'descending') {
          sort = d3.descending;
        }
        return d3.layout.pie()
          .sort(sort)
          .padAngle(this.toRadians(padAngle))
          .startAngle(0)
          .endAngle(this.toRadians(amplitude))
          .value(function(d) {
            return d + Math.random() / 100000;
          });
      };

      return new ArcService();
    }
  ]);

'use strict';

angular.module('ark.graphs.common')
  .service('ark.graphs.color-service', function() {
    var RED = '#EA4F6B';
    var YELLOW = '#F8A740';
    var BLUE = '#203B73';
    var GREEN = '#4AC764';

    var THRESHOLDS_4 = [RED, YELLOW, BLUE, GREEN];
    var THRESHOLDS_3 = [RED, YELLOW, GREEN];
    var THRESHOLDS_2 = [RED, GREEN];
    var THRESHOLDS_1 = [GREEN];

    var PALETTE = ['#203B73', '#2E69DB', '#5E99FF', '#9BBCE0', '#5A6B8C', '#75A8FF', '#0F6A51', '#569180', '#14819C', '#7EC0C2', '#AFD6D2', '#584FB3', '#7272E0', '#B9B9F0', '#575746', '#827C75', '#C9C4B7', '#8C6542', '#8A4D67', '#C48C88', '#EBC8BE', '#724787', '#B07EC2', '#D1B4D9'];

    var ColorService = function() {};

    ColorService.prototype.getStatusColors = function(n) { // supports for up to four status colors
      switch (n) {
        case 4:
          return THRESHOLDS_4;
        case 3:
          return THRESHOLDS_3;
        case 2:
          return THRESHOLDS_2;
        default: // n == 1
          return THRESHOLDS_1;
      }
    };

    ColorService.prototype.arkPalette = function() {
      return PALETTE;
    };

    ColorService.prototype.arkBlueColors = function() {
      return this.arkPalette().slice(0, 5); //return the first 5 items of the palette which are blue colors
    };

    ColorService.prototype.arkThresholdIcon = function() {
      return RED;
    };

    return new ColorService();
  });

'use strict';

angular.module('ark.graphs.common')
  .service('ark.graphs.config-service', ['ark.graphs.color-service',
    function(ColorService) {
      var ConfigService = function() {};

      ConfigService.prototype.getData = function(index, arr) {
        return (arr[index]) ? arr[index] : arr[0];
      };

      ConfigService.prototype.getStatusColors = function(obj) {
        if ((obj.data.thresholds.values.length + 1) !== obj.data.thresholds.statusColors.length) {
          obj.data.thresholds.statusColors = ColorService.getStatusColors(obj.data.thresholds.values.length + 1);
        }
      };

      ConfigService.prototype.updateColors = function(obj) {
        var colors = obj.data.thresholds ? obj.data.thresholds.statusColors : obj.data.colors;
        if (colors.length !== obj.numberOfData) {
          colors = ColorService.arkPalette().slice(obj.numberOfData - 1);
        }
      };

      ConfigService.prototype.updateInitialValues = function(obj) {
        if (obj.initialValues.length === 0) {
          obj.initialValues = Array.apply(null, new Array(obj.numberOfData)).map(function() {
            return 0;
          });
        }
      };

      ConfigService.prototype.updateLegendLabels = function(obj) {
        if (obj.numberOfData !== obj.legend.title.length) {
          obj.legend.title = Array.apply(null, new Array(obj.numberOfData)).map(function(x, i) {
            return 'Label ' + String.fromCharCode(i + 65);
          });
        }
      };

      ConfigService.prototype.updateTooltipLabels = function(obj) {
        if (obj.numberOfData !== obj.data.labels.length) {
          obj.data.labels = Array.apply(null, new Array(obj.numberOfData)).map(function(x, i) {
            return 'data ' + i.toString();
          });
        }
      };

      return new ConfigService();
    }
  ]);

'use strict';

angular.module('ark.graphs.common')
  .service('ark.graphs.d3', function() {
    return d3;
  });

/* global nv */
'use strict';

angular.module('ark.graphs.common')
  .service('ark.graphs.nvd3', function() {
    return nv;
  });

'use strict';

angular.module('ark.graphs.common')
  .service('ark.graphs.line-service', ['ark.graphs.d3', function(d3) {
    var LineService = function() {};

    LineService.prototype.createLine = function(rangeX, rangeY, fieldX, fieldY) {
      return d3.svg.area()
        .x(function(d) {
          return rangeX(d[fieldX] || 0);
        })
        .y(function(d) {
          return rangeY(d[fieldY] || 0);
        });
    };

    LineService.prototype.generateTicks = function(axis, n) {
      var arr = [];
      var difference = Math.abs(axis.domain()[0] - axis.domain()[1]);
      var interval = difference / (n - 1);
      var start = axis.domain()[0];

      for (var i = 0; i < n; i++) {
        arr.push(start);
        start += interval;
      }
      return arr;
    };

    LineService.prototype.createRange = function(start, end) {
      return d3.time.scale().range([start, end]);
    };

    LineService.prototype.createLinearRange = function(start, end) {
      return d3.scale.linear().range([start, end]);
    };

    LineService.prototype.createAxisBubble = function(d3range, info) {
      var axis = d3.svg.axis().scale(d3range);

      if (typeof info.orient !== 'undefined') {
        axis.orient(info.orient);
      }
      if (typeof info.ticks !== 'undefined') {
        axis.ticks(info.ticks);
      }
      if (typeof info.tickValues !== 'undefined') {
        axis.tickValues(info.tickValues);
      }
      if (typeof info.ticks !== 'undefined') {
        axis.ticks(info.ticks);
      }
      if (typeof info.innerTickSize !== 'undefined') {
        axis.innerTickSize(info.innerTickSize);
      }
      if (typeof info.tickFormat !== 'undefined') {
        axis.tickFormat(info.tickFormat);
      }
      return axis;
    };

    LineService.prototype.createAxis = function(d3range, ticks, orient) {
      return d3.svg.axis().scale(d3range).orient(orient ? orient : 'left').ticks(ticks);
    };

    LineService.prototype.scaleFromDomain = function(axis, domain) {
      return axis.domain(domain);
    };

    LineService.prototype.scaleFromData = function(axis, data, field, offset) {
      var min = d3.min(data, function(d) {
        return (d[field] !== undefined) ? d[field] : d3.min(d, function(c) {
          return c[field] || 0;
        });
      });
      var max = d3.max(data, function(d) {
        return (d[field] !== undefined) ? d[field] : d3.max(d, function(c) {
          return c[field] || min + 1;
        });
      });
      if (typeof offset !== 'undefined') {
        max += offset.max * (max - min);
        min -= offset.min * (max - min);
      }
      return axis.domain([min, max]);
    };

    LineService.prototype.scaleFromDataBubble = function(axis, data, field, offset) {
      var min = d3.min(data, function(d) {
        return (typeof d[field] !== 'undefined') ? d[field] : d3.min(d, function(c) {
          return c[field];
        });
      });
      var max = d3.max(data, function(d) {
        return (typeof d[field] !== 'undefined') ? d[field] : d3.max(d, function(c) {
          return c[field];
        });
      });

      if (typeof offset !== 'undefined') {
        max += offset.max * (max - min);
        min -= offset.min * (max - min);
      }
      return axis.domain([min, max]);
    };

    LineService.prototype.scaleFrom0ToMax = function(axis, data, field) {
      return axis.domain([
        0,
        d3.max(data, function(d) {
          return (d.constructor !== Array) ? d[field] : d3.max(d, function(c) {
            return c[field];
          });
        })
      ]);
    };

    LineService.prototype.scale = function(axis, range) {
      return axis.domain(range);
    };

    LineService.prototype.computeLine = function(x1, y1, x2, y2) {
      return ['M', x1, y1, 'L', x2, y2, 'Z'].join(' ');
    };

    return new LineService();
  }]);

'use strict';

angular.module('ark.graphs.common')
  .service('ark.graphs.pie-service', ['ark.graphs.arc-service',
    function(ArcService) {
      var PieService = function() {};
      var that = new PieService();

      PieService.prototype.getLabelTranslation = function(d, config) {
        var size;
        if (config.slice.label.position === 'out') {
          size = config.radius + config.slice.hover.growBy + config.slice.label.line.size;
        } else if (config.slice.label.position === 'in') {
          //If it is a pie chart

          if (config.radius === config.strokeWidth) {
            size = config.radius - config.slice.label.value.fontsize * 3;
          } else {
            size = config.radius - config.strokeWidth / 2 - config.slice.label.value.fontsize;
          }
        }
        return 'translate(' +
          Math.cos(((d.startAngle + d.endAngle + d.padAngle * 2 - Math.PI) / 2)) * (size) + ',' +
          Math.sin((d.startAngle + d.endAngle + d.padAngle * 2 - Math.PI) / 2) * (size) + ')';
      };

      PieService.prototype.getTextAnchor = function(d) {
        return (that.getMiddle(d) < Math.PI) ? 'beginning' : 'end';
      };

      PieService.prototype.getMiddle = function(d) {
        return (d.startAngle + d.endAngle) / 2 + d.padAngle * 4;
      };

      PieService.prototype.getLabelValueDY = function(d) {
        var middle = that.getMiddle(d);
        if (middle <= Math.PI / 2) {
          return 0;
        } else if (middle > Math.PI / 2 && middle <= Math.PI * 1.5) {
          return 10;
        } else {
          return 0;
        }
      };

      PieService.prototype.getLabelNameDY = function(d) {
        var middle = that.getMiddle(d);
        if (middle <= Math.PI / 2) {
          return 0;
        } else if (middle > Math.PI / 2 && middle <= Math.PI * 1.5) {
          return 10;
        } else {
          return 0;
        }
      };

      PieService.prototype.getLabelRotation = function(d) {
        return ArcService.computeRotation(
          ArcService.toDegrees(that.getMiddle(d)),
          0,
          0
        );
      };

      PieService.prototype.drawLabels = function(widget, pieData, config) {
        if (config.slice.label.position === 'out' && config.slice.label.line.display) {
          var lines = widget.select('.ark-graphs-label-group').selectAll('line');

          lines.data(pieData)
            .enter()
            .append('line')
            .attr('class', config.slice.label.line.class)
            .attr('x1', 0)
            .attr('x2', 0)
            .attr('y1', -config.radius - config.slice.hover.growBy)
            .attr('y2', -config.radius - config.slice.hover.growBy - config.slice.label.line.size)
            .attr('stroke', config.slice.label.line.color)
            .attr('transform', function(d) {
              return that.getLabelRotation(d);
            });

          lines = widget.select('.ark-graphs-label-group').selectAll('line');
          lines.transition()
            .duration(config.transitions.arc)
            .attr('transform', function(d) {
              return that.getLabelRotation(d);
            });
        }

        if (config.slice.label.value.display) {
          var valueLabels = widget.select('.ark-graphs-label-group').selectAll('text.' + config.slice.label.value.class);
          valueLabels.data(pieData)
            .enter()
            .append('text')
            .attr('fill', config.slice.label.value.color)
            .attr('font-size', config.slice.label.value.fontsize + 'px')
            .attr('class', config.slice.label.value.class + ' ' + config.slice.label.position)
            .attr('transform', function(d) {
              return that.getLabelTranslation(d, config);
            })
            .attr('dy', that.getLabelValueDY)
            .attr('text-anchor', that.getTextAnchor);

          valueLabels = widget.select('.ark-graphs-label-group').selectAll('text.' + config.slice.label.value.class);
          valueLabels.transition()
            .duration(config.transitions.arc)
            .text(config.slice.label.value.format)
            .attr('dy', that.getLabelValueDY)
            .attr('text-anchor', that.getTextAnchor)
            .attr('transform', function(d) {
              return that.getLabelTranslation(d, config);
            });
        }

        var nameLabels = widget.select('.ark-graphs-label-group').selectAll('text.' + config.slice.label.name.class);

        nameLabels.data(pieData)
          .enter()
          .append('text')
          .attr('fill', config.slice.label.name.color)
          .attr('font-size', config.slice.label.name.fontsize + 'px')
          .attr('class', config.slice.label.name.class + ' ' + config.slice.label.position)
          .attr('transform', function(d) {
            return that.getLabelTranslation(d, config);
          })
          .attr('dy', that.getLabelNameDY)
          .attr('text-anchor', that.getTextAnchor)
          .text(function(d, i) {
            return config.slice.label.name.format(d, i);
          });

        nameLabels = widget.select('.ark-graphs-label-group').selectAll('text.' + config.slice.label.name.class);
        nameLabels
          .transition()
          .duration(config.transitions.arc)
          .attr('dy', that.getLabelNameDY)
          .attr('text-anchor', that.getTextAnchor)
          .text(function(d, i) {
            return config.slice.label.name.format(d, i);
          })
          .attr('transform', function(d) {
            return that.getLabelTranslation(d, config);
          });
      };
      return that;
    }
  ]);

'use strict';

angular.module('ark.graphs.common')
  .service('ark.graphs.text-service', ['ark.graphs.color-service', 'ark.graphs.threshold-service',
    function(ColorService, ThresholdService) {
      var TextService = function() {};

      TextService.prototype.showText = function(configuration, widget, data) {
        if (!configuration.legend.display) {
          return;
        }

        var legend = widget.select('.ark-graphs-' + configuration.type + '-div')
          .append('div')
          .attr('class', 'ark-graphs-' + configuration.type + '-legend')
          .style('width', configuration.legend.width.toString() + 'px');

        if (configuration.legend.padding.top) {
          legend
            .style('padding-top', configuration.legend.padding.top + 'px');
        }

        legend.selectAll('div').data(data)
          .enter()
          .append('div')
          .attr('class', function(d, i) {
            return 'ark-graphs-' + configuration.type + '-legend-text ark-graphs-' + configuration.type + '-legend-text-' + i;
          });

        var legendLines = legend.selectAll('div');
        legendLines
          .append('span')
          .attr('class', 'metric-name')
          .style('left', function() {
            if (typeof configuration.legend.letters !== 'undefined' && configuration.legend.letters.display) {
              return '20px';
            }
            return '0px';
          })
          .append('text')
          .text(function(d, i) {
            return configuration.legend.title[i];
          })
          .style('font-size', configuration.legend.fontsize + 'px');

        if (typeof configuration.legend.letters !== 'undefined' && configuration.legend.letters.display) {
          legendLines
            .append('span')
            .attr('class', 'letters')
            .text(function(d, i) {
              return String.fromCharCode(i + 65);
            })
            .style('font-size', configuration.legend.fontsize + 'px');
        }

        legendLines
          .append('span')
          .attr('class', 'data')
          .text(function(d, i) {
            return configuration.legend.format(data[i], i);
          })
          .style('font-size', configuration.legend.fontsize + 'px');

        legendLines
          .append('span')
          .attr('class', function(d) {
            var icon = 'font-icon ';
            if (typeof configuration.data.thresholds !== 'undefined' && configuration.data.thresholds.display === true) {
              icon += configuration.legend.icon.display ? ThresholdService.getFontIcon(d, configuration) : '';
            } else {
              if (configuration.legend.icon.display && d < configuration.legend.icon.minValue) {
                icon += 'icon-alert-circle';
              }
            }
            return icon;
          })
          .style('color', function(d) {
            if (typeof configuration.data.thresholds !== 'undefined' && configuration.data.thresholds.display === true) {
              return ThresholdService.getDataColor(d, configuration);
            } else {
              return ColorService.arkThresholdIcon();
            }
          });
      };

      TextService.prototype.showTextUpdate = function(configuration, widget, data) {
        var legend = widget.select('.ark-graphs-' + configuration.type + '-legend');
        var legendLines = legend.selectAll('div');
        legendLines.select('.font-icon').data(data)
          .attr('class', function(d) {
            var icon = 'font-icon ';
            if (typeof configuration.data.thresholds !== 'undefined' && configuration.data.thresholds.display === true) {
              icon += configuration.legend.icon.display ? ThresholdService.getFontIcon(d, configuration) : '';
            } else {
              if (configuration.legend.icon.display && d < configuration.legend.icon.minValue) {
                icon += 'icon-alert-circle';
              }
            }
            return icon;
          })
          .style('font-size', '16px')
          .style('top', function() {
            var base = 16;
            var current = configuration.legend.fontsize;
            var top = (current / base * 4);

            return configuration.type === 'spark-line' ? -top / 2 + 'px' : -top + 'px';
          })
          .style('color', function(d) {
            if (typeof configuration.data.thresholds !== 'undefined' && configuration.data.thresholds.display === true) {
              return ThresholdService.getDataColor(d, configuration);
            } else {
              return ColorService.arkThresholdIcon();
            }
          });
        legendLines.select('.data')
          .text(function(d, i) {
            return configuration.legend.format(data[i], i);
          });
      };

      TextService.prototype.showTextChart = function(configuration, widget, data) {
        if (!configuration.legend.display) {
          return;
        }

        var legend = widget.select('.ark-graphs-' + configuration.type + '-div')
          .append('div')
          .attr('class', 'ark-graphs-' + configuration.type + '-legend')
          .style('width', configuration.legend.width.toString() + 'px');

        legend.selectAll('div').data(data)
          .enter()
          .append('div')
          .attr('class', function(d, i) {
            return 'ark-graphs-' + configuration.type + '-legend-text ark-graphs-' + configuration.type + '-legend-text-' + i;
          })
          .style('display', 'inline-block');

        var legendLines = legend.selectAll('div');
        legendLines
          .append('div')
          .attr('class', 'color')
          .style('background-color', function(d, i) {
            return configuration.data.colors[i];
          });

        legendLines
          .append('text')
          .style('padding-left', '8px')
          .style('padding-right', '8px')
          .text(function(d, i) {
            return configuration.legend.title[i];
          });

      };

      return new TextService();
    }
  ]);

'use strict';

angular.module('ark.graphs.common')
  .service('ark.graphs.threshold-service', ['ark.graphs.d3',
    function(d3) {
      var ThresholdService = function() {};

      ThresholdService.prototype.isBelowThreshold = function(data, configuration) {
        var i = this.findRegion(data, configuration);
        if (i === configuration.data.thresholds.values.length) {
          return false;
        }
        return true;
      };

      ThresholdService.prototype.findRegion = function(data, configuration) {
        var comparator = configuration.data.thresholds.comparator;
        var i = 0;
        for (i = 0; i < configuration.data.thresholds.values.length; i++) {
          if (comparator(data, configuration.data.thresholds.values[i])) {
            return i;
          }
        }
        return i;
      };

      ThresholdService.prototype.getDataColor = function(data, configuration) {
        var i = this.findRegion(data, configuration);
        return configuration.data.thresholds.statusColors[i];
      };

      ThresholdService.prototype.getFontIcon = function(data, configuration) {
        var i = this.findRegion(data, configuration);
        return configuration.fontIcons[i];
      };

      ThresholdService.prototype.getMiddleIcon = function(configuration, data) {
        d3.select('#' + configuration.id).select('.ark-graphs-' + configuration.type + '-span')
          .attr('class', (this.getFontIcon(data, configuration) === '') ? 'ark-graphs-' + configuration.type + '-span icon-alert-checkmark' : 'ark-graphs-' + configuration.type + '-span ' + this.getFontIcon(data, configuration))
          .style('opacity', (this.getFontIcon(data, configuration) === '') ? '0' : '1')
          .style('color', this.getDataColor(data, configuration));
      };

      ThresholdService.prototype.drawThresholds = function(configuration, arrangedData) {
        angular.forEach(arrangedData, function(value, key) {
          var thresholds = d3.select('#' + configuration.id).select('.ark-graphs-' + configuration.type + '-threshold').selectAll('.path-' + key.toString());
          thresholds.data(configuration.data.thresholds.values, function(d) {
              return d + Math.random() / 1000;
            })
            .enter()
            .append('path')
            .attr('class', 'path-' + key.toString() + ' ark-graphs-' + configuration.type + '-threshold-path-' + key.toString())
            .attr('opacity', configuration.data.thresholds.opacity)
            .attr('stroke-width', configuration.data.thresholds.strokeWidth)
            .transition()
            .attrTween('stroke', function(d, i) {
              var color;
              if (d < arrangedData[key]) {
                color = configuration.data.thresholds.barColorOver;
              } else {
                color = configuration.data.thresholds.barColor;
              }
              var interpolate = d3.interpolate(configuration.currentThresholdColors[key][i], color);
              configuration.currentThresholdColors[key][i] = color;
              return function(t) {
                return interpolate(t);
              };
            })
            .duration(configuration.transitions.thresholds);
          thresholds.remove();
        });
      };

      ThresholdService.prototype.initThresholdValues = function(configuration) {
        configuration.fontIcons = ['icon-alert-checkmark', 'icon-alert-triangle', '', 'icon-alert-circle'];
        configuration.fontIcons = configuration.fontIcons.slice(0, configuration.data.thresholds.values.length);
        configuration.fontIcons.push('icon-alert-circle');
        configuration.currentThresholdColors = [];

        var length = configuration.numberOfData ? configuration.numberOfData : 1;

        for (var i = 0; i < length; i++) {
          var tmp = [];
          for (var j = 0; j < configuration.data.thresholds.values.length; j++) {
            tmp.push(configuration.data.thresholds.color);
          }
          configuration.currentThresholdColors.push(tmp);
        }
      };

      return new ThresholdService();

    }
  ]);

'use strict';

angular.module('ark.graphs.common').service('ark.graphs.tooltip-service', ['ark.graphs.d3', function(d3) {

  var TooltipService = function() {};

  TooltipService.prototype.initTooltip = function(scope) {
    if (scope.tooltip) {
      d3.select('#tooltip-' + scope.internalConfiguration.id)
        .remove();
    }

    scope.tooltip = d3.tip(scope.internalConfiguration.id)
      .attr('id', 'tooltip-' + scope.internalConfiguration.id)
      .attr('class', 'ark-graphs-' + scope.internalConfiguration.type + '-tooltip ark-graphs-tooltip')
      .offset([10, 10])
      .html(scope.internalConfiguration.tooltip.format);
    scope.widget.select('.ark-graphs-' + scope.internalConfiguration.type + '-svg').call(scope.tooltip);

  };

  TooltipService.prototype.showTooltip = function(tooltip) {
    tooltip.show.apply(this, Array.prototype.slice.call(arguments, 1));
  };

  TooltipService.prototype.setCoordinates = function(tooltip, pageX, pageY) {
    tooltip.coordinate({
      x: pageX,
      y: pageY
    });
  };

  TooltipService.prototype.hideTooltip = function(tooltip) {
    tooltip.hide();
  };

  return new TooltipService();
}]);

'use strict';

angular.module('ark.graphs.common').service('ark.graphs.utils', function() {
  var Utils = function() {};

  Utils.prototype.prepareData = function(data, previousData, configuration) {
    var tmp;
    if (previousData.length > configuration.numberOfData) {
      previousData = previousData.slice(0, configuration.numberOfData);
    }
    if (typeof data === 'object') {
      if (data.length === configuration.numberOfData) {
        return data;
      } else {
        tmp = angular.copy(previousData);
        for (var i = 0; i < data.length; i++) {
          tmp.unshift(data);
        }
        tmp = tmp.slice(0, configuration.numberOfData);
        return tmp;
      }
    } else {
      tmp = angular.copy(previousData);
      tmp.unshift(data);
      tmp = tmp.slice(0, configuration.numberOfData);
      return tmp;
    }
  };

  Utils.prototype.generateID = function(type) {
    var id = '';
    var possible = 'abcdef0123456789';

    for (var i = 0; i < 16; i++) {
      id += possible.charAt(Math.floor(Math.random() * possible.length));
    }

    return type + '-' + id;
  };

  Utils.prototype.mergeRecursive = function(obj1, obj2) {
    for (var p in obj2) {
      try {
        obj1[p] = (obj2[p].constructor === Object) ? this.mergeRecursive(obj1[p], obj2[p]) : obj2[p];
      } catch (e) {
        obj1[p] = obj2[p];
      }
    }
    return obj1;
  };

  Utils.prototype.getWidthAndHeight = function(element) {
    var computedStyles = getComputedStyle(element);
    var compute = function(total, sub1, sub2){
      return parseInt(total, 10) - parseInt(sub1, 10) - parseInt(sub2, 10);
    };

    var width = compute(computedStyles.width, computedStyles.paddingLeft, computedStyles.paddingRight);
    var height = compute(computedStyles.height, computedStyles.paddingTop, computedStyles.paddingBottom);

    return {
      width: width,
      height: height
    };
  };

  Utils.prototype.resize = function(configuration, element, skipWidthResize) {
    var e = angular.element(element[0].parentElement);
    var widthAndHeight = this.getWidthAndHeight(e[0]);

    var trueWidth = widthAndHeight.width;

    if (configuration.autosnap && configuration.autosnap.enabled) {
      var tmpConfiguration;
      for (var d in configuration.autosnap.thresholds) {
        tmpConfiguration = configuration.autosnap.thresholds[d].configuration;
        if (trueWidth < configuration.autosnap.thresholds[d].maxWidth) {
          break;
        }
      }
      configuration = this.mergeRecursive(configuration, tmpConfiguration);
    } else {
      if(!skipWidthResize) {
        if (configuration.type !== 'spark-line') {
          configuration.svg.width = trueWidth;
        } else {
          configuration.div.width = trueWidth;
        }
      }
    }
    configuration.parentWidth = widthAndHeight.width;
    configuration.parentHeight = widthAndHeight.height;
    configuration.update(configuration);
  };

  return new Utils();
});

'use strict';

angular.module('ark.graphs.bar').directive('arkBar', ['ark.graphs.bar-config', 'ark.graphs.line-service', 'ark.graphs.text-service', 'ark.graphs.d3', 'ark.graphs.utils', 'ark.graphs.tooltip-service', 'ark.graphs.threshold-service', 'ark.graphs.config-service',
  function(BarConfiguration, LineService, TextService, d3, Utils, TooltipService, ThresholdService, ConfigService) {
    return {
      restrict: 'E',
      scope: {
        configuration: '=',
        data: '='
      },
      link: function($scope, element) {
        $scope.internalConfiguration = new BarConfiguration($scope.configuration);
        $scope.previousData = $scope.internalConfiguration.initialValues;

        $scope.draw = function(data) {
          //Prepare data, adds in previous values of data if required
          $scope.arrangedData = Utils.prepareData(angular.copy(data), $scope.previousData, $scope.internalConfiguration);

          //Draw bars
          var paths = $scope.widget.select('.ark-graphs-bar-container').selectAll('path');
          paths.data($scope.arrangedData, function(d) {
              return d + Math.random() / 1000;
            })
            .enter()
            .append('path')
            .attr('class', 'path')
            .attr('stroke', function(d) {
              return ThresholdService.getDataColor(d, $scope.internalConfiguration);
            })
            .attr('transform', function(d, i) {
              return ['translate(', 0, $scope.internalConfiguration.svg.height * i, ')'].join(' ');
            })
            .attr('stroke-width', $scope.internalConfiguration.strokeWidth)
            .attr('height', $scope.internalConfiguration.strokeWidth / 2)
            .attr('width', $scope.internalConfiguration.svg.width)
            .attr('opacity', 1)
            .transition()
            .attrTween('d', function(d, i) {
              var interpolate = d3.interpolate($scope.previousData[i], $scope.arrangedData[i]);
              if ($scope.internalConfiguration.tooltip.display) {
                TooltipService.hideTooltip($scope.tooltip);
              }
              return function(t) {
                var y = (0 + $scope.internalConfiguration.strokeWidth) / 2;
                var x = (interpolate(t) / $scope.internalConfiguration.max) * $scope.internalConfiguration.svg.width;
                return LineService.computeLine(0, y, x, y);
              };
            })
            .duration($scope.internalConfiguration.transitions.bar)
            .each('end', function() {
              $scope.allowUpdate($scope.arrangedData);
            });
          paths.remove();

          //Draw thesholds and gauge fonticon
          if ($scope.internalConfiguration.data.thresholds.display) {
            ThresholdService.drawThresholds($scope.internalConfiguration, $scope.arrangedData);
            angular.forEach($scope.arrangedData, function(value, key) {
              var thresholds = $scope.widget.select('.ark-graphs-bar-threshold').selectAll('.path-' + key.toString());
              thresholds.attr('d', function(d) {
                  var x = (d / $scope.internalConfiguration.max) * $scope.internalConfiguration.svg.width;
                  return LineService.computeLine(x, 0, x, $scope.internalConfiguration.strokeWidth);
                })
                .attr('transform', ['translate(', 0, $scope.internalConfiguration.svg.height * key, ')'].join(' '));
            });
          }

          //Draw legend
          if ($scope.internalConfiguration.legend.display) {
            TextService.showTextUpdate($scope.internalConfiguration, $scope.widget, $scope.arrangedData);
          }
        };

        //When bar stops transitioning, allow mouse events
        $scope.allowUpdate = function(data) {
          var selection = $scope.widget.selectAll('.ark-graphs-bar-svg');
          selection
            .on('mousemove', function(d, i) {
              $scope.coordinate = d3.mouse($scope.widget.select('.ark-graphs-bar-container')[0][0]);
              $scope.mousemovePath($scope.arrangedData[i], i, this);
            })
            .on('mouseout', function(d, i) {
              $scope.mouseoutPath(i, this);
            });
          $scope.previousData = angular.copy(data);
        };

        $scope.mousemovePath = function(d, i, elem) {
          if ($scope.internalConfiguration.hover.apply) {
            d3.select(elem).select('.ark-graphs-bar-container').selectAll('path').transition()
              .duration(200)
              .attr('stroke-width', $scope.internalConfiguration.hover.growBy + $scope.internalConfiguration.strokeWidth);
            if ($scope.internalConfiguration.data.thresholds.display) {
              $scope.widget.select('.ark-graphs-bar-threshold').selectAll('.ark-graphs-bar-threshold-path-' + i.toString())
                .attr('d', function(d) {
                  var x = (d / $scope.internalConfiguration.max) * $scope.internalConfiguration.svg.width;
                  return LineService.computeLine(x, -($scope.internalConfiguration.hover.growBy / 2), x, $scope.internalConfiguration.strokeWidth + $scope.internalConfiguration.hover.growBy / 2);
                });
            }
          }
          if ($scope.internalConfiguration.tooltip.display) {
            var tooltipWidth = $scope.widget.select('.ark-graphs-tooltip').node().getBoundingClientRect().width;
            var rightXOffset = $scope.widget.select('svg').node().getBoundingClientRect().width - tooltipWidth - 20;
            var tooltipX = ($scope.coordinate[0] < rightXOffset) ? $scope.coordinate[0] : rightXOffset;
            TooltipService.setCoordinates($scope.tooltip, tooltipX, $scope.coordinate[1]);
            TooltipService.showTooltip($scope.tooltip, d, i, ThresholdService.getDataColor(d, $scope.internalConfiguration), ConfigService.getData(i, $scope.internalConfiguration.data.labels));
          }
        };

        $scope.mouseoutPath = function(i, elem) {
          if ($scope.internalConfiguration.hover.apply) {
            d3.select(elem).select('.ark-graphs-bar-container').selectAll('path').transition()
              .duration(200)
              .attr('stroke-width', $scope.internalConfiguration.strokeWidth);
            if ($scope.internalConfiguration.data.thresholds.display) {
              $scope.widget.select('.ark-graphs-bar-threshold').selectAll('.ark-graphs-bar-threshold-path-' + i.toString())
                .attr('d', function(d) {
                  var x = (d / $scope.internalConfiguration.max) * $scope.internalConfiguration.svg.width;
                  return LineService.computeLine(x, 0, x, $scope.internalConfiguration.strokeWidth);
                });
            }
          }
          if ($scope.internalConfiguration.tooltip.display) {
            TooltipService.hideTooltip($scope.tooltip);
          }
        };

        $scope.init = function() {
          $scope.widget = d3.select(element[0]);
          $scope.widget.selectAll('*').remove();

          //Prepare data, adds in previous values of data if required
          $scope.arrangedData = Utils.prepareData(angular.copy($scope.data), $scope.previousData, $scope.internalConfiguration);

          //Main bar widget container
          $scope.widget.append('div')
            .attr('class', 'ark-graphs-bar-div')
            .attr('id', $scope.internalConfiguration.id)
            .style('position', 'relative')
            .style('white-space', 'nowrap')
            .style('width', $scope.internalConfiguration.div.width.toString() + 'px')
            .style('height', ($scope.internalConfiguration.div.height.toString()) + 'px');

          //SVG portion of widget
          $scope.widget.select('.ark-graphs-bar-div')
            .append('svg')
            .attr('class', 'ark-graphs-bar-svg')
            .attr('width', $scope.internalConfiguration.svg.width)
            .attr('height', $scope.internalConfiguration.svg.height * $scope.internalConfiguration.numberOfData);

          $scope.widget.select('.ark-graphs-bar-svg')
            .append('g')
            .attr('class', 'ark-graphs-bar-svg-inner')
            .attr('transform', ['translate(', 0, ',', $scope.internalConfiguration.div.padding.top, ')'].join(' '));

          //Draw background bar/line
          if ($scope.internalConfiguration.background.display) {
            var y = ($scope.internalConfiguration.strokeWidth) / 2;
            angular.forEach($scope.arrangedData, function(value, key) {
              $scope.widget.select('.ark-graphs-bar-svg-inner')
                .append('g')
                .attr('class', 'ark-graphs-bar-line')
                .append('path')
                .attr('stroke', $scope.internalConfiguration.background.color.toString())
                .attr('transform', ['translate(', 0, $scope.internalConfiguration.svg.height * key, ')'].join(' '))
                .attr('stroke-width', $scope.internalConfiguration.background.strokeWidth.toString())
                .attr('d', LineService.computeLine(0, y, $scope.internalConfiguration.svg.width, y));
            });
          }

          //Bar container for drawing/updating bars
          $scope.widget.select('.ark-graphs-bar-svg-inner')
            .append('g')
            .attr('class', 'ark-graphs-bar-container');

          //Threshold container for drawing/updating thresholds
          if ($scope.internalConfiguration.data.thresholds.display) {
            $scope.widget.select('.ark-graphs-bar-svg-inner')
              .append('g')
              .attr('class', 'ark-graphs-bar-threshold');
            ThresholdService.initThresholdValues($scope.internalConfiguration);
          }

          //Draw initial legend
          if ($scope.internalConfiguration.legend.display) {
            //TextService.showText($scope.internalConfiguration, $scope.widget, $scope.arrangedData);
          }

          if ($scope.internalConfiguration.legend.display) {
            TextService.showText($scope.internalConfiguration, $scope.widget, $scope.arrangedData);
          }

          //Initalizes tooltip
          if ($scope.internalConfiguration.tooltip.display) {
            TooltipService.initTooltip($scope);
          }
        };

        if ($scope.internalConfiguration.autoresize) {
          Utils.resize($scope.internalConfiguration, element);
        }

        $scope.init();

        //Check for resizing
        $scope.$watch(function() {
          var e = angular.element(element[0].parentElement);
          return Utils.getWidthAndHeight(e[0]);
        }, function(newVal, oldVal) {
          if (($scope.internalConfiguration.autoresize && newVal.width !== oldVal.width) || ($scope.internalConfiguration.autoresizeY && newVal.height !== oldVal.height)) {
            Utils.resize($scope.internalConfiguration, element);
            $scope.init();
            $scope.draw($scope.data);
          }
        }, true);

        //Check for changes in internalConfiguration
        $scope.$watch('configuration', function() {
          $scope.internalConfiguration.update($scope.configuration);
          $scope.init();
          $scope.draw($scope.data);
        }, true);

        //Check for changes in data
        $scope.$watch('data', function(newVals) {
          if (newVals !== undefined) {
            return $scope.draw(newVals);
          }
        });
      }
    };
  }
]);

'use strict';

angular.module('ark.graphs.bar')
  .factory('ark.graphs.bar-config', ['ark.graphs.color-service', 'ark.graphs.utils', 'ark.graphs.config-service',
    function(ColorService, Utils, ConfigService) {
      var BarConfiguration = function(configuration) {
        this.type = 'bar';
        this.id = Utils.generateID(this.type);
        this.initialValues = [];
        this.max = 100;
        this.strokeWidth = 7;
        this.numberOfDataSet = 1;
        this.numberOfData = this.numberOfDataSet;
        this.autoresize = false; //If set to true, given divWidth will not be taken into account
        this.autoresizeY = false;

        this.svg = {
          height: 14,
          width: 208,
          padding: {
            top: 2
          },
          dataGap: 5
        };

        this.legend = {
          display: true,
          fontsize: 12,
          height: 26,
          width: this.svg.width,
          title: ['Metric name'],
          format: function(d, i) {
            return d;
          },
          letters: {
            display: true
          },
          padding: {
            top: -6,
            left: 0,
            right: 20
          },
          icon: {
            display: true,
            minValue: 40
          }
        };

        this.data = {
          labels: ['data 0'],
          thresholds: {
            display: true,
            values: [75],
            strokeWidth: 1,
            opacity: 1,
            statusColors: ColorService.getStatusColors(2),
            barColorOver: '#FFFFFF',
            barColor: '#C4CDD6',
            comparator: function(value, threshold) {
              return value < threshold;
            }
          }
        };

        this.tooltip = {
          display: true,
          format: function(value, index, color, name) {
            return '<table class="ark-graphs-bar-tooltip"><tbody><tr class="ark-graphs-bar-tooltip-name-data"><td class="name"><span class="ark-graphs-bar-tooltip-square" style="background-color: ' + color + ';"></span><text class="name-container">' + name + '</text></td><td class="value">' + value + '</td></tr></tbody></table>';
          }
        };

        this.background = {
          display: true,
          color: '#E3E9EF',
          strokeWidth: 1
        };

        this.hover = {
          apply: true,
          growBy: 2
        };

        this.transitions = {
          bar: 1000,
          thresholds: 1000
        };

        this.autosnap = {
          enabled: false,
          thresholds: [{
            maxWidth: 516,
            configuration: {
              svg: {
                width: 208
              }
            }
          }, {
            maxWidth: 'none',
            configuration: {
              svg: {
                width: 516
              }
            }
          }]
        };

        this.update(configuration);
      };

      BarConfiguration.prototype.update = function(configuration) {
        Utils.mergeRecursive(this, configuration);

        this.legend.width = this.svg.width;
        this.div = {
          width: this.svg.width,
          height: (this.svg.height + (this.legend.display ? this.legend.height : 0)) * this.numberOfData,
          padding: {
            top: this.svg.padding.top
          }
        };

        if(this.autoresizeY) {
          var legendHeight = (this.legend.display ? this.legend.height : 0);
          var marginBottom = 20;
          this.div.height = this.parentHeight;
          this.svg.height = (this.div.height - marginBottom) / this.numberOfData - legendHeight;
        }
        
        this.dataGap = (this.svg.height - this.strokeWidth * this.numberOfData) / this.numberOfData;

        ConfigService.getStatusColors(this);
        ConfigService.updateInitialValues(this);
        ConfigService.updateLegendLabels(this);
        ConfigService.updateTooltipLabels(this);
      };

      return BarConfiguration;
    }
  ]);

'use strict';

angular.module('ark.graphs.bubble-graph').directive('arkBubbleGraph', ['ark.graphs.bubble-graph-config', 'ark.graphs.line-service', 'ark.graphs.text-service', 'ark.graphs.d3', 'ark.graphs.tooltip-service', 'ark.graphs.utils',
  function(BubbleGraphConfiguration, LineService, TextService, d3, TooltipService, Utils) {
    return {
      restrict: 'E',
      scope: {
        configuration: '=',
        data: '='
      },
      link: function($scope, element) {
        $scope.internalConfiguration = new BubbleGraphConfiguration($scope.configuration);

        $scope.dataMouseOut = function() {
          $scope.bubbleGraphContainer.selectAll('.circles').attr('fill-opacity', $scope.internalConfiguration.data.circle.opacity);
          $scope.bubbleGraphContainer.selectAll('.text').attr('fill-opacity', $scope.internalConfiguration.data.text.opacity);
          TooltipService.hideTooltip($scope.tooltip);
        };

        $scope.dataMouseMove = function(d) {
          $scope.bubbleGraphContainer.selectAll('.circles').attr('fill-opacity', 0.1);
          $scope.bubbleGraphContainer.select('.circles.' + d[$scope.internalConfiguration.data.label].replace(' ', '').toLowerCase()).attr('fill-opacity', 1);
          $scope.bubbleGraphContainer.selectAll('.text').attr('fill-opacity', 0.1);
          $scope.bubbleGraphContainer.select('.text.' + d[$scope.internalConfiguration.data.label].replace(' ', '').toLowerCase()).attr('fill-opacity', 1);

          $scope.tooltipBox = d3.select('#tooltip-' + $scope.internalConfiguration.id).node().getBoundingClientRect();

          var x = $scope.mousePositionSVG[0];
          var y = $scope.mousePositionSVG[1];

          if (x + $scope.tooltipBox.width > $scope.internalConfiguration.svg.width) {
            x -= ($scope.tooltipBox.width + 20); // snap to other side of mouse
          }

          if (y + $scope.tooltipBox.height > $scope.internalConfiguration.svg.height) {
            y -= ($scope.tooltipBox.height + 20); // snap to other side of mouse
          }

          TooltipService.setCoordinates($scope.tooltip, x, y);

          TooltipService.showTooltip($scope.tooltip, d[$scope.internalConfiguration.data.label], d.color, [$scope.internalConfiguration.data.x, $scope.internalConfiguration.data.y, $scope.internalConfiguration.data.z], [d[$scope.internalConfiguration.data.x], d[$scope.internalConfiguration.data.y], d[$scope.internalConfiguration.data.z]]);
        };

        var initScales = function() {
          $scope.xScale = LineService.createLinearRange(0, $scope.internalConfiguration.svg.trueWidth);
          LineService.scaleFromDataBubble($scope.xScale, $scope.data, $scope.internalConfiguration.data.x, $scope.internalConfiguration.data.offset);

          $scope.yScale = LineService.createLinearRange($scope.internalConfiguration.svg.trueHeight, 0);
          LineService.scaleFromDataBubble($scope.yScale, $scope.data, $scope.internalConfiguration.data.y, $scope.internalConfiguration.data.offset);

          $scope.zScale = LineService.createLinearRange($scope.internalConfiguration.data.circle.r, $scope.internalConfiguration.data.circle.maxR);
          LineService.scaleFromDataBubble($scope.zScale, $scope.data, $scope.internalConfiguration.data.z);
        };

        var initAxes = function() {
          if ($scope.internalConfiguration.axes.x.ticks.generate) {
            $scope.xTickValues = LineService.generateTicks($scope.xScale, $scope.internalConfiguration.axes.x.ticks.amount);
          }

          if ($scope.internalConfiguration.axes.y.ticks.generate) {
            $scope.yTickValues = LineService.generateTicks($scope.yScale, $scope.internalConfiguration.axes.y.ticks.amount);
          }

          var infoX = {
            innerTickSize: $scope.internalConfiguration.axes.x.ticks.size,
            tickValues: $scope.xTickValues
          };

          var infoY = {
            innerTickSize: $scope.internalConfiguration.axes.y.ticks.size,
            tickValues: $scope.yTickValues,
            orient: 'left'
          };

          $scope.xAxis = LineService.createAxisBubble($scope.xScale, infoX);

          $scope.yAxis = LineService.createAxisBubble($scope.yScale, infoY);
        };

        $scope.brushed = function() {
          if ($scope.internalConfiguration.tooltip.display) {
            TooltipService.hideTooltip($scope.tooltip);
          }
          $scope.extent = $scope.brush.extent();

          $scope.circles
            .classed('selected', function(d) {
              return $scope.extent[0][0] < d[$scope.internalConfiguration.data.x] && d[$scope.internalConfiguration.data.x] < $scope.extent[1][0] && $scope.extent[0][1] < d[$scope.internalConfiguration.data.y] && d[$scope.internalConfiguration.data.y] < $scope.extent[1][1];
            });

          $scope.text
            .classed('selected', function(d) {
              return $scope.extent[0][0] < d[$scope.internalConfiguration.data.x] && d[$scope.internalConfiguration.data.x] < $scope.extent[1][0] && $scope.extent[0][1] < d[$scope.internalConfiguration.data.y] && d[$scope.internalConfiguration.data.y] < $scope.extent[1][1];
            });
        };

        $scope.brushended = function() {
          if (Math.abs($scope.xScale($scope.brush.extent()[0][0]) - $scope.xScale($scope.brush.extent()[1][0])) <= $scope.internalConfiguration.brush.brushMinExtent &&
            Math.abs($scope.yScale($scope.brush.extent()[0][1]) - $scope.yScale($scope.brush.extent()[1][1])) <= $scope.internalConfiguration.brush.brushMinExtent
          ) {
            LineService.scaleFromDataBubble($scope.xScale, $scope.data, $scope.internalConfiguration.data.x, $scope.internalConfiguration.data.offset);
            LineService.scaleFromDataBubble($scope.yScale, $scope.data, $scope.internalConfiguration.data.y, $scope.internalConfiguration.data.offset);
          } else {
            $scope.xScale.domain([$scope.brush.extent()[0][0], $scope.brush.extent()[1][0]]);
            $scope.yScale.domain([$scope.brush.extent()[0][1], $scope.brush.extent()[1][1]]);
          }
          $scope.transitionData();
          $scope.unselectSelected();
          $scope.widget.select('.ark-graphs-bubble-graph-svg-data-container .brush').call($scope.brush.clear());
        };

        $scope.unselectSelected = function() {
          d3.select('#' + $scope.internalConfiguration.id).selectAll('.selected').classed('selected', false);
        };

        $scope.transitionData = function() {
          $scope.bubbleGraphAxisX
            .transition()
            .duration(200)
            .call($scope.xAxis);

          $scope.bubbleGraphAxisY
            .transition()
            .duration(200)
            .call($scope.yAxis);

          $scope.bubbleGraphGuidelinesX.selectAll('path')
            .transition()
            .duration(200)
            .attr('transform', function(d) {
              return 'translate(' + $scope.xScale(d) + ', 0)';
            });

          $scope.bubbleGraphGuidelinesY.selectAll('path')
            .transition()
            .duration(200)
            .attr('transform', function(d) {
              return 'translate(0, ' + $scope.yScale(d) + ')';
            });

          $scope.circles
            .transition()
            .duration(200)
            .attr({
              'cx': function(d) {
                return $scope.xScale(d[$scope.internalConfiguration.data.x]);
              },
              'cy': function(d) {
                return $scope.yScale(d[$scope.internalConfiguration.data.y]);
              }
            });

          $scope.text
            .transition()
            .duration(200)
            .attr({
              'x': function(d) {
                return $scope.xScale(d[$scope.internalConfiguration.data.x]);
              },
              'y': function(d) {
                return $scope.yScale(d[$scope.internalConfiguration.data.y]);
              }
            });
        };

        $scope.draw = function() {
          $scope.widget.select('.x.axis').remove();
          $scope.widget.select('.y.axis').remove();

          if ($scope.internalConfiguration.axes.x.show) {
            $scope.bubbleGraphAxisX = $scope.bubbleGraphContainer
              .append('g')
              .attr({
                'class': 'x axis',
                'transform': 'translate(0,' + $scope.internalConfiguration.svg.trueHeight + ')'
              })
              .call($scope.xAxis);
          }

          if ($scope.internalConfiguration.axes.y.show) {
            $scope.bubbleGraphAxisY = $scope.bubbleGraphContainer
              .append('g')
              .attr({
                'class': 'y axis'
              })
              .call($scope.yAxis);
          }

          if ($scope.internalConfiguration.axes.x.label.show) {
            $scope.bubbleGraphAxisX
              .append('g')
              .attr({
                'class': 'label',
                'text-anchor': 'middle',
                'transform': 'translate(' + $scope.internalConfiguration.svg.trueWidth / 2 + ',' + $scope.internalConfiguration.axes.x.label.offset.y + ')'
              })
              .append('text')
              .text(function() {
                if ($scope.internalConfiguration.axes.x.label.text) {
                  return $scope.internalConfiguration.axes.x.label.text;
                } else {
                  return $scope.internalConfiguration.data.x; // return x data key
                }
              });
          }

          if ($scope.internalConfiguration.axes.y.label.show) {
            $scope.bubbleGraphAxisY
              .append('g')
              .attr({
                'class': 'label',
                'text-anchor': 'middle',
                'transform': 'rotate(-90) translate(' + (-$scope.internalConfiguration.svg.trueHeight / 2) + ',' + $scope.internalConfiguration.axes.y.label.offset.y + ')'
              })
              .append('text')
              .text(function() {
                if ($scope.internalConfiguration.axes.y.label.text) {
                  return $scope.internalConfiguration.axes.y.label.text;
                } else {
                  return $scope.internalConfiguration.data.y; // return y data key
                }
              });
          }

          $scope.circles = $scope.widget.selectAll('.circles');
          $scope.text = $scope.widget.selectAll('.text');

          // Reinitialize scales
          initScales();

          // Reitialize axes
          initAxes();

          if ($scope.internalConfiguration.brush.enabled) {
            $scope.brush
              .x($scope.xScale)
              .y($scope.yScale)
              .on('brush', $scope.brushed)
              .on('brushend', $scope.brushended);
          }

          $scope.circles
            .transition()
            .duration(200)
            .attr('r', function(d) {
              return $scope.zScale(d[$scope.internalConfiguration.data.z]);
            })
            .attr({
              'cx': function(d) {
                return $scope.xScale(d[$scope.internalConfiguration.data.x]);
              },
              'cy': function(d) {
                return $scope.yScale(d[$scope.internalConfiguration.data.y]);
              }
            });

          $scope.text
            .transition()
            .duration(200)
            .attr({
              'x': function(d) {
                return $scope.xScale(d[$scope.internalConfiguration.data.x]);
              },
              'y': function(d) {
                return $scope.yScale(d[$scope.internalConfiguration.data.y]);
              },
              'transform': function(d) {
                return 'translate(' + $scope.internalConfiguration.data.text.xOffset + ',' + ($scope.internalConfiguration.data.text.yOffset - $scope.zScale(d[$scope.internalConfiguration.data.z])) + ')';
              }
            });
        };

        $scope.init = function() {

          // Initialize scales
          initScales();

          // Initialize axes
          initAxes();

          $scope.widget = d3.select(element[0]);
          $scope.widget.selectAll('*').remove();

          // Main div container
          $scope.widget
            .append('div')
            .attr({
              'class': 'ark-graphs-bubble-graph-div',
              'id': $scope.internalConfiguration.id
            })
            .style({ // Set container to same dimensions as svg
              'height': $scope.internalConfiguration.svg.height + 'px',
              'width': $scope.internalConfiguration.svg.width + 'px',
              'position': 'relative',
              'white-space': 'nowrap'
            });

          // SVG creation
          $scope.bubbleGraph = $scope.widget.select('#' + $scope.internalConfiguration.id)
            .append('svg')
            .attr({
              'class': 'ark-graphs-bubble-graph-svg',
              'width': $scope.internalConfiguration.svg.width,
              'height': $scope.internalConfiguration.svg.height
            })
            .on('mousemove', function() { // get mouse position relative to container
              $scope.mousePositionSVG = d3.mouse(this);
            });

          // Bubble Graph container
          $scope.bubbleGraphContainer = $scope.bubbleGraph
            .append('g')
            .attr({
              'class': 'ark-graphs-bubble-graph-svg-container',
              'transform': 'translate(' + $scope.internalConfiguration.svg.padding.left + ',' + $scope.internalConfiguration.svg.padding.top + ')'
            });

          $scope.bubbleGraphDataContainer = $scope.bubbleGraphContainer
            .append('g')
            .attr({
              'class': 'ark-graphs-bubble-graph-svg-data-container'
            })
            .style('clip-path', 'url(#clipPath-' + $scope.internalConfiguration.id + ')');

          // clip path to crops graph
          $scope.bubbleGraphDataContainer
            .append('defs')
            .append('clipPath')
            .attr('id', 'clipPath-' + $scope.internalConfiguration.id)
            .append('rect')
            .attr('x', 0)
            .attr('y', 0)
            .attr('width', $scope.internalConfiguration.svg.trueWidth)
            .attr('height', $scope.internalConfiguration.svg.trueHeight);

          // Brush creation
          if ($scope.internalConfiguration.brush.enabled) {
            $scope.brush = d3.svg.brush()
              .x($scope.xScale)
              .y($scope.yScale);

            $scope.bubbleGraphDataContainer
              .append('g')
              .attr('class', 'brush')
              .call($scope.brush);
          }

          if ($scope.internalConfiguration.axes.x.guidelines.display) {
            $scope.bubbleGraphGuidelinesX = $scope.bubbleGraphDataContainer
              .append('g')
              .attr({
                'class': 'x guidelines'
              });

            $scope.bubbleGraphGuidelinesX.selectAll('.x.guidelines').data($scope.internalConfiguration.axes.x.ticks.generate ? $scope.xAxis.tickValues() : $scope.xScale.ticks())
              .enter()
              .append('path')
              .attr({
                'class': function(d, i) {
                  return 'x guideline ' + i;
                },
                'transform': function(d) {
                  return 'translate(' + $scope.xScale(d) + ', 0)';
                },
                'd': LineService.computeLine(0, 0, 0, $scope.internalConfiguration.svg.trueHeight),
                'stroke': $scope.internalConfiguration.axes.x.guidelines.color
              });
          }

          if ($scope.internalConfiguration.axes.y.guidelines.display) {
            $scope.bubbleGraphGuidelinesY = $scope.bubbleGraphDataContainer
              .append('g')
              .attr({
                'class': 'y guidelines'
              });

            $scope.bubbleGraphGuidelinesY.selectAll('.y.guidelines').data($scope.internalConfiguration.axes.y.ticks.generate ? $scope.yAxis.tickValues() : $scope.yScale.ticks())
              .enter()
              .append('path')
              .attr({
                'class': 'y guidelines',
                'd': LineService.computeLine(0, 0, $scope.internalConfiguration.svg.trueWidth, 0),
                'stroke': $scope.internalConfiguration.axes.y.guidelines.color,
                'transform': function(d) {
                  return 'translate(0,' + $scope.yScale(d) + ')';
                }
              });
          }

          if ($scope.internalConfiguration.data.text.show) {
            $scope.bubbleGraphDataText = $scope.bubbleGraphDataContainer
              .append('g')
              .attr('class', 'data-text-container');

            $scope.bubbleGraphDataText.selectAll('.text').data($scope.data)
              .enter()
              .append('text')
              .attr({
                'class': function(d) {
                  return 'disable-select text ' + d[$scope.internalConfiguration.data.label].replace(' ', '').toLowerCase();
                },
                'x': function(d) {
                  return $scope.xScale(d[$scope.internalConfiguration.data.x]);
                },
                'y': function(d) {
                  return $scope.yScale(d[$scope.internalConfiguration.data.y]);
                },
                'transform': function(d) {
                  return 'translate(' + $scope.internalConfiguration.data.text.xOffset + ',' + ($scope.internalConfiguration.data.text.yOffset - $scope.zScale(d[$scope.internalConfiguration.data.z])) + ')';
                },
                'fill': $scope.internalConfiguration.data.text.color,
                'fill-opacity': $scope.internalConfiguration.data.text.opacity,
                'text-anchor': $scope.internalConfiguration.data.text.anchor
              })
              .text(function(d) {
                var name = d[$scope.internalConfiguration.data.label];
                var initials = name.split(' ').map(function(d) {
                  return d[0];
                });

                return initials.join('');
              });
          }

          TooltipService.initTooltip($scope);

          // Data container
          $scope.bubbleGraphData = $scope.bubbleGraphDataContainer
            .append('g')
            .attr({
              'class': 'data-container',
            });

          $scope.bubbleGraphData.selectAll('.circles').data($scope.data)
            .enter()
            .append('circle')
            .attr({
              'class': function(d, i) {
                return 'circles ' + i + ' ' + d[$scope.internalConfiguration.data.label].replace(' ', '').toLowerCase();
              },
              'cx': function(d) {
                return $scope.xScale(d[$scope.internalConfiguration.data.x]);
              },
              'cy': function(d) {
                return $scope.yScale(d[$scope.internalConfiguration.data.y]);
              },
              'r': function(d) {
                return $scope.zScale(d[$scope.internalConfiguration.data.z]);
              },
              'fill': function(d) {
                d.color = 'hsl(' + Math.random() * 360 + ',70%,40%)';
                return d.color;
              },
              'stroke': $scope.internalConfiguration.data.circle.stroke,
              'stroke-width': $scope.internalConfiguration.data.circle.strokeWidth,
              'fill-opacity': $scope.internalConfiguration.data.circle.opacity,
              'transform': 'translate(' + $scope.internalConfiguration.data.circle.cx + ',' + $scope.internalConfiguration.data.circle.cy + ')'
            })
            .on('mousemove', function(d) {
              $scope.dataMouseMove(d);
            })
            .on('mouseout', function() {
              $scope.dataMouseOut();
            });
        };

        if ($scope.internalConfiguration.autoresize) {
          Utils.resize($scope.internalConfiguration, element);
        }

        //Check for resizing
        $scope.$watch(function() {
          var e = angular.element(element[0].parentElement);
          return Utils.getWidthAndHeight(e[0]);
        }, function() {
          if ($scope.internalConfiguration.autoresize) {
            Utils.resize($scope.internalConfiguration, element);
            $scope.init();
            $scope.draw();
          }
        }, true);

        $scope.init();
        $scope.draw();

        $scope.$watch('configuration', function() {
          $scope.internalConfiguration.update($scope.configuration);
          return $scope.draw();
        }, true);
      }
    };
  }
]);

'use strict';

angular.module('ark.graphs.bubble-graph')
  .factory('ark.graphs.bubble-graph-config', ['ark.graphs.color-service', 'ark.graphs.utils',
    function(ColorService, Utils) {
      var BubbleGraphConfiguration = function(configuration) {

        this.type = 'bubble-graph';
        this.id = Utils.generateID(this.type);
        this.autoresize = false;

        this.data = {
          x: 'dataKey', // set this to an approprate key for given data
          y: 'dataKey', // set this to an approprate key for given data
          z: 'dataKey', // set this to an approprate key for given data
          label: 'Player', // set this to an approprate key for given data
          offset: {
            max: 0.1,
            min: 0.1
          },
          circle: {
            cx: 0,
            cy: 0,
            r: 5,
            maxR: 20,
            stroke: 'black',
            strokeWidth: 0,
            fill: 'red',
            opacity: 0.5
          },
          text: {
            show: true,
            color: '#444A52',
            anchor: 'middle',
            xOffset: 0,
            yOffset: -5,
            opacity: 0.2
          }
        };

        this.axes = {
          x: {
            show: true,
            label: {
              show: true, // only set to true if parent y.show is true
              text: 'x-axis label (unit)', // if this is set to false, it will get the label from this.data.x
              offset: {
                x: 0,
                y: 48
              }
            },
            ticks: {
              amount: 10,
              generate: true, // will manually generate ticks of even spacing from range of scale
              size: 14,
            },
            guidelines: {
              display: true,
              color: '#E3E9EF'
            }
          },
          y: {
            show: true,
            label: {
              show: true, // only set to true if parent y.show is true
              text: 'y-axis label (unit)', // if this is set to false, it will get the label from this.data.y
              offset: {
                x: 0,
                y: -40
              }
            },
            ticks: {
              amount: 10,
              generate: true, // will manually generate ticks of even spacing from range of scale
              size: 6,
            },
            guidelines: {
              display: true,
              color: '#E3E9EF'
            }
          }
        };

        this.svg = {
          height: 696,
          width: 696,
          margin: { //specifies space between div container and svg
            top: 40,
            left: 40,
            bottom: 40,
            right: 40
          },
          padding: { //specifies internal spacing for axis
            top: 40,
            left: 60,
            bottom: 60,
            right: 20
          }
        };

        this.brush = {
          enabled: false,
          brushMinExtent: 15 // used to zoom out (if extent size is less than this, it will zoom all the way out
        };

        this.tooltip = {
          display: true,
          format: function(name, color, label, value) {
            var tableRows = '';
            for (var i = 0; i < label.length; i++) {
              tableRows += '<tr class="ark-graphs-bubble-graph-tooltip-name-data"><td class="name"><text class="name-container">' + label[i] + '</text></td><td class="value">' + value[i].toFixed(1) + '</td></tr>';
            }
            var tableHead = '<table class="ark-graphs-bubble-graph-tooltip"><tbody><tr><th class="header" colspan="2"><span class="circle" style="background-color: ' + color + ';"></span>' + name + '</th></tr>' + tableRows + '</tbody></table>';

            return tableHead;
          }
        };

        this.autosnap = {
          enabled: false,
          thresholds: [{
            maxWidth: 464,
            configuration: {
              svg: {
                height: 232,
                width: 232
              },
              data: {
                circle: {
                  r: 3,
                  maxR: 7
                }
              }
            }
          }, {
            maxWidth: 696,
            configuration: {
              svg: {
                height: 464,
                width: 464
              },
              data: {
                circle: {
                  r: 4,
                  maxR: 12
                }
              }
            }
          }, {
            maxWidth: 'none',
            configuration: {
              svg: {
                height: 696,
                width: 696
              },
              data: {
                circle: {
                  r: 5,
                  maxR: 20
                }
              }
            }
          }]
        };

        this.update(configuration);
      };

      BubbleGraphConfiguration.prototype.update = function(configuration) {
        Utils.mergeRecursive(this, configuration);

        this.svg.trueWidth = this.svg.width - this.svg.padding.left - this.svg.padding.right;
        this.svg.trueHeight = this.svg.height - this.svg.padding.top - this.svg.padding.bottom;

      };

      return BubbleGraphConfiguration;
    }
  ]);

'use strict';

angular.module('ark.graphs.donut').directive('arkDonut', ['ark.graphs.donut-chart-config', 'ark.graphs.arc-service', 'ark.graphs.pie-service', 'ark.graphs.text-service', 'ark.graphs.d3', 'ark.graphs.utils', 'ark.graphs.tooltip-service', 'ark.graphs.config-service',
  function(DonutConfiguration, ArcService, PieService, TextService, d3, Utils, TooltipService, ConfigService) {
    return {
      restrict: 'E',
      scope: {
        configuration: '=',
        data: '='
      },
      link: function($scope, element) {
        $scope.internalConfiguration = new DonutConfiguration($scope.configuration, $scope.data);
        $scope.previousData = $scope.internalConfiguration.initialValues;

        angular.forEach($scope.data, function() {
          $scope.previousData.push(0);
        });
        $scope.draw = function(data) {

          var paths = $scope.widget.select('.ark-graphs-donut-container').selectAll('path');

          var arc = ArcService.d3Arc($scope.internalConfiguration.radius, $scope.internalConfiguration.strokeWidth);
          var arcOver = ArcService.d3Arc($scope.internalConfiguration.radius + $scope.internalConfiguration.slice.hover.growBy, $scope.internalConfiguration.strokeWidth + $scope.internalConfiguration.slice.hover.growBy);

          var pie = ArcService.d3Pie($scope.internalConfiguration.amplitude, $scope.internalConfiguration.padAngle, $scope.internalConfiguration.sort);

          var previousPie = pie($scope.previousData);

          if ($scope.internalConfiguration.slice.label.display) {
            PieService.drawLabels($scope.widget, pie(data), $scope.internalConfiguration);
          }

          paths.data(pie(data), function(d) {
              return d + Math.random() / 10000;
            })
            .enter()
            .append('path')
            .attr('class', 'path')
            .attr('fill', function(d, i) {
              return $scope.internalConfiguration.data.colors[i];
            })
            .attr('stroke', $scope.internalConfiguration.slice.border.color)
            .attr('stroke-width', ($scope.internalConfiguration.slice.border.display ? $scope.internalConfiguration.slice.border.width : 0))
            .attr('opacity', $scope.internalConfiguration.opacity)
            .attr('transform',
              ArcService.computeRotation(
                $scope.internalConfiguration.startAngle,
                $scope.internalConfiguration.svg.width,
                $scope.internalConfiguration.svg.height
              ) + ' ' +
              ArcService.translate(
                $scope.internalConfiguration.svg.width,
                $scope.internalConfiguration.svg.height
              ))
            .on('click', $scope.internalConfiguration.slice.click)
            .transition()
            .attrTween('d', function(d, i) {
              TooltipService.hideTooltip($scope.tooltip);
              var previous = previousPie[i] || {
                startAngle: 0,
                endAngle: 0
              };
              var interpolate = d3.interpolate(previous, d);
              return function(t) {
                return arc(interpolate(t));
              };
            })
            .duration($scope.internalConfiguration.transitions.arc)
            .each('end', function() {
              $scope.allowUpdate(data, arc, arcOver);
            });
          paths.remove();
          $scope._updateLabels(data);
          TextService.showTextUpdate($scope.internalConfiguration, $scope.widget, $scope.data);
        };

        $scope.allowUpdate = function(data, arc, arcOver) {
          var selection = $scope.widget.select('.ark-graphs-donut-container').selectAll('path');
          selection
            .on('mousemove', function(d, i) {
              $scope.coordinate = d3.mouse($scope.widget.select('.ark-graphs-donut-container')[0][0]);
              $scope.mousemovePath(d, i, this, arcOver);
            })
            .on('mouseout', function() {
              $scope.mouseoutPath(this, arc);
            });
          $scope.previousData = angular.copy(data);
        };

        $scope.setMiddleLabelFont = function(fontsize) {
          $scope.internalConfiguration.label.fontsize = fontsize;
        };

        $scope.setMiddleLabel = function() {
          //Middle label
          $scope.widget.select('.ark-graphs-donut-svg').select('g').select('text')
            .attr('font-size', $scope.internalConfiguration.label.fontsize + 'px');
        };

        $scope._updateLabels = function(data) {
          if ($scope.internalConfiguration.label.display) {
            $scope.widget.select('.ark-graphs-donut-middle-label').select('text')
              .attr('opacity', 0.2)
              .transition()
              .duration($scope.internalConfiguration.transitions.label)
              .attr('opacity', 1)
              .text($scope.internalConfiguration.label.format(data));
            if ($scope.internalConfiguration.label.symbol.display) {
              $scope.widget.select('.ark-graphs-donut-middle-label-symbol').select('text')
                .attr('opacity', 0.2)
                .transition()
                .duration($scope.internalConfiguration.transitions.label)
                .attr('opacity', 1)
                .text($scope.internalConfiguration.label.symbol.format(data));
            }
          }
        };

        $scope.mousemovePath = function(d, i, elem, arcOver) {
          if ($scope.internalConfiguration.slice.hover.apply) {
            d3.select(elem).transition()
              .duration(50)
              .attr('d', arcOver);
          }
          if ($scope.internalConfiguration.tooltip.display) {
            TooltipService.setCoordinates($scope.tooltip, $scope.coordinate[0], $scope.coordinate[1] + $scope.internalConfiguration.div.padding.top);
            TooltipService.showTooltip($scope.tooltip, d, i, ConfigService.getData(i, $scope.internalConfiguration.data.colors), ConfigService.getData(i, $scope.internalConfiguration.data.labels));
          }
        };

        $scope.mouseoutPath = function(elem, arc) {
          if ($scope.internalConfiguration.slice.hover.apply) {
            d3.select(elem).transition()
              .duration(50)
              .attr('d', arc);
          }
          if ($scope.internalConfiguration.tooltip.display) {
            TooltipService.hideTooltip($scope.tooltip);
          }
        };

        var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

        $scope.init = function() {
          $scope.widget = d3.select(element[0]);
          $scope.widget.selectAll('*').remove();

          $scope.widget.append('div')
            .attr('class', 'ark-graphs-donut-div')
            .attr('id', $scope.internalConfiguration.id)
            .style('position', 'relative')
            .style('white-space', 'nowrap')
            .style('width', $scope.internalConfiguration.div.width.toString() + 'px')
            .style('height', $scope.internalConfiguration.div.height.toString() + 'px')
            .style('padding-top', $scope.internalConfiguration.div.padding.top.toString() + 'px');

          $scope.widget.select('.ark-graphs-donut-div').append('svg')
            .attr('class', 'ark-graphs-donut-svg')
            .attr('width', $scope.internalConfiguration.svg.width)
            .attr('height', $scope.internalConfiguration.svg.height + 100);

          if ($scope.internalConfiguration.label.display) {
            //Middle label
            $scope.widget.select('.ark-graphs-donut-svg').append('g')
              .attr('class', 'ark-graphs-donut-middle-label')
              .append('text')
              .text($scope.internalConfiguration.label.format(0))
              .attr('x', $scope.internalConfiguration.svg.width / 2)
              .attr('y', $scope.internalConfiguration.svg.height / 2)
              .attr('opacity', $scope.internalConfiguration.label.opacity)
              .attr('font-size', $scope.internalConfiguration.label.fontsize + 'px')
              .attr('fill', $scope.internalConfiguration.label.color)
              .style('dominant-baseline', 'middle')
              .style('text-anchor', 'middle');

            if ($scope.internalConfiguration.label.symbol.display) {
              $scope.widget.select('.ark-graphs-donut-svg').append('g')
                .attr('class', 'ark-graphs-donut-middle-label-symbol')
                .append('text')
                .text($scope.internalConfiguration.label.symbol.format(0))
                .attr('x', $scope.internalConfiguration.svg.width / 2)
                .attr('y', $scope.internalConfiguration.svg.height / 2 + $scope.internalConfiguration.label.fontsize)
                .attr('opacity', $scope.internalConfiguration.label.symbol.opacity)
                .attr('font-size', $scope.internalConfiguration.label.symbol.fontsize + 'px')
                .attr('fill', $scope.internalConfiguration.label.symbol.color)
                .style('text-anchor', 'middle');
            }
          }

          //Main widget content
          $scope.widget.select('.ark-graphs-donut-svg').append('g')
            .attr('class', 'ark-graphs-donut-container');

          $scope.widget.select('.ark-graphs-donut-svg').append('g')
            .attr('class', 'ark-graphs-label-group')
            .attr('transform', ArcService.translate(
              $scope.internalConfiguration.svg.width,
              $scope.internalConfiguration.svg.height
            ));

          //legend==  
          var legend = $scope.widget.select('.ark-graphs-donut-svg').append('g')
              .attr('class', 'ark-graphs-donut-legend-custom');
          for (var i = 0; i < $scope.configuration.data.labels.length; i++){
              legend.append('text')
                .text(alphabet.substring(i, i + 1))
                .attr({
                  'x': Math.floor(i / 4) * 80,
                  'y': ((i % 4) * 15) + 250
                })
                .style('font-weight', 'bold');
              legend.append('text')
                .text($scope.configuration.data.labels[i])
                .attr({
                  'x': Math.floor(i / 4) * 80 + 15,
                  'y': ((i % 4)* 15) + 250
                })
                .style('font-weight', 'light');
          }
          //legend== ends

          TextService.showText($scope.internalConfiguration, $scope.widget, $scope.data);

          TooltipService.initTooltip($scope);
        };

        $scope.resize = function() {
          Utils.resize($scope.internalConfiguration, element);
          $scope.init();
          $scope.draw($scope.data);
        };

        if ($scope.internalConfiguration.autoresize) {
          Utils.resize($scope.internalConfiguration, element);
        }

        $scope.init();

        //Check for resizing
        $scope.$watch(function() {
          var e = angular.element(element[0].parentElement);
          return Utils.getWidthAndHeight(e[0]);
        }, function(newVal, oldVal) {
          if (($scope.internalConfiguration.autoresize && newVal.width !== oldVal.width) || ($scope.internalConfiguration.autoresizeY && newVal.height !== oldVal.height)) {
            Utils.resize($scope.internalConfiguration, element);
            $scope.init();
            $scope.previousData = angular.copy($scope.data);
            $scope.draw($scope.data);
          }
        }, true);

        //Check for text overflow
        $scope.$watch(function() {
          var a = element[0].querySelector('.ark-graphs-donut-middle-label text');
          if (a === null) {
            return $scope.internalConfiguration.radius * 2;
          }
          return a.getBoundingClientRect().width;
        }, function(newVal) {
          if (newVal > $scope.internalConfiguration.radius * 2 * 0.7) { // text is greater than 0.7 of donut diameter
            $scope.setMiddleLabelFont($scope.internalConfiguration.label.fontsize / 2);
            $scope.setMiddleLabel();
          }

          if ($scope.internalConfiguration.label.maxFontsize > $scope.internalConfiguration.label.fontsize) {
            if (newVal < $scope.internalConfiguration.radius * 2 * 0.6) { // text is less than 0.6 of donut diameter
              $scope.setMiddleLabelFont($scope.internalConfiguration.label.fontsize * 1.5);
              $scope.setMiddleLabel();
            }
          }
        });

        //Check for changes in internalConfiguration
        $scope.$watch('configuration', function() {
          $scope.internalConfiguration.update($scope.configuration);
          $scope.init();
          $scope.draw($scope.data);
        }, true);

        $scope.$watch('data', function(newVals) {
          return $scope.draw(newVals);
        });
      }
    };
  }
]);

'use strict';

angular.module('ark.graphs.donut')
  .factory('ark.graphs.donut-chart-config', ['ark.graphs.d3', 'ark.graphs.color-service',
    'ark.graphs.utils', 'ark.graphs.config-service',
    function(d3, ColorService, Utils, ConfigService) {
      var DonutConfiguration = function(configuration, data) {
        this.type = 'donut';
        this.id = Utils.generateID(this.type);
        this.startAngle = 0;
        this.strokeWidth = 20;
        this.amplitude = 360;
        this.padAngle = 0.75;
        this.radius = 69;
        this.opacity = 1;
        this.initialValues = [];
        this.sort = null;
        this.autoresize = false;
        this.autoresizeY = false;
        this.numberOfDataSet = data && data.length ? data.length : 0;
        this.numberOfData = this.numberOfDataSet;

        this.svg = {
          height: this.radius * 2 + this.strokeWidth / 2 + 26,
          width: 208,
          padding: {
            top: 0
          }
        };

        this.legend = {
          display: true,
          fontsize: 12,
          height: 72,
          width: this.svg.width,
          title: ['Metric name'],
          format: function(d, i) {
            return d;
          },
          letters: {
            display: true
          },
          padding: {
            top: 0,
            left: 20,
            right: 20
          },
          icon: {
            display: true,
            minValue: 3
          }
        };

        this.data = {
          colors: ColorService.arkBlueColors(),
          labels: ['data 0']
        };

        this.tooltip = {
          display: true,
          format: function(value, index, color, name) {
            return '<table class="ark-graphs-donut-tooltip"><tbody><tr class="ark-graphs-donut-tooltip-name-data"><td class="name"><span class="ark-graphs-donut-tooltip-square" style="background-color: ' + color + ';"></span><text class="name-container">' + name + '</text></td><td class="value">' + value.data + '</td></tr></tbody></table>';
          }
        };

        this.slice = {
          border: {
            display: true,
            color: '#FDFDFD',
            width: 1
          },
          label: {
            position: 'out',
            display: true,
            line: {
              display: false,
              size: 0,
              color: 'gray',
              class: 'ark-graphs-donut-label-line'
            },
            value: {
              display: false,
              fontsize: 12,
              color: 'gray',
              class: 'ark-graphs-donut-label-value',
              format: function(value) {

                return value.data;
              }
            },
            name: {
              topOffset: 4,
              fontsize: 11,
              color: '#444A52',
              class: 'ark-graphs-donut-label-name',
              format: function(d, i) {
                var chr = String.fromCharCode(i + 65);
                return chr;
              }
            }
          },
          hover: {
            apply: true,
            callback: function() {},
            growBy: 4
          },
          click: function() {}
        };
        this.label = {
          display: true,
          fontsize: 32,
          maxFontsize: 32,
          color: 'black',
          format: function(values) {
            return d3.sum(values);
          },
          opacity: 1,
          symbol: {
            display: true,
            fontsize: 20,
            color: 'black',
            format: function() {
              return '';
            },
            opacity: 1
          }
        };
        this.transitions = {
          arc: 1000,
          label: 500
        };

        this.autosnap = {
          enabled: false,
          threshold: 516,
          smallConfig: {
            radius: 69,
            svg: {
              width: 208
            },
            strokeWidth: 8,
            slice: {
              label: {
                name: {
                  fontsize: 12
                }
              }
            },
            label: {
              fontsize: 32,
              maxFontsize: 32
            },
            legend: {
              fontsize: 12,
              height: 72,
              width: 208,
              padding: {
                top: 0,
                left: 20,
                right: 20
              }
            }
          },
          largeConfig: {
            radius: 124,
            svg: {
              width: 516
            },
            strokeWidth: 12,
            slice: {
              label: {
                name: {
                  fontsize: 14
                }
              }
            },
            label: {
              fontsize: 48,
              maxFontsize: 48
            },
            legend: {
              fontsize: 14,
              height: 160,
              width: 260,
              padding: {
                top: 20,
                left: 24,
                right: 24
              }
            }
          }
        };

        this.update(configuration);
      };

      DonutConfiguration.prototype.update = function(configuration) {
        Utils.mergeRecursive(this, configuration);

        this.svg.height = this.radius * 2 + this.strokeWidth / 2 + 26;
        this.div = {
          width: this.svg.width,
          height: this.svg.height + (this.legend.display ? this.legend.height : 0),
          padding: {
            top: this.svg.padding.top
          }
        };

        if(this.autoresizeY) {
          this.div.padding.top = Math.max((this.parentHeight - this.div.height)/2, 0);
        }

        ConfigService.updateColors(this);
        ConfigService.updateInitialValues(this);
        ConfigService.updateLegendLabels(this);
        ConfigService.updateTooltipLabels(this);
      };

      return DonutConfiguration;
    }
  ]);

'use strict';

angular.module('ark.graphs.gauge').directive('arkCircularGauge', ['ark.graphs.gauge-config', 'ark.graphs.arc-service', 'ark.graphs.text-service', 'ark.graphs.d3', 'ark.graphs.utils', 'ark.graphs.tooltip-service', 'ark.graphs.threshold-service', 'ark.graphs.config-service',
  function(GaugeConfiguration, ArcService, TextService, d3, Utils, TooltipService, ThresholdService, ConfigService) {
    return {
      restrict: 'E',
      scope: {
        configuration: '=',
        data: '='
      },
      link: function($scope, element) {
        $scope.internalConfiguration = new GaugeConfiguration($scope.configuration);
        $scope.previousData = $scope.internalConfiguration.initialValues;

        var toggle = false;
        $scope.draw = function(data) {
          toggle = false;
          $scope.arrangedData = Utils.prepareData(angular.copy(data), $scope.previousData, $scope.internalConfiguration);
          var paths = $scope.widget.select('.ark-graphs-gauge-container').selectAll('path');
          paths.data($scope.arrangedData, function(d) {
              return d + Math.random() / 1000;
            })
            .enter()
            .append('path')
            .attr('class', 'path')
            .attr('fill', 'none')
            .attr('transform', ArcService.computeRotation(
              $scope.internalConfiguration.startAngle,
              $scope.internalConfiguration.svg.width,
              $scope.internalConfiguration.svg.height
            ))
            .attr('stroke', function(d) {
              return ThresholdService.getDataColor(d, $scope.internalConfiguration);
            })
            .attr('stroke-width', $scope.internalConfiguration.strokeWidth)
            .attr('opacity', 1)
            .transition()
            .attrTween('d', function(d, i) {
              toggle = true;
              TooltipService.hideTooltip($scope.tooltip);
              var interpolate = d3.interpolate($scope.previousData[i], $scope.arrangedData[i]);
              return function(t) {
                return ArcService.computeArc(
                  0,
                  $scope.internalConfiguration.amplitude,
                  interpolate(t),
                  $scope.internalConfiguration.max,
                  $scope.internalConfiguration.svg.width,
                  $scope.internalConfiguration.svg.height, $scope.internalConfiguration.computeGaugeWidth(d, i)
                );
              };
            })
            .duration($scope.internalConfiguration.transitions.arc)
            .each('end', function() {
              $scope.allowUpdate($scope.arrangedData);
            });
          paths.remove();

          // display thesholds and middle fonticon
          if ($scope.internalConfiguration.data.thresholds.display) {
            ThresholdService.getMiddleIcon($scope.internalConfiguration, data);
            ThresholdService.drawThresholds($scope.internalConfiguration, $scope.arrangedData);
            angular.forEach($scope.arrangedData, function(value, key) {
              var thresholds = $scope.widget.select('.ark-graphs-gauge-threshold').selectAll('.path-' + key.toString());
              thresholds.attr('d', function(d) {
                  return ArcService.computeArc(
                    ($scope.internalConfiguration.amplitude * (d / $scope.internalConfiguration.max)) - $scope.internalConfiguration.data.thresholds.amplitude / 2, ($scope.internalConfiguration.amplitude * (d / $scope.internalConfiguration.max)) + $scope.internalConfiguration.data.thresholds.amplitude / 2,
                    $scope.internalConfiguration.max,
                    $scope.internalConfiguration.max,
                    $scope.internalConfiguration.svg.width,
                    $scope.internalConfiguration.svg.height, $scope.internalConfiguration.computeGaugeWidth(d, key)
                  );
                })
                .attr('transform', ArcService.computeRotation(
                  $scope.internalConfiguration.startAngle,
                  $scope.internalConfiguration.svg.width,
                  $scope.internalConfiguration.svg.height
                ));
            });
          }

          $scope._updateLabels($scope.arrangedData[0]);
          TextService.showTextUpdate($scope.internalConfiguration, $scope.widget, $scope.arrangedData);
        };

        $scope.allowUpdate = function(data) {

          var selection = $scope.widget.select('.ark-graphs-gauge-container').selectAll('path').data(data);
          toggle = false;
          selection
            .on('mousemove', function(d, i) {
              if (!toggle) {
                toggle = true;
                $scope.mouseoverPath(this, $scope.internalConfiguration.computeGaugeWidth(d, i), d, i);
              }
              $scope.coordinate = d3.mouse($scope.widget.select('.ark-graphs-gauge-container')[0][0]);
              $scope.mousemovePath(d, i);
            })
            .on('mouseout', function(d, i) {
              toggle = false;
              $scope.mouseoutPath(this, $scope.internalConfiguration.computeGaugeWidth(d, i), d, i);
            });
          $scope.previousData = angular.copy(data);
        };

        $scope.setMiddleLabelFont = function(fontsize) {
          $scope.internalConfiguration.label.fontsize = fontsize;
        };

        $scope.setMiddleLabel = function() {
          //Middle label
          $scope.widget.select('.ark-graphs-gauge-middle-label').select('text')
            .attr('font-size', $scope.internalConfiguration.label.fontsize + 'px');
        };

        $scope._updateLabels = function(data) {
          if ($scope.internalConfiguration.label.display) {
            $scope.widget.select('.ark-graphs-gauge-middle-label').select('text')
              .attr('opacity', 0.2)
              .transition()
              .duration($scope.internalConfiguration.transitions.label)
              .attr('opacity', 1)
              .text($scope.internalConfiguration.label.format(data));

            if ($scope.internalConfiguration.label.symbol.display) {

              $scope.widget.select('.ark-graphs-gauge-middle-label-symbol')
                .text(data);
            }
            if ($scope.internalConfiguration.svg.icon.display) {
              $scope.widget.select('.ark-graphs-gauge-span')
                .style('top', ($scope.internalConfiguration.svg.height / 1.55 + $scope.internalConfiguration.div.padding.top).toString() + 'px')
                .style('font-size', ($scope.internalConfiguration.label.fontsize / 2).toString() + 'px')
                .style('display', 'inline-block');
            }
          }
        };

        $scope.mouseoverPath = function(elem, gaugeWidth, d, i) {
          if ($scope.internalConfiguration.hover.apply) {
            var interpolateGaugeMiddle = d3.interpolate(gaugeWidth, gaugeWidth + $scope.internalConfiguration.hover.growBy / 2);
            var interpolateGaugeWidth = d3.interpolate($scope.internalConfiguration.strokeWidth, $scope.internalConfiguration.strokeWidth + $scope.internalConfiguration.hover.growBy);
            d3.select(elem).transition()
              .duration(200)
              .attrTween('d', function() {
                return function(t) {
                  return ArcService.computeArc(
                    0,
                    $scope.internalConfiguration.amplitude,
                    d,
                    $scope.internalConfiguration.max,
                    $scope.internalConfiguration.svg.width,
                    $scope.internalConfiguration.svg.height,
                    interpolateGaugeMiddle(t)
                  );
                };
              })
              .duration(200)
              .attrTween('stroke-width', function() {
                return function(t) {
                  return interpolateGaugeWidth(t);
                };
              });

            if ($scope.internalConfiguration.data.thresholds.display) {
              var thresholds = $scope.widget.select('.ark-graphs-gauge-threshold').selectAll('.ark-graphs-gauge-threshold-path-' + i.toString());
              thresholds
                .transition()
                .duration(200)
                .attr('d', function(d) {
                  return ArcService.computeArc(
                    $scope.internalConfiguration.amplitude * (d / $scope.internalConfiguration.max) - $scope.internalConfiguration.data.thresholds.amplitude / 2,
                    $scope.internalConfiguration.amplitude * (d / $scope.internalConfiguration.max) + $scope.internalConfiguration.data.thresholds.amplitude / 2,
                    $scope.internalConfiguration.max,
                    $scope.internalConfiguration.max,
                    $scope.internalConfiguration.svg.width,
                    $scope.internalConfiguration.svg.height,
                    gaugeWidth + (($scope.arrangedData[i] > d) ? $scope.internalConfiguration.hover.growBy / 2 : 0)
                  );
                })
                .attrTween('stroke-width', function(d) {
                  var interpolateStrokeWidth = d3.interpolate($scope.internalConfiguration.strokeWidth, $scope.internalConfiguration.strokeWidth + $scope.internalConfiguration.hover.growBy);
                  return function(t) {
                    return ($scope.arrangedData[i] > d ? interpolateStrokeWidth(t) : $scope.internalConfiguration.strokeWidth);
                  };
                });
            }
          }
        };

        $scope.mousemovePath = function(d, i) {
          if ($scope.internalConfiguration.tooltip.display) {
            TooltipService.setCoordinates($scope.tooltip, $scope.coordinate[0], $scope.coordinate[1] + $scope.internalConfiguration.div.padding.top);
            TooltipService.showTooltip($scope.tooltip, d, i, ThresholdService.getDataColor(d, $scope.internalConfiguration), ConfigService.getData(i, $scope.internalConfiguration.data.labels));
          }
        };

        $scope.mouseoutPath = function(elem, gaugeWidth, d, i) {
          var interpolateGaugeMiddle = d3.interpolate(gaugeWidth + $scope.internalConfiguration.hover.growBy / 2, gaugeWidth);
          var interpolateGaugeWidth = d3.interpolate($scope.internalConfiguration.strokeWidth + $scope.internalConfiguration.hover.growBy, $scope.internalConfiguration.strokeWidth);
          if ($scope.internalConfiguration.hover.apply) {
            d3.select(elem).transition()
              .duration(200)
              .attrTween('d', function() {
                return function(t) {
                  return ArcService.computeArc(
                    0,
                    $scope.internalConfiguration.amplitude,
                    d,
                    $scope.internalConfiguration.max,
                    $scope.internalConfiguration.svg.width,
                    $scope.internalConfiguration.svg.height,
                    interpolateGaugeMiddle(t)
                  );
                };
              })
              .attrTween('stroke-width', function() {
                return function(t) {
                  return interpolateGaugeWidth(t);
                };
              });

            if ($scope.internalConfiguration.data.thresholds.display) {
              var thresholds = $scope.widget.select('.ark-graphs-gauge-threshold').selectAll('.ark-graphs-gauge-threshold-path-' + i.toString());
              thresholds
                .transition()
                .duration(200)
                .attr('d', function(d) {
                  return ArcService.computeArc(
                    ($scope.internalConfiguration.amplitude * (d / $scope.internalConfiguration.max)) - $scope.internalConfiguration.data.thresholds.amplitude / 2, ($scope.internalConfiguration.amplitude * (d / $scope.internalConfiguration.max)) + $scope.internalConfiguration.data.thresholds.amplitude / 2,
                    $scope.internalConfiguration.max,
                    $scope.internalConfiguration.max,
                    $scope.internalConfiguration.svg.width,
                    $scope.internalConfiguration.svg.height,
                    gaugeWidth
                  );
                })
                .attr('stroke-width', $scope.internalConfiguration.strokeWidth);
            }
          }
          if ($scope.internalConfiguration.tooltip.display) {
            TooltipService.hideTooltip($scope.tooltip);
          }
        };

        $scope.init = function() {
          $scope.widget = d3.select(element[0]);
          $scope.widget.selectAll('*').remove();

          $scope.widget.append('div')
            .attr('class', 'ark-graphs-gauge-div')
            .attr('id', $scope.internalConfiguration.id)
            .style('width', $scope.internalConfiguration.div.width.toString() + 'px')
            .style('height', $scope.internalConfiguration.div.height.toString() + 'px')
            .style('padding-top', $scope.internalConfiguration.div.padding.top.toString() + 'px')
            .style('white-space', 'nowrap')
            .style('position', 'relative');

          if ($scope.internalConfiguration.svg.icon.display) {
            $scope.widget.select('.ark-graphs-gauge-div')
              .append('span')
              .attr('class', 'ark-graphs-gauge-span')
              .style('position', 'absolute')
              .style('text-align', 'center')
              .style('width', '100%');
          }

          $scope.widget.select('.ark-graphs-gauge-div')
            .append('svg')
            .attr('class', 'ark-graphs-gauge-svg')
            .attr('width', $scope.internalConfiguration.div.width)
            .attr('height', $scope.internalConfiguration.svg.height);

          if ($scope.internalConfiguration.background.display) {
            //Setting background
            $scope.widget.select('.ark-graphs-gauge-svg')
              .append('g')
              .attr('class', 'ark-graphs-gauge-background').selectAll('path').data($scope.internalConfiguration.maxData)
              .enter()
              .append('path')
              .attr('d', function(d, i) {
                return ArcService.computeArc(
                  0,
                  $scope.internalConfiguration.amplitude,
                  $scope.internalConfiguration.max,
                  $scope.internalConfiguration.max,
                  $scope.internalConfiguration.svg.width,
                  $scope.internalConfiguration.svg.height, $scope.internalConfiguration.computeGaugeWidth(d, i) + $scope.internalConfiguration.background.offset
                );
              })
              .attr('opacity', $scope.internalConfiguration.background.opacity)
              .attr('fill', 'none')
              .attr('transform', ArcService.computeRotation(
                $scope.internalConfiguration.startAngle,
                $scope.internalConfiguration.svg.width,
                $scope.internalConfiguration.svg.height
              ))
              .attr('stroke', $scope.internalConfiguration.background.color)
              .attr('stroke-width', $scope.internalConfiguration.background.strokeWidth);
          }

          if ($scope.internalConfiguration.label.display) {
            //Middle label
            $scope.widget.select('.ark-graphs-gauge-svg')
              .append('g')
              .attr('class', 'ark-graphs-gauge-middle-label')
              .append('text')
              .text($scope.internalConfiguration.label.format(0))
              .attr('x', $scope.internalConfiguration.svg.width / 2)
              .attr('y', $scope.internalConfiguration.svg.height / 2)
              .attr('opacity', $scope.internalConfiguration.label.opacity)
              .attr('font-size', $scope.internalConfiguration.label.fontsize + 'px')
              .attr('fill', $scope.internalConfiguration.label.color)
              .style('dominant-baseline', 'middle')
              .style('text-anchor', 'middle');
            if ($scope.internalConfiguration.label.symbol.display) {
              $scope.widget.select('.ark-graphs-gauge-svg')
                .append('g')
                .attr('class', 'ark-graphs-gauge-middle-label-symbol')
                .append('text')
                .text($scope.internalConfiguration.label.symbol.format(0))
                .attr('x', $scope.internalConfiguration.svg.width / 2)
                .attr('y', $scope.internalConfiguration.svg.height / 2 + $scope.internalConfiguration.label.fontsize)
                .attr('opacity', $scope.internalConfiguration.label.symbol.opacity)
                .attr('font-size', $scope.internalConfiguration.label.symbol.fontsize + 'px')
                .attr('fill', $scope.internalConfiguration.label.symbol.color)
                .style('text-anchor', 'middle');
            }
          }

          //Main widget content
          $scope.widget.select('.ark-graphs-gauge-svg')
            .append('g')
            .attr('class', 'ark-graphs-gauge-container');

          if ($scope.internalConfiguration.data.thresholds.display) {
            //Display thresholds
            $scope.widget.select('.ark-graphs-gauge-svg')
              .append('g')
              .attr('class', 'ark-graphs-gauge-threshold');
            ThresholdService.initThresholdValues($scope.internalConfiguration);
          }

          TextService.showText($scope.internalConfiguration, $scope.widget, $scope.previousData);

          TooltipService.initTooltip($scope);
        };

        if ($scope.internalConfiguration.autoresize) {
          Utils.resize($scope.internalConfiguration, element);
        }

        $scope.init();

        //Check for resizing
        $scope.$watch(function() {
          var e = angular.element(element[0].parentElement);
          return Utils.getWidthAndHeight(e[0]);
        }, function(newVal, oldVal) {
          if (($scope.internalConfiguration.autoresize && newVal.width !== oldVal.width) || ($scope.internalConfiguration.autoresizeY && newVal.height !== oldVal.height)) {
            Utils.resize($scope.internalConfiguration, element);
            $scope.init();
            $scope.draw($scope.data);
          }
        }, true);

        //Check for text overflow
        $scope.$watch(function() {
          var a = element[0].querySelector('.ark-graphs-gauge-middle-label text');
          if (a === null) {
            return $scope.internalConfiguration.radius * 2;
          }
          return a.getBoundingClientRect().width;
        }, function(newVal) {
          if (newVal > $scope.internalConfiguration.radius * 2 * (0.9 - 0.1 * $scope.internalConfiguration.numberOfData)) {
            $scope.setMiddleLabelFont($scope.internalConfiguration.label.fontsize / 2);
            $scope.setMiddleLabel();
          }

          if ($scope.internalConfiguration.label.maxFontsize > $scope.internalConfiguration.label.fontsize) {
            if (newVal < $scope.internalConfiguration.radius * 2 * (0.7 - 0.1 * $scope.internalConfiguration.numberOfData)) {
              $scope.setMiddleLabelFont($scope.internalConfiguration.label.fontsize * 1.5);
              $scope.setMiddleLabel();
            }
          }
        });

        //Check for changes in internalConfiguration
        $scope.$watch('configuration', function() {
          $scope.internalConfiguration.update($scope.configuration);
          $scope.init();
          $scope.draw($scope.data);
        }, true);

        $scope.$watch('data', function(newVals) {
          if (newVals !== undefined) {
            return $scope.draw(newVals);
          }
        });
      }
    };
  }
]);

'use strict';

angular.module('ark.graphs.gauge')
  .factory('ark.graphs.gauge-config', ['ark.graphs.color-service', 'ark.graphs.utils',
    'ark.graphs.config-service',
    function(ColorService, Utils, ConfigService) {
      var GaugeConfiguration = function(configuration) {
        this.type = 'gauge';
        this.id = Utils.generateID(this.type);
        this.initialValues = [];
        this.max = 100;
        this.startAngle = -150;
        this.amplitude = 300;
        this.strokeWidth = 8;
        this.radius = 69;
        this.numberOfDataSet = 1;
        this.numberOfData = this.numberOfDataSet;
        this.autoresize = false;
        this.autoresizeY = false;

        this.radiusRule = {
          apply: true,
          rule: function(value, index, initialRadius, initialWidth) {
            return initialRadius - index * initialWidth * 1.5;
          }
        };

        this.svg = {
          height: this.radius * 2 + this.strokeWidth / 2 + 26,
          width: 208,
          padding: {
            top: 9
          },
          icon: {
            display: true
          }
        };

        this.legend = {
          display: true,
          fontsize: 12,
          height: 72,
          width: this.svg.width,
          title: ['Metric name'],
          format: function(d, i) {
            return d;
          },
          letters: {
            display: false
          },
          padding: {
            top: 10,
            left: 20,
            right: 20
          },
          icon: {
            display: true,
            minValue: 50
          }
        };

        this.data = {
          labels: ['data 0'],
          thresholds: {
            display: true,
            values: [75],
            strokeWidth: 8,
            amplitude: 1,
            opacity: 1,
            statusColors: ColorService.getStatusColors(2),
            barColor: '#C4CDD6',
            barColorOver: '#FFF',
            aboveGauge: false,
            comparator: function(value, threshold) {
              return value < threshold;
            }
          }
        };

        this.tooltip = {
          display: true,
          format: function(value, index, color, name) {
            return '<table class="ark-graphs-gauge-tooltip"><tbody><tr class="ark-graphs-gauge-tooltip-name-data"><td class="name"><span class="ark-graphs-gauge-tooltip-square" style="background-color: ' + color + ';"></span><text class="name-container">' + name + '</text></td><td class="value">' + value + '</td></tr></tbody></table>';
          }
        };

        this.hover = {
          apply: true,
          growBy: 2
        };

        this.background = {
          display: true,
          color: '#E3E9EF',
          strokeWidth: 2,
          opacity: 1,
          offset: this.strokeWidth / 2
        };

        this.label = {
          display: true,
          fontsize: 32,
          maxFontsize: 32,
          color: 'black',
          format: function(value) {
            return value + '%';
          },
          opacity: 1,
          symbol: {
            display: true,
            fontsize: 20,
            color: 'black',
            format: function() {
              return '';
            },
            opacity: 1
          }
        };

        this.transitions = {
          arc: 1000,
          label: 500,
          thresholds: 1000
        };

        this.autosnap = {
          enabled: false,
          threshold: 516,
          smallConfig: {
            radius: 69,
            svg: {
              width: 208
            },
            strokeWidth: 8,
            label: {
              fontsize: 32,
              maxFontsize: 32
            },
            legend: {
              fontsize: 12,
              height: 72,
              width: 208,
              padding: {
                top: 10,
                left: 20,
                right: 20
              }
            },
            data: {
              thresholds: {
                strokeWidth: 8
              }
            }
          },
          largeConfig: {
            radius: 124,
            svg: {
              width: 516
            },
            strokeWidth: 12,
            label: {
              fontsize: 48,
              maxFontsize: 48
            },
            thresholds: {
              strokeWidth: 12
            },
            legend: {
              fontsize: 14,
              height: 160,
              width: 260,
              padding: {
                top: 20,
                left: 24,
                right: 24
              }
            },
            data: {
              thresholds: {
                strokeWidth: 12
              }
            }
          }
        };

        this.update(configuration);
      };

      GaugeConfiguration.prototype.update = function(configuration) {
        Utils.mergeRecursive(this, configuration);

        this.maxData = [];
        this.multiThreshold = [];
        this.svg.height = this.radius * 2 + this.strokeWidth / 2 + 26;
        this.div = {
          width: this.svg.width,
          height: this.svg.height + (this.legend.display ? this.legend.height : 0),
          padding: {
            top: this.svg.padding.top
          }
        };

        if(this.autoresizeY) {
          this.div.padding.top = Math.max((this.parentHeight - this.div.height)/2, 0);
        }

        var i = 0;
        for (i = 0; i < this.numberOfData; i++) {
          this.maxData.push(this.max);
        }

        ConfigService.getStatusColors(this);
        ConfigService.updateInitialValues(this);
        ConfigService.updateLegendLabels(this);
        ConfigService.updateTooltipLabels(this);
      };

      GaugeConfiguration.prototype.computeGaugeWidth = function(d, i) {
        if (this.radiusRule.apply) {
          return this.radiusRule.rule(d, i, this.radius - this.strokeWidth / 2, this.strokeWidth);
        } else {
          return (this.radius - this.strokeWidth / 2);
        }
      };

      return GaugeConfiguration;
    }
  ]);

  'use strict';

  angular.module('ark.graphs.multiBar')
    .directive('arkMultiBar', ['ark.graphs.multiBarConfig', 'ark.graphs.d3', 'ark.graphs.utils', '$timeout',
      function(MultiBarGraphConfiguration, d3, Utils, $timeout) {
        return {
          restrict: 'E',
          scope: {
            configuration: '=',
            data: '='
          },
          link: function($scope, element) {

            $scope.config = new MultiBarGraphConfiguration($scope.configuration);
            if ($scope.config.autoresizeY) {
              //Check for resizing
              $scope.$watch(function() {
                var e = angular.element(element[0].parentElement);
                return Utils.getWidthAndHeight(e[0]);
              }, function(newVal, oldVal) {
                if (newVal.height !== oldVal.height) {
                  $timeout(function(){
                    Utils.resize($scope.config, element, true);
                  });
                }
              }, true);

              Utils.resize($scope.config, element, true);
            } else {
              if ($scope.config.chart.height === undefined && $scope.data[1] !== undefined) {
                $scope.config.chart.height = 48 * $scope.data[1].values.length + 100;
              }

              // If the legend is to be shown, the chart's margin-top property is
              // overwritten by the legend's height. In order to move the legend
              // without changing the chart margin, the legend's top and bottom
              // margins must balance to 0.
              if ($scope.config.chart.legend.margin.top === undefined) {
                $scope.config.chart.legend.margin.top = $scope.config.chart.height / 2 - 8;
              }
              if ($scope.config.chart.legend.margin.bottom === undefined) {
                $scope.config.chart.legend.margin.bottom = -1 * ($scope.config.chart.height / 2 - 8) + 16;
              }
            }
            
            $scope.padXAxis = function() {
              d3.select(element[0]).select('.nv-y')
                .selectAll('.tick text')
                .attr('dx', '-4px');
            };

            $scope.config.chart.callback = function() {

              d3.select(element[0]).selectAll('.nv-legend')
                .selectAll('.nv-series circle')
                .attr('cx', '-6px');

              // Commented out because all of the multi-bar unit tests are failing.
              // This is the exact same code as the one in the Ark-Vertical-Multi-Bar-Graph
              // as are the unit tests. But that one doesn't fail and this one does.
              //  $scope.padXAxis();
              //  chart.xAxis.dispatch.on('renderEnd.text', function() {
              //    $scope.padXAxis();
              //  });

            };

          },
          template: '<nvd3 class="ark-graphs-multi-bar-graph" options="config" data="data"></nvd3>'
        };
      }
    ]);

  'use strict';

  angular.module('ark.graphs.multiBar')
    .factory('ark.graphs.multiBarConfig', ['ark.graphs.utils', 'ark.graphs.config-service', 'ark.graphs.d3',
      function(Utils, ConfigService, d3) {
        var MultiBarGraphConfiguration = function(configuration) {
          this.autoresizeY = false;
          this.chart = {};
          this.chart.type = 'multiBarHorizontalChart';
          this.chart.id = Utils.generateID(this.chart.type);

          this.chart.showControls = true;
          this.chart.showLegend = true;
          this.chart.showValues = false;
          this.chart.duration = 500;

          this.chart.x = function(d) {
            return d.label;
          };
          this.chart.y = function(d) {
            return d.value;
          };

          this.chart.margin = {
            top: 36,
            bottom: 36,
            right: 8
          };

          this.chart.color = ['#203B73', '#07A8B4', '#844ED2'];

          this.chart.xAxis = {
            showMaxMin: false,
            tickPadding: 8
          };
          this.chart.yAxis = {
            showMaxMin: false,
            tickPadding: -12,
            tickFormat: function(d) {
              return d3.format(',.0f')(d);
            }
          };

          this.chart.stacked = true;
          this.chart.multibar = {
            margin: {
              bottom: 16
            },
            groupSpacing: 0.5
          };

          this.chart.tooltip = {
            classes: 'ark-multi-bar-tooltip',
            gravity: 'n',
            contentGenerator: function(key) {
              return '<div>' + d3.format(',f')(key.data.value) + ' ' + key.data.key + '</div>';
            }
          };

          this.chart.legend = {
            rightAlign: false,
            height: 20,
            margin: {},
            padding: 60
          };

          this.chart.controls = {
            rightAlign: false,
            height: 36,
            margin: {
              left: 6,
              right: -180
            },
            padding: 60
          };

          if (!this.chart.showControls) {
            this.chart.legend.margin.left = 6;
          } else {
            // The legend is positioned based on the width of the controls, if
            // they are shown. Controls have a fixed width of 180
            this.chart.legend.margin.left = -174;
          }

          this.update(configuration);
        };

        MultiBarGraphConfiguration.prototype.update = function(configuration) {
          Utils.mergeRecursive(this, configuration);
          if(this.autoresizeY) {
            this.chart.legend.margin.top = this.parentHeight / 2 - 8;
            this.chart.legend.margin.bottom = -1 * (this.parentHeight / 2 - 8) + 16;
          }
        };

        return MultiBarGraphConfiguration;
      }
    ]);

'use strict';

angular.module('ark.graphs.multi-line-graph', ['underscore'])
  .directive('arkMultiLineGraph', ['ark.graphs.multi-line-graph-config', 'ark.graphs.text-service',
    'ark.graphs.line-service', 'ark.graphs.d3', 'ark.graphs.utils', 'ark.graphs.tooltip-service',
    function(MultiLineGraphConfiguration, TextService, LineService, d3, Utils, TooltipService) {
      return {
        restrict: 'E',
        scope: {
          configuration: '=',
          data: '='
        },
        link: function($scope, element) {

          $scope.internalConfiguration = new MultiLineGraphConfiguration($scope.configuration);
          $scope.initialized = false;
          $scope.previousData = angular.copy($scope.data);

          $scope.selectedDataSetIndex = 0;
          $scope.tooltipDisplayed = false;
          $scope.previouslyEmpty = false;

          $scope.noDataAvailable = function() {
            $scope.widget = d3.select(element[0]);

            $scope.widget.selectAll('*').remove();
            $scope.widget.append('div')
              .attr('class', 'ark-graphs-multi-line-graph-no-data-container')
              .style('text-align', 'center')
              .style('display', 'table')
              .style('width', $scope.internalConfiguration.width + 'px')
              .style('height', $scope.internalConfiguration.height + 'px');

            $scope.widget.select('.ark-graphs-multi-line-graph-no-data-container')
              .append('span')
              .attr('class', 'ark-graphs-multi-line-graph-no-data-message')
              .style('display', 'table-cell')
              .style('vertical-align', 'middle')
              .html($scope.internalConfiguration.data.noDataAvailable);
          };

          $scope.isDataEmpty = function() {
            var allEmpty = true;
            angular.forEach($scope.data, function(line) {
              allEmpty = allEmpty && Boolean(!line.length);
            });
            return allEmpty;
          };

          $scope.checkSelectedDataSetIndex = function() {
            if (!$scope.data[$scope.selectedDataSetIndex].length) {
              angular.forEach($scope.data, function(data, index) {
                if (data.length) {
                  $scope.selectedDataSetIndex = index;
                }
              });
            }
          };

          $scope.draw = function() {
            if ($scope.isDataEmpty()) {
              $scope.previouslyEmpty = true;
              return $scope.init();
            } else if ($scope.previouslyEmpty) {
              $scope.previouslyEmpty = false;
              return $scope.init();
            }

            $scope.checkSelectedDataSetIndex();

            $scope.drawAxis();
            $scope.drawThresholds();
            $scope.drawThresholdIcon();
            $scope.updateHighlightedLine();
            $scope.highlightAxis();

            var paths = $scope.widget.select('.ark-graphs-multi-line-graph-data-lines').selectAll('path');
            paths.transition()
              .attrTween('d', function(d, i) {
                var line = $scope.internalConfiguration.data.binding[i];
                var interpolate = d3.interpolate($scope.previousData[i] || {}, $scope.data[i] || {});
                return function(t) {
                  return $scope.data[i].length ? $scope.lines[line](interpolate(t)) : '';
                };
              })
              .duration($scope.internalConfiguration.transition.duration)
              .each('end', function() {
                $scope.previousData = angular.copy($scope.data);
              });

            angular.forEach($scope.data, function(dataset, lineIndex) {
              var circles = $scope.widget.select('.ark-graphs-multi-line-graph-data-circle-line-' + lineIndex.toString()).selectAll('circle');
              circles.transition()
                .attrTween('cx', function(d, i) {
                  var interpolate = d3.interpolate(
                    $scope.ranges.x($scope.previousData[lineIndex].length ? $scope.previousData[lineIndex][i][$scope.internalConfiguration.xAxis.field] : 0),
                    $scope.ranges.x($scope.data[lineIndex].length ? $scope.data[lineIndex][i][$scope.internalConfiguration.xAxis.field] : 0)
                  );
                  return function(t) {
                    return interpolate(t);
                  };
                })
                .attrTween('cy', function(d, i) {
                  var range = $scope.internalConfiguration.data.binding[lineIndex];
                  var interpolate = d3.interpolate(
                    $scope.ranges.y[range]($scope.previousData[lineIndex].length ? $scope.previousData[lineIndex][i][$scope.internalConfiguration.yAxis[range].field] : 0),
                    $scope.ranges.y[range]($scope.data[lineIndex].length ? $scope.data[lineIndex][i][$scope.internalConfiguration.yAxis[range].field] : 0)
                  );
                  return function(t) {
                    return interpolate(t);
                  };
                })
                .duration($scope.internalConfiguration.transition.duration);
            });
            $scope.drawLegend();
            if ($scope.tooltipDisplayed) {
              TooltipService.showTooltip($scope.tooltip, $scope.data, _.pluck($scope.closest, 'index'), $scope.selectedDataSetIndex, $scope.internalConfiguration.data.colors, $scope.internalConfiguration.data.labels, $scope.internalConfiguration.data.thresholds.values[$scope.selectedDataSetIndex], $scope.internalConfiguration.data.thresholds.icons[$scope.selectedDataSetIndex], $scope.internalConfiguration.data.thresholds.colors[$scope.selectedDataSetIndex]);
            }
          };

          $scope.updateHighlightedLine = function() {
            var paths = $scope.widget.select('.ark-graphs-multi-line-graph-data-lines').selectAll('path');
            paths.each(function(d, i) {
              var elt = d3.select(this);
              elt.classed('selected', (i === $scope.selectedDataSetIndex));
            });
          };

          $scope.drawAxis = function() {
            $scope.createOrUpdateGraphElements();
            if ($scope.internalConfiguration.xAxis.display) {
              $scope.widget.select('.ark-graphs-multi-line-graph-x-axis')
                .attr('transform', 'translate(0, ' + ($scope.internalConfiguration.height - $scope.internalConfiguration.margin.top - $scope.internalConfiguration.margin.bottom) + ')')
                .call($scope.axis.x);
            }
            if ($scope.internalConfiguration.yAxis.left.display) {
              $scope.widget.select('.ark-graphs-multi-line-graph-y-left-axis')
                .call($scope.axis.y.left);
            }
            if ($scope.internalConfiguration.yAxis.right.display) {
              $scope.widget.select('.ark-graphs-multi-line-graph-y-right-axis')
                .attr('transform', 'translate(' + ($scope.internalConfiguration.width - $scope.internalConfiguration.margin.left - $scope.internalConfiguration.margin.right) + ', 0)')
                .call($scope.axis.y.right);
            }
          };

          $scope.legendClickEvent = function() {
            $scope.updateHighlightedLine();
            $scope.drawLegend();
            $scope.updateThresholds();
            $scope.drawAxis();
            $scope.highlightAxis();
          };

          $scope.drawLegend = function() {
            if ($scope.internalConfiguration.legend.display) {
              var container = $scope.widget.select('.ark-graphs-multi-line-graph-legend-container');
              container.html('');
              angular.forEach($scope.data, function(dataset, index) {
                var legendItem = container.append('a')
                  .attr('class', 'ark-graphs-multi-line-graph-legend-' + index + (($scope.selectedDataSetIndex === index) ? ' selected' : ''))
                  .on('click', function() {
                    if ($scope.data[index].length) {
                      $scope.selectedDataSetIndex = index;
                      $scope.legendClickEvent();
                    }
                  });

                var data = $scope.data[index];
                var side = $scope.internalConfiguration.data.binding[index];

                legendItem.append('span')
                  .attr('class', 'color' + (($scope.selectedDataSetIndex === index) ? ' selected' : ''))
                  .style('background-color', $scope.internalConfiguration.data.colors[index]);
                legendItem.append('span')
                  .attr('class', 'value')
                  .html($scope.internalConfiguration.legend.format((data.length) ? data[data.length - 1][$scope.internalConfiguration.yAxis[side].field] : '', index));
                legendItem.append('span')
                  .attr('class', 'label')
                  .html($scope.internalConfiguration.data.labels[index]);
              });
            }

          };

          $scope.drawThresholds = function() {
            if ($scope.internalConfiguration.data.thresholds.display) {
              $scope.widget.select('.ark-graphs-multi-line-graph-threshold-lines').selectAll('line').remove();
              $scope.widget.select('.ark-graphs-multi-line-graph-threshold-lines').selectAll('line')
                .data($scope.internalConfiguration.data.thresholds.values[$scope.selectedDataSetIndex])
                .enter()
                .append('line')
                .attr('x1', 0)
                .attr('x2', ($scope.internalConfiguration.width - $scope.internalConfiguration.margin.left - $scope.internalConfiguration.margin.right))
                .attr('y1', function(d) {
                  var side = $scope.internalConfiguration.data.binding[$scope.selectedDataSetIndex];
                  return $scope.ranges.y[side](d);
                })
                .attr('y2', function(d) {
                  var side = $scope.internalConfiguration.data.binding[$scope.selectedDataSetIndex];
                  return $scope.ranges.y[side](d);
                })
                .attr('stroke', function(d, i) {
                  return $scope.internalConfiguration.data.thresholds.colors[$scope.selectedDataSetIndex][i];
                })
                .attr('stroke-linecap', 'square')
                .attr('stroke-dasharray', $scope.internalConfiguration.data.thresholds.dash);
            }
          };

          $scope.updateThresholds = function() {
            if ($scope.internalConfiguration.data.thresholds.display) {
              var thresholds = $scope.widget.select('.ark-graphs-multi-line-graph-threshold-lines').selectAll('line');
              thresholds.transition()
                .attr('x1', 0)
                .attr('x2', ($scope.internalConfiguration.width - $scope.internalConfiguration.margin.left - $scope.internalConfiguration.margin.right))
                .attrTween('y1', function(d, i) {
                  var side = $scope.internalConfiguration.data.binding[$scope.selectedDataSetIndex];
                  var interpolate = d3.interpolate(d, $scope.internalConfiguration.data.thresholds.values[$scope.selectedDataSetIndex][i]);
                  return function(t) {
                    return $scope.ranges.y[side](interpolate(t));
                  };
                })
                .attrTween('y2', function(d, i) {
                  var side = $scope.internalConfiguration.data.binding[$scope.selectedDataSetIndex];
                  var interpolate = d3.interpolate(d, $scope.internalConfiguration.data.thresholds.values[$scope.selectedDataSetIndex][i]);
                  return function(t) {
                    return $scope.ranges.y[side](interpolate(t));
                  };
                })
                .attr('stroke', function(d, i) {
                  return $scope.internalConfiguration.data.thresholds.colors[$scope.selectedDataSetIndex][i];
                })
                .duration(500);
            }
          };

          $scope.drawThresholdIcon = function() {
            if ($scope.internalConfiguration.data.thresholds.display) {
              var side = $scope.internalConfiguration.data.binding[$scope.selectedDataSetIndex];

              if (!$scope.closest[$scope.selectedDataSetIndex]) {
                $scope.checkSelectedDataSetIndex();
              }

              var value = $scope.data[$scope.selectedDataSetIndex].length ? $scope.data[$scope.selectedDataSetIndex][$scope.closest[$scope.selectedDataSetIndex].index][$scope.internalConfiguration.yAxis[side].field] : undefined;
              var icon = null;
              var color = null;
              if (value) {
                angular.forEach($scope.internalConfiguration.data.thresholds.values[$scope.selectedDataSetIndex], function(threshold, index) {
                  if (value > threshold) {
                    icon = $scope.internalConfiguration.data.thresholds.icons[$scope.selectedDataSetIndex][index];
                    color = $scope.internalConfiguration.data.thresholds.colors[$scope.selectedDataSetIndex][index];
                  }
                });
              }

              var container = $scope.widget.select('.ark-graphs-multi-line-graph-threshold-icon-container');
              container.html('');
              container.append('span')
                .attr('class', 'ark-graphs-multi-line-graph-threshold-top-icon fonticon ' + icon)
                .style('color', color)
                .style('font-size', $scope.internalConfiguration.data.thresholds.iconFontsize + 'px')
                .style('left', ($scope.ranges.x($scope.closest[$scope.selectedDataSetIndex].value[$scope.internalConfiguration.xAxis.field]) - $scope.internalConfiguration.data.thresholds.iconFontsize / 2) + 1 + 'px');
            }
          };

          $scope.drawMouseEventOrInitial = function() {
            $scope.highlightCircles();
            var x = $scope.ranges.x($scope.closest[$scope.selectedDataSetIndex].value[$scope.internalConfiguration.xAxis.field]) || 0;
            $scope.widget.select('.ark-graphs-multi-line-graph-hover-line')
              .attr('x1', x)
              .attr('x2', x);

            $scope.drawThresholdIcon();
          };

          $scope.mousemove = function() {
            $scope.coordinates = d3.mouse($scope.widget.select('.ark-graphs-multi-line-graph-overlay')[0][0]);
            var x = $scope.ranges.x.invert($scope.coordinates[0]);
            $scope.closest = $scope.findClosestIndexTo(x);
            var indexes = _.pluck($scope.closest, 'index');
            TooltipService.showTooltip($scope.tooltip, $scope.data, indexes, $scope.selectedDataSetIndex, $scope.internalConfiguration.data.colors, $scope.internalConfiguration.data.labels, $scope.internalConfiguration.data.thresholds.values[$scope.selectedDataSetIndex], $scope.internalConfiguration.data.thresholds.icons[$scope.selectedDataSetIndex], $scope.internalConfiguration.data.thresholds.colors[$scope.selectedDataSetIndex]);
            TooltipService.setCoordinates($scope.tooltip, $scope.ranges.x($scope.closest[$scope.selectedDataSetIndex].value[$scope.internalConfiguration.xAxis.field]) + $scope.internalConfiguration.tooltip.offset.x, $scope.coordinates[1] + $scope.internalConfiguration.tooltip.offset.y);
            $scope.tooltipDisplayed = true;
            $scope.drawMouseEventOrInitial();
          };

          $scope.getDataForAxis = function(axis) {
            var data = [];
            angular.forEach($scope.data, function(dataset, index) {
              if ($scope.internalConfiguration.data.binding[index] === axis) {
                data.push(dataset);
              }
            });
            return data;
          };

          $scope.rescaleLines = function() {
            if ($scope.internalConfiguration.xAxis.rangePredefined) {
              LineService.scale($scope.ranges.x, $scope.internalConfiguration.xAxis.range);
            } else {
              LineService.scaleFromData($scope.ranges.x, $scope.data, $scope.internalConfiguration.xAxis.field, $scope.internalConfiguration.data.offset);
            }
            if ($scope.internalConfiguration.yAxis.left.rangePredefined) {
              LineService.scale($scope.ranges.y.left, $scope.internalConfiguration.yAxis.left.range);
            } else {

              LineService.scaleFromData($scope.ranges.y.left, $scope.getDataForAxis('left'), $scope.internalConfiguration.yAxis.left.field, $scope.internalConfiguration.yAxis.left.offset);
            }
            if ($scope.internalConfiguration.yAxis.right.rangePredefined) {
              LineService.scale($scope.ranges.y.right, $scope.internalConfiguration.yAxis.right.range);
            } else {
              LineService.scaleFromData($scope.ranges.y.right, $scope.getDataForAxis('right'), $scope.internalConfiguration.yAxis.right.field, $scope.internalConfiguration.yAxis.right.offset);
            }
          };

          $scope.highlightAxis = function() {
            $scope.widget.selectAll('.y.axis').selectAll('.domain').attr('stroke', $scope.internalConfiguration.axis.stroke).attr('stroke-width', '1px');
            $scope.widget.selectAll('.x.axis').selectAll('.domain').attr('stroke', $scope.internalConfiguration.axis.stroke).attr('stroke-width', '1px');
            $scope.widget.selectAll('.tick').attr('stroke', $scope.internalConfiguration.axis.stroke);
            $scope.widget.selectAll('.x.axis').selectAll('text')
              .attr('transform', 'translate(0,15)');
            if ($scope.internalConfiguration.yAxis.left.display && $scope.internalConfiguration.yAxis.right.display && $scope.internalConfiguration.axis.highlight) {
              var binding = $scope.internalConfiguration.data.binding[$scope.selectedDataSetIndex];
              var textOffset = (binding === 'left') ? -15 : 15;
              $scope.widget.selectAll('.y.axis').selectAll('.domain').attr('stroke', $scope.internalConfiguration.axis.stroke).attr('stroke-width', '0px');
              $scope.widget.selectAll('.y.axis').selectAll('text')
                .attr('transform', 'translate(0,0)')
                .style('font-weight', 'normal');
              $scope.widget.selectAll('.ark-graphs-multi-line-graph-y-' + binding + '-axis').selectAll('text')
                .attr('transform', 'translate(' + textOffset + ',0)')
                .style('font-weight', 'bold');
              $scope.widget.selectAll('.ark-graphs-multi-line-graph-y-' + binding + '-axis').selectAll('.domain').attr('stroke', $scope.internalConfiguration.data.colors[$scope.selectedDataSetIndex]).attr('stroke-width', '2px');
              $scope.widget.selectAll('.x.axis').selectAll('.domain').attr('stroke', $scope.internalConfiguration.data.colors[$scope.selectedDataSetIndex]).attr('stroke-width', '2px');
            }
          };

          $scope.highlightCircles = function() {
            var circles = $scope.widget.select('.ark-graphs-multi-line-graph-data-circles').selectAll('circle');
            circles.attr('r', $scope.internalConfiguration.data.circles.radius);
            var selected;
            angular.forEach($scope.closest, function(value, index) {
              selected = $scope.widget.select('.ark-graphs-multi-line-graph-data-circle-line-' + index).select('[index="' + value.index + '"]');
              if (index === $scope.selectedDataSetIndex) {
                selected.attr('r', $scope.internalConfiguration.data.circles.selected.radius);
              } else {
                selected.attr('r', $scope.internalConfiguration.data.circles.hover.radius);
              }
            });
          };

          $scope.findClosestIndexTo = function(value) {
            var bisector = d3.bisector(function(d) {
              return d ? d[$scope.internalConfiguration.xAxis.field] : 0;
            }).left;
            var tmp;
            var closest = [];
            angular.forEach($scope.data, function(d) {
              if (d.length) {
                var i = bisector(d, value, 1);
                var d0 = d[i - 1];
                var d1 = d[i];
                if (!d1) {
                  tmp = d0;
                } else {
                  tmp = value - d0[$scope.internalConfiguration.xAxis.field] > d0[$scope.internalConfiguration.xAxis.field] - value ? d1 : d0;
                }
                closest.push({
                  index: (tmp === d0) ? (i - 1) : i,
                  value: tmp
                });
              } else {
                closest.push({
                  index: undefined,
                  value: {}
                });
              }
            });
            return closest;
          };

          $scope.createOrUpdateGraphElements = function() {
            $scope.ranges = {
              x: LineService.createRange(0, ($scope.internalConfiguration.width - $scope.internalConfiguration.margin.left - $scope.internalConfiguration.margin.right)),
              y: {
                left: LineService.createLinearRange(($scope.internalConfiguration.height - $scope.internalConfiguration.margin.top - $scope.internalConfiguration.margin.bottom), 0),
                right: LineService.createLinearRange(($scope.internalConfiguration.height - $scope.internalConfiguration.margin.top - $scope.internalConfiguration.margin.bottom), 0)
              }
            };

            $scope.axis = {
              x: LineService.createAxis($scope.ranges.x, $scope.internalConfiguration.xAxis.ticks, 'bottom').tickFormat($scope.internalConfiguration.xAxis.tickFormat),
              y: {
                left: LineService.createAxis($scope.ranges.y.left, $scope.internalConfiguration.yAxis.left.ticks, 'left').tickFormat($scope.internalConfiguration.yAxis.left.tickFormat),
                right: LineService.createAxis($scope.ranges.y.right, $scope.internalConfiguration.yAxis.right.ticks, 'right').tickFormat($scope.internalConfiguration.yAxis.right.tickFormat)
              }
            };
            if ($scope.internalConfiguration.grid.display) {
              $scope.axis.x.innerTickSize(-($scope.internalConfiguration.height - $scope.internalConfiguration.margin.top - $scope.internalConfiguration.margin.bottom));
              if ($scope.internalConfiguration.data.binding[$scope.selectedDataSetIndex] === 'left') {
                $scope.axis.y.left.innerTickSize(-($scope.internalConfiguration.width - $scope.internalConfiguration.margin.left - $scope.internalConfiguration.margin.right));
                $scope.axis.y.right.innerTickSize(0);
              } else {
                $scope.axis.y.right.innerTickSize(-($scope.internalConfiguration.width - $scope.internalConfiguration.margin.left - $scope.internalConfiguration.margin.right));
                $scope.axis.y.left.innerTickSize(0);
              }
            }

            $scope.lines = {
              left: LineService.createLine($scope.ranges.x, $scope.ranges.y.left, $scope.internalConfiguration.xAxis.field, $scope.internalConfiguration.yAxis.left.field), //lines that refers to the left y axis
              right: LineService.createLine($scope.ranges.x, $scope.ranges.y.right, $scope.internalConfiguration.xAxis.field, $scope.internalConfiguration.yAxis.right.field) //lines that refers to the right y axis
            };

            $scope.rescaleLines();
          };

          $scope.getDefaultSelectedItem = function() {
            var maxIndex = 0;
            var currentValue = {};
            var closest = [];
            angular.forEach($scope.data, function(d) {
              if (maxIndex < d.length) {
                maxIndex = d.length - 1;
                currentValue = d[maxIndex];
                closest.push({
                  index: maxIndex,
                  value: currentValue
                });
              } else {
                closest.push({
                  index: undefined,
                  value: undefined
                });
              }
            });
            return closest;
          };

          $scope.init = function() {
            if ($scope.isDataEmpty()) {
              return $scope.noDataAvailable();
            }

            $scope.checkSelectedDataSetIndex();

            $scope.widget = d3.select(element[0]);

            $scope.widget.selectAll('*').remove();

            $scope.widget = $scope.widget.append('div')
              .attr('id', $scope.internalConfiguration.id)
              .attr('width', $scope.internalConfiguration.width + 'px')
              .style('white-space', 'nowrap')
              .style('position', 'relative');

            $scope.widget.append('div')
              .attr('class', 'ark-graphs-multi-line-graph-threshold-icon-container')
              .style('width', ($scope.internalConfiguration.width - $scope.internalConfiguration.margin.left - $scope.internalConfiguration.margin.right) + 'px')
              .style('height', $scope.internalConfiguration.data.thresholds.iconFontsize + 'px')
              .style('left', $scope.internalConfiguration.margin.left + 'px');

            $scope.widget.append('svg')
              .attr('class', 'ark-graphs-multi-line-graph-svg')
              .attr('width', $scope.internalConfiguration.width + 'px')
              .attr('height', $scope.internalConfiguration.height + 'px')
              .append('g')
              .attr('class', 'ark-graphs-multi-line-graph-container')
              .attr('transform', 'translate(' + $scope.internalConfiguration.margin.left + ', ' + $scope.internalConfiguration.margin.top + ')');

            $scope.widget.append('div')
              .attr('class', 'ark-graphs-multi-line-graph-legend-container')
              .attr('width', $scope.internalConfiguration.width + 'px');

            var svg = $scope.widget.select('.ark-graphs-multi-line-graph-container');

            svg.append('defs')
              .append('clipPath')
              .attr('id', 'ark-graphs-multi-line-graph-clipPath-' + $scope.internalConfiguration.id)
              .append('rect')
              .attr('x', 0)
              .attr('y', 0)
              .attr('width', ($scope.internalConfiguration.width - $scope.internalConfiguration.margin.left - $scope.internalConfiguration.margin.right) + 'px')
              .attr('height', ($scope.internalConfiguration.height - $scope.internalConfiguration.margin.top - $scope.internalConfiguration.margin.bottom) + 'px');

            if ($scope.internalConfiguration.xAxis.display) {
              svg.append('g')
                .attr('class', 'x axis ark-graphs-multi-line-graph-x-axis');
              if ($scope.internalConfiguration.xAxis.label.show) {
                svg.select('.ark-graphs-multi-line-graph-x-axis').append('g')
                  .attr('class', 'ark-multi-line-graphs-graph-x-label')
                  .attr('text-anchor', 'middle')
                  .attr('transform', 'translate(' + (($scope.internalConfiguration.width) / 2 - $scope.internalConfiguration.margin.left) + ',' + $scope.internalConfiguration.xAxis.label.offset.y + ')')
                  .append('text')
                  .attr('class', 'x label')
                  .text($scope.internalConfiguration.xAxis.label.text);
              }
            }
            if ($scope.internalConfiguration.yAxis.left.display) {
              svg.append('g')
                .attr('class', 'y axis ark-graphs-multi-line-graph-y-left-axis');
              if ($scope.internalConfiguration.yAxis.left.label.show) {
                svg.select('.ark-graphs-multi-line-graph-x-axis').append('g')
                  .attr('class', 'ark-multi-line-graphs-graph-y-label')
                  .attr('text-anchor', 'middle')
                  .attr('transform', 'rotate(-90) translate(' + (($scope.internalConfiguration.height) / 2 - $scope.internalConfiguration.margin.top) + ',' + $scope.internalConfiguration.yAxis.left.label.offset.y + ')')
                  .append('text')
                  .attr('class', 'y left label')
                  .text($scope.internalConfiguration.yAxis.left.label.text);
              }
            }
            if ($scope.internalConfiguration.yAxis.right.display) {
              svg.append('g')
                .attr('class', 'y axis ark-graphs-multi-line-graph-y-right-axis');
              if ($scope.internalConfiguration.yAxis.right.label.show) {
                svg.select('.ark-graphs-multi-line-graph-x-axis').append('g')
                  .attr('class', 'ark-multi-line-graphs-graph-y-label')
                  .attr('text-anchor', 'middle')
                  .attr('transform', 'rotate(90) translate(' + (-(($scope.internalConfiguration.height) / 2 - $scope.internalConfiguration.margin.top)) + ',' + (-($scope.internalConfiguration.width - $scope.internalConfiguration.margin.left - $scope.internalConfiguration.margin.right + $scope.internalConfiguration.yAxis.right.label.offset.y)) + ')')
                  .append('text')
                  .attr('class', 'y right label')
                  .text($scope.internalConfiguration.yAxis.right.label.text);
              }
            }

            $scope.drawAxis();

            //Create main container for graph
            svg.append('g')
              .attr('class', 'ark-graphs-multi-line-graph-data')
              .style('clip-path', 'url(#ark-graphs-multi-line-graph-clipPath-' + $scope.internalConfiguration.id + ')');

            var graphContainer = svg.select('.ark-graphs-multi-line-graph-data');

            graphContainer.append('g')
              .attr('class', 'ark-graphs-multi-line-graph-data-lines');

            graphContainer.append('line')
              .attr('class', 'ark-graphs-multi-line-graph-hover-line');

            graphContainer.append('g')
              .attr('class', 'ark-graphs-multi-line-graph-threshold-lines');

            graphContainer.append('g')
              .attr('class', 'ark-graphs-multi-line-graph-data-circles');

            angular.forEach($scope.data, function(dataset, i) {
              graphContainer.select('.ark-graphs-multi-line-graph-data-circles')
                .append('g')
                .attr('class', 'ark-graphs-multi-line-graph-data-circle-line-' + i.toString());
            });

            graphContainer.select('.ark-graphs-multi-line-graph-hover-line')
              .attr('x1', 0)
              .attr('x2', 0)
              .attr('y1', 0)
              .attr('y2', $scope.internalConfiguration.height - $scope.internalConfiguration.margin.top - $scope.internalConfiguration.margin.bottom)
              .attr('stroke-linecap', 'square')
              .attr('stroke-dasharray', $scope.internalConfiguration.data.guideline.dash);

            if ($scope.isDataEmpty()) {
              return;
            }
            //Otherwise fill in widget with data
            graphContainer.select('.ark-graphs-multi-line-graph-data-lines').selectAll('path')
              .data($scope.data)
              .enter()
              .append('path')
              .attr('d', function(d, i) {
                var line = $scope.internalConfiguration.data.binding[i];
                return d ? $scope.lines[line](d) : '';
              })
              .attr('stroke', function(d, i) {
                return $scope.internalConfiguration.data.colors[i];
              });

            if ($scope.internalConfiguration.data.thresholds.display) {
              $scope.drawThresholds();
            }

            if ($scope.internalConfiguration.data.circles.display) {
              angular.forEach($scope.data, function(dataset, i) {
                graphContainer.select('.ark-graphs-multi-line-graph-data-circle-line-' + i.toString()).selectAll('circle')
                  .data(dataset)
                  .enter()
                  .append('circle')
                  .attr('fill', function() {
                    return $scope.internalConfiguration.data.colors[i];
                  })
                  .attr('r', $scope.internalConfiguration.data.circles.radius)
                  .attr('index', function(d, j) {
                    return j;
                  })
                  .attr('cx', function(d) {
                    return $scope.ranges.x(d[$scope.internalConfiguration.xAxis.field]);
                  })
                  .attr('cy', function(d) {
                    var range = $scope.internalConfiguration.data.binding[i];
                    return $scope.ranges.y[range](d[$scope.internalConfiguration.yAxis[range].field]);
                  });
              });
            }
            svg.append('rect')
              .attr('class', 'ark-graphs-multi-line-graph-overlay')
              .attr('width', $scope.internalConfiguration.width - $scope.internalConfiguration.margin.left - $scope.internalConfiguration.margin.right)
              .attr('height', $scope.internalConfiguration.height - $scope.internalConfiguration.margin.top - $scope.internalConfiguration.margin.bottom)
              .on('mousemove', $scope.mousemove)
              .on('mouseover', function() {
                $scope.widget.select('.ark-graphs-multi-line-graph-hover-line')
                  .style('display', 'block');
              })
              .on('mouseout', function() {
                TooltipService.hideTooltip($scope.tooltip);
                $scope.closest = $scope.getDefaultSelectedItem();
                $scope.drawMouseEventOrInitial();
                $scope.tooltipDisplayed = false;
              });

            TooltipService.initTooltip($scope);

            $scope.closest = $scope.getDefaultSelectedItem();
            $scope.drawMouseEventOrInitial();
            $scope.drawLegend();
            $scope.updateHighlightedLine();
            $scope.updateThresholds();
            //$scope.drawAxis();
            $scope.highlightAxis();
            $scope.initialized = true;
          };

          if ($scope.internalConfiguration.autofit || $scope.internalConfiguration.autofitY) {
            Utils.resize($scope.internalConfiguration, element, true);
          }

          $scope.init();

          $scope.$watch('configuration', function() {
            $scope.initialized = false;
            $scope.internalConfiguration.update($scope.configuration);
            $scope.init();
          }, true);

          $scope.$watch(function() {
            var e = angular.element(element[0].parentElement);
            return Utils.getWidthAndHeight(e[0]);
          }, function(newVal, oldVal) {
            if (($scope.internalConfiguration.autofit && newVal.width !== oldVal.width) || ($scope.internalConfiguration.autofitY && newVal.height !== oldVal.height)) {
              Utils.resize($scope.internalConfiguration, element, true);
              $scope.initialized = false;
              $scope.drawAxis();
              $scope.init();
              $scope.drawAxis();
            }
          }, true);

          $scope.$watch('data', function() {
            if ($scope.initialized) {
              $scope.draw();
            }
          });
        }
      };
    }
  ]);

'use strict';

angular.module('ark.graphs.multi-line-graph')
  .factory('ark.graphs.multi-line-graph-config', ['ark.graphs.color-service', 'ark.graphs.utils', 'ark.graphs.config-service', 'ark.graphs.d3',
    function(ColorService, Utils, ConfigService, d3) {
      var MultiLineGraphConfiguration = function(configuration) {
        this.type = 'multi-line-graph';
        this.id = Utils.generateID(this.type);
        this.autoresize = false;

        this.width = 600;
        this.height = 280;

        this.autofit = true; //Widget will automatically fit to parent container's width
        this.autofitY = true;

        this.fontsize = 12;
        this.yAxis = {
          left: {
            display: true,
            rangePredefined: false,
            range: [0, 100], //Only used if rangePredefined is true
            offset: {
              max: 0.1, //Space left between the end of lines end right axis here = 1/10 of total range
              min: 0.05
            },
            field: 'value',
            label: {
              show: false,
              text: 'y-axis left label (unit)',
              offset: {
                x: 0,
                y: -45
              }
            },
            ticks: 4,
            tickFormat: function(d, i) {
              return d3.format(',.0f')(d);
            }
          },
          right: {
            display: true,
            rangePredefined: false,
            range: [0, 100], //Only used if rangePredefined is true
            offset: {
              max: 0.1, //Space left between the end of lines end right axis here = 1/10 of total range
              min: 0
            },
            field: 'value',
            label: {
              show: false,
              text: 'y-axis right label (unit)',
              offset: {
                x: 0,
                y: 45
              }
            },
            ticks: 3,
            tickFormat: function(d, i) {
              return d + ' %';
            }
          }
        };
        this.xAxis = {
          display: true,
          rangePredefined: false,
          range: [0, 100], //Only used if rangePredefined is true
          label: {
            show: false,
            text: 'x-axis label (unit)',
            offset: {
              x: 0,
              y: 40
            }
          },
          field: 'time',
          ticks: 3,
          tickFormat: function(d, i) {
            var format = d3.time.format('%-I:%M %p');
            return format(new Date(d));
          }
        };
        this.axis = {
          stroke: 'lightgrey', //Default stroke color
          highlight: true //Only applies if 2 axis are presented
        };
        this.grid = {
          display: true
        };
        this.margin = {
          left: 50,
          top: 10,
          right: 50,
          bottom: 45
        };

        this.data = {
          offset: {
            max: 0.1, //Space left between the end of lines end right axis here = 1/10 of total range
            min: 0
          },
          binding: ['left', 'left', 'right'], //Ordered array to determine on which axis the data is bound
          noDataAvailable: 'No Data Available.',
          colors: ColorService.arkBlueColors(),
          thresholds: {
            display: true,
            values: [
              [100, 300, 400],
              [80, 350, 500],
              [10, 30, 70]
            ],
            colors: [ColorService.getStatusColors(3), ColorService.getStatusColors(3), ColorService.getStatusColors(3)],
            icons: [
              ['icon-alert-circle', 'icon-alert-triangle', 'icon-alert-checkmark'],
              ['icon-alert-circle', 'icon-alert-triangle', 'icon-alert-checkmark'],
              ['icon-alert-circle', 'icon-alert-triangle', 'icon-alert-checkmark']
            ],
            iconFontsize: 16, // size in px
            dash: '4,3'
          },
          guideline: {
            dash: '1,2'
          },
          labels: ['Line A', 'Line B', 'Line C'],
          circles: {
            display: true,
            radius: 0,
            hover: {
              radius: 4
            },
            selected: {
              radius: 6
            }
          }
        };

        this.transition = {
          duration: 1000
        };

        this.tooltip = {
          display: true,
          offset: {
            x: 40,
            y: 30
          },
          format: function(data, indexesSelected, lineIndexSelected, colors, labels, thresholds, icons, thresholdColors) {
            var tableData = '';
            var time = (data[lineIndexSelected].length && data[lineIndexSelected][indexesSelected[lineIndexSelected]]) ? data[lineIndexSelected][indexesSelected[lineIndexSelected]].time : 0;
            var date = new Date(parseInt(time));
            var hours = date.getHours();
            var minutes = '0' + date.getMinutes();
            var formattedTime = hours + ':' + minutes.substr(-2);
            var thresholdIndex = 0;
            var value = (data[lineIndexSelected].length && data[lineIndexSelected][indexesSelected[lineIndexSelected]]) ? data[lineIndexSelected][indexesSelected[lineIndexSelected]].value : 0;
            angular.forEach(thresholds, function(threshold, index) {
              if (value > threshold) {
                thresholdIndex = index;
              }
            });
            for (var i = 0; i < data.length; i++) {
              if (data[i].length && data[i][indexesSelected[i]]) {
                tableData += '<div class="ark-graphs-multi-line-graph-tooltip-row';
                if (i === lineIndexSelected) {
                  tableData += ' selected-row';
                }
                tableData += '">' +
                  '<span class="color' + ((i === lineIndexSelected) ? ' selected' : '') + '" style="background-color: ' + colors[i] + ';"></span>' +
                  '<span class="label">' + labels[i] + '</span>' +
                  '<span class="value">' + data[i][indexesSelected[i]].value + ((i === 2) ? '%' : '') + '</span>' +
                  '<span class="icon">';
                if (i === lineIndexSelected) {
                  tableData += '<span ng-show="' + (i === lineIndexSelected) + '" class="fonticon ' + icons[thresholdIndex] + '" style="color:' + thresholdColors[thresholdIndex] + ';"></span>';
                }
                tableData += '</span></div>';
              }
            }
            return '<table class="ark-graphs-multi-line-graph-tooltip"><thead><th class="header" colspan="4">' + formattedTime + '</th></thead></table><div class="ark-graphs-multi-line-graph-tooltip-table">' + tableData + '</div>';
          }
        };

        this.legend = {
          display: true,
          format: function(d, i) {
            return d;
          }
        };

        this.update(configuration);
      };

      MultiLineGraphConfiguration.prototype.update = function(configuration) {
        Utils.mergeRecursive(this, configuration);
        if(this.autofit) {
          this.width = this.parentWidth;
        }
        if(this.autofitY) {
          this.height = this.parentHeight - (this.legend.display ? 50 : 0); 
        }
      };

      return MultiLineGraphConfiguration;
    }
  ]);

'use strict';

angular.module('ark.graphs.simpleLine')
  .factory('ark.graphs.simpleLineConfig', ['ark.graphs.color-service', 'ark.graphs.utils', 'ark.graphs.config-service', 'ark.graphs.d3',
    function(ColorService, Utils, ConfigService, d3) {
      var SimpleLineGraphConfiguration = function(configuration) {
        this.autoresizeY = false;
        this.chart = {};
        this.chart.type = 'lineChart';
        this.chart.id = Utils.generateID(this.chart.type);
        this.chart.autoresize = false;

        this.chart.height = 400;

        this.chart.showLegend = true;
        this.chart.duration = 300;

        this.chart.x = function(d) {
          return d.x;
        };
        this.chart.y = function(d) {
          return d.y;
        };

        this.chart.margin = {
          top: 0,
          left: 90,
          bottom: 60,
          right: 8
        };

        this.chart.xAxis = {
          showMaxMin: false,
          tickPadding: 1
        };
        this.chart.yAxis = {
          showMaxMin: false,
          tickFormat: d3.format(',0f')
        };
        this.chart.forceY = [0];

        this.chart.clipEdge = false;
        this.chart.lines = {
          pointSize: 50
        };
        this.chart.clipRadius = 10;
        this.chart.tooltip = {
          classes: 'ark-simple-line-tooltip',
          gravity: 'n',
          contentGenerator: function(key) {
            return '<div>' + d3.format(',0f')(key.series[0].value) + ' ' + key.series[0].key + '</div>';
          }
        };

        this.chart.legend = {
          rightAlign: false,
          height: 20,
          margin: {
            left: 6
          },
          padding: 60
        };

        this.update(configuration);

        this.chart.yAxis.tickPadding = this.chart.margin.left;

        // Allows the user to override the tickPadding altogether
        this.update(configuration);
      };

      SimpleLineGraphConfiguration.prototype.update = function(configuration) {
        Utils.mergeRecursive(this, configuration);
        if(this.autoresizeY) {
          this.chart.legend.margin.top = this.parentHeight / 2 - 7;
          this.chart.legend.margin.bottom = -1 * (this.parentHeight / 2) - 7;
        }
      };

      return SimpleLineGraphConfiguration;
    }
  ]);

'use strict';

angular.module('ark.graphs.simpleLine')
  .directive('arkSimpleLine', ['ark.graphs.simpleLineConfig', 'ark.graphs.d3', 'ark.graphs.color-service', 'ark.graphs.utils', '$timeout',
    function(SimpleLineGraphConfiguration, d3, ColorService, Utils, $timeout) {
      return {
        restrict: 'E',
        scope: {
          configuration: '=',
          data: '='
        },
        link: function($scope, element) {

          $scope.config = new SimpleLineGraphConfiguration($scope.configuration);
          if ($scope.config.autoresizeY) {
            delete $scope.config.chart.height;

            //Check for resizing
            $scope.$watch(function() {
              var e = angular.element(element[0].parentElement);
              return Utils.getWidthAndHeight(e[0]);
            }, function(newVal, oldVal) {
              if (newVal.height !== oldVal.height) {
                $timeout(function(){
                  Utils.resize($scope.config, element, true);
                });
              }
            }, true);

            Utils.resize($scope.config, element, true);
          } else {
            // If the legend is to be shown, the chart's margin-top property is
            // overwritten by the legend's height. In order to move the legend
            // without changing the chart margin, the legend's top and bottom
            // margins must balance to 0.
            if ($scope.config.chart.legend.margin.top === undefined) {
              $scope.config.chart.legend.margin.top = $scope.config.chart.height / 2 - 7;
            }
            if ($scope.config.chart.legend.margin.bottom === undefined) {
              $scope.config.chart.legend.margin.bottom = -1 * ($scope.config.chart.height / 2 - 7);
            }
          }

          $scope.extendYAxis = function() {
            d3.select(element[0]).select('.nv-y')
              .selectAll('.tick line')
              .attr('x1', -1 * $scope.config.chart.margin.left);
          };

          $scope.padXAxis = function() {
            d3.select(element[0]).select('.nv-x')
              .selectAll('.tick text')
              .attr('dx', '-4px');
          };

          $scope.padYAxis = function() {
            d3.select(element[0]).select('.nv-y')
              .selectAll('.tick text')
              .attr('dy', '10px');
          };

          $scope.config.chart.callback = function(chart) {

            d3.select(element[0]).selectAll('.nv-legend')
              .selectAll('.nv-series circle')
              .attr('cx', '-6px');

            $scope.extendYAxis();
            // NOTE: calling padYAxis and padXAxis causes a weird flickering effect
            // when the chart re-renders
            $scope.padYAxis();
            $scope.padXAxis();
            chart.yAxis.dispatch.on('renderEnd.lines', function() {
              $scope.extendYAxis();
            }).on('renderEnd.text', function() {
              $scope.padYAxis();
            });

            chart.xAxis.dispatch.on('renderEnd.text', function() {
              $scope.padXAxis();
            });

          };

          $scope.$watch('data', function(newData) {
            newData.forEach(function(series, i) {
              if (series.color) {
                return;
              }
              series.color = ColorService.arkBlueColors()[i];
            });
          });

        },
        template: '<nvd3 class="ark-graphs-simple-line" options="config" data="data"></nvd3>'
      };
    }
  ]);

'use strict';

angular.module('ark.graphs.spark-line')
  .directive('arkSparkLine', ['ark.graphs.spark-line-config', 'ark.graphs.text-service',
    'ark.graphs.line-service', 'ark.graphs.d3', 'ark.graphs.utils', 'ark.graphs.tooltip-service',
    'ark.graphs.threshold-service',
    function(SparkLineConfiguration, TextService, LineService, d3, Utils, TooltipService, ThresholdService) {
      return {
        restrict: 'E',
        scope: {
          configuration: '=',
          data: '='
        },
        link: function($scope, element) {

          $scope.internalConfiguration = new SparkLineConfiguration($scope.configuration);
          $scope.originalStrokeWidth = $scope.internalConfiguration.data.strokeWidth;
          $scope.originalRadius = $scope.internalConfiguration.data.circle.r;
          $scope.svgWidth = $scope.internalConfiguration.svg.width;

          $scope.prepareLine = function() {
            var strokeWidth = $scope.internalConfiguration.data.strokeWidth - 1;
            var r = $scope.internalConfiguration.data.circle.r;
            $scope.x = d3.scale.ordinal()
              .domain($scope.data.map(function(d, i) {
                return i;
              }))
              .rangePoints([0, $scope.internalConfiguration.svg.width]);

            $scope.y = d3.scale.linear()
              .domain([0, $scope.max])
              .range([$scope.internalConfiguration.svg.height - strokeWidth - r, strokeWidth + r]);

            $scope.line = d3.svg.line()
              .interpolate('basis')
              .x(function(d, i) {
                return $scope.x(i);
              })
              .y(function(d) {
                return $scope.y(d);
              });
          };

          $scope.draw = function() {
            $scope.widget.select('.ark-graphs-spark-line-path')
              .attr('d', $scope.line($scope.data))
              .attr('stroke', function() {
                return ThresholdService.getDataColor(parseInt(($scope.data[$scope.data.length - 1] / $scope.max) * 100, 10), $scope.internalConfiguration);
              });

            $scope.widget.select('.ark-graphs-spark-line-tip')
              .attr({
                'cx': $scope.internalConfiguration.svg.width,
                'cy': $scope.y($scope.data[$scope.data.length - 1]),
                'r': $scope.internalConfiguration.data.circle.r,
                'fill': ThresholdService.getDataColor(parseInt(($scope.data[$scope.data.length - 1] / $scope.max) * 100, 10), $scope.internalConfiguration)
              });

            TextService.showTextUpdate($scope.internalConfiguration, $scope.widget, [parseInt(($scope.data[$scope.data.length - 1] / $scope.max) * 100, 10)]);
            $scope.widget.select('.ark-graphs-spark-line-legend-name')
              .style('width', $scope.internalConfiguration.objectName.width.toString() + 'px');
          };

          $scope.update = function() {
            $scope.internalConfiguration.data.strokeWidth = 0.6 * $scope.internalConfiguration.svg.width / $scope.svgWidth * $scope.originalStrokeWidth;
            $scope.internalConfiguration.data.circle.r = 0.6 * $scope.internalConfiguration.svg.width / $scope.svgWidth * $scope.originalRadius;
          };

          $scope.init = function() {
            $scope.widget = d3.select(element[0]);
            $scope.widget.selectAll('*').remove();

            $scope.max = d3.max($scope.data);

            var div = $scope.widget
              .append('div')
              .attr('class', 'ark-graphs-spark-line-div')
              .style('width', $scope.internalConfiguration.div.width.toString() + 'px')
              .style('height', $scope.internalConfiguration.div.height.toString() + 'px')
              .style('padding-top', ($scope.internalConfiguration.div.padding.top - $scope.internalConfiguration.div.border.top.width).toString() + 'px')
              .style('padding-bottom', $scope.internalConfiguration.div.padding.bottom.toString() + 'px')
              .style('border-top', 'solid')
              .style('border-top-width', $scope.internalConfiguration.div.border.top.width.toString() + 'px')
              .style('border-top-color', $scope.internalConfiguration.div.border.top.color)
              .style('position', 'relative');

            var svg = div
              .append('svg')
              .attr('class', 'ark-graphs-spark-line-svg')
              .attr('width', $scope.internalConfiguration.svg.width + $scope.internalConfiguration.data.circle.r)
              .attr('height', $scope.internalConfiguration.svg.height + $scope.internalConfiguration.data.circle.r * 2)
              .style('position', 'absolute')
              .style('left', $scope.internalConfiguration.svg.position.left + 'px')
              .append('g');

            $scope.prepareLine();

            svg.append('path')
              .attr('d', $scope.line($scope.data))
              .attr('class', 'ark-graphs-spark-line-path')
              .attr('fill', 'none')
              .attr('stroke', function() {
                return ThresholdService.getDataColor(parseInt(($scope.data[$scope.data.length - 1] / $scope.max) * 100, 10), $scope.internalConfiguration);
              })
              .attr('stroke-width', $scope.internalConfiguration.data.strokeWidth);

            svg.append('circle')
              .attr('class', 'ark-graphs-spark-line-tip');

            ThresholdService.initThresholdValues($scope.internalConfiguration);

            TextService.showText($scope.internalConfiguration, $scope.widget, [parseInt(($scope.data[$scope.data.length - 1] / $scope.max) * 100, 10)]);
            $scope.widget.select('.ark-graphs-spark-line-legend-text').select('.metric-name')
              .style('width', ($scope.internalConfiguration.autoresize ? $scope.internalConfiguration.objectName.width : $scope.internalConfiguration.legend.width).toString() + 'px');
          };

          if ($scope.internalConfiguration.autoresize || $scope.internalConfiguration.autoresizeY) {
            Utils.resize($scope.internalConfiguration, element);
            if (!$scope.internalConfiguration.autosnap.enabled) {
              $scope.update();
            }
          }

          $scope.init();

          //Check for resizing
          $scope.$watch(function() {
            var e = angular.element(element[0].parentElement);
            return Utils.getWidthAndHeight(e[0]);
          }, function() {
            if (($scope.internalConfiguration.autofit && newVal.width !== oldVal.width) || ($scope.internalConfiguration.autofitY && newVal.height !== oldVal.height)) {
              Utils.resize($scope.internalConfiguration, element);
              if (!$scope.internalConfiguration.autosnap.enabled) {
                $scope.update();
              }
              $scope.init();
              $scope.draw($scope.data);
            }
          }, true);

          //Check for changes in internalConfiguration
          $scope.$watch('configuration', function() {
            $scope.internalConfiguration.update($scope.configuration);
            $scope.init();
            $scope.draw();
          }, true);

          //Check for changes in data
          $scope.$watch('data', function() {
            return $scope.draw();
          }, true);
        }
      };
    }
  ]);

'use strict';

angular.module('ark.graphs.spark-line')
  .factory('ark.graphs.spark-line-config', ['ark.graphs.color-service',
    'ark.graphs.utils', 'ark.graphs.config-service',
    function(ColorService, Utils, ConfigService) {
      var SparkLineConfiguration = function(configuration, data) {

        this.type = 'spark-line';
        this.id = Utils.generateID(this.type);
        this.numerOfData = 1;
        this.autoresize = false;
        this.autoresizeY = false;

        this.div = {
          width: 208,
          height: 40,
          border: {
            top: {
              width: 1,
              color: '#E3E9EF'
            }
          }
        };

        this.svg = {
          width: 72,
          height: 24,
          position: {
            left: 72
          }
        };

        this.objectName = {
          width: 64
        };

        this.initialRatio = {
          svgWidth: this.svg.width / this.div.width,
          svgPositionLeft: this.svg.position.left / this.div.width,
          objectNameWidth: this.objectName.width / this.div.width
        };

        this.legend = {
          display: true,
          fontsize: 12,
          title: ['Metric name'],
          format: function(d, i) {
            return d;
          },
          letters: {
            display: false
          },
          padding: {
            top: 0,
            left: 0,
            right: 20
          },
          icon: {
            display: true
          }
        };

        this.data = {
          circle: {
            r: 1.5
          },
          strokeWidth: 1.5,
          thresholds: {
            display: true,
            values: [25, 50, 75],
            statusColors: ColorService.getStatusColors(2),
            comparator: function(value, threshold) {
              return value < threshold;
            }
          }
        };

        this.autosnap = {
          enabled: false,
          thresholds: [{
            maxWidth: 516,
            configuration: {
              div: {
                width: 208,
                height: 40
              },
              data: {
                strokeWidth: 1.5,
                circle: {
                  r: 1.5
                }
              }
            }
          }, {
            maxWidth: 'none',
            configuration: {
              div: {
                width: 516,
                height: 40
              },
              data: {
                strokeWidth: 2,
                circle: {
                  r: 2
                }
              }
            }
          }]
        };

        this.update(configuration, data);
      };

      SparkLineConfiguration.prototype.update = function(configuration) {
        Utils.mergeRecursive(this, configuration);
        ConfigService.getStatusColors(this);

        if (this.autoresize) {
          this.svg.width = this.initialRatio.svgWidth * this.div.width;
          this.svg.position.left = this.initialRatio.svgPositionLeft * this.div.width;
          this.objectName.width = this.initialRatio.objectNameWidth * this.div.width;
        }

        if(this.autoresizeY) {
          this.div.height = this.parentHeight;
        }

        var paddingHeight = this.div.height - this.svg.height;

        this.div.padding = {
          top: paddingHeight / 2,
          bottom: paddingHeight / 2,
        };

        this.legend.width = this.div.width;
        this.legend.height = this.svg.height;
        this.legend.lineHeight = this.svg.height;
      };

      return SparkLineConfiguration;
    }
  ]);

'use strict';

angular.module('ark.graphs.standardDeviation')
  .directive('arkStandardDeviation', ['ark.graphs.standardDeviationConfig', 'ark.graphs.d3', 'ark.graphs.nvd3', 'ark.graphs.utils', '$timeout',
    function(StandardDeviationGraphConfiguration, d3, nv, Utils, $timeout) {
      return {
        restrict: 'E',
        scope: {
          configuration: '=',
          data: '='
        },
        template: '<nvd3 class="ark-graphs-standard-deviation" options="config" data="data"></nvd3>',
        link: function($scope, element) {
          $scope.config = new StandardDeviationGraphConfiguration($scope.configuration);
          var chartConfig = $scope.config.chart;

          $scope.update = function() {
            var height = $scope.config.autoresizeY ? $scope.config.parentHeight : chartConfig.height;
            $scope.chartWidth = chartConfig.autoresize ? $scope.config.parentWidth : chartConfig.width;

            var numTicks = $scope.data[0].values.length;

            var axisHeight = height -
              chartConfig.margin.top - chartConfig.margin.bottom -
              chartConfig.multibar.margin.top - chartConfig.multibar.margin.bottom;

            $scope.barHeight = (axisHeight / numTicks) * (1 + chartConfig.multibar.groupSpacing);

            $scope.y2 = d3.scale.linear()
              .domain([0, numTicks - 1])
              .range([0, axisHeight - $scope.barHeight]);

            $scope.yAxis
              .height(axisHeight)
              .scale($scope.y2)
              .ticks(numTicks)
              .tickSize($scope.chartWidth)
              .tickPadding(-1 * $scope.chartWidth + chartConfig.margin.right);

            chartConfig.yAxis.tickPadding = (height - chartConfig.margin.bottom - 5) * -1;
          }

          $scope.adjustTicks = function(g) {
            g.selectAll('.tick line')
              .attr('y1', -1 * $scope.barHeight / 2 * (1 - chartConfig.multibar.groupSpacing))
              .attr('y2', -1 * $scope.barHeight / 2 * (1 - chartConfig.multibar.groupSpacing));
          }

          $scope.setupDeviationAxis = function() {
            $scope.svg.insert('g', '.nv-multiBarHorizontalChart')
              .attr('class', 'ark-deviation-axis')
              .attr('transform', 'translate(' + $scope.chartWidth + ',' + (chartConfig.margin.top + $scope.barHeight / 2) + ')')
              .transition().duration(500)
              .call($scope.yAxis)
              .call($scope.adjustTicks);
          };

          
          $scope.bindResizeListener = function() {
            nv.utils.windowResize(function(){
              Utils.resize($scope.config, element, true);
              $scope.update();
            });
          };

          $scope.initialized = false;

          chartConfig.callback = function() {
            $scope.svg = d3.select(element[0]).select('svg');
            if ($scope.data.length > 0) {
              if(!$scope.initialized) {
                Utils.resize($scope.config, element, true);
                $scope.update();
              }

              $scope.setupDeviationAxis();
              $scope.initialized = true;
            }
          };

          $scope.$watch('data', function(newData, oldData) {
            if (newData.length > 0) {
              $scope.yAxis = nv.models.axis()
                .orient('left')
                .showMaxMin(false)
                .tickFormat(function(d, i) {
                  if(i >= $scope.data[0].values.length){ console.log('here'); return ''; }
                  var value = $scope.data[0].values[i].value;
                  return (value > 0 ? '+' : '') +
                    (value < 0 ? '-' : '') +
                    value + '%'; // +
                });
            }
          });

          var autoresizable = $scope.config.chart.autoresize || $scope.config.autoresizeY;
          if(autoresizable && !$scope.attachedResizeListener) {
            if($scope.config.chart.autoresize){
              delete chartConfig.width;
            }
            if($scope.config.autoresizeY) {
              delete chartConfig.height;
            }
              
            $scope.bindResizeListener();  
          }
        }
      };
    }
  ]);

'use strict';

angular.module('ark.graphs.standardDeviation')
  .factory('ark.graphs.standardDeviationConfig', ['ark.graphs.color-service', 'ark.graphs.utils', 'ark.graphs.config-service', 'ark.graphs.d3',
    function(ColorService, Utils, ConfigService, d3) {
      var StandardDeviationGraphConfiguration = function(configuration) {
        this.autoresizeY = false;
        this.chart = {};
        this.chart.type = 'multiBarHorizontalChart';
        this.chart.id = Utils.generateID(this.type);
        this.chart.autoresize = false;

        this.chart.height = 350;
        this.chart.width = 350;

        this.chart.margin = {
          top: 20,
          right: 0,
          bottom: 0,
          left: 0
        };

        this.chart.showControls = false;
        this.chart.showLegend = false;
        this.chart.showValues = false;
        this.chart.duration = 500;

        this.chart.x = function(d) {
          return d.label;
        };
        this.chart.y = function(d) {
          return d.value;
        };

        this.chart.xAxis = {
          showMaxMin: false,
          tickPadding: 0
        };

        this.chart.multibar = {
          fillOpacity: 1,
          groupSpacing: 0.3,
          margin: {
            top: 0,
            bottom: 0
          }
        };
        this.chart.tooltip = {
          enabled: true,
          classes: 'ark-standard-deviation-tooltip',
          gravity: 'n',
          contentGenerator: function(key, data) {
            return '<div>' + key.data.value + '% ' + key.value + '</div>';
          }
        };

        this.chart.thresholds = [-50, 0, 50];
        this.chart.color = ['#EA4F6B', '#203D73', '#4AC764'];

        // Calls update in case the user configured a height and thresholds
        this.update(configuration);

        // Needs to define config in order to use thresholds inside the
        // functions 'this' doesn't refer to the config in the directive
        var chartConfig = this.chart;
        this.chart.forceY = [chartConfig.thresholds[0] * 2, chartConfig.thresholds[2] * 2];
        this.chart.yAxis = {
          showMaxMin: true,
          tickValues: this.chart.thresholds,
          tickPadding: (this.chart.height - this.chart.margin.bottom - 5) * -1,
          tickFormat: function(d) {
            var label;
            switch (d) {
              case chartConfig.thresholds[0] * 2:
                label = 'Name';
                break;
              case 0:
                label = 'Goal';
                break;
              case chartConfig.thresholds[2] * 2:
                label = 'Deviation';
                break;
              default:
                label = '';
            }
            return label;
          }
        };

        this.chart.barColor = function(d) {
          if (d.value < chartConfig.thresholds[0]) {
            return chartConfig.color[0];
          } else if (d.value > chartConfig.thresholds[2]) {
            return chartConfig.color[2];
          } else {
            return chartConfig.color[1];
          }
        };

        this.update(configuration);
      };

      StandardDeviationGraphConfiguration.prototype.update = function(configuration) {
        Utils.mergeRecursive(this, configuration);
      };

      return StandardDeviationGraphConfiguration;
    }
  ]);

'use strict';

angular.module('ark.graphs.verticalMultiBar')
  .directive('arkVerticalMultiBar', ['ark.graphs.verticalMultiBarConfig', 'ark.graphs.d3', 'ark.graphs.utils', '$timeout',
    function(VerticalMultiBarGraphConfiguration, d3, Utils, $timeout) {
      return {
        restrict: 'E',
        scope: {
          configuration: '=',
          data: '='
        },
        link: function($scope, element) {

          $scope.config = new VerticalMultiBarGraphConfiguration($scope.configuration);
          if ($scope.config.autoresizeY) {
            delete $scope.config.chart.height;

            //Check for resizing
            $scope.$watch(function() {
              var e = angular.element(element[0].parentElement);
              return Utils.getWidthAndHeight(e[0]);
            }, function(newVal, oldVal) {
              if (newVal.height !== oldVal.height) {
                $timeout(function(){
                  Utils.resize($scope.config, element, true);
                });
              }
            }, true);

            Utils.resize($scope.config, element, true);
          } else {
            // If the legend is to be shown, the chart's margin-top property is
            // overwritten by the legend's height. In order to move the legend
            // without changing the chart margin, the legend's top and bottom
            // margins must balance to 0.
            if ($scope.config.chart.legend.margin.top === undefined) {
              $scope.config.chart.legend.margin.top = $scope.config.chart.height / 2 - 8;
            }
            if ($scope.config.chart.legend.margin.bottom === undefined) {
              $scope.config.chart.legend.margin.bottom = -1 * ($scope.config.chart.height / 2 - 8) + 16;
            }
          }

          $scope.extendYAxis = function() {
            d3.select(element[0]).select('.nv-y')
              .selectAll('.tick line')
              .attr('x1', -1 * $scope.config.chart.margin.left);
          };

          $scope.config.chart.callback = function(chart) {

            d3.select(element[0]).selectAll('.nv-legend')
              .selectAll('.nv-series circle')
              .attr('cx', '-6px');

            $scope.extendYAxis();
            chart.yAxis.dispatch.on('renderEnd.lines', function() {
              $scope.extendYAxis();
            });

          };

        },
        template: '<nvd3 class="ark-graphs-vertical-multi-bar" options="config" data="data"></nvd3>'
      };
    }
  ]);

'use strict';

angular.module('ark.graphs.verticalMultiBar')
  .factory('ark.graphs.verticalMultiBarConfig', ['ark.graphs.utils', 'ark.graphs.config-service', 'ark.graphs.d3',
    function(Utils, ConfigService, d3) {
      var VerticalMultiBarGraphConfiguration = function(configuration) {
        this.autoresizeY = false;

        this.chart = {};
        this.chart.type = 'multiBarChart';
        this.chart.id = Utils.generateID(this.chart.type);

        this.chart.height = 580;

        this.chart.showControls = true;
        this.chart.showValues = false;
        this.chart.reduceXTicks = true;
        this.chart.duration = 500;

        this.chart.x = function(d) {
          return d.label;
        };
        this.chart.y = function(d) {
          return d.value;
        };

        this.chart.color = ['#203B73', '#07A8B4', '#844ED2'];

        this.chart.xAxis = {
          showMaxMin: false,
          tickPadding: 8
        };
        this.chart.yAxis = {
          showMaxMin: false,
          tickFormat: function(d) {
            return d3.format(',.1')(d);
          }
        };

        this.chart.margin = {
          top: 36,
          left: 90,
          bottom: 60,
          right: 0
        };

        this.chart.stacked = true;

        this.chart.tooltip = {
          classes: 'ark-vertical-multi-bar-tooltip',
          gravity: 'n',
          contentGenerator: function(key) {
            return '<div>' + d3.format(',0f')(key.data.value) + ' ' + key.data.key + '</div>';
          }
        };

        this.chart.legend = {
          rightAlign: false,
          height: 20,
          margin: {},
          padding: 60
        };

        this.chart.controls = {
          rightAlign: false,
          height: 20,
          margin: {
            top: 2,
            left: 6,
            bottom: 16,
            right: -180
          },
          padding: 60
        };

        if (this.chart.showControls) {
          // The legend is positioned based on the width of the controls, if
          // they are shown. Controls have a fixed width of 180
          this.chart.legend.margin.left = -170;
        }

        this.update(configuration);

        this.chart.yAxis.tickPadding = this.chart.margin.left;

        // Allows the user to override the tickPadding altogether
        this.update(configuration);
      };

      VerticalMultiBarGraphConfiguration.prototype.update = function(configuration) {
        Utils.mergeRecursive(this, configuration);
        if(this.autoresizeY) {
          this.chart.legend.margin.top = this.parentHeight / 2 - 8;
          this.chart.legend.margin.bottom = -1 * (this.parentHeight / 2 - 8) + 16;
        }
      };

      return VerticalMultiBarGraphConfiguration;
    }
  ]);
