github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/pubnot/chartist/chartist.js (about)

     1  (function (root, factory) {
     2    if (typeof define === 'function' && define.amd) {
     3      // AMD. Register as an anonymous module unless amdModuleId is set
     4      define('Chartist', [], function () {
     5        return (root['Chartist'] = factory());
     6      });
     7    } else if (typeof module === 'object' && module.exports) {
     8      // Node. Does not work with strict CommonJS, but
     9      // only CommonJS-like environments that support module.exports,
    10      // like Node.
    11      module.exports = factory();
    12    } else {
    13      root['Chartist'] = factory();
    14    }
    15  }(this, function () {
    16  
    17  /* Chartist.js 0.11.0
    18   * Copyright © 2017 Gion Kunz
    19   * Free to use under either the WTFPL license or the MIT license.
    20   * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-WTFPL
    21   * https://raw.githubusercontent.com/gionkunz/chartist-js/master/LICENSE-MIT
    22   */
    23  /**
    24   * The core module of Chartist that is mainly providing static functions and higher level functions for chart modules.
    25   *
    26   * @module Chartist.Core
    27   */
    28  var Chartist = {
    29    version: '0.11.0'
    30  };
    31  
    32  (function (window, document, Chartist) {
    33    'use strict';
    34  
    35    /**
    36     * This object contains all namespaces used within Chartist.
    37     *
    38     * @memberof Chartist.Core
    39     * @type {{svg: string, xmlns: string, xhtml: string, xlink: string, ct: string}}
    40     */
    41    Chartist.namespaces = {
    42      svg: 'http://www.w3.org/2000/svg',
    43      xmlns: 'http://www.w3.org/2000/xmlns/',
    44      xhtml: 'http://www.w3.org/1999/xhtml',
    45      xlink: 'http://www.w3.org/1999/xlink',
    46      ct: 'http://gionkunz.github.com/chartist-js/ct'
    47    };
    48  
    49    /**
    50     * Helps to simplify functional style code
    51     *
    52     * @memberof Chartist.Core
    53     * @param {*} n This exact value will be returned by the noop function
    54     * @return {*} The same value that was provided to the n parameter
    55     */
    56    Chartist.noop = function (n) {
    57      return n;
    58    };
    59  
    60    /**
    61     * Generates a-z from a number 0 to 26
    62     *
    63     * @memberof Chartist.Core
    64     * @param {Number} n A number from 0 to 26 that will result in a letter a-z
    65     * @return {String} A character from a-z based on the input number n
    66     */
    67    Chartist.alphaNumerate = function (n) {
    68      // Limit to a-z
    69      return String.fromCharCode(97 + n % 26);
    70    };
    71  
    72    /**
    73     * Simple recursive object extend
    74     *
    75     * @memberof Chartist.Core
    76     * @param {Object} target Target object where the source will be merged into
    77     * @param {Object...} sources This object (objects) will be merged into target and then target is returned
    78     * @return {Object} An object that has the same reference as target but is extended and merged with the properties of source
    79     */
    80    Chartist.extend = function (target) {
    81      var i, source, sourceProp;
    82      target = target || {};
    83  
    84      for (i = 1; i < arguments.length; i++) {
    85        source = arguments[i];
    86        for (var prop in source) {
    87          sourceProp = source[prop];
    88          if (typeof sourceProp === 'object' && sourceProp !== null && !(sourceProp instanceof Array)) {
    89            target[prop] = Chartist.extend(target[prop], sourceProp);
    90          } else {
    91            target[prop] = sourceProp;
    92          }
    93        }
    94      }
    95  
    96      return target;
    97    };
    98  
    99    /**
   100     * Replaces all occurrences of subStr in str with newSubStr and returns a new string.
   101     *
   102     * @memberof Chartist.Core
   103     * @param {String} str
   104     * @param {String} subStr
   105     * @param {String} newSubStr
   106     * @return {String}
   107     */
   108    Chartist.replaceAll = function(str, subStr, newSubStr) {
   109      return str.replace(new RegExp(subStr, 'g'), newSubStr);
   110    };
   111  
   112    /**
   113     * Converts a number to a string with a unit. If a string is passed then this will be returned unmodified.
   114     *
   115     * @memberof Chartist.Core
   116     * @param {Number} value
   117     * @param {String} unit
   118     * @return {String} Returns the passed number value with unit.
   119     */
   120    Chartist.ensureUnit = function(value, unit) {
   121      if(typeof value === 'number') {
   122        value = value + unit;
   123      }
   124  
   125      return value;
   126    };
   127  
   128    /**
   129     * Converts a number or string to a quantity object.
   130     *
   131     * @memberof Chartist.Core
   132     * @param {String|Number} input
   133     * @return {Object} Returns an object containing the value as number and the unit as string.
   134     */
   135    Chartist.quantity = function(input) {
   136      if (typeof input === 'string') {
   137        var match = (/^(\d+)\s*(.*)$/g).exec(input);
   138        return {
   139          value : +match[1],
   140          unit: match[2] || undefined
   141        };
   142      }
   143      return { value: input };
   144    };
   145  
   146    /**
   147     * This is a wrapper around document.querySelector that will return the query if it's already of type Node
   148     *
   149     * @memberof Chartist.Core
   150     * @param {String|Node} query The query to use for selecting a Node or a DOM node that will be returned directly
   151     * @return {Node}
   152     */
   153    Chartist.querySelector = function(query) {
   154      return query instanceof Node ? query : document.querySelector(query);
   155    };
   156  
   157    /**
   158     * Functional style helper to produce array with given length initialized with undefined values
   159     *
   160     * @memberof Chartist.Core
   161     * @param length
   162     * @return {Array}
   163     */
   164    Chartist.times = function(length) {
   165      return Array.apply(null, new Array(length));
   166    };
   167  
   168    /**
   169     * Sum helper to be used in reduce functions
   170     *
   171     * @memberof Chartist.Core
   172     * @param previous
   173     * @param current
   174     * @return {*}
   175     */
   176    Chartist.sum = function(previous, current) {
   177      return previous + (current ? current : 0);
   178    };
   179  
   180    /**
   181     * Multiply helper to be used in `Array.map` for multiplying each value of an array with a factor.
   182     *
   183     * @memberof Chartist.Core
   184     * @param {Number} factor
   185     * @returns {Function} Function that can be used in `Array.map` to multiply each value in an array
   186     */
   187    Chartist.mapMultiply = function(factor) {
   188      return function(num) {
   189        return num * factor;
   190      };
   191    };
   192  
   193    /**
   194     * Add helper to be used in `Array.map` for adding a addend to each value of an array.
   195     *
   196     * @memberof Chartist.Core
   197     * @param {Number} addend
   198     * @returns {Function} Function that can be used in `Array.map` to add a addend to each value in an array
   199     */
   200    Chartist.mapAdd = function(addend) {
   201      return function(num) {
   202        return num + addend;
   203      };
   204    };
   205  
   206    /**
   207     * Map for multi dimensional arrays where their nested arrays will be mapped in serial. The output array will have the length of the largest nested array. The callback function is called with variable arguments where each argument is the nested array value (or undefined if there are no more values).
   208     *
   209     * @memberof Chartist.Core
   210     * @param arr
   211     * @param cb
   212     * @return {Array}
   213     */
   214    Chartist.serialMap = function(arr, cb) {
   215      var result = [],
   216          length = Math.max.apply(null, arr.map(function(e) {
   217            return e.length;
   218          }));
   219  
   220      Chartist.times(length).forEach(function(e, index) {
   221        var args = arr.map(function(e) {
   222          return e[index];
   223        });
   224  
   225        result[index] = cb.apply(null, args);
   226      });
   227  
   228      return result;
   229    };
   230  
   231    /**
   232     * This helper function can be used to round values with certain precision level after decimal. This is used to prevent rounding errors near float point precision limit.
   233     *
   234     * @memberof Chartist.Core
   235     * @param {Number} value The value that should be rounded with precision
   236     * @param {Number} [digits] The number of digits after decimal used to do the rounding
   237     * @returns {number} Rounded value
   238     */
   239    Chartist.roundWithPrecision = function(value, digits) {
   240      var precision = Math.pow(10, digits || Chartist.precision);
   241      return Math.round(value * precision) / precision;
   242    };
   243  
   244    /**
   245     * Precision level used internally in Chartist for rounding. If you require more decimal places you can increase this number.
   246     *
   247     * @memberof Chartist.Core
   248     * @type {number}
   249     */
   250    Chartist.precision = 8;
   251  
   252    /**
   253     * A map with characters to escape for strings to be safely used as attribute values.
   254     *
   255     * @memberof Chartist.Core
   256     * @type {Object}
   257     */
   258    Chartist.escapingMap = {
   259      '&': '&amp;',
   260      '<': '&lt;',
   261      '>': '&gt;',
   262      '"': '&quot;',
   263      '\'': '&#039;'
   264    };
   265  
   266    /**
   267     * This function serializes arbitrary data to a string. In case of data that can't be easily converted to a string, this function will create a wrapper object and serialize the data using JSON.stringify. The outcoming string will always be escaped using Chartist.escapingMap.
   268     * If called with null or undefined the function will return immediately with null or undefined.
   269     *
   270     * @memberof Chartist.Core
   271     * @param {Number|String|Object} data
   272     * @return {String}
   273     */
   274    Chartist.serialize = function(data) {
   275      if(data === null || data === undefined) {
   276        return data;
   277      } else if(typeof data === 'number') {
   278        data = ''+data;
   279      } else if(typeof data === 'object') {
   280        data = JSON.stringify({data: data});
   281      }
   282  
   283      return Object.keys(Chartist.escapingMap).reduce(function(result, key) {
   284        return Chartist.replaceAll(result, key, Chartist.escapingMap[key]);
   285      }, data);
   286    };
   287  
   288    /**
   289     * This function de-serializes a string previously serialized with Chartist.serialize. The string will always be unescaped using Chartist.escapingMap before it's returned. Based on the input value the return type can be Number, String or Object. JSON.parse is used with try / catch to see if the unescaped string can be parsed into an Object and this Object will be returned on success.
   290     *
   291     * @memberof Chartist.Core
   292     * @param {String} data
   293     * @return {String|Number|Object}
   294     */
   295    Chartist.deserialize = function(data) {
   296      if(typeof data !== 'string') {
   297        return data;
   298      }
   299  
   300      data = Object.keys(Chartist.escapingMap).reduce(function(result, key) {
   301        return Chartist.replaceAll(result, Chartist.escapingMap[key], key);
   302      }, data);
   303  
   304      try {
   305        data = JSON.parse(data);
   306        data = data.data !== undefined ? data.data : data;
   307      } catch(e) {}
   308  
   309      return data;
   310    };
   311  
   312    /**
   313     * Create or reinitialize the SVG element for the chart
   314     *
   315     * @memberof Chartist.Core
   316     * @param {Node} container The containing DOM Node object that will be used to plant the SVG element
   317     * @param {String} width Set the width of the SVG element. Default is 100%
   318     * @param {String} height Set the height of the SVG element. Default is 100%
   319     * @param {String} className Specify a class to be added to the SVG element
   320     * @return {Object} The created/reinitialized SVG element
   321     */
   322    Chartist.createSvg = function (container, width, height, className) {
   323      var svg;
   324  
   325      width = width || '100%';
   326      height = height || '100%';
   327  
   328      // Check if there is a previous SVG element in the container that contains the Chartist XML namespace and remove it
   329      // Since the DOM API does not support namespaces we need to manually search the returned list http://www.w3.org/TR/selectors-api/
   330      Array.prototype.slice.call(container.querySelectorAll('svg')).filter(function filterChartistSvgObjects(svg) {
   331        return svg.getAttributeNS(Chartist.namespaces.xmlns, 'ct');
   332      }).forEach(function removePreviousElement(svg) {
   333        container.removeChild(svg);
   334      });
   335  
   336      // Create svg object with width and height or use 100% as default
   337      svg = new Chartist.Svg('svg').attr({
   338        width: width,
   339        height: height
   340      }).addClass(className);
   341  
   342      svg._node.style.width = width;
   343      svg._node.style.height = height;
   344  
   345      // Add the DOM node to our container
   346      container.appendChild(svg._node);
   347  
   348      return svg;
   349    };
   350  
   351    /**
   352     * Ensures that the data object passed as second argument to the charts is present and correctly initialized.
   353     *
   354     * @param  {Object} data The data object that is passed as second argument to the charts
   355     * @return {Object} The normalized data object
   356     */
   357    Chartist.normalizeData = function(data, reverse, multi) {
   358      var labelCount;
   359      var output = {
   360        raw: data,
   361        normalized: {}
   362      };
   363  
   364      // Check if we should generate some labels based on existing series data
   365      output.normalized.series = Chartist.getDataArray({
   366        series: data.series || []
   367      }, reverse, multi);
   368  
   369      // If all elements of the normalized data array are arrays we're dealing with
   370      // multi series data and we need to find the largest series if they are un-even
   371      if (output.normalized.series.every(function(value) {
   372          return value instanceof Array;
   373        })) {
   374        // Getting the series with the the most elements
   375        labelCount = Math.max.apply(null, output.normalized.series.map(function(series) {
   376          return series.length;
   377        }));
   378      } else {
   379        // We're dealing with Pie data so we just take the normalized array length
   380        labelCount = output.normalized.series.length;
   381      }
   382  
   383      output.normalized.labels = (data.labels || []).slice();
   384      // Padding the labels to labelCount with empty strings
   385      Array.prototype.push.apply(
   386        output.normalized.labels,
   387        Chartist.times(Math.max(0, labelCount - output.normalized.labels.length)).map(function() {
   388          return '';
   389        })
   390      );
   391  
   392      if(reverse) {
   393        Chartist.reverseData(output.normalized);
   394      }
   395  
   396      return output;
   397    };
   398  
   399    /**
   400     * This function safely checks if an objects has an owned property.
   401     *
   402     * @param {Object} object The object where to check for a property
   403     * @param {string} property The property name
   404     * @returns {boolean} Returns true if the object owns the specified property
   405     */
   406    Chartist.safeHasProperty = function(object, property) {
   407      return object !== null &&
   408        typeof object === 'object' &&
   409        object.hasOwnProperty(property);
   410    };
   411  
   412    /**
   413     * Checks if a value is considered a hole in the data series.
   414     *
   415     * @param {*} value
   416     * @returns {boolean} True if the value is considered a data hole
   417     */
   418    Chartist.isDataHoleValue = function(value) {
   419      return value === null ||
   420        value === undefined ||
   421        (typeof value === 'number' && isNaN(value));
   422    };
   423  
   424    /**
   425     * Reverses the series, labels and series data arrays.
   426     *
   427     * @memberof Chartist.Core
   428     * @param data
   429     */
   430    Chartist.reverseData = function(data) {
   431      data.labels.reverse();
   432      data.series.reverse();
   433      for (var i = 0; i < data.series.length; i++) {
   434        if(typeof(data.series[i]) === 'object' && data.series[i].data !== undefined) {
   435          data.series[i].data.reverse();
   436        } else if(data.series[i] instanceof Array) {
   437          data.series[i].reverse();
   438        }
   439      }
   440    };
   441  
   442    /**
   443     * Convert data series into plain array
   444     *
   445     * @memberof Chartist.Core
   446     * @param {Object} data The series object that contains the data to be visualized in the chart
   447     * @param {Boolean} [reverse] If true the whole data is reversed by the getDataArray call. This will modify the data object passed as first parameter. The labels as well as the series order is reversed. The whole series data arrays are reversed too.
   448     * @param {Boolean} [multi] Create a multi dimensional array from a series data array where a value object with `x` and `y` values will be created.
   449     * @return {Array} A plain array that contains the data to be visualized in the chart
   450     */
   451    Chartist.getDataArray = function(data, reverse, multi) {
   452      // Recursively walks through nested arrays and convert string values to numbers and objects with value properties
   453      // to values. Check the tests in data core -> data normalization for a detailed specification of expected values
   454      function recursiveConvert(value) {
   455        if(Chartist.safeHasProperty(value, 'value')) {
   456          // We are dealing with value object notation so we need to recurse on value property
   457          return recursiveConvert(value.value);
   458        } else if(Chartist.safeHasProperty(value, 'data')) {
   459          // We are dealing with series object notation so we need to recurse on data property
   460          return recursiveConvert(value.data);
   461        } else if(value instanceof Array) {
   462          // Data is of type array so we need to recurse on the series
   463          return value.map(recursiveConvert);
   464        } else if(Chartist.isDataHoleValue(value)) {
   465          // We're dealing with a hole in the data and therefore need to return undefined
   466          // We're also returning undefined for multi value output
   467          return undefined;
   468        } else {
   469          // We need to prepare multi value output (x and y data)
   470          if(multi) {
   471            var multiValue = {};
   472  
   473            // Single series value arrays are assumed to specify the Y-Axis value
   474            // For example: [1, 2] => [{x: undefined, y: 1}, {x: undefined, y: 2}]
   475            // If multi is a string then it's assumed that it specified which dimension should be filled as default
   476            if(typeof multi === 'string') {
   477              multiValue[multi] = Chartist.getNumberOrUndefined(value);
   478            } else {
   479              multiValue.y = Chartist.getNumberOrUndefined(value);
   480            }
   481  
   482            multiValue.x = value.hasOwnProperty('x') ? Chartist.getNumberOrUndefined(value.x) : multiValue.x;
   483            multiValue.y = value.hasOwnProperty('y') ? Chartist.getNumberOrUndefined(value.y) : multiValue.y;
   484  
   485            return multiValue;
   486  
   487          } else {
   488            // We can return simple data
   489            return Chartist.getNumberOrUndefined(value);
   490          }
   491        }
   492      }
   493  
   494      return data.series.map(recursiveConvert);
   495    };
   496  
   497    /**
   498     * Converts a number into a padding object.
   499     *
   500     * @memberof Chartist.Core
   501     * @param {Object|Number} padding
   502     * @param {Number} [fallback] This value is used to fill missing values if a incomplete padding object was passed
   503     * @returns {Object} Returns a padding object containing top, right, bottom, left properties filled with the padding number passed in as argument. If the argument is something else than a number (presumably already a correct padding object) then this argument is directly returned.
   504     */
   505    Chartist.normalizePadding = function(padding, fallback) {
   506      fallback = fallback || 0;
   507  
   508      return typeof padding === 'number' ? {
   509        top: padding,
   510        right: padding,
   511        bottom: padding,
   512        left: padding
   513      } : {
   514        top: typeof padding.top === 'number' ? padding.top : fallback,
   515        right: typeof padding.right === 'number' ? padding.right : fallback,
   516        bottom: typeof padding.bottom === 'number' ? padding.bottom : fallback,
   517        left: typeof padding.left === 'number' ? padding.left : fallback
   518      };
   519    };
   520  
   521    Chartist.getMetaData = function(series, index) {
   522      var value = series.data ? series.data[index] : series[index];
   523      return value ? value.meta : undefined;
   524    };
   525  
   526    /**
   527     * Calculate the order of magnitude for the chart scale
   528     *
   529     * @memberof Chartist.Core
   530     * @param {Number} value The value Range of the chart
   531     * @return {Number} The order of magnitude
   532     */
   533    Chartist.orderOfMagnitude = function (value) {
   534      return Math.floor(Math.log(Math.abs(value)) / Math.LN10);
   535    };
   536  
   537    /**
   538     * Project a data length into screen coordinates (pixels)
   539     *
   540     * @memberof Chartist.Core
   541     * @param {Object} axisLength The svg element for the chart
   542     * @param {Number} length Single data value from a series array
   543     * @param {Object} bounds All the values to set the bounds of the chart
   544     * @return {Number} The projected data length in pixels
   545     */
   546    Chartist.projectLength = function (axisLength, length, bounds) {
   547      return length / bounds.range * axisLength;
   548    };
   549  
   550    /**
   551     * Get the height of the area in the chart for the data series
   552     *
   553     * @memberof Chartist.Core
   554     * @param {Object} svg The svg element for the chart
   555     * @param {Object} options The Object that contains all the optional values for the chart
   556     * @return {Number} The height of the area in the chart for the data series
   557     */
   558    Chartist.getAvailableHeight = function (svg, options) {
   559      return Math.max((Chartist.quantity(options.height).value || svg.height()) - (options.chartPadding.top +  options.chartPadding.bottom) - options.axisX.offset, 0);
   560    };
   561  
   562    /**
   563     * Get highest and lowest value of data array. This Array contains the data that will be visualized in the chart.
   564     *
   565     * @memberof Chartist.Core
   566     * @param {Array} data The array that contains the data to be visualized in the chart
   567     * @param {Object} options The Object that contains the chart options
   568     * @param {String} dimension Axis dimension 'x' or 'y' used to access the correct value and high / low configuration
   569     * @return {Object} An object that contains the highest and lowest value that will be visualized on the chart.
   570     */
   571    Chartist.getHighLow = function (data, options, dimension) {
   572      // TODO: Remove workaround for deprecated global high / low config. Axis high / low configuration is preferred
   573      options = Chartist.extend({}, options, dimension ? options['axis' + dimension.toUpperCase()] : {});
   574  
   575      var highLow = {
   576          high: options.high === undefined ? -Number.MAX_VALUE : +options.high,
   577          low: options.low === undefined ? Number.MAX_VALUE : +options.low
   578        };
   579      var findHigh = options.high === undefined;
   580      var findLow = options.low === undefined;
   581  
   582      // Function to recursively walk through arrays and find highest and lowest number
   583      function recursiveHighLow(data) {
   584        if(data === undefined) {
   585          return undefined;
   586        } else if(data instanceof Array) {
   587          for (var i = 0; i < data.length; i++) {
   588            recursiveHighLow(data[i]);
   589          }
   590        } else {
   591          var value = dimension ? +data[dimension] : +data;
   592  
   593          if (findHigh && value > highLow.high) {
   594            highLow.high = value;
   595          }
   596  
   597          if (findLow && value < highLow.low) {
   598            highLow.low = value;
   599          }
   600        }
   601      }
   602  
   603      // Start to find highest and lowest number recursively
   604      if(findHigh || findLow) {
   605        recursiveHighLow(data);
   606      }
   607  
   608      // Overrides of high / low based on reference value, it will make sure that the invisible reference value is
   609      // used to generate the chart. This is useful when the chart always needs to contain the position of the
   610      // invisible reference value in the view i.e. for bipolar scales.
   611      if (options.referenceValue || options.referenceValue === 0) {
   612        highLow.high = Math.max(options.referenceValue, highLow.high);
   613        highLow.low = Math.min(options.referenceValue, highLow.low);
   614      }
   615  
   616      // If high and low are the same because of misconfiguration or flat data (only the same value) we need
   617      // to set the high or low to 0 depending on the polarity
   618      if (highLow.high <= highLow.low) {
   619        // If both values are 0 we set high to 1
   620        if (highLow.low === 0) {
   621          highLow.high = 1;
   622        } else if (highLow.low < 0) {
   623          // If we have the same negative value for the bounds we set bounds.high to 0
   624          highLow.high = 0;
   625        } else if (highLow.high > 0) {
   626          // If we have the same positive value for the bounds we set bounds.low to 0
   627          highLow.low = 0;
   628        } else {
   629          // If data array was empty, values are Number.MAX_VALUE and -Number.MAX_VALUE. Set bounds to prevent errors
   630          highLow.high = 1;
   631          highLow.low = 0;
   632        }
   633      }
   634  
   635      return highLow;
   636    };
   637  
   638    /**
   639     * Checks if a value can be safely coerced to a number. This includes all values except null which result in finite numbers when coerced. This excludes NaN, since it's not finite.
   640     *
   641     * @memberof Chartist.Core
   642     * @param value
   643     * @returns {Boolean}
   644     */
   645    Chartist.isNumeric = function(value) {
   646      return value === null ? false : isFinite(value);
   647    };
   648  
   649    /**
   650     * Returns true on all falsey values except the numeric value 0.
   651     *
   652     * @memberof Chartist.Core
   653     * @param value
   654     * @returns {boolean}
   655     */
   656    Chartist.isFalseyButZero = function(value) {
   657      return !value && value !== 0;
   658    };
   659  
   660    /**
   661     * Returns a number if the passed parameter is a valid number or the function will return undefined. On all other values than a valid number, this function will return undefined.
   662     *
   663     * @memberof Chartist.Core
   664     * @param value
   665     * @returns {*}
   666     */
   667    Chartist.getNumberOrUndefined = function(value) {
   668      return Chartist.isNumeric(value) ? +value : undefined;
   669    };
   670  
   671    /**
   672     * Checks if provided value object is multi value (contains x or y properties)
   673     *
   674     * @memberof Chartist.Core
   675     * @param value
   676     */
   677    Chartist.isMultiValue = function(value) {
   678      return typeof value === 'object' && ('x' in value || 'y' in value);
   679    };
   680  
   681    /**
   682     * Gets a value from a dimension `value.x` or `value.y` while returning value directly if it's a valid numeric value. If the value is not numeric and it's falsey this function will return `defaultValue`.
   683     *
   684     * @memberof Chartist.Core
   685     * @param value
   686     * @param dimension
   687     * @param defaultValue
   688     * @returns {*}
   689     */
   690    Chartist.getMultiValue = function(value, dimension) {
   691      if(Chartist.isMultiValue(value)) {
   692        return Chartist.getNumberOrUndefined(value[dimension || 'y']);
   693      } else {
   694        return Chartist.getNumberOrUndefined(value);
   695      }
   696    };
   697  
   698    /**
   699     * Pollard Rho Algorithm to find smallest factor of an integer value. There are more efficient algorithms for factorization, but this one is quite efficient and not so complex.
   700     *
   701     * @memberof Chartist.Core
   702     * @param {Number} num An integer number where the smallest factor should be searched for
   703     * @returns {Number} The smallest integer factor of the parameter num.
   704     */
   705    Chartist.rho = function(num) {
   706      if(num === 1) {
   707        return num;
   708      }
   709  
   710      function gcd(p, q) {
   711        if (p % q === 0) {
   712          return q;
   713        } else {
   714          return gcd(q, p % q);
   715        }
   716      }
   717  
   718      function f(x) {
   719        return x * x + 1;
   720      }
   721  
   722      var x1 = 2, x2 = 2, divisor;
   723      if (num % 2 === 0) {
   724        return 2;
   725      }
   726  
   727      do {
   728        x1 = f(x1) % num;
   729        x2 = f(f(x2)) % num;
   730        divisor = gcd(Math.abs(x1 - x2), num);
   731      } while (divisor === 1);
   732  
   733      return divisor;
   734    };
   735  
   736    /**
   737     * Calculate and retrieve all the bounds for the chart and return them in one array
   738     *
   739     * @memberof Chartist.Core
   740     * @param {Number} axisLength The length of the Axis used for
   741     * @param {Object} highLow An object containing a high and low property indicating the value range of the chart.
   742     * @param {Number} scaleMinSpace The minimum projected length a step should result in
   743     * @param {Boolean} onlyInteger
   744     * @return {Object} All the values to set the bounds of the chart
   745     */
   746    Chartist.getBounds = function (axisLength, highLow, scaleMinSpace, onlyInteger) {
   747      var i,
   748        optimizationCounter = 0,
   749        newMin,
   750        newMax,
   751        bounds = {
   752          high: highLow.high,
   753          low: highLow.low
   754        };
   755  
   756      bounds.valueRange = bounds.high - bounds.low;
   757      bounds.oom = Chartist.orderOfMagnitude(bounds.valueRange);
   758      bounds.step = Math.pow(10, bounds.oom);
   759      bounds.min = Math.floor(bounds.low / bounds.step) * bounds.step;
   760      bounds.max = Math.ceil(bounds.high / bounds.step) * bounds.step;
   761      bounds.range = bounds.max - bounds.min;
   762      bounds.numberOfSteps = Math.round(bounds.range / bounds.step);
   763  
   764      // Optimize scale step by checking if subdivision is possible based on horizontalGridMinSpace
   765      // If we are already below the scaleMinSpace value we will scale up
   766      var length = Chartist.projectLength(axisLength, bounds.step, bounds);
   767      var scaleUp = length < scaleMinSpace;
   768      var smallestFactor = onlyInteger ? Chartist.rho(bounds.range) : 0;
   769  
   770      // First check if we should only use integer steps and if step 1 is still larger than scaleMinSpace so we can use 1
   771      if(onlyInteger && Chartist.projectLength(axisLength, 1, bounds) >= scaleMinSpace) {
   772        bounds.step = 1;
   773      } else if(onlyInteger && smallestFactor < bounds.step && Chartist.projectLength(axisLength, smallestFactor, bounds) >= scaleMinSpace) {
   774        // If step 1 was too small, we can try the smallest factor of range
   775        // If the smallest factor is smaller than the current bounds.step and the projected length of smallest factor
   776        // is larger than the scaleMinSpace we should go for it.
   777        bounds.step = smallestFactor;
   778      } else {
   779        // Trying to divide or multiply by 2 and find the best step value
   780        while (true) {
   781          if (scaleUp && Chartist.projectLength(axisLength, bounds.step, bounds) <= scaleMinSpace) {
   782            bounds.step *= 2;
   783          } else if (!scaleUp && Chartist.projectLength(axisLength, bounds.step / 2, bounds) >= scaleMinSpace) {
   784            bounds.step /= 2;
   785            if(onlyInteger && bounds.step % 1 !== 0) {
   786              bounds.step *= 2;
   787              break;
   788            }
   789          } else {
   790            break;
   791          }
   792  
   793          if(optimizationCounter++ > 1000) {
   794            throw new Error('Exceeded maximum number of iterations while optimizing scale step!');
   795          }
   796        }
   797      }
   798  
   799      var EPSILON = 2.221E-16;
   800      bounds.step = Math.max(bounds.step, EPSILON);
   801      function safeIncrement(value, increment) {
   802        // If increment is too small use *= (1+EPSILON) as a simple nextafter
   803        if (value === (value += increment)) {
   804        	value *= (1 + (increment > 0 ? EPSILON : -EPSILON));
   805        }
   806        return value;
   807      }
   808  
   809      // Narrow min and max based on new step
   810      newMin = bounds.min;
   811      newMax = bounds.max;
   812      while (newMin + bounds.step <= bounds.low) {
   813      	newMin = safeIncrement(newMin, bounds.step);
   814      }
   815      while (newMax - bounds.step >= bounds.high) {
   816      	newMax = safeIncrement(newMax, -bounds.step);
   817      }
   818      bounds.min = newMin;
   819      bounds.max = newMax;
   820      bounds.range = bounds.max - bounds.min;
   821  
   822      var values = [];
   823      for (i = bounds.min; i <= bounds.max; i = safeIncrement(i, bounds.step)) {
   824        var value = Chartist.roundWithPrecision(i);
   825        if (value !== values[values.length - 1]) {
   826          values.push(value);
   827        }
   828      }
   829      bounds.values = values;
   830      return bounds;
   831    };
   832  
   833    /**
   834     * Calculate cartesian coordinates of polar coordinates
   835     *
   836     * @memberof Chartist.Core
   837     * @param {Number} centerX X-axis coordinates of center point of circle segment
   838     * @param {Number} centerY X-axis coordinates of center point of circle segment
   839     * @param {Number} radius Radius of circle segment
   840     * @param {Number} angleInDegrees Angle of circle segment in degrees
   841     * @return {{x:Number, y:Number}} Coordinates of point on circumference
   842     */
   843    Chartist.polarToCartesian = function (centerX, centerY, radius, angleInDegrees) {
   844      var angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;
   845  
   846      return {
   847        x: centerX + (radius * Math.cos(angleInRadians)),
   848        y: centerY + (radius * Math.sin(angleInRadians))
   849      };
   850    };
   851  
   852    /**
   853     * Initialize chart drawing rectangle (area where chart is drawn) x1,y1 = bottom left / x2,y2 = top right
   854     *
   855     * @memberof Chartist.Core
   856     * @param {Object} svg The svg element for the chart
   857     * @param {Object} options The Object that contains all the optional values for the chart
   858     * @param {Number} [fallbackPadding] The fallback padding if partial padding objects are used
   859     * @return {Object} The chart rectangles coordinates inside the svg element plus the rectangles measurements
   860     */
   861    Chartist.createChartRect = function (svg, options, fallbackPadding) {
   862      var hasAxis = !!(options.axisX || options.axisY);
   863      var yAxisOffset = hasAxis ? options.axisY.offset : 0;
   864      var xAxisOffset = hasAxis ? options.axisX.offset : 0;
   865      // If width or height results in invalid value (including 0) we fallback to the unitless settings or even 0
   866      var width = svg.width() || Chartist.quantity(options.width).value || 0;
   867      var height = svg.height() || Chartist.quantity(options.height).value || 0;
   868      var normalizedPadding = Chartist.normalizePadding(options.chartPadding, fallbackPadding);
   869  
   870      // If settings were to small to cope with offset (legacy) and padding, we'll adjust
   871      width = Math.max(width, yAxisOffset + normalizedPadding.left + normalizedPadding.right);
   872      height = Math.max(height, xAxisOffset + normalizedPadding.top + normalizedPadding.bottom);
   873  
   874      var chartRect = {
   875        padding: normalizedPadding,
   876        width: function () {
   877          return this.x2 - this.x1;
   878        },
   879        height: function () {
   880          return this.y1 - this.y2;
   881        }
   882      };
   883  
   884      if(hasAxis) {
   885        if (options.axisX.position === 'start') {
   886          chartRect.y2 = normalizedPadding.top + xAxisOffset;
   887          chartRect.y1 = Math.max(height - normalizedPadding.bottom, chartRect.y2 + 1);
   888        } else {
   889          chartRect.y2 = normalizedPadding.top;
   890          chartRect.y1 = Math.max(height - normalizedPadding.bottom - xAxisOffset, chartRect.y2 + 1);
   891        }
   892  
   893        if (options.axisY.position === 'start') {
   894          chartRect.x1 = normalizedPadding.left + yAxisOffset;
   895          chartRect.x2 = Math.max(width - normalizedPadding.right, chartRect.x1 + 1);
   896        } else {
   897          chartRect.x1 = normalizedPadding.left;
   898          chartRect.x2 = Math.max(width - normalizedPadding.right - yAxisOffset, chartRect.x1 + 1);
   899        }
   900      } else {
   901        chartRect.x1 = normalizedPadding.left;
   902        chartRect.x2 = Math.max(width - normalizedPadding.right, chartRect.x1 + 1);
   903        chartRect.y2 = normalizedPadding.top;
   904        chartRect.y1 = Math.max(height - normalizedPadding.bottom, chartRect.y2 + 1);
   905      }
   906  
   907      return chartRect;
   908    };
   909  
   910    /**
   911     * Creates a grid line based on a projected value.
   912     *
   913     * @memberof Chartist.Core
   914     * @param position
   915     * @param index
   916     * @param axis
   917     * @param offset
   918     * @param length
   919     * @param group
   920     * @param classes
   921     * @param eventEmitter
   922     */
   923    Chartist.createGrid = function(position, index, axis, offset, length, group, classes, eventEmitter) {
   924      var positionalData = {};
   925      positionalData[axis.units.pos + '1'] = position;
   926      positionalData[axis.units.pos + '2'] = position;
   927      positionalData[axis.counterUnits.pos + '1'] = offset;
   928      positionalData[axis.counterUnits.pos + '2'] = offset + length;
   929  
   930      var gridElement = group.elem('line', positionalData, classes.join(' '));
   931  
   932      // Event for grid draw
   933      eventEmitter.emit('draw',
   934        Chartist.extend({
   935          type: 'grid',
   936          axis: axis,
   937          index: index,
   938          group: group,
   939          element: gridElement
   940        }, positionalData)
   941      );
   942    };
   943  
   944    /**
   945     * Creates a grid background rect and emits the draw event.
   946     *
   947     * @memberof Chartist.Core
   948     * @param gridGroup
   949     * @param chartRect
   950     * @param className
   951     * @param eventEmitter
   952     */
   953    Chartist.createGridBackground = function (gridGroup, chartRect, className, eventEmitter) {
   954      var gridBackground = gridGroup.elem('rect', {
   955          x: chartRect.x1,
   956          y: chartRect.y2,
   957          width: chartRect.width(),
   958          height: chartRect.height(),
   959        }, className, true);
   960  
   961        // Event for grid background draw
   962        eventEmitter.emit('draw', {
   963          type: 'gridBackground',
   964          group: gridGroup,
   965          element: gridBackground
   966        });
   967    };
   968  
   969    /**
   970     * Creates a label based on a projected value and an axis.
   971     *
   972     * @memberof Chartist.Core
   973     * @param position
   974     * @param length
   975     * @param index
   976     * @param labels
   977     * @param axis
   978     * @param axisOffset
   979     * @param labelOffset
   980     * @param group
   981     * @param classes
   982     * @param useForeignObject
   983     * @param eventEmitter
   984     */
   985    Chartist.createLabel = function(position, length, index, labels, axis, axisOffset, labelOffset, group, classes, useForeignObject, eventEmitter) {
   986      var labelElement;
   987      var positionalData = {};
   988  
   989      positionalData[axis.units.pos] = position + labelOffset[axis.units.pos];
   990      positionalData[axis.counterUnits.pos] = labelOffset[axis.counterUnits.pos];
   991      positionalData[axis.units.len] = length;
   992      positionalData[axis.counterUnits.len] = Math.max(0, axisOffset - 10);
   993  
   994      if(useForeignObject) {
   995        // We need to set width and height explicitly to px as span will not expand with width and height being
   996        // 100% in all browsers
   997        var content = document.createElement('span');
   998        content.className = classes.join(' ');
   999        content.setAttribute('xmlns', Chartist.namespaces.xhtml);
  1000        content.innerText = labels[index];
  1001        content.style[axis.units.len] = Math.round(positionalData[axis.units.len]) + 'px';
  1002        content.style[axis.counterUnits.len] = Math.round(positionalData[axis.counterUnits.len]) + 'px';
  1003  
  1004        labelElement = group.foreignObject(content, Chartist.extend({
  1005          style: 'overflow: visible;'
  1006        }, positionalData));
  1007      } else {
  1008        labelElement = group.elem('text', positionalData, classes.join(' ')).text(labels[index]);
  1009      }
  1010  
  1011      eventEmitter.emit('draw', Chartist.extend({
  1012        type: 'label',
  1013        axis: axis,
  1014        index: index,
  1015        group: group,
  1016        element: labelElement,
  1017        text: labels[index]
  1018      }, positionalData));
  1019    };
  1020  
  1021    /**
  1022     * Helper to read series specific options from options object. It automatically falls back to the global option if
  1023     * there is no option in the series options.
  1024     *
  1025     * @param {Object} series Series object
  1026     * @param {Object} options Chartist options object
  1027     * @param {string} key The options key that should be used to obtain the options
  1028     * @returns {*}
  1029     */
  1030    Chartist.getSeriesOption = function(series, options, key) {
  1031      if(series.name && options.series && options.series[series.name]) {
  1032        var seriesOptions = options.series[series.name];
  1033        return seriesOptions.hasOwnProperty(key) ? seriesOptions[key] : options[key];
  1034      } else {
  1035        return options[key];
  1036      }
  1037    };
  1038  
  1039    /**
  1040     * Provides options handling functionality with callback for options changes triggered by responsive options and media query matches
  1041     *
  1042     * @memberof Chartist.Core
  1043     * @param {Object} options Options set by user
  1044     * @param {Array} responsiveOptions Optional functions to add responsive behavior to chart
  1045     * @param {Object} eventEmitter The event emitter that will be used to emit the options changed events
  1046     * @return {Object} The consolidated options object from the defaults, base and matching responsive options
  1047     */
  1048    Chartist.optionsProvider = function (options, responsiveOptions, eventEmitter) {
  1049      var baseOptions = Chartist.extend({}, options),
  1050        currentOptions,
  1051        mediaQueryListeners = [],
  1052        i;
  1053  
  1054      function updateCurrentOptions(mediaEvent) {
  1055        var previousOptions = currentOptions;
  1056        currentOptions = Chartist.extend({}, baseOptions);
  1057  
  1058        if (responsiveOptions) {
  1059          for (i = 0; i < responsiveOptions.length; i++) {
  1060            var mql = window.matchMedia(responsiveOptions[i][0]);
  1061            if (mql.matches) {
  1062              currentOptions = Chartist.extend(currentOptions, responsiveOptions[i][1]);
  1063            }
  1064          }
  1065        }
  1066  
  1067        if(eventEmitter && mediaEvent) {
  1068          eventEmitter.emit('optionsChanged', {
  1069            previousOptions: previousOptions,
  1070            currentOptions: currentOptions
  1071          });
  1072        }
  1073      }
  1074  
  1075      function removeMediaQueryListeners() {
  1076        mediaQueryListeners.forEach(function(mql) {
  1077          mql.removeListener(updateCurrentOptions);
  1078        });
  1079      }
  1080  
  1081      if (!window.matchMedia) {
  1082        throw 'window.matchMedia not found! Make sure you\'re using a polyfill.';
  1083      } else if (responsiveOptions) {
  1084  
  1085        for (i = 0; i < responsiveOptions.length; i++) {
  1086          var mql = window.matchMedia(responsiveOptions[i][0]);
  1087          mql.addListener(updateCurrentOptions);
  1088          mediaQueryListeners.push(mql);
  1089        }
  1090      }
  1091      // Execute initially without an event argument so we get the correct options
  1092      updateCurrentOptions();
  1093  
  1094      return {
  1095        removeMediaQueryListeners: removeMediaQueryListeners,
  1096        getCurrentOptions: function getCurrentOptions() {
  1097          return Chartist.extend({}, currentOptions);
  1098        }
  1099      };
  1100    };
  1101  
  1102  
  1103    /**
  1104     * Splits a list of coordinates and associated values into segments. Each returned segment contains a pathCoordinates
  1105     * valueData property describing the segment.
  1106     *
  1107     * With the default options, segments consist of contiguous sets of points that do not have an undefined value. Any
  1108     * points with undefined values are discarded.
  1109     *
  1110     * **Options**
  1111     * The following options are used to determine how segments are formed
  1112     * ```javascript
  1113     * var options = {
  1114     *   // If fillHoles is true, undefined values are simply discarded without creating a new segment. Assuming other options are default, this returns single segment.
  1115     *   fillHoles: false,
  1116     *   // If increasingX is true, the coordinates in all segments have strictly increasing x-values.
  1117     *   increasingX: false
  1118     * };
  1119     * ```
  1120     *
  1121     * @memberof Chartist.Core
  1122     * @param {Array} pathCoordinates List of point coordinates to be split in the form [x1, y1, x2, y2 ... xn, yn]
  1123     * @param {Array} values List of associated point values in the form [v1, v2 .. vn]
  1124     * @param {Object} options Options set by user
  1125     * @return {Array} List of segments, each containing a pathCoordinates and valueData property.
  1126     */
  1127    Chartist.splitIntoSegments = function(pathCoordinates, valueData, options) {
  1128      var defaultOptions = {
  1129        increasingX: false,
  1130        fillHoles: false
  1131      };
  1132  
  1133      options = Chartist.extend({}, defaultOptions, options);
  1134  
  1135      var segments = [];
  1136      var hole = true;
  1137  
  1138      for(var i = 0; i < pathCoordinates.length; i += 2) {
  1139        // If this value is a "hole" we set the hole flag
  1140        if(Chartist.getMultiValue(valueData[i / 2].value) === undefined) {
  1141        // if(valueData[i / 2].value === undefined) {
  1142          if(!options.fillHoles) {
  1143            hole = true;
  1144          }
  1145        } else {
  1146          if(options.increasingX && i >= 2 && pathCoordinates[i] <= pathCoordinates[i-2]) {
  1147            // X is not increasing, so we need to make sure we start a new segment
  1148            hole = true;
  1149          }
  1150  
  1151  
  1152          // If it's a valid value we need to check if we're coming out of a hole and create a new empty segment
  1153          if(hole) {
  1154            segments.push({
  1155              pathCoordinates: [],
  1156              valueData: []
  1157            });
  1158            // As we have a valid value now, we are not in a "hole" anymore
  1159            hole = false;
  1160          }
  1161  
  1162          // Add to the segment pathCoordinates and valueData
  1163          segments[segments.length - 1].pathCoordinates.push(pathCoordinates[i], pathCoordinates[i + 1]);
  1164          segments[segments.length - 1].valueData.push(valueData[i / 2]);
  1165        }
  1166      }
  1167  
  1168      return segments;
  1169    };
  1170  }(window, document, Chartist));
  1171  ;/**
  1172   * Chartist path interpolation functions.
  1173   *
  1174   * @module Chartist.Interpolation
  1175   */
  1176  /* global Chartist */
  1177  (function(window, document, Chartist) {
  1178    'use strict';
  1179  
  1180    Chartist.Interpolation = {};
  1181  
  1182    /**
  1183     * This interpolation function does not smooth the path and the result is only containing lines and no curves.
  1184     *
  1185     * @example
  1186     * var chart = new Chartist.Line('.ct-chart', {
  1187     *   labels: [1, 2, 3, 4, 5],
  1188     *   series: [[1, 2, 8, 1, 7]]
  1189     * }, {
  1190     *   lineSmooth: Chartist.Interpolation.none({
  1191     *     fillHoles: false
  1192     *   })
  1193     * });
  1194     *
  1195     *
  1196     * @memberof Chartist.Interpolation
  1197     * @return {Function}
  1198     */
  1199    Chartist.Interpolation.none = function(options) {
  1200      var defaultOptions = {
  1201        fillHoles: false
  1202      };
  1203      options = Chartist.extend({}, defaultOptions, options);
  1204      return function none(pathCoordinates, valueData) {
  1205        var path = new Chartist.Svg.Path();
  1206        var hole = true;
  1207  
  1208        for(var i = 0; i < pathCoordinates.length; i += 2) {
  1209          var currX = pathCoordinates[i];
  1210          var currY = pathCoordinates[i + 1];
  1211          var currData = valueData[i / 2];
  1212  
  1213          if(Chartist.getMultiValue(currData.value) !== undefined) {
  1214  
  1215            if(hole) {
  1216              path.move(currX, currY, false, currData);
  1217            } else {
  1218              path.line(currX, currY, false, currData);
  1219            }
  1220  
  1221            hole = false;
  1222          } else if(!options.fillHoles) {
  1223            hole = true;
  1224          }
  1225        }
  1226  
  1227        return path;
  1228      };
  1229    };
  1230  
  1231    /**
  1232     * Simple smoothing creates horizontal handles that are positioned with a fraction of the length between two data points. You can use the divisor option to specify the amount of smoothing.
  1233     *
  1234     * Simple smoothing can be used instead of `Chartist.Smoothing.cardinal` if you'd like to get rid of the artifacts it produces sometimes. Simple smoothing produces less flowing lines but is accurate by hitting the points and it also doesn't swing below or above the given data point.
  1235     *
  1236     * All smoothing functions within Chartist are factory functions that accept an options parameter. The simple interpolation function accepts one configuration parameter `divisor`, between 1 and ∞, which controls the smoothing characteristics.
  1237     *
  1238     * @example
  1239     * var chart = new Chartist.Line('.ct-chart', {
  1240     *   labels: [1, 2, 3, 4, 5],
  1241     *   series: [[1, 2, 8, 1, 7]]
  1242     * }, {
  1243     *   lineSmooth: Chartist.Interpolation.simple({
  1244     *     divisor: 2,
  1245     *     fillHoles: false
  1246     *   })
  1247     * });
  1248     *
  1249     *
  1250     * @memberof Chartist.Interpolation
  1251     * @param {Object} options The options of the simple interpolation factory function.
  1252     * @return {Function}
  1253     */
  1254    Chartist.Interpolation.simple = function(options) {
  1255      var defaultOptions = {
  1256        divisor: 2,
  1257        fillHoles: false
  1258      };
  1259      options = Chartist.extend({}, defaultOptions, options);
  1260  
  1261      var d = 1 / Math.max(1, options.divisor);
  1262  
  1263      return function simple(pathCoordinates, valueData) {
  1264        var path = new Chartist.Svg.Path();
  1265        var prevX, prevY, prevData;
  1266  
  1267        for(var i = 0; i < pathCoordinates.length; i += 2) {
  1268          var currX = pathCoordinates[i];
  1269          var currY = pathCoordinates[i + 1];
  1270          var length = (currX - prevX) * d;
  1271          var currData = valueData[i / 2];
  1272  
  1273          if(currData.value !== undefined) {
  1274  
  1275            if(prevData === undefined) {
  1276              path.move(currX, currY, false, currData);
  1277            } else {
  1278              path.curve(
  1279                prevX + length,
  1280                prevY,
  1281                currX - length,
  1282                currY,
  1283                currX,
  1284                currY,
  1285                false,
  1286                currData
  1287              );
  1288            }
  1289  
  1290            prevX = currX;
  1291            prevY = currY;
  1292            prevData = currData;
  1293          } else if(!options.fillHoles) {
  1294            prevX = currX = prevData = undefined;
  1295          }
  1296        }
  1297  
  1298        return path;
  1299      };
  1300    };
  1301  
  1302    /**
  1303     * Cardinal / Catmull-Rome spline interpolation is the default smoothing function in Chartist. It produces nice results where the splines will always meet the points. It produces some artifacts though when data values are increased or decreased rapidly. The line may not follow a very accurate path and if the line should be accurate this smoothing function does not produce the best results.
  1304     *
  1305     * Cardinal splines can only be created if there are more than two data points. If this is not the case this smoothing will fallback to `Chartist.Smoothing.none`.
  1306     *
  1307     * All smoothing functions within Chartist are factory functions that accept an options parameter. The cardinal interpolation function accepts one configuration parameter `tension`, between 0 and 1, which controls the smoothing intensity.
  1308     *
  1309     * @example
  1310     * var chart = new Chartist.Line('.ct-chart', {
  1311     *   labels: [1, 2, 3, 4, 5],
  1312     *   series: [[1, 2, 8, 1, 7]]
  1313     * }, {
  1314     *   lineSmooth: Chartist.Interpolation.cardinal({
  1315     *     tension: 1,
  1316     *     fillHoles: false
  1317     *   })
  1318     * });
  1319     *
  1320     * @memberof Chartist.Interpolation
  1321     * @param {Object} options The options of the cardinal factory function.
  1322     * @return {Function}
  1323     */
  1324    Chartist.Interpolation.cardinal = function(options) {
  1325      var defaultOptions = {
  1326        tension: 1,
  1327        fillHoles: false
  1328      };
  1329  
  1330      options = Chartist.extend({}, defaultOptions, options);
  1331  
  1332      var t = Math.min(1, Math.max(0, options.tension)),
  1333        c = 1 - t;
  1334  
  1335      return function cardinal(pathCoordinates, valueData) {
  1336        // First we try to split the coordinates into segments
  1337        // This is necessary to treat "holes" in line charts
  1338        var segments = Chartist.splitIntoSegments(pathCoordinates, valueData, {
  1339          fillHoles: options.fillHoles
  1340        });
  1341  
  1342        if(!segments.length) {
  1343          // If there were no segments return 'Chartist.Interpolation.none'
  1344          return Chartist.Interpolation.none()([]);
  1345        } else if(segments.length > 1) {
  1346          // If the split resulted in more that one segment we need to interpolate each segment individually and join them
  1347          // afterwards together into a single path.
  1348            var paths = [];
  1349          // For each segment we will recurse the cardinal function
  1350          segments.forEach(function(segment) {
  1351            paths.push(cardinal(segment.pathCoordinates, segment.valueData));
  1352          });
  1353          // Join the segment path data into a single path and return
  1354          return Chartist.Svg.Path.join(paths);
  1355        } else {
  1356          // If there was only one segment we can proceed regularly by using pathCoordinates and valueData from the first
  1357          // segment
  1358          pathCoordinates = segments[0].pathCoordinates;
  1359          valueData = segments[0].valueData;
  1360  
  1361          // If less than two points we need to fallback to no smoothing
  1362          if(pathCoordinates.length <= 4) {
  1363            return Chartist.Interpolation.none()(pathCoordinates, valueData);
  1364          }
  1365  
  1366          var path = new Chartist.Svg.Path().move(pathCoordinates[0], pathCoordinates[1], false, valueData[0]),
  1367            z;
  1368  
  1369          for (var i = 0, iLen = pathCoordinates.length; iLen - 2 * !z > i; i += 2) {
  1370            var p = [
  1371              {x: +pathCoordinates[i - 2], y: +pathCoordinates[i - 1]},
  1372              {x: +pathCoordinates[i], y: +pathCoordinates[i + 1]},
  1373              {x: +pathCoordinates[i + 2], y: +pathCoordinates[i + 3]},
  1374              {x: +pathCoordinates[i + 4], y: +pathCoordinates[i + 5]}
  1375            ];
  1376            if (z) {
  1377              if (!i) {
  1378                p[0] = {x: +pathCoordinates[iLen - 2], y: +pathCoordinates[iLen - 1]};
  1379              } else if (iLen - 4 === i) {
  1380                p[3] = {x: +pathCoordinates[0], y: +pathCoordinates[1]};
  1381              } else if (iLen - 2 === i) {
  1382                p[2] = {x: +pathCoordinates[0], y: +pathCoordinates[1]};
  1383                p[3] = {x: +pathCoordinates[2], y: +pathCoordinates[3]};
  1384              }
  1385            } else {
  1386              if (iLen - 4 === i) {
  1387                p[3] = p[2];
  1388              } else if (!i) {
  1389                p[0] = {x: +pathCoordinates[i], y: +pathCoordinates[i + 1]};
  1390              }
  1391            }
  1392  
  1393            path.curve(
  1394              (t * (-p[0].x + 6 * p[1].x + p[2].x) / 6) + (c * p[2].x),
  1395              (t * (-p[0].y + 6 * p[1].y + p[2].y) / 6) + (c * p[2].y),
  1396              (t * (p[1].x + 6 * p[2].x - p[3].x) / 6) + (c * p[2].x),
  1397              (t * (p[1].y + 6 * p[2].y - p[3].y) / 6) + (c * p[2].y),
  1398              p[2].x,
  1399              p[2].y,
  1400              false,
  1401              valueData[(i + 2) / 2]
  1402            );
  1403          }
  1404  
  1405          return path;
  1406        }
  1407      };
  1408    };
  1409  
  1410    /**
  1411     * Monotone Cubic spline interpolation produces a smooth curve which preserves monotonicity. Unlike cardinal splines, the curve will not extend beyond the range of y-values of the original data points.
  1412     *
  1413     * Monotone Cubic splines can only be created if there are more than two data points. If this is not the case this smoothing will fallback to `Chartist.Smoothing.none`.
  1414     *
  1415     * The x-values of subsequent points must be increasing to fit a Monotone Cubic spline. If this condition is not met for a pair of adjacent points, then there will be a break in the curve between those data points.
  1416     *
  1417     * All smoothing functions within Chartist are factory functions that accept an options parameter.
  1418     *
  1419     * @example
  1420     * var chart = new Chartist.Line('.ct-chart', {
  1421     *   labels: [1, 2, 3, 4, 5],
  1422     *   series: [[1, 2, 8, 1, 7]]
  1423     * }, {
  1424     *   lineSmooth: Chartist.Interpolation.monotoneCubic({
  1425     *     fillHoles: false
  1426     *   })
  1427     * });
  1428     *
  1429     * @memberof Chartist.Interpolation
  1430     * @param {Object} options The options of the monotoneCubic factory function.
  1431     * @return {Function}
  1432     */
  1433    Chartist.Interpolation.monotoneCubic = function(options) {
  1434      var defaultOptions = {
  1435        fillHoles: false
  1436      };
  1437  
  1438      options = Chartist.extend({}, defaultOptions, options);
  1439  
  1440      return function monotoneCubic(pathCoordinates, valueData) {
  1441        // First we try to split the coordinates into segments
  1442        // This is necessary to treat "holes" in line charts
  1443        var segments = Chartist.splitIntoSegments(pathCoordinates, valueData, {
  1444          fillHoles: options.fillHoles,
  1445          increasingX: true
  1446        });
  1447  
  1448        if(!segments.length) {
  1449          // If there were no segments return 'Chartist.Interpolation.none'
  1450          return Chartist.Interpolation.none()([]);
  1451        } else if(segments.length > 1) {
  1452          // If the split resulted in more that one segment we need to interpolate each segment individually and join them
  1453          // afterwards together into a single path.
  1454            var paths = [];
  1455          // For each segment we will recurse the monotoneCubic fn function
  1456          segments.forEach(function(segment) {
  1457            paths.push(monotoneCubic(segment.pathCoordinates, segment.valueData));
  1458          });
  1459          // Join the segment path data into a single path and return
  1460          return Chartist.Svg.Path.join(paths);
  1461        } else {
  1462          // If there was only one segment we can proceed regularly by using pathCoordinates and valueData from the first
  1463          // segment
  1464          pathCoordinates = segments[0].pathCoordinates;
  1465          valueData = segments[0].valueData;
  1466  
  1467          // If less than three points we need to fallback to no smoothing
  1468          if(pathCoordinates.length <= 4) {
  1469            return Chartist.Interpolation.none()(pathCoordinates, valueData);
  1470          }
  1471  
  1472          var xs = [],
  1473            ys = [],
  1474            i,
  1475            n = pathCoordinates.length / 2,
  1476            ms = [],
  1477            ds = [], dys = [], dxs = [],
  1478            path;
  1479  
  1480          // Populate x and y coordinates into separate arrays, for readability
  1481  
  1482          for(i = 0; i < n; i++) {
  1483            xs[i] = pathCoordinates[i * 2];
  1484            ys[i] = pathCoordinates[i * 2 + 1];
  1485          }
  1486  
  1487          // Calculate deltas and derivative
  1488  
  1489          for(i = 0; i < n - 1; i++) {
  1490            dys[i] = ys[i + 1] - ys[i];
  1491            dxs[i] = xs[i + 1] - xs[i];
  1492            ds[i] = dys[i] / dxs[i];
  1493          }
  1494  
  1495          // Determine desired slope (m) at each point using Fritsch-Carlson method
  1496          // See: http://math.stackexchange.com/questions/45218/implementation-of-monotone-cubic-interpolation
  1497  
  1498          ms[0] = ds[0];
  1499          ms[n - 1] = ds[n - 2];
  1500  
  1501          for(i = 1; i < n - 1; i++) {
  1502            if(ds[i] === 0 || ds[i - 1] === 0 || (ds[i - 1] > 0) !== (ds[i] > 0)) {
  1503              ms[i] = 0;
  1504            } else {
  1505              ms[i] = 3 * (dxs[i - 1] + dxs[i]) / (
  1506                (2 * dxs[i] + dxs[i - 1]) / ds[i - 1] +
  1507                (dxs[i] + 2 * dxs[i - 1]) / ds[i]);
  1508  
  1509              if(!isFinite(ms[i])) {
  1510                ms[i] = 0;
  1511              }
  1512            }
  1513          }
  1514  
  1515          // Now build a path from the slopes
  1516  
  1517          path = new Chartist.Svg.Path().move(xs[0], ys[0], false, valueData[0]);
  1518  
  1519          for(i = 0; i < n - 1; i++) {
  1520            path.curve(
  1521              // First control point
  1522              xs[i] + dxs[i] / 3,
  1523              ys[i] + ms[i] * dxs[i] / 3,
  1524              // Second control point
  1525              xs[i + 1] - dxs[i] / 3,
  1526              ys[i + 1] - ms[i + 1] * dxs[i] / 3,
  1527              // End point
  1528              xs[i + 1],
  1529              ys[i + 1],
  1530  
  1531              false,
  1532              valueData[i + 1]
  1533            );
  1534          }
  1535  
  1536          return path;
  1537        }
  1538      };
  1539    };
  1540  
  1541    /**
  1542     * Step interpolation will cause the line chart to move in steps rather than diagonal or smoothed lines. This interpolation will create additional points that will also be drawn when the `showPoint` option is enabled.
  1543     *
  1544     * All smoothing functions within Chartist are factory functions that accept an options parameter. The step interpolation function accepts one configuration parameter `postpone`, that can be `true` or `false`. The default value is `true` and will cause the step to occur where the value actually changes. If a different behaviour is needed where the step is shifted to the left and happens before the actual value, this option can be set to `false`.
  1545     *
  1546     * @example
  1547     * var chart = new Chartist.Line('.ct-chart', {
  1548     *   labels: [1, 2, 3, 4, 5],
  1549     *   series: [[1, 2, 8, 1, 7]]
  1550     * }, {
  1551     *   lineSmooth: Chartist.Interpolation.step({
  1552     *     postpone: true,
  1553     *     fillHoles: false
  1554     *   })
  1555     * });
  1556     *
  1557     * @memberof Chartist.Interpolation
  1558     * @param options
  1559     * @returns {Function}
  1560     */
  1561    Chartist.Interpolation.step = function(options) {
  1562      var defaultOptions = {
  1563        postpone: true,
  1564        fillHoles: false
  1565      };
  1566  
  1567      options = Chartist.extend({}, defaultOptions, options);
  1568  
  1569      return function step(pathCoordinates, valueData) {
  1570        var path = new Chartist.Svg.Path();
  1571  
  1572        var prevX, prevY, prevData;
  1573  
  1574        for (var i = 0; i < pathCoordinates.length; i += 2) {
  1575          var currX = pathCoordinates[i];
  1576          var currY = pathCoordinates[i + 1];
  1577          var currData = valueData[i / 2];
  1578  
  1579          // If the current point is also not a hole we can draw the step lines
  1580          if(currData.value !== undefined) {
  1581            if(prevData === undefined) {
  1582              path.move(currX, currY, false, currData);
  1583            } else {
  1584              if(options.postpone) {
  1585                // If postponed we should draw the step line with the value of the previous value
  1586                path.line(currX, prevY, false, prevData);
  1587              } else {
  1588                // If not postponed we should draw the step line with the value of the current value
  1589                path.line(prevX, currY, false, currData);
  1590              }
  1591              // Line to the actual point (this should only be a Y-Axis movement
  1592              path.line(currX, currY, false, currData);
  1593            }
  1594  
  1595            prevX = currX;
  1596            prevY = currY;
  1597            prevData = currData;
  1598          } else if(!options.fillHoles) {
  1599            prevX = prevY = prevData = undefined;
  1600          }
  1601        }
  1602  
  1603        return path;
  1604      };
  1605    };
  1606  
  1607  }(window, document, Chartist));
  1608  ;/**
  1609   * A very basic event module that helps to generate and catch events.
  1610   *
  1611   * @module Chartist.Event
  1612   */
  1613  /* global Chartist */
  1614  (function (window, document, Chartist) {
  1615    'use strict';
  1616  
  1617    Chartist.EventEmitter = function () {
  1618      var handlers = [];
  1619  
  1620      /**
  1621       * Add an event handler for a specific event
  1622       *
  1623       * @memberof Chartist.Event
  1624       * @param {String} event The event name
  1625       * @param {Function} handler A event handler function
  1626       */
  1627      function addEventHandler(event, handler) {
  1628        handlers[event] = handlers[event] || [];
  1629        handlers[event].push(handler);
  1630      }
  1631  
  1632      /**
  1633       * Remove an event handler of a specific event name or remove all event handlers for a specific event.
  1634       *
  1635       * @memberof Chartist.Event
  1636       * @param {String} event The event name where a specific or all handlers should be removed
  1637       * @param {Function} [handler] An optional event handler function. If specified only this specific handler will be removed and otherwise all handlers are removed.
  1638       */
  1639      function removeEventHandler(event, handler) {
  1640        // Only do something if there are event handlers with this name existing
  1641        if(handlers[event]) {
  1642          // If handler is set we will look for a specific handler and only remove this
  1643          if(handler) {
  1644            handlers[event].splice(handlers[event].indexOf(handler), 1);
  1645            if(handlers[event].length === 0) {
  1646              delete handlers[event];
  1647            }
  1648          } else {
  1649            // If no handler is specified we remove all handlers for this event
  1650            delete handlers[event];
  1651          }
  1652        }
  1653      }
  1654  
  1655      /**
  1656       * Use this function to emit an event. All handlers that are listening for this event will be triggered with the data parameter.
  1657       *
  1658       * @memberof Chartist.Event
  1659       * @param {String} event The event name that should be triggered
  1660       * @param {*} data Arbitrary data that will be passed to the event handler callback functions
  1661       */
  1662      function emit(event, data) {
  1663        // Only do something if there are event handlers with this name existing
  1664        if(handlers[event]) {
  1665          handlers[event].forEach(function(handler) {
  1666            handler(data);
  1667          });
  1668        }
  1669  
  1670        // Emit event to star event handlers
  1671        if(handlers['*']) {
  1672          handlers['*'].forEach(function(starHandler) {
  1673            starHandler(event, data);
  1674          });
  1675        }
  1676      }
  1677  
  1678      return {
  1679        addEventHandler: addEventHandler,
  1680        removeEventHandler: removeEventHandler,
  1681        emit: emit
  1682      };
  1683    };
  1684  
  1685  }(window, document, Chartist));
  1686  ;/**
  1687   * This module provides some basic prototype inheritance utilities.
  1688   *
  1689   * @module Chartist.Class
  1690   */
  1691  /* global Chartist */
  1692  (function(window, document, Chartist) {
  1693    'use strict';
  1694  
  1695    function listToArray(list) {
  1696      var arr = [];
  1697      if (list.length) {
  1698        for (var i = 0; i < list.length; i++) {
  1699          arr.push(list[i]);
  1700        }
  1701      }
  1702      return arr;
  1703    }
  1704  
  1705    /**
  1706     * Method to extend from current prototype.
  1707     *
  1708     * @memberof Chartist.Class
  1709     * @param {Object} properties The object that serves as definition for the prototype that gets created for the new class. This object should always contain a constructor property that is the desired constructor for the newly created class.
  1710     * @param {Object} [superProtoOverride] By default extens will use the current class prototype or Chartist.class. With this parameter you can specify any super prototype that will be used.
  1711     * @return {Function} Constructor function of the new class
  1712     *
  1713     * @example
  1714     * var Fruit = Class.extend({
  1715       * color: undefined,
  1716       *   sugar: undefined,
  1717       *
  1718       *   constructor: function(color, sugar) {
  1719       *     this.color = color;
  1720       *     this.sugar = sugar;
  1721       *   },
  1722       *
  1723       *   eat: function() {
  1724       *     this.sugar = 0;
  1725       *     return this;
  1726       *   }
  1727       * });
  1728     *
  1729     * var Banana = Fruit.extend({
  1730       *   length: undefined,
  1731       *
  1732       *   constructor: function(length, sugar) {
  1733       *     Banana.super.constructor.call(this, 'Yellow', sugar);
  1734       *     this.length = length;
  1735       *   }
  1736       * });
  1737     *
  1738     * var banana = new Banana(20, 40);
  1739     * console.log('banana instanceof Fruit', banana instanceof Fruit);
  1740     * console.log('Fruit is prototype of banana', Fruit.prototype.isPrototypeOf(banana));
  1741     * console.log('bananas prototype is Fruit', Object.getPrototypeOf(banana) === Fruit.prototype);
  1742     * console.log(banana.sugar);
  1743     * console.log(banana.eat().sugar);
  1744     * console.log(banana.color);
  1745     */
  1746    function extend(properties, superProtoOverride) {
  1747      var superProto = superProtoOverride || this.prototype || Chartist.Class;
  1748      var proto = Object.create(superProto);
  1749  
  1750      Chartist.Class.cloneDefinitions(proto, properties);
  1751  
  1752      var constr = function() {
  1753        var fn = proto.constructor || function () {},
  1754          instance;
  1755  
  1756        // If this is linked to the Chartist namespace the constructor was not called with new
  1757        // To provide a fallback we will instantiate here and return the instance
  1758        instance = this === Chartist ? Object.create(proto) : this;
  1759        fn.apply(instance, Array.prototype.slice.call(arguments, 0));
  1760  
  1761        // If this constructor was not called with new we need to return the instance
  1762        // This will not harm when the constructor has been called with new as the returned value is ignored
  1763        return instance;
  1764      };
  1765  
  1766      constr.prototype = proto;
  1767      constr.super = superProto;
  1768      constr.extend = this.extend;
  1769  
  1770      return constr;
  1771    }
  1772  
  1773    // Variable argument list clones args > 0 into args[0] and retruns modified args[0]
  1774    function cloneDefinitions() {
  1775      var args = listToArray(arguments);
  1776      var target = args[0];
  1777  
  1778      args.splice(1, args.length - 1).forEach(function (source) {
  1779        Object.getOwnPropertyNames(source).forEach(function (propName) {
  1780          // If this property already exist in target we delete it first
  1781          delete target[propName];
  1782          // Define the property with the descriptor from source
  1783          Object.defineProperty(target, propName,
  1784            Object.getOwnPropertyDescriptor(source, propName));
  1785        });
  1786      });
  1787  
  1788      return target;
  1789    }
  1790  
  1791    Chartist.Class = {
  1792      extend: extend,
  1793      cloneDefinitions: cloneDefinitions
  1794    };
  1795  
  1796  }(window, document, Chartist));
  1797  ;/**
  1798   * Base for all chart types. The methods in Chartist.Base are inherited to all chart types.
  1799   *
  1800   * @module Chartist.Base
  1801   */
  1802  /* global Chartist */
  1803  (function(window, document, Chartist) {
  1804    'use strict';
  1805  
  1806    // TODO: Currently we need to re-draw the chart on window resize. This is usually very bad and will affect performance.
  1807    // This is done because we can't work with relative coordinates when drawing the chart because SVG Path does not
  1808    // work with relative positions yet. We need to check if we can do a viewBox hack to switch to percentage.
  1809    // See http://mozilla.6506.n7.nabble.com/Specyfing-paths-with-percentages-unit-td247474.html
  1810    // Update: can be done using the above method tested here: http://codepen.io/gionkunz/pen/KDvLj
  1811    // The problem is with the label offsets that can't be converted into percentage and affecting the chart container
  1812    /**
  1813     * Updates the chart which currently does a full reconstruction of the SVG DOM
  1814     *
  1815     * @param {Object} [data] Optional data you'd like to set for the chart before it will update. If not specified the update method will use the data that is already configured with the chart.
  1816     * @param {Object} [options] Optional options you'd like to add to the previous options for the chart before it will update. If not specified the update method will use the options that have been already configured with the chart.
  1817     * @param {Boolean} [override] If set to true, the passed options will be used to extend the options that have been configured already. Otherwise the chart default options will be used as the base
  1818     * @memberof Chartist.Base
  1819     */
  1820    function update(data, options, override) {
  1821      if(data) {
  1822        this.data = data || {};
  1823        this.data.labels = this.data.labels || [];
  1824        this.data.series = this.data.series || [];
  1825        // Event for data transformation that allows to manipulate the data before it gets rendered in the charts
  1826        this.eventEmitter.emit('data', {
  1827          type: 'update',
  1828          data: this.data
  1829        });
  1830      }
  1831  
  1832      if(options) {
  1833        this.options = Chartist.extend({}, override ? this.options : this.defaultOptions, options);
  1834  
  1835        // If chartist was not initialized yet, we just set the options and leave the rest to the initialization
  1836        // Otherwise we re-create the optionsProvider at this point
  1837        if(!this.initializeTimeoutId) {
  1838          this.optionsProvider.removeMediaQueryListeners();
  1839          this.optionsProvider = Chartist.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter);
  1840        }
  1841      }
  1842  
  1843      // Only re-created the chart if it has been initialized yet
  1844      if(!this.initializeTimeoutId) {
  1845        this.createChart(this.optionsProvider.getCurrentOptions());
  1846      }
  1847  
  1848      // Return a reference to the chart object to chain up calls
  1849      return this;
  1850    }
  1851  
  1852    /**
  1853     * This method can be called on the API object of each chart and will un-register all event listeners that were added to other components. This currently includes a window.resize listener as well as media query listeners if any responsive options have been provided. Use this function if you need to destroy and recreate Chartist charts dynamically.
  1854     *
  1855     * @memberof Chartist.Base
  1856     */
  1857    function detach() {
  1858      // Only detach if initialization already occurred on this chart. If this chart still hasn't initialized (therefore
  1859      // the initializationTimeoutId is still a valid timeout reference, we will clear the timeout
  1860      if(!this.initializeTimeoutId) {
  1861        window.removeEventListener('resize', this.resizeListener);
  1862        this.optionsProvider.removeMediaQueryListeners();
  1863      } else {
  1864        window.clearTimeout(this.initializeTimeoutId);
  1865      }
  1866  
  1867      return this;
  1868    }
  1869  
  1870    /**
  1871     * Use this function to register event handlers. The handler callbacks are synchronous and will run in the main thread rather than the event loop.
  1872     *
  1873     * @memberof Chartist.Base
  1874     * @param {String} event Name of the event. Check the examples for supported events.
  1875     * @param {Function} handler The handler function that will be called when an event with the given name was emitted. This function will receive a data argument which contains event data. See the example for more details.
  1876     */
  1877    function on(event, handler) {
  1878      this.eventEmitter.addEventHandler(event, handler);
  1879      return this;
  1880    }
  1881  
  1882    /**
  1883     * Use this function to un-register event handlers. If the handler function parameter is omitted all handlers for the given event will be un-registered.
  1884     *
  1885     * @memberof Chartist.Base
  1886     * @param {String} event Name of the event for which a handler should be removed
  1887     * @param {Function} [handler] The handler function that that was previously used to register a new event handler. This handler will be removed from the event handler list. If this parameter is omitted then all event handlers for the given event are removed from the list.
  1888     */
  1889    function off(event, handler) {
  1890      this.eventEmitter.removeEventHandler(event, handler);
  1891      return this;
  1892    }
  1893  
  1894    function initialize() {
  1895      // Add window resize listener that re-creates the chart
  1896      window.addEventListener('resize', this.resizeListener);
  1897  
  1898      // Obtain current options based on matching media queries (if responsive options are given)
  1899      // This will also register a listener that is re-creating the chart based on media changes
  1900      this.optionsProvider = Chartist.optionsProvider(this.options, this.responsiveOptions, this.eventEmitter);
  1901      // Register options change listener that will trigger a chart update
  1902      this.eventEmitter.addEventHandler('optionsChanged', function() {
  1903        this.update();
  1904      }.bind(this));
  1905  
  1906      // Before the first chart creation we need to register us with all plugins that are configured
  1907      // Initialize all relevant plugins with our chart object and the plugin options specified in the config
  1908      if(this.options.plugins) {
  1909        this.options.plugins.forEach(function(plugin) {
  1910          if(plugin instanceof Array) {
  1911            plugin[0](this, plugin[1]);
  1912          } else {
  1913            plugin(this);
  1914          }
  1915        }.bind(this));
  1916      }
  1917  
  1918      // Event for data transformation that allows to manipulate the data before it gets rendered in the charts
  1919      this.eventEmitter.emit('data', {
  1920        type: 'initial',
  1921        data: this.data
  1922      });
  1923  
  1924      // Create the first chart
  1925      this.createChart(this.optionsProvider.getCurrentOptions());
  1926  
  1927      // As chart is initialized from the event loop now we can reset our timeout reference
  1928      // This is important if the chart gets initialized on the same element twice
  1929      this.initializeTimeoutId = undefined;
  1930    }
  1931  
  1932    /**
  1933     * Constructor of chart base class.
  1934     *
  1935     * @param query
  1936     * @param data
  1937     * @param defaultOptions
  1938     * @param options
  1939     * @param responsiveOptions
  1940     * @constructor
  1941     */
  1942    function Base(query, data, defaultOptions, options, responsiveOptions) {
  1943      this.container = Chartist.querySelector(query);
  1944      this.data = data || {};
  1945      this.data.labels = this.data.labels || [];
  1946      this.data.series = this.data.series || [];
  1947      this.defaultOptions = defaultOptions;
  1948      this.options = options;
  1949      this.responsiveOptions = responsiveOptions;
  1950      this.eventEmitter = Chartist.EventEmitter();
  1951      this.supportsForeignObject = Chartist.Svg.isSupported('Extensibility');
  1952      this.supportsAnimations = Chartist.Svg.isSupported('AnimationEventsAttribute');
  1953      this.resizeListener = function resizeListener(){
  1954        this.update();
  1955      }.bind(this);
  1956  
  1957      if(this.container) {
  1958        // If chartist was already initialized in this container we are detaching all event listeners first
  1959        if(this.container.__chartist__) {
  1960          this.container.__chartist__.detach();
  1961        }
  1962  
  1963        this.container.__chartist__ = this;
  1964      }
  1965  
  1966      // Using event loop for first draw to make it possible to register event listeners in the same call stack where
  1967      // the chart was created.
  1968      this.initializeTimeoutId = setTimeout(initialize.bind(this), 0);
  1969    }
  1970  
  1971    // Creating the chart base class
  1972    Chartist.Base = Chartist.Class.extend({
  1973      constructor: Base,
  1974      optionsProvider: undefined,
  1975      container: undefined,
  1976      svg: undefined,
  1977      eventEmitter: undefined,
  1978      createChart: function() {
  1979        throw new Error('Base chart type can\'t be instantiated!');
  1980      },
  1981      update: update,
  1982      detach: detach,
  1983      on: on,
  1984      off: off,
  1985      version: Chartist.version,
  1986      supportsForeignObject: false
  1987    });
  1988  
  1989  }(window, document, Chartist));
  1990  ;/**
  1991   * Chartist SVG module for simple SVG DOM abstraction
  1992   *
  1993   * @module Chartist.Svg
  1994   */
  1995  /* global Chartist */
  1996  (function(window, document, Chartist) {
  1997    'use strict';
  1998  
  1999    /**
  2000     * Chartist.Svg creates a new SVG object wrapper with a starting element. You can use the wrapper to fluently create sub-elements and modify them.
  2001     *
  2002     * @memberof Chartist.Svg
  2003     * @constructor
  2004     * @param {String|Element} name The name of the SVG element to create or an SVG dom element which should be wrapped into Chartist.Svg
  2005     * @param {Object} attributes An object with properties that will be added as attributes to the SVG element that is created. Attributes with undefined values will not be added.
  2006     * @param {String} className This class or class list will be added to the SVG element
  2007     * @param {Object} parent The parent SVG wrapper object where this newly created wrapper and it's element will be attached to as child
  2008     * @param {Boolean} insertFirst If this param is set to true in conjunction with a parent element the newly created element will be added as first child element in the parent element
  2009     */
  2010    function Svg(name, attributes, className, parent, insertFirst) {
  2011      // If Svg is getting called with an SVG element we just return the wrapper
  2012      if(name instanceof Element) {
  2013        this._node = name;
  2014      } else {
  2015        this._node = document.createElementNS(Chartist.namespaces.svg, name);
  2016  
  2017        // If this is an SVG element created then custom namespace
  2018        if(name === 'svg') {
  2019          this.attr({
  2020            'xmlns:ct': Chartist.namespaces.ct
  2021          });
  2022        }
  2023      }
  2024  
  2025      if(attributes) {
  2026        this.attr(attributes);
  2027      }
  2028  
  2029      if(className) {
  2030        this.addClass(className);
  2031      }
  2032  
  2033      if(parent) {
  2034        if (insertFirst && parent._node.firstChild) {
  2035          parent._node.insertBefore(this._node, parent._node.firstChild);
  2036        } else {
  2037          parent._node.appendChild(this._node);
  2038        }
  2039      }
  2040    }
  2041  
  2042    /**
  2043     * Set attributes on the current SVG element of the wrapper you're currently working on.
  2044     *
  2045     * @memberof Chartist.Svg
  2046     * @param {Object|String} attributes An object with properties that will be added as attributes to the SVG element that is created. Attributes with undefined values will not be added. If this parameter is a String then the function is used as a getter and will return the attribute value.
  2047     * @param {String} [ns] If specified, the attribute will be obtained using getAttributeNs. In order to write namepsaced attributes you can use the namespace:attribute notation within the attributes object.
  2048     * @return {Object|String} The current wrapper object will be returned so it can be used for chaining or the attribute value if used as getter function.
  2049     */
  2050    function attr(attributes, ns) {
  2051      if(typeof attributes === 'string') {
  2052        if(ns) {
  2053          return this._node.getAttributeNS(ns, attributes);
  2054        } else {
  2055          return this._node.getAttribute(attributes);
  2056        }
  2057      }
  2058  
  2059      Object.keys(attributes).forEach(function(key) {
  2060        // If the attribute value is undefined we can skip this one
  2061        if(attributes[key] === undefined) {
  2062          return;
  2063        }
  2064  
  2065        if (key.indexOf(':') !== -1) {
  2066          var namespacedAttribute = key.split(':');
  2067          this._node.setAttributeNS(Chartist.namespaces[namespacedAttribute[0]], key, attributes[key]);
  2068        } else {
  2069          this._node.setAttribute(key, attributes[key]);
  2070        }
  2071      }.bind(this));
  2072  
  2073      return this;
  2074    }
  2075  
  2076    /**
  2077     * Create a new SVG element whose wrapper object will be selected for further operations. This way you can also create nested groups easily.
  2078     *
  2079     * @memberof Chartist.Svg
  2080     * @param {String} name The name of the SVG element that should be created as child element of the currently selected element wrapper
  2081     * @param {Object} [attributes] An object with properties that will be added as attributes to the SVG element that is created. Attributes with undefined values will not be added.
  2082     * @param {String} [className] This class or class list will be added to the SVG element
  2083     * @param {Boolean} [insertFirst] If this param is set to true in conjunction with a parent element the newly created element will be added as first child element in the parent element
  2084     * @return {Chartist.Svg} Returns a Chartist.Svg wrapper object that can be used to modify the containing SVG data
  2085     */
  2086    function elem(name, attributes, className, insertFirst) {
  2087      return new Chartist.Svg(name, attributes, className, this, insertFirst);
  2088    }
  2089  
  2090    /**
  2091     * Returns the parent Chartist.SVG wrapper object
  2092     *
  2093     * @memberof Chartist.Svg
  2094     * @return {Chartist.Svg} Returns a Chartist.Svg wrapper around the parent node of the current node. If the parent node is not existing or it's not an SVG node then this function will return null.
  2095     */
  2096    function parent() {
  2097      return this._node.parentNode instanceof SVGElement ? new Chartist.Svg(this._node.parentNode) : null;
  2098    }
  2099  
  2100    /**
  2101     * This method returns a Chartist.Svg wrapper around the root SVG element of the current tree.
  2102     *
  2103     * @memberof Chartist.Svg
  2104     * @return {Chartist.Svg} The root SVG element wrapped in a Chartist.Svg element
  2105     */
  2106    function root() {
  2107      var node = this._node;
  2108      while(node.nodeName !== 'svg') {
  2109        node = node.parentNode;
  2110      }
  2111      return new Chartist.Svg(node);
  2112    }
  2113  
  2114    /**
  2115     * Find the first child SVG element of the current element that matches a CSS selector. The returned object is a Chartist.Svg wrapper.
  2116     *
  2117     * @memberof Chartist.Svg
  2118     * @param {String} selector A CSS selector that is used to query for child SVG elements
  2119     * @return {Chartist.Svg} The SVG wrapper for the element found or null if no element was found
  2120     */
  2121    function querySelector(selector) {
  2122      var foundNode = this._node.querySelector(selector);
  2123      return foundNode ? new Chartist.Svg(foundNode) : null;
  2124    }
  2125  
  2126    /**
  2127     * Find the all child SVG elements of the current element that match a CSS selector. The returned object is a Chartist.Svg.List wrapper.
  2128     *
  2129     * @memberof Chartist.Svg
  2130     * @param {String} selector A CSS selector that is used to query for child SVG elements
  2131     * @return {Chartist.Svg.List} The SVG wrapper list for the element found or null if no element was found
  2132     */
  2133    function querySelectorAll(selector) {
  2134      var foundNodes = this._node.querySelectorAll(selector);
  2135      return foundNodes.length ? new Chartist.Svg.List(foundNodes) : null;
  2136    }
  2137  
  2138    /**
  2139     * Returns the underlying SVG node for the current element.
  2140     *
  2141     * @memberof Chartist.Svg
  2142     * @returns {Node}
  2143     */
  2144    function getNode() {
  2145      return this._node;
  2146    }
  2147  
  2148    /**
  2149     * This method creates a foreignObject (see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/foreignObject) that allows to embed HTML content into a SVG graphic. With the help of foreignObjects you can enable the usage of regular HTML elements inside of SVG where they are subject for SVG positioning and transformation but the Browser will use the HTML rendering capabilities for the containing DOM.
  2150     *
  2151     * @memberof Chartist.Svg
  2152     * @param {Node|String} content The DOM Node, or HTML string that will be converted to a DOM Node, that is then placed into and wrapped by the foreignObject
  2153     * @param {String} [attributes] An object with properties that will be added as attributes to the foreignObject element that is created. Attributes with undefined values will not be added.
  2154     * @param {String} [className] This class or class list will be added to the SVG element
  2155     * @param {Boolean} [insertFirst] Specifies if the foreignObject should be inserted as first child
  2156     * @return {Chartist.Svg} New wrapper object that wraps the foreignObject element
  2157     */
  2158    function foreignObject(content, attributes, className, insertFirst) {
  2159      // If content is string then we convert it to DOM
  2160      // TODO: Handle case where content is not a string nor a DOM Node
  2161      if(typeof content === 'string') {
  2162        var container = document.createElement('div');
  2163        container.innerHTML = content;
  2164        content = container.firstChild;
  2165      }
  2166  
  2167      // Adding namespace to content element
  2168      content.setAttribute('xmlns', Chartist.namespaces.xmlns);
  2169  
  2170      // Creating the foreignObject without required extension attribute (as described here
  2171      // http://www.w3.org/TR/SVG/extend.html#ForeignObjectElement)
  2172      var fnObj = this.elem('foreignObject', attributes, className, insertFirst);
  2173  
  2174      // Add content to foreignObjectElement
  2175      fnObj._node.appendChild(content);
  2176  
  2177      return fnObj;
  2178    }
  2179  
  2180    /**
  2181     * This method adds a new text element to the current Chartist.Svg wrapper.
  2182     *
  2183     * @memberof Chartist.Svg
  2184     * @param {String} t The text that should be added to the text element that is created
  2185     * @return {Chartist.Svg} The same wrapper object that was used to add the newly created element
  2186     */
  2187    function text(t) {
  2188      this._node.appendChild(document.createTextNode(t));
  2189      return this;
  2190    }
  2191  
  2192    /**
  2193     * This method will clear all child nodes of the current wrapper object.
  2194     *
  2195     * @memberof Chartist.Svg
  2196     * @return {Chartist.Svg} The same wrapper object that got emptied
  2197     */
  2198    function empty() {
  2199      while (this._node.firstChild) {
  2200        this._node.removeChild(this._node.firstChild);
  2201      }
  2202  
  2203      return this;
  2204    }
  2205  
  2206    /**
  2207     * This method will cause the current wrapper to remove itself from its parent wrapper. Use this method if you'd like to get rid of an element in a given DOM structure.
  2208     *
  2209     * @memberof Chartist.Svg
  2210     * @return {Chartist.Svg} The parent wrapper object of the element that got removed
  2211     */
  2212    function remove() {
  2213      this._node.parentNode.removeChild(this._node);
  2214      return this.parent();
  2215    }
  2216  
  2217    /**
  2218     * This method will replace the element with a new element that can be created outside of the current DOM.
  2219     *
  2220     * @memberof Chartist.Svg
  2221     * @param {Chartist.Svg} newElement The new Chartist.Svg object that will be used to replace the current wrapper object
  2222     * @return {Chartist.Svg} The wrapper of the new element
  2223     */
  2224    function replace(newElement) {
  2225      this._node.parentNode.replaceChild(newElement._node, this._node);
  2226      return newElement;
  2227    }
  2228  
  2229    /**
  2230     * This method will append an element to the current element as a child.
  2231     *
  2232     * @memberof Chartist.Svg
  2233     * @param {Chartist.Svg} element The Chartist.Svg element that should be added as a child
  2234     * @param {Boolean} [insertFirst] Specifies if the element should be inserted as first child
  2235     * @return {Chartist.Svg} The wrapper of the appended object
  2236     */
  2237    function append(element, insertFirst) {
  2238      if(insertFirst && this._node.firstChild) {
  2239        this._node.insertBefore(element._node, this._node.firstChild);
  2240      } else {
  2241        this._node.appendChild(element._node);
  2242      }
  2243  
  2244      return this;
  2245    }
  2246  
  2247    /**
  2248     * Returns an array of class names that are attached to the current wrapper element. This method can not be chained further.
  2249     *
  2250     * @memberof Chartist.Svg
  2251     * @return {Array} A list of classes or an empty array if there are no classes on the current element
  2252     */
  2253    function classes() {
  2254      return this._node.getAttribute('class') ? this._node.getAttribute('class').trim().split(/\s+/) : [];
  2255    }
  2256  
  2257    /**
  2258     * Adds one or a space separated list of classes to the current element and ensures the classes are only existing once.
  2259     *
  2260     * @memberof Chartist.Svg
  2261     * @param {String} names A white space separated list of class names
  2262     * @return {Chartist.Svg} The wrapper of the current element
  2263     */
  2264    function addClass(names) {
  2265      this._node.setAttribute('class',
  2266        this.classes(this._node)
  2267          .concat(names.trim().split(/\s+/))
  2268          .filter(function(elem, pos, self) {
  2269            return self.indexOf(elem) === pos;
  2270          }).join(' ')
  2271      );
  2272  
  2273      return this;
  2274    }
  2275  
  2276    /**
  2277     * Removes one or a space separated list of classes from the current element.
  2278     *
  2279     * @memberof Chartist.Svg
  2280     * @param {String} names A white space separated list of class names
  2281     * @return {Chartist.Svg} The wrapper of the current element
  2282     */
  2283    function removeClass(names) {
  2284      var removedClasses = names.trim().split(/\s+/);
  2285  
  2286      this._node.setAttribute('class', this.classes(this._node).filter(function(name) {
  2287        return removedClasses.indexOf(name) === -1;
  2288      }).join(' '));
  2289  
  2290      return this;
  2291    }
  2292  
  2293    /**
  2294     * Removes all classes from the current element.
  2295     *
  2296     * @memberof Chartist.Svg
  2297     * @return {Chartist.Svg} The wrapper of the current element
  2298     */
  2299    function removeAllClasses() {
  2300      this._node.setAttribute('class', '');
  2301  
  2302      return this;
  2303    }
  2304  
  2305    /**
  2306     * Get element height using `getBoundingClientRect`
  2307     *
  2308     * @memberof Chartist.Svg
  2309     * @return {Number} The elements height in pixels
  2310     */
  2311    function height() {
  2312      return this._node.getBoundingClientRect().height;
  2313    }
  2314  
  2315    /**
  2316     * Get element width using `getBoundingClientRect`
  2317     *
  2318     * @memberof Chartist.Core
  2319     * @return {Number} The elements width in pixels
  2320     */
  2321    function width() {
  2322      return this._node.getBoundingClientRect().width;
  2323    }
  2324  
  2325    /**
  2326     * The animate function lets you animate the current element with SMIL animations. You can add animations for multiple attributes at the same time by using an animation definition object. This object should contain SMIL animation attributes. Please refer to http://www.w3.org/TR/SVG/animate.html for a detailed specification about the available animation attributes. Additionally an easing property can be passed in the animation definition object. This can be a string with a name of an easing function in `Chartist.Svg.Easing` or an array with four numbers specifying a cubic Bézier curve.
  2327     * **An animations object could look like this:**
  2328     * ```javascript
  2329     * element.animate({
  2330     *   opacity: {
  2331     *     dur: 1000,
  2332     *     from: 0,
  2333     *     to: 1
  2334     *   },
  2335     *   x1: {
  2336     *     dur: '1000ms',
  2337     *     from: 100,
  2338     *     to: 200,
  2339     *     easing: 'easeOutQuart'
  2340     *   },
  2341     *   y1: {
  2342     *     dur: '2s',
  2343     *     from: 0,
  2344     *     to: 100
  2345     *   }
  2346     * });
  2347     * ```
  2348     * **Automatic unit conversion**
  2349     * For the `dur` and the `begin` animate attribute you can also omit a unit by passing a number. The number will automatically be converted to milli seconds.
  2350     * **Guided mode**
  2351     * The default behavior of SMIL animations with offset using the `begin` attribute is that the attribute will keep it's original value until the animation starts. Mostly this behavior is not desired as you'd like to have your element attributes already initialized with the animation `from` value even before the animation starts. Also if you don't specify `fill="freeze"` on an animate element or if you delete the animation after it's done (which is done in guided mode) the attribute will switch back to the initial value. This behavior is also not desired when performing simple one-time animations. For one-time animations you'd want to trigger animations immediately instead of relative to the document begin time. That's why in guided mode Chartist.Svg will also use the `begin` property to schedule a timeout and manually start the animation after the timeout. If you're using multiple SMIL definition objects for an attribute (in an array), guided mode will be disabled for this attribute, even if you explicitly enabled it.
  2352     * If guided mode is enabled the following behavior is added:
  2353     * - Before the animation starts (even when delayed with `begin`) the animated attribute will be set already to the `from` value of the animation
  2354     * - `begin` is explicitly set to `indefinite` so it can be started manually without relying on document begin time (creation)
  2355     * - The animate element will be forced to use `fill="freeze"`
  2356     * - The animation will be triggered with `beginElement()` in a timeout where `begin` of the definition object is interpreted in milli seconds. If no `begin` was specified the timeout is triggered immediately.
  2357     * - After the animation the element attribute value will be set to the `to` value of the animation
  2358     * - The animate element is deleted from the DOM
  2359     *
  2360     * @memberof Chartist.Svg
  2361     * @param {Object} animations An animations object where the property keys are the attributes you'd like to animate. The properties should be objects again that contain the SMIL animation attributes (usually begin, dur, from, and to). The property begin and dur is auto converted (see Automatic unit conversion). You can also schedule multiple animations for the same attribute by passing an Array of SMIL definition objects. Attributes that contain an array of SMIL definition objects will not be executed in guided mode.
  2362     * @param {Boolean} guided Specify if guided mode should be activated for this animation (see Guided mode). If not otherwise specified, guided mode will be activated.
  2363     * @param {Object} eventEmitter If specified, this event emitter will be notified when an animation starts or ends.
  2364     * @return {Chartist.Svg} The current element where the animation was added
  2365     */
  2366    function animate(animations, guided, eventEmitter) {
  2367      if(guided === undefined) {
  2368        guided = true;
  2369      }
  2370  
  2371      Object.keys(animations).forEach(function createAnimateForAttributes(attribute) {
  2372  
  2373        function createAnimate(animationDefinition, guided) {
  2374          var attributeProperties = {},
  2375            animate,
  2376            timeout,
  2377            easing;
  2378  
  2379          // Check if an easing is specified in the definition object and delete it from the object as it will not
  2380          // be part of the animate element attributes.
  2381          if(animationDefinition.easing) {
  2382            // If already an easing Bézier curve array we take it or we lookup a easing array in the Easing object
  2383            easing = animationDefinition.easing instanceof Array ?
  2384              animationDefinition.easing :
  2385              Chartist.Svg.Easing[animationDefinition.easing];
  2386            delete animationDefinition.easing;
  2387          }
  2388  
  2389          // If numeric dur or begin was provided we assume milli seconds
  2390          animationDefinition.begin = Chartist.ensureUnit(animationDefinition.begin, 'ms');
  2391          animationDefinition.dur = Chartist.ensureUnit(animationDefinition.dur, 'ms');
  2392  
  2393          if(easing) {
  2394            animationDefinition.calcMode = 'spline';
  2395            animationDefinition.keySplines = easing.join(' ');
  2396            animationDefinition.keyTimes = '0;1';
  2397          }
  2398  
  2399          // Adding "fill: freeze" if we are in guided mode and set initial attribute values
  2400          if(guided) {
  2401            animationDefinition.fill = 'freeze';
  2402            // Animated property on our element should already be set to the animation from value in guided mode
  2403            attributeProperties[attribute] = animationDefinition.from;
  2404            this.attr(attributeProperties);
  2405  
  2406            // In guided mode we also set begin to indefinite so we can trigger the start manually and put the begin
  2407            // which needs to be in ms aside
  2408            timeout = Chartist.quantity(animationDefinition.begin || 0).value;
  2409            animationDefinition.begin = 'indefinite';
  2410          }
  2411  
  2412          animate = this.elem('animate', Chartist.extend({
  2413            attributeName: attribute
  2414          }, animationDefinition));
  2415  
  2416          if(guided) {
  2417            // If guided we take the value that was put aside in timeout and trigger the animation manually with a timeout
  2418            setTimeout(function() {
  2419              // If beginElement fails we set the animated attribute to the end position and remove the animate element
  2420              // This happens if the SMIL ElementTimeControl interface is not supported or any other problems occured in
  2421              // the browser. (Currently FF 34 does not support animate elements in foreignObjects)
  2422              try {
  2423                animate._node.beginElement();
  2424              } catch(err) {
  2425                // Set animated attribute to current animated value
  2426                attributeProperties[attribute] = animationDefinition.to;
  2427                this.attr(attributeProperties);
  2428                // Remove the animate element as it's no longer required
  2429                animate.remove();
  2430              }
  2431            }.bind(this), timeout);
  2432          }
  2433  
  2434          if(eventEmitter) {
  2435            animate._node.addEventListener('beginEvent', function handleBeginEvent() {
  2436              eventEmitter.emit('animationBegin', {
  2437                element: this,
  2438                animate: animate._node,
  2439                params: animationDefinition
  2440              });
  2441            }.bind(this));
  2442          }
  2443  
  2444          animate._node.addEventListener('endEvent', function handleEndEvent() {
  2445            if(eventEmitter) {
  2446              eventEmitter.emit('animationEnd', {
  2447                element: this,
  2448                animate: animate._node,
  2449                params: animationDefinition
  2450              });
  2451            }
  2452  
  2453            if(guided) {
  2454              // Set animated attribute to current animated value
  2455              attributeProperties[attribute] = animationDefinition.to;
  2456              this.attr(attributeProperties);
  2457              // Remove the animate element as it's no longer required
  2458              animate.remove();
  2459            }
  2460          }.bind(this));
  2461        }
  2462  
  2463        // If current attribute is an array of definition objects we create an animate for each and disable guided mode
  2464        if(animations[attribute] instanceof Array) {
  2465          animations[attribute].forEach(function(animationDefinition) {
  2466            createAnimate.bind(this)(animationDefinition, false);
  2467          }.bind(this));
  2468        } else {
  2469          createAnimate.bind(this)(animations[attribute], guided);
  2470        }
  2471  
  2472      }.bind(this));
  2473  
  2474      return this;
  2475    }
  2476  
  2477    Chartist.Svg = Chartist.Class.extend({
  2478      constructor: Svg,
  2479      attr: attr,
  2480      elem: elem,
  2481      parent: parent,
  2482      root: root,
  2483      querySelector: querySelector,
  2484      querySelectorAll: querySelectorAll,
  2485      getNode: getNode,
  2486      foreignObject: foreignObject,
  2487      text: text,
  2488      empty: empty,
  2489      remove: remove,
  2490      replace: replace,
  2491      append: append,
  2492      classes: classes,
  2493      addClass: addClass,
  2494      removeClass: removeClass,
  2495      removeAllClasses: removeAllClasses,
  2496      height: height,
  2497      width: width,
  2498      animate: animate
  2499    });
  2500  
  2501    /**
  2502     * This method checks for support of a given SVG feature like Extensibility, SVG-animation or the like. Check http://www.w3.org/TR/SVG11/feature for a detailed list.
  2503     *
  2504     * @memberof Chartist.Svg
  2505     * @param {String} feature The SVG 1.1 feature that should be checked for support.
  2506     * @return {Boolean} True of false if the feature is supported or not
  2507     */
  2508    Chartist.Svg.isSupported = function(feature) {
  2509      return document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#' + feature, '1.1');
  2510    };
  2511  
  2512    /**
  2513     * This Object contains some standard easing cubic bezier curves. Then can be used with their name in the `Chartist.Svg.animate`. You can also extend the list and use your own name in the `animate` function. Click the show code button to see the available bezier functions.
  2514     *
  2515     * @memberof Chartist.Svg
  2516     */
  2517    var easingCubicBeziers = {
  2518      easeInSine: [0.47, 0, 0.745, 0.715],
  2519      easeOutSine: [0.39, 0.575, 0.565, 1],
  2520      easeInOutSine: [0.445, 0.05, 0.55, 0.95],
  2521      easeInQuad: [0.55, 0.085, 0.68, 0.53],
  2522      easeOutQuad: [0.25, 0.46, 0.45, 0.94],
  2523      easeInOutQuad: [0.455, 0.03, 0.515, 0.955],
  2524      easeInCubic: [0.55, 0.055, 0.675, 0.19],
  2525      easeOutCubic: [0.215, 0.61, 0.355, 1],
  2526      easeInOutCubic: [0.645, 0.045, 0.355, 1],
  2527      easeInQuart: [0.895, 0.03, 0.685, 0.22],
  2528      easeOutQuart: [0.165, 0.84, 0.44, 1],
  2529      easeInOutQuart: [0.77, 0, 0.175, 1],
  2530      easeInQuint: [0.755, 0.05, 0.855, 0.06],
  2531      easeOutQuint: [0.23, 1, 0.32, 1],
  2532      easeInOutQuint: [0.86, 0, 0.07, 1],
  2533      easeInExpo: [0.95, 0.05, 0.795, 0.035],
  2534      easeOutExpo: [0.19, 1, 0.22, 1],
  2535      easeInOutExpo: [1, 0, 0, 1],
  2536      easeInCirc: [0.6, 0.04, 0.98, 0.335],
  2537      easeOutCirc: [0.075, 0.82, 0.165, 1],
  2538      easeInOutCirc: [0.785, 0.135, 0.15, 0.86],
  2539      easeInBack: [0.6, -0.28, 0.735, 0.045],
  2540      easeOutBack: [0.175, 0.885, 0.32, 1.275],
  2541      easeInOutBack: [0.68, -0.55, 0.265, 1.55]
  2542    };
  2543  
  2544    Chartist.Svg.Easing = easingCubicBeziers;
  2545  
  2546    /**
  2547     * This helper class is to wrap multiple `Chartist.Svg` elements into a list where you can call the `Chartist.Svg` functions on all elements in the list with one call. This is helpful when you'd like to perform calls with `Chartist.Svg` on multiple elements.
  2548     * An instance of this class is also returned by `Chartist.Svg.querySelectorAll`.
  2549     *
  2550     * @memberof Chartist.Svg
  2551     * @param {Array<Node>|NodeList} nodeList An Array of SVG DOM nodes or a SVG DOM NodeList (as returned by document.querySelectorAll)
  2552     * @constructor
  2553     */
  2554    function SvgList(nodeList) {
  2555      var list = this;
  2556  
  2557      this.svgElements = [];
  2558      for(var i = 0; i < nodeList.length; i++) {
  2559        this.svgElements.push(new Chartist.Svg(nodeList[i]));
  2560      }
  2561  
  2562      // Add delegation methods for Chartist.Svg
  2563      Object.keys(Chartist.Svg.prototype).filter(function(prototypeProperty) {
  2564        return ['constructor',
  2565            'parent',
  2566            'querySelector',
  2567            'querySelectorAll',
  2568            'replace',
  2569            'append',
  2570            'classes',
  2571            'height',
  2572            'width'].indexOf(prototypeProperty) === -1;
  2573      }).forEach(function(prototypeProperty) {
  2574        list[prototypeProperty] = function() {
  2575          var args = Array.prototype.slice.call(arguments, 0);
  2576          list.svgElements.forEach(function(element) {
  2577            Chartist.Svg.prototype[prototypeProperty].apply(element, args);
  2578          });
  2579          return list;
  2580        };
  2581      });
  2582    }
  2583  
  2584    Chartist.Svg.List = Chartist.Class.extend({
  2585      constructor: SvgList
  2586    });
  2587  }(window, document, Chartist));
  2588  ;/**
  2589   * Chartist SVG path module for SVG path description creation and modification.
  2590   *
  2591   * @module Chartist.Svg.Path
  2592   */
  2593  /* global Chartist */
  2594  (function(window, document, Chartist) {
  2595    'use strict';
  2596  
  2597    /**
  2598     * Contains the descriptors of supported element types in a SVG path. Currently only move, line and curve are supported.
  2599     *
  2600     * @memberof Chartist.Svg.Path
  2601     * @type {Object}
  2602     */
  2603    var elementDescriptions = {
  2604      m: ['x', 'y'],
  2605      l: ['x', 'y'],
  2606      c: ['x1', 'y1', 'x2', 'y2', 'x', 'y'],
  2607      a: ['rx', 'ry', 'xAr', 'lAf', 'sf', 'x', 'y']
  2608    };
  2609  
  2610    /**
  2611     * Default options for newly created SVG path objects.
  2612     *
  2613     * @memberof Chartist.Svg.Path
  2614     * @type {Object}
  2615     */
  2616    var defaultOptions = {
  2617      // The accuracy in digit count after the decimal point. This will be used to round numbers in the SVG path. If this option is set to false then no rounding will be performed.
  2618      accuracy: 3
  2619    };
  2620  
  2621    function element(command, params, pathElements, pos, relative, data) {
  2622      var pathElement = Chartist.extend({
  2623        command: relative ? command.toLowerCase() : command.toUpperCase()
  2624      }, params, data ? { data: data } : {} );
  2625  
  2626      pathElements.splice(pos, 0, pathElement);
  2627    }
  2628  
  2629    function forEachParam(pathElements, cb) {
  2630      pathElements.forEach(function(pathElement, pathElementIndex) {
  2631        elementDescriptions[pathElement.command.toLowerCase()].forEach(function(paramName, paramIndex) {
  2632          cb(pathElement, paramName, pathElementIndex, paramIndex, pathElements);
  2633        });
  2634      });
  2635    }
  2636  
  2637    /**
  2638     * Used to construct a new path object.
  2639     *
  2640     * @memberof Chartist.Svg.Path
  2641     * @param {Boolean} close If set to true then this path will be closed when stringified (with a Z at the end)
  2642     * @param {Object} options Options object that overrides the default objects. See default options for more details.
  2643     * @constructor
  2644     */
  2645    function SvgPath(close, options) {
  2646      this.pathElements = [];
  2647      this.pos = 0;
  2648      this.close = close;
  2649      this.options = Chartist.extend({}, defaultOptions, options);
  2650    }
  2651  
  2652    /**
  2653     * Gets or sets the current position (cursor) inside of the path. You can move around the cursor freely but limited to 0 or the count of existing elements. All modifications with element functions will insert new elements at the position of this cursor.
  2654     *
  2655     * @memberof Chartist.Svg.Path
  2656     * @param {Number} [pos] If a number is passed then the cursor is set to this position in the path element array.
  2657     * @return {Chartist.Svg.Path|Number} If the position parameter was passed then the return value will be the path object for easy call chaining. If no position parameter was passed then the current position is returned.
  2658     */
  2659    function position(pos) {
  2660      if(pos !== undefined) {
  2661        this.pos = Math.max(0, Math.min(this.pathElements.length, pos));
  2662        return this;
  2663      } else {
  2664        return this.pos;
  2665      }
  2666    }
  2667  
  2668    /**
  2669     * Removes elements from the path starting at the current position.
  2670     *
  2671     * @memberof Chartist.Svg.Path
  2672     * @param {Number} count Number of path elements that should be removed from the current position.
  2673     * @return {Chartist.Svg.Path} The current path object for easy call chaining.
  2674     */
  2675    function remove(count) {
  2676      this.pathElements.splice(this.pos, count);
  2677      return this;
  2678    }
  2679  
  2680    /**
  2681     * Use this function to add a new move SVG path element.
  2682     *
  2683     * @memberof Chartist.Svg.Path
  2684     * @param {Number} x The x coordinate for the move element.
  2685     * @param {Number} y The y coordinate for the move element.
  2686     * @param {Boolean} [relative] If set to true the move element will be created with relative coordinates (lowercase letter)
  2687     * @param {*} [data] Any data that should be stored with the element object that will be accessible in pathElement
  2688     * @return {Chartist.Svg.Path} The current path object for easy call chaining.
  2689     */
  2690    function move(x, y, relative, data) {
  2691      element('M', {
  2692        x: +x,
  2693        y: +y
  2694      }, this.pathElements, this.pos++, relative, data);
  2695      return this;
  2696    }
  2697  
  2698    /**
  2699     * Use this function to add a new line SVG path element.
  2700     *
  2701     * @memberof Chartist.Svg.Path
  2702     * @param {Number} x The x coordinate for the line element.
  2703     * @param {Number} y The y coordinate for the line element.
  2704     * @param {Boolean} [relative] If set to true the line element will be created with relative coordinates (lowercase letter)
  2705     * @param {*} [data] Any data that should be stored with the element object that will be accessible in pathElement
  2706     * @return {Chartist.Svg.Path} The current path object for easy call chaining.
  2707     */
  2708    function line(x, y, relative, data) {
  2709      element('L', {
  2710        x: +x,
  2711        y: +y
  2712      }, this.pathElements, this.pos++, relative, data);
  2713      return this;
  2714    }
  2715  
  2716    /**
  2717     * Use this function to add a new curve SVG path element.
  2718     *
  2719     * @memberof Chartist.Svg.Path
  2720     * @param {Number} x1 The x coordinate for the first control point of the bezier curve.
  2721     * @param {Number} y1 The y coordinate for the first control point of the bezier curve.
  2722     * @param {Number} x2 The x coordinate for the second control point of the bezier curve.
  2723     * @param {Number} y2 The y coordinate for the second control point of the bezier curve.
  2724     * @param {Number} x The x coordinate for the target point of the curve element.
  2725     * @param {Number} y The y coordinate for the target point of the curve element.
  2726     * @param {Boolean} [relative] If set to true the curve element will be created with relative coordinates (lowercase letter)
  2727     * @param {*} [data] Any data that should be stored with the element object that will be accessible in pathElement
  2728     * @return {Chartist.Svg.Path} The current path object for easy call chaining.
  2729     */
  2730    function curve(x1, y1, x2, y2, x, y, relative, data) {
  2731      element('C', {
  2732        x1: +x1,
  2733        y1: +y1,
  2734        x2: +x2,
  2735        y2: +y2,
  2736        x: +x,
  2737        y: +y
  2738      }, this.pathElements, this.pos++, relative, data);
  2739      return this;
  2740    }
  2741  
  2742    /**
  2743     * Use this function to add a new non-bezier curve SVG path element.
  2744     *
  2745     * @memberof Chartist.Svg.Path
  2746     * @param {Number} rx The radius to be used for the x-axis of the arc.
  2747     * @param {Number} ry The radius to be used for the y-axis of the arc.
  2748     * @param {Number} xAr Defines the orientation of the arc
  2749     * @param {Number} lAf Large arc flag
  2750     * @param {Number} sf Sweep flag
  2751     * @param {Number} x The x coordinate for the target point of the curve element.
  2752     * @param {Number} y The y coordinate for the target point of the curve element.
  2753     * @param {Boolean} [relative] If set to true the curve element will be created with relative coordinates (lowercase letter)
  2754     * @param {*} [data] Any data that should be stored with the element object that will be accessible in pathElement
  2755     * @return {Chartist.Svg.Path} The current path object for easy call chaining.
  2756     */
  2757    function arc(rx, ry, xAr, lAf, sf, x, y, relative, data) {
  2758      element('A', {
  2759        rx: +rx,
  2760        ry: +ry,
  2761        xAr: +xAr,
  2762        lAf: +lAf,
  2763        sf: +sf,
  2764        x: +x,
  2765        y: +y
  2766      }, this.pathElements, this.pos++, relative, data);
  2767      return this;
  2768    }
  2769  
  2770    /**
  2771     * Parses an SVG path seen in the d attribute of path elements, and inserts the parsed elements into the existing path object at the current cursor position. Any closing path indicators (Z at the end of the path) will be ignored by the parser as this is provided by the close option in the options of the path object.
  2772     *
  2773     * @memberof Chartist.Svg.Path
  2774     * @param {String} path Any SVG path that contains move (m), line (l) or curve (c) components.
  2775     * @return {Chartist.Svg.Path} The current path object for easy call chaining.
  2776     */
  2777    function parse(path) {
  2778      // Parsing the SVG path string into an array of arrays [['M', '10', '10'], ['L', '100', '100']]
  2779      var chunks = path.replace(/([A-Za-z])([0-9])/g, '$1 $2')
  2780        .replace(/([0-9])([A-Za-z])/g, '$1 $2')
  2781        .split(/[\s,]+/)
  2782        .reduce(function(result, element) {
  2783          if(element.match(/[A-Za-z]/)) {
  2784            result.push([]);
  2785          }
  2786  
  2787          result[result.length - 1].push(element);
  2788          return result;
  2789        }, []);
  2790  
  2791      // If this is a closed path we remove the Z at the end because this is determined by the close option
  2792      if(chunks[chunks.length - 1][0].toUpperCase() === 'Z') {
  2793        chunks.pop();
  2794      }
  2795  
  2796      // Using svgPathElementDescriptions to map raw path arrays into objects that contain the command and the parameters
  2797      // For example {command: 'M', x: '10', y: '10'}
  2798      var elements = chunks.map(function(chunk) {
  2799          var command = chunk.shift(),
  2800            description = elementDescriptions[command.toLowerCase()];
  2801  
  2802          return Chartist.extend({
  2803            command: command
  2804          }, description.reduce(function(result, paramName, index) {
  2805            result[paramName] = +chunk[index];
  2806            return result;
  2807          }, {}));
  2808        });
  2809  
  2810      // Preparing a splice call with the elements array as var arg params and insert the parsed elements at the current position
  2811      var spliceArgs = [this.pos, 0];
  2812      Array.prototype.push.apply(spliceArgs, elements);
  2813      Array.prototype.splice.apply(this.pathElements, spliceArgs);
  2814      // Increase the internal position by the element count
  2815      this.pos += elements.length;
  2816  
  2817      return this;
  2818    }
  2819  
  2820    /**
  2821     * This function renders to current SVG path object into a final SVG string that can be used in the d attribute of SVG path elements. It uses the accuracy option to round big decimals. If the close parameter was set in the constructor of this path object then a path closing Z will be appended to the output string.
  2822     *
  2823     * @memberof Chartist.Svg.Path
  2824     * @return {String}
  2825     */
  2826    function stringify() {
  2827      var accuracyMultiplier = Math.pow(10, this.options.accuracy);
  2828  
  2829      return this.pathElements.reduce(function(path, pathElement) {
  2830          var params = elementDescriptions[pathElement.command.toLowerCase()].map(function(paramName) {
  2831            return this.options.accuracy ?
  2832              (Math.round(pathElement[paramName] * accuracyMultiplier) / accuracyMultiplier) :
  2833              pathElement[paramName];
  2834          }.bind(this));
  2835  
  2836          return path + pathElement.command + params.join(',');
  2837        }.bind(this), '') + (this.close ? 'Z' : '');
  2838    }
  2839  
  2840    /**
  2841     * Scales all elements in the current SVG path object. There is an individual parameter for each coordinate. Scaling will also be done for control points of curves, affecting the given coordinate.
  2842     *
  2843     * @memberof Chartist.Svg.Path
  2844     * @param {Number} x The number which will be used to scale the x, x1 and x2 of all path elements.
  2845     * @param {Number} y The number which will be used to scale the y, y1 and y2 of all path elements.
  2846     * @return {Chartist.Svg.Path} The current path object for easy call chaining.
  2847     */
  2848    function scale(x, y) {
  2849      forEachParam(this.pathElements, function(pathElement, paramName) {
  2850        pathElement[paramName] *= paramName[0] === 'x' ? x : y;
  2851      });
  2852      return this;
  2853    }
  2854  
  2855    /**
  2856     * Translates all elements in the current SVG path object. The translation is relative and there is an individual parameter for each coordinate. Translation will also be done for control points of curves, affecting the given coordinate.
  2857     *
  2858     * @memberof Chartist.Svg.Path
  2859     * @param {Number} x The number which will be used to translate the x, x1 and x2 of all path elements.
  2860     * @param {Number} y The number which will be used to translate the y, y1 and y2 of all path elements.
  2861     * @return {Chartist.Svg.Path} The current path object for easy call chaining.
  2862     */
  2863    function translate(x, y) {
  2864      forEachParam(this.pathElements, function(pathElement, paramName) {
  2865        pathElement[paramName] += paramName[0] === 'x' ? x : y;
  2866      });
  2867      return this;
  2868    }
  2869  
  2870    /**
  2871     * This function will run over all existing path elements and then loop over their attributes. The callback function will be called for every path element attribute that exists in the current path.
  2872     * The method signature of the callback function looks like this:
  2873     * ```javascript
  2874     * function(pathElement, paramName, pathElementIndex, paramIndex, pathElements)
  2875     * ```
  2876     * If something else than undefined is returned by the callback function, this value will be used to replace the old value. This allows you to build custom transformations of path objects that can't be achieved using the basic transformation functions scale and translate.
  2877     *
  2878     * @memberof Chartist.Svg.Path
  2879     * @param {Function} transformFnc The callback function for the transformation. Check the signature in the function description.
  2880     * @return {Chartist.Svg.Path} The current path object for easy call chaining.
  2881     */
  2882    function transform(transformFnc) {
  2883      forEachParam(this.pathElements, function(pathElement, paramName, pathElementIndex, paramIndex, pathElements) {
  2884        var transformed = transformFnc(pathElement, paramName, pathElementIndex, paramIndex, pathElements);
  2885        if(transformed || transformed === 0) {
  2886          pathElement[paramName] = transformed;
  2887        }
  2888      });
  2889      return this;
  2890    }
  2891  
  2892    /**
  2893     * This function clones a whole path object with all its properties. This is a deep clone and path element objects will also be cloned.
  2894     *
  2895     * @memberof Chartist.Svg.Path
  2896     * @param {Boolean} [close] Optional option to set the new cloned path to closed. If not specified or false, the original path close option will be used.
  2897     * @return {Chartist.Svg.Path}
  2898     */
  2899    function clone(close) {
  2900      var c = new Chartist.Svg.Path(close || this.close);
  2901      c.pos = this.pos;
  2902      c.pathElements = this.pathElements.slice().map(function cloneElements(pathElement) {
  2903        return Chartist.extend({}, pathElement);
  2904      });
  2905      c.options = Chartist.extend({}, this.options);
  2906      return c;
  2907    }
  2908  
  2909    /**
  2910     * Split a Svg.Path object by a specific command in the path chain. The path chain will be split and an array of newly created paths objects will be returned. This is useful if you'd like to split an SVG path by it's move commands, for example, in order to isolate chunks of drawings.
  2911     *
  2912     * @memberof Chartist.Svg.Path
  2913     * @param {String} command The command you'd like to use to split the path
  2914     * @return {Array<Chartist.Svg.Path>}
  2915     */
  2916    function splitByCommand(command) {
  2917      var split = [
  2918        new Chartist.Svg.Path()
  2919      ];
  2920  
  2921      this.pathElements.forEach(function(pathElement) {
  2922        if(pathElement.command === command.toUpperCase() && split[split.length - 1].pathElements.length !== 0) {
  2923          split.push(new Chartist.Svg.Path());
  2924        }
  2925  
  2926        split[split.length - 1].pathElements.push(pathElement);
  2927      });
  2928  
  2929      return split;
  2930    }
  2931  
  2932    /**
  2933     * This static function on `Chartist.Svg.Path` is joining multiple paths together into one paths.
  2934     *
  2935     * @memberof Chartist.Svg.Path
  2936     * @param {Array<Chartist.Svg.Path>} paths A list of paths to be joined together. The order is important.
  2937     * @param {boolean} close If the newly created path should be a closed path
  2938     * @param {Object} options Path options for the newly created path.
  2939     * @return {Chartist.Svg.Path}
  2940     */
  2941  
  2942    function join(paths, close, options) {
  2943      var joinedPath = new Chartist.Svg.Path(close, options);
  2944      for(var i = 0; i < paths.length; i++) {
  2945        var path = paths[i];
  2946        for(var j = 0; j < path.pathElements.length; j++) {
  2947          joinedPath.pathElements.push(path.pathElements[j]);
  2948        }
  2949      }
  2950      return joinedPath;
  2951    }
  2952  
  2953    Chartist.Svg.Path = Chartist.Class.extend({
  2954      constructor: SvgPath,
  2955      position: position,
  2956      remove: remove,
  2957      move: move,
  2958      line: line,
  2959      curve: curve,
  2960      arc: arc,
  2961      scale: scale,
  2962      translate: translate,
  2963      transform: transform,
  2964      parse: parse,
  2965      stringify: stringify,
  2966      clone: clone,
  2967      splitByCommand: splitByCommand
  2968    });
  2969  
  2970    Chartist.Svg.Path.elementDescriptions = elementDescriptions;
  2971    Chartist.Svg.Path.join = join;
  2972  }(window, document, Chartist));
  2973  ;/* global Chartist */
  2974  (function (window, document, Chartist) {
  2975    'use strict';
  2976  
  2977    var axisUnits = {
  2978      x: {
  2979        pos: 'x',
  2980        len: 'width',
  2981        dir: 'horizontal',
  2982        rectStart: 'x1',
  2983        rectEnd: 'x2',
  2984        rectOffset: 'y2'
  2985      },
  2986      y: {
  2987        pos: 'y',
  2988        len: 'height',
  2989        dir: 'vertical',
  2990        rectStart: 'y2',
  2991        rectEnd: 'y1',
  2992        rectOffset: 'x1'
  2993      }
  2994    };
  2995  
  2996    function Axis(units, chartRect, ticks, options) {
  2997      this.units = units;
  2998      this.counterUnits = units === axisUnits.x ? axisUnits.y : axisUnits.x;
  2999      this.chartRect = chartRect;
  3000      this.axisLength = chartRect[units.rectEnd] - chartRect[units.rectStart];
  3001      this.gridOffset = chartRect[units.rectOffset];
  3002      this.ticks = ticks;
  3003      this.options = options;
  3004    }
  3005  
  3006    function createGridAndLabels(gridGroup, labelGroup, useForeignObject, chartOptions, eventEmitter) {
  3007      var axisOptions = chartOptions['axis' + this.units.pos.toUpperCase()];
  3008      var projectedValues = this.ticks.map(this.projectValue.bind(this));
  3009      var labelValues = this.ticks.map(axisOptions.labelInterpolationFnc);
  3010  
  3011      projectedValues.forEach(function(projectedValue, index) {
  3012        var labelOffset = {
  3013          x: 0,
  3014          y: 0
  3015        };
  3016  
  3017        // TODO: Find better solution for solving this problem
  3018        // Calculate how much space we have available for the label
  3019        var labelLength;
  3020        if(projectedValues[index + 1]) {
  3021          // If we still have one label ahead, we can calculate the distance to the next tick / label
  3022          labelLength = projectedValues[index + 1] - projectedValue;
  3023        } else {
  3024          // If we don't have a label ahead and we have only two labels in total, we just take the remaining distance to
  3025          // on the whole axis length. We limit that to a minimum of 30 pixel, so that labels close to the border will
  3026          // still be visible inside of the chart padding.
  3027          labelLength = Math.max(this.axisLength - projectedValue, 30);
  3028        }
  3029  
  3030        // Skip grid lines and labels where interpolated label values are falsey (execpt for 0)
  3031        if(Chartist.isFalseyButZero(labelValues[index]) && labelValues[index] !== '') {
  3032          return;
  3033        }
  3034  
  3035        // Transform to global coordinates using the chartRect
  3036        // We also need to set the label offset for the createLabel function
  3037        if(this.units.pos === 'x') {
  3038          projectedValue = this.chartRect.x1 + projectedValue;
  3039          labelOffset.x = chartOptions.axisX.labelOffset.x;
  3040  
  3041          // If the labels should be positioned in start position (top side for vertical axis) we need to set a
  3042          // different offset as for positioned with end (bottom)
  3043          if(chartOptions.axisX.position === 'start') {
  3044            labelOffset.y = this.chartRect.padding.top + chartOptions.axisX.labelOffset.y + (useForeignObject ? 5 : 20);
  3045          } else {
  3046            labelOffset.y = this.chartRect.y1 + chartOptions.axisX.labelOffset.y + (useForeignObject ? 5 : 20);
  3047          }
  3048        } else {
  3049          projectedValue = this.chartRect.y1 - projectedValue;
  3050          labelOffset.y = chartOptions.axisY.labelOffset.y - (useForeignObject ? labelLength : 0);
  3051  
  3052          // If the labels should be positioned in start position (left side for horizontal axis) we need to set a
  3053          // different offset as for positioned with end (right side)
  3054          if(chartOptions.axisY.position === 'start') {
  3055            labelOffset.x = useForeignObject ? this.chartRect.padding.left + chartOptions.axisY.labelOffset.x : this.chartRect.x1 - 10;
  3056          } else {
  3057            labelOffset.x = this.chartRect.x2 + chartOptions.axisY.labelOffset.x + 10;
  3058          }
  3059        }
  3060  
  3061        if(axisOptions.showGrid) {
  3062          Chartist.createGrid(projectedValue, index, this, this.gridOffset, this.chartRect[this.counterUnits.len](), gridGroup, [
  3063            chartOptions.classNames.grid,
  3064            chartOptions.classNames[this.units.dir]
  3065          ], eventEmitter);
  3066        }
  3067  
  3068        if(axisOptions.showLabel) {
  3069          Chartist.createLabel(projectedValue, labelLength, index, labelValues, this, axisOptions.offset, labelOffset, labelGroup, [
  3070            chartOptions.classNames.label,
  3071            chartOptions.classNames[this.units.dir],
  3072            (axisOptions.position === 'start' ? chartOptions.classNames[axisOptions.position] : chartOptions.classNames['end'])
  3073          ], useForeignObject, eventEmitter);
  3074        }
  3075      }.bind(this));
  3076    }
  3077  
  3078    Chartist.Axis = Chartist.Class.extend({
  3079      constructor: Axis,
  3080      createGridAndLabels: createGridAndLabels,
  3081      projectValue: function(value, index, data) {
  3082        throw new Error('Base axis can\'t be instantiated!');
  3083      }
  3084    });
  3085  
  3086    Chartist.Axis.units = axisUnits;
  3087  
  3088  }(window, document, Chartist));
  3089  ;/**
  3090   * The auto scale axis uses standard linear scale projection of values along an axis. It uses order of magnitude to find a scale automatically and evaluates the available space in order to find the perfect amount of ticks for your chart.
  3091   * **Options**
  3092   * The following options are used by this axis in addition to the default axis options outlined in the axis configuration of the chart default settings.
  3093   * ```javascript
  3094   * var options = {
  3095   *   // If high is specified then the axis will display values explicitly up to this value and the computed maximum from the data is ignored
  3096   *   high: 100,
  3097   *   // If low is specified then the axis will display values explicitly down to this value and the computed minimum from the data is ignored
  3098   *   low: 0,
  3099   *   // This option will be used when finding the right scale division settings. The amount of ticks on the scale will be determined so that as many ticks as possible will be displayed, while not violating this minimum required space (in pixel).
  3100   *   scaleMinSpace: 20,
  3101   *   // Can be set to true or false. If set to true, the scale will be generated with whole numbers only.
  3102   *   onlyInteger: true,
  3103   *   // The reference value can be used to make sure that this value will always be on the chart. This is especially useful on bipolar charts where the bipolar center always needs to be part of the chart.
  3104   *   referenceValue: 5
  3105   * };
  3106   * ```
  3107   *
  3108   * @module Chartist.AutoScaleAxis
  3109   */
  3110  /* global Chartist */
  3111  (function (window, document, Chartist) {
  3112    'use strict';
  3113  
  3114    function AutoScaleAxis(axisUnit, data, chartRect, options) {
  3115      // Usually we calculate highLow based on the data but this can be overriden by a highLow object in the options
  3116      var highLow = options.highLow || Chartist.getHighLow(data, options, axisUnit.pos);
  3117      this.bounds = Chartist.getBounds(chartRect[axisUnit.rectEnd] - chartRect[axisUnit.rectStart], highLow, options.scaleMinSpace || 20, options.onlyInteger);
  3118      this.range = {
  3119        min: this.bounds.min,
  3120        max: this.bounds.max
  3121      };
  3122  
  3123      Chartist.AutoScaleAxis.super.constructor.call(this,
  3124        axisUnit,
  3125        chartRect,
  3126        this.bounds.values,
  3127        options);
  3128    }
  3129  
  3130    function projectValue(value) {
  3131      return this.axisLength * (+Chartist.getMultiValue(value, this.units.pos) - this.bounds.min) / this.bounds.range;
  3132    }
  3133  
  3134    Chartist.AutoScaleAxis = Chartist.Axis.extend({
  3135      constructor: AutoScaleAxis,
  3136      projectValue: projectValue
  3137    });
  3138  
  3139  }(window, document, Chartist));
  3140  ;/**
  3141   * The fixed scale axis uses standard linear projection of values along an axis. It makes use of a divisor option to divide the range provided from the minimum and maximum value or the options high and low that will override the computed minimum and maximum.
  3142   * **Options**
  3143   * The following options are used by this axis in addition to the default axis options outlined in the axis configuration of the chart default settings.
  3144   * ```javascript
  3145   * var options = {
  3146   *   // If high is specified then the axis will display values explicitly up to this value and the computed maximum from the data is ignored
  3147   *   high: 100,
  3148   *   // If low is specified then the axis will display values explicitly down to this value and the computed minimum from the data is ignored
  3149   *   low: 0,
  3150   *   // If specified then the value range determined from minimum to maximum (or low and high) will be divided by this number and ticks will be generated at those division points. The default divisor is 1.
  3151   *   divisor: 4,
  3152   *   // If ticks is explicitly set, then the axis will not compute the ticks with the divisor, but directly use the data in ticks to determine at what points on the axis a tick need to be generated.
  3153   *   ticks: [1, 10, 20, 30]
  3154   * };
  3155   * ```
  3156   *
  3157   * @module Chartist.FixedScaleAxis
  3158   */
  3159  /* global Chartist */
  3160  (function (window, document, Chartist) {
  3161    'use strict';
  3162  
  3163    function FixedScaleAxis(axisUnit, data, chartRect, options) {
  3164      var highLow = options.highLow || Chartist.getHighLow(data, options, axisUnit.pos);
  3165      this.divisor = options.divisor || 1;
  3166      this.ticks = options.ticks || Chartist.times(this.divisor).map(function(value, index) {
  3167        return highLow.low + (highLow.high - highLow.low) / this.divisor * index;
  3168      }.bind(this));
  3169      this.ticks.sort(function(a, b) {
  3170        return a - b;
  3171      });
  3172      this.range = {
  3173        min: highLow.low,
  3174        max: highLow.high
  3175      };
  3176  
  3177      Chartist.FixedScaleAxis.super.constructor.call(this,
  3178        axisUnit,
  3179        chartRect,
  3180        this.ticks,
  3181        options);
  3182  
  3183      this.stepLength = this.axisLength / this.divisor;
  3184    }
  3185  
  3186    function projectValue(value) {
  3187      return this.axisLength * (+Chartist.getMultiValue(value, this.units.pos) - this.range.min) / (this.range.max - this.range.min);
  3188    }
  3189  
  3190    Chartist.FixedScaleAxis = Chartist.Axis.extend({
  3191      constructor: FixedScaleAxis,
  3192      projectValue: projectValue
  3193    });
  3194  
  3195  }(window, document, Chartist));
  3196  ;/**
  3197   * The step axis for step based charts like bar chart or step based line charts. It uses a fixed amount of ticks that will be equally distributed across the whole axis length. The projection is done using the index of the data value rather than the value itself and therefore it's only useful for distribution purpose.
  3198   * **Options**
  3199   * The following options are used by this axis in addition to the default axis options outlined in the axis configuration of the chart default settings.
  3200   * ```javascript
  3201   * var options = {
  3202   *   // Ticks to be used to distribute across the axis length. As this axis type relies on the index of the value rather than the value, arbitrary data that can be converted to a string can be used as ticks.
  3203   *   ticks: ['One', 'Two', 'Three'],
  3204   *   // If set to true the full width will be used to distribute the values where the last value will be at the maximum of the axis length. If false the spaces between the ticks will be evenly distributed instead.
  3205   *   stretch: true
  3206   * };
  3207   * ```
  3208   *
  3209   * @module Chartist.StepAxis
  3210   */
  3211  /* global Chartist */
  3212  (function (window, document, Chartist) {
  3213    'use strict';
  3214  
  3215    function StepAxis(axisUnit, data, chartRect, options) {
  3216      Chartist.StepAxis.super.constructor.call(this,
  3217        axisUnit,
  3218        chartRect,
  3219        options.ticks,
  3220        options);
  3221  
  3222      var calc = Math.max(1, options.ticks.length - (options.stretch ? 1 : 0));
  3223      this.stepLength = this.axisLength / calc;
  3224    }
  3225  
  3226    function projectValue(value, index) {
  3227      return this.stepLength * index;
  3228    }
  3229  
  3230    Chartist.StepAxis = Chartist.Axis.extend({
  3231      constructor: StepAxis,
  3232      projectValue: projectValue
  3233    });
  3234  
  3235  }(window, document, Chartist));
  3236  ;/**
  3237   * The Chartist line chart can be used to draw Line or Scatter charts. If used in the browser you can access the global `Chartist` namespace where you find the `Line` function as a main entry point.
  3238   *
  3239   * For examples on how to use the line chart please check the examples of the `Chartist.Line` method.
  3240   *
  3241   * @module Chartist.Line
  3242   */
  3243  /* global Chartist */
  3244  (function(window, document, Chartist){
  3245    'use strict';
  3246  
  3247    /**
  3248     * Default options in line charts. Expand the code view to see a detailed list of options with comments.
  3249     *
  3250     * @memberof Chartist.Line
  3251     */
  3252    var defaultOptions = {
  3253      // Options for X-Axis
  3254      axisX: {
  3255        // The offset of the labels to the chart area
  3256        offset: 30,
  3257        // Position where labels are placed. Can be set to `start` or `end` where `start` is equivalent to left or top on vertical axis and `end` is equivalent to right or bottom on horizontal axis.
  3258        position: 'end',
  3259        // Allows you to correct label positioning on this axis by positive or negative x and y offset.
  3260        labelOffset: {
  3261          x: 0,
  3262          y: 0
  3263        },
  3264        // If labels should be shown or not
  3265        showLabel: true,
  3266        // If the axis grid should be drawn or not
  3267        showGrid: true,
  3268        // Interpolation function that allows you to intercept the value from the axis label
  3269        labelInterpolationFnc: Chartist.noop,
  3270        // Set the axis type to be used to project values on this axis. If not defined, Chartist.StepAxis will be used for the X-Axis, where the ticks option will be set to the labels in the data and the stretch option will be set to the global fullWidth option. This type can be changed to any axis constructor available (e.g. Chartist.FixedScaleAxis), where all axis options should be present here.
  3271        type: undefined
  3272      },
  3273      // Options for Y-Axis
  3274      axisY: {
  3275        // The offset of the labels to the chart area
  3276        offset: 40,
  3277        // Position where labels are placed. Can be set to `start` or `end` where `start` is equivalent to left or top on vertical axis and `end` is equivalent to right or bottom on horizontal axis.
  3278        position: 'start',
  3279        // Allows you to correct label positioning on this axis by positive or negative x and y offset.
  3280        labelOffset: {
  3281          x: 0,
  3282          y: 0
  3283        },
  3284        // If labels should be shown or not
  3285        showLabel: true,
  3286        // If the axis grid should be drawn or not
  3287        showGrid: true,
  3288        // Interpolation function that allows you to intercept the value from the axis label
  3289        labelInterpolationFnc: Chartist.noop,
  3290        // Set the axis type to be used to project values on this axis. If not defined, Chartist.AutoScaleAxis will be used for the Y-Axis, where the high and low options will be set to the global high and low options. This type can be changed to any axis constructor available (e.g. Chartist.FixedScaleAxis), where all axis options should be present here.
  3291        type: undefined,
  3292        // This value specifies the minimum height in pixel of the scale steps
  3293        scaleMinSpace: 20,
  3294        // Use only integer values (whole numbers) for the scale steps
  3295        onlyInteger: false
  3296      },
  3297      // Specify a fixed width for the chart as a string (i.e. '100px' or '50%')
  3298      width: undefined,
  3299      // Specify a fixed height for the chart as a string (i.e. '100px' or '50%')
  3300      height: undefined,
  3301      // If the line should be drawn or not
  3302      showLine: true,
  3303      // If dots should be drawn or not
  3304      showPoint: true,
  3305      // If the line chart should draw an area
  3306      showArea: false,
  3307      // The base for the area chart that will be used to close the area shape (is normally 0)
  3308      areaBase: 0,
  3309      // Specify if the lines should be smoothed. This value can be true or false where true will result in smoothing using the default smoothing interpolation function Chartist.Interpolation.cardinal and false results in Chartist.Interpolation.none. You can also choose other smoothing / interpolation functions available in the Chartist.Interpolation module, or write your own interpolation function. Check the examples for a brief description.
  3310      lineSmooth: true,
  3311      // If the line chart should add a background fill to the .ct-grids group.
  3312      showGridBackground: false,
  3313      // Overriding the natural low of the chart allows you to zoom in or limit the charts lowest displayed value
  3314      low: undefined,
  3315      // Overriding the natural high of the chart allows you to zoom in or limit the charts highest displayed value
  3316      high: undefined,
  3317      // Padding of the chart drawing area to the container element and labels as a number or padding object {top: 5, right: 5, bottom: 5, left: 5}
  3318      chartPadding: {
  3319        top: 15,
  3320        right: 15,
  3321        bottom: 5,
  3322        left: 10
  3323      },
  3324      // When set to true, the last grid line on the x-axis is not drawn and the chart elements will expand to the full available width of the chart. For the last label to be drawn correctly you might need to add chart padding or offset the last label with a draw event handler.
  3325      fullWidth: false,
  3326      // If true the whole data is reversed including labels, the series order as well as the whole series data arrays.
  3327      reverseData: false,
  3328      // Override the class names that get used to generate the SVG structure of the chart
  3329      classNames: {
  3330        chart: 'ct-chart-line',
  3331        label: 'ct-label',
  3332        labelGroup: 'ct-labels',
  3333        series: 'ct-series',
  3334        line: 'ct-line',
  3335        point: 'ct-point',
  3336        area: 'ct-area',
  3337        grid: 'ct-grid',
  3338        gridGroup: 'ct-grids',
  3339        gridBackground: 'ct-grid-background',
  3340        vertical: 'ct-vertical',
  3341        horizontal: 'ct-horizontal',
  3342        start: 'ct-start',
  3343        end: 'ct-end'
  3344      }
  3345    };
  3346  
  3347    /**
  3348     * Creates a new chart
  3349     *
  3350     */
  3351    function createChart(options) {
  3352      var data = Chartist.normalizeData(this.data, options.reverseData, true);
  3353  
  3354      // Create new svg object
  3355      this.svg = Chartist.createSvg(this.container, options.width, options.height, options.classNames.chart);
  3356      // Create groups for labels, grid and series
  3357      var gridGroup = this.svg.elem('g').addClass(options.classNames.gridGroup);
  3358      var seriesGroup = this.svg.elem('g');
  3359      var labelGroup = this.svg.elem('g').addClass(options.classNames.labelGroup);
  3360  
  3361      var chartRect = Chartist.createChartRect(this.svg, options, defaultOptions.padding);
  3362      var axisX, axisY;
  3363  
  3364      if(options.axisX.type === undefined) {
  3365        axisX = new Chartist.StepAxis(Chartist.Axis.units.x, data.normalized.series, chartRect, Chartist.extend({}, options.axisX, {
  3366          ticks: data.normalized.labels,
  3367          stretch: options.fullWidth
  3368        }));
  3369      } else {
  3370        axisX = options.axisX.type.call(Chartist, Chartist.Axis.units.x, data.normalized.series, chartRect, options.axisX);
  3371      }
  3372  
  3373      if(options.axisY.type === undefined) {
  3374        axisY = new Chartist.AutoScaleAxis(Chartist.Axis.units.y, data.normalized.series, chartRect, Chartist.extend({}, options.axisY, {
  3375          high: Chartist.isNumeric(options.high) ? options.high : options.axisY.high,
  3376          low: Chartist.isNumeric(options.low) ? options.low : options.axisY.low
  3377        }));
  3378      } else {
  3379        axisY = options.axisY.type.call(Chartist, Chartist.Axis.units.y, data.normalized.series, chartRect, options.axisY);
  3380      }
  3381  
  3382      axisX.createGridAndLabels(gridGroup, labelGroup, this.supportsForeignObject, options, this.eventEmitter);
  3383      axisY.createGridAndLabels(gridGroup, labelGroup, this.supportsForeignObject, options, this.eventEmitter);
  3384  
  3385      if (options.showGridBackground) {
  3386        Chartist.createGridBackground(gridGroup, chartRect, options.classNames.gridBackground, this.eventEmitter);
  3387      }
  3388  
  3389      // Draw the series
  3390      data.raw.series.forEach(function(series, seriesIndex) {
  3391        var seriesElement = seriesGroup.elem('g');
  3392  
  3393        // Write attributes to series group element. If series name or meta is undefined the attributes will not be written
  3394        seriesElement.attr({
  3395          'ct:series-name': series.name,
  3396          'ct:meta': Chartist.serialize(series.meta)
  3397        });
  3398  
  3399        // Use series class from series data or if not set generate one
  3400        seriesElement.addClass([
  3401          options.classNames.series,
  3402          (series.className || options.classNames.series + '-' + Chartist.alphaNumerate(seriesIndex))
  3403        ].join(' '));
  3404  
  3405        var pathCoordinates = [],
  3406          pathData = [];
  3407  
  3408        data.normalized.series[seriesIndex].forEach(function(value, valueIndex) {
  3409          var p = {
  3410            x: chartRect.x1 + axisX.projectValue(value, valueIndex, data.normalized.series[seriesIndex]),
  3411            y: chartRect.y1 - axisY.projectValue(value, valueIndex, data.normalized.series[seriesIndex])
  3412          };
  3413          pathCoordinates.push(p.x, p.y);
  3414          pathData.push({
  3415            value: value,
  3416            valueIndex: valueIndex,
  3417            meta: Chartist.getMetaData(series, valueIndex)
  3418          });
  3419        }.bind(this));
  3420  
  3421        var seriesOptions = {
  3422          lineSmooth: Chartist.getSeriesOption(series, options, 'lineSmooth'),
  3423          showPoint: Chartist.getSeriesOption(series, options, 'showPoint'),
  3424          showLine: Chartist.getSeriesOption(series, options, 'showLine'),
  3425          showArea: Chartist.getSeriesOption(series, options, 'showArea'),
  3426          areaBase: Chartist.getSeriesOption(series, options, 'areaBase')
  3427        };
  3428  
  3429        var smoothing = typeof seriesOptions.lineSmooth === 'function' ?
  3430          seriesOptions.lineSmooth : (seriesOptions.lineSmooth ? Chartist.Interpolation.monotoneCubic() : Chartist.Interpolation.none());
  3431        // Interpolating path where pathData will be used to annotate each path element so we can trace back the original
  3432        // index, value and meta data
  3433        var path = smoothing(pathCoordinates, pathData);
  3434  
  3435        // If we should show points we need to create them now to avoid secondary loop
  3436        // Points are drawn from the pathElements returned by the interpolation function
  3437        // Small offset for Firefox to render squares correctly
  3438        if (seriesOptions.showPoint) {
  3439  
  3440          path.pathElements.forEach(function(pathElement) {
  3441            var point = seriesElement.elem('line', {
  3442              x1: pathElement.x,
  3443              y1: pathElement.y,
  3444              x2: pathElement.x + 0.01,
  3445              y2: pathElement.y
  3446            }, options.classNames.point).attr({
  3447              'ct:value': [pathElement.data.value.x, pathElement.data.value.y].filter(Chartist.isNumeric).join(','),
  3448              'ct:meta': Chartist.serialize(pathElement.data.meta)
  3449            });
  3450  
  3451            this.eventEmitter.emit('draw', {
  3452              type: 'point',
  3453              value: pathElement.data.value,
  3454              index: pathElement.data.valueIndex,
  3455              meta: pathElement.data.meta,
  3456              series: series,
  3457              seriesIndex: seriesIndex,
  3458              axisX: axisX,
  3459              axisY: axisY,
  3460              group: seriesElement,
  3461              element: point,
  3462              x: pathElement.x,
  3463              y: pathElement.y
  3464            });
  3465          }.bind(this));
  3466        }
  3467  
  3468        if(seriesOptions.showLine) {
  3469          var line = seriesElement.elem('path', {
  3470            d: path.stringify()
  3471          }, options.classNames.line, true);
  3472  
  3473          this.eventEmitter.emit('draw', {
  3474            type: 'line',
  3475            values: data.normalized.series[seriesIndex],
  3476            path: path.clone(),
  3477            chartRect: chartRect,
  3478            index: seriesIndex,
  3479            series: series,
  3480            seriesIndex: seriesIndex,
  3481            seriesMeta: series.meta,
  3482            axisX: axisX,
  3483            axisY: axisY,
  3484            group: seriesElement,
  3485            element: line
  3486          });
  3487        }
  3488  
  3489        // Area currently only works with axes that support a range!
  3490        if(seriesOptions.showArea && axisY.range) {
  3491          // If areaBase is outside the chart area (< min or > max) we need to set it respectively so that
  3492          // the area is not drawn outside the chart area.
  3493          var areaBase = Math.max(Math.min(seriesOptions.areaBase, axisY.range.max), axisY.range.min);
  3494  
  3495          // We project the areaBase value into screen coordinates
  3496          var areaBaseProjected = chartRect.y1 - axisY.projectValue(areaBase);
  3497  
  3498          // In order to form the area we'll first split the path by move commands so we can chunk it up into segments
  3499          path.splitByCommand('M').filter(function onlySolidSegments(pathSegment) {
  3500            // We filter only "solid" segments that contain more than one point. Otherwise there's no need for an area
  3501            return pathSegment.pathElements.length > 1;
  3502          }).map(function convertToArea(solidPathSegments) {
  3503            // Receiving the filtered solid path segments we can now convert those segments into fill areas
  3504            var firstElement = solidPathSegments.pathElements[0];
  3505            var lastElement = solidPathSegments.pathElements[solidPathSegments.pathElements.length - 1];
  3506  
  3507            // Cloning the solid path segment with closing option and removing the first move command from the clone
  3508            // We then insert a new move that should start at the area base and draw a straight line up or down
  3509            // at the end of the path we add an additional straight line to the projected area base value
  3510            // As the closing option is set our path will be automatically closed
  3511            return solidPathSegments.clone(true)
  3512              .position(0)
  3513              .remove(1)
  3514              .move(firstElement.x, areaBaseProjected)
  3515              .line(firstElement.x, firstElement.y)
  3516              .position(solidPathSegments.pathElements.length + 1)
  3517              .line(lastElement.x, areaBaseProjected);
  3518  
  3519          }).forEach(function createArea(areaPath) {
  3520            // For each of our newly created area paths, we'll now create path elements by stringifying our path objects
  3521            // and adding the created DOM elements to the correct series group
  3522            var area = seriesElement.elem('path', {
  3523              d: areaPath.stringify()
  3524            }, options.classNames.area, true);
  3525  
  3526            // Emit an event for each area that was drawn
  3527            this.eventEmitter.emit('draw', {
  3528              type: 'area',
  3529              values: data.normalized.series[seriesIndex],
  3530              path: areaPath.clone(),
  3531              series: series,
  3532              seriesIndex: seriesIndex,
  3533              axisX: axisX,
  3534              axisY: axisY,
  3535              chartRect: chartRect,
  3536              index: seriesIndex,
  3537              group: seriesElement,
  3538              element: area
  3539            });
  3540          }.bind(this));
  3541        }
  3542      }.bind(this));
  3543  
  3544      this.eventEmitter.emit('created', {
  3545        bounds: axisY.bounds,
  3546        chartRect: chartRect,
  3547        axisX: axisX,
  3548        axisY: axisY,
  3549        svg: this.svg,
  3550        options: options
  3551      });
  3552    }
  3553  
  3554    /**
  3555     * This method creates a new line chart.
  3556     *
  3557     * @memberof Chartist.Line
  3558     * @param {String|Node} query A selector query string or directly a DOM element
  3559     * @param {Object} data The data object that needs to consist of a labels and a series array
  3560     * @param {Object} [options] The options object with options that override the default options. Check the examples for a detailed list.
  3561     * @param {Array} [responsiveOptions] Specify an array of responsive option arrays which are a media query and options object pair => [[mediaQueryString, optionsObject],[more...]]
  3562     * @return {Object} An object which exposes the API for the created chart
  3563     *
  3564     * @example
  3565     * // Create a simple line chart
  3566     * var data = {
  3567     *   // A labels array that can contain any sort of values
  3568     *   labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
  3569     *   // Our series array that contains series objects or in this case series data arrays
  3570     *   series: [
  3571     *     [5, 2, 4, 2, 0]
  3572     *   ]
  3573     * };
  3574     *
  3575     * // As options we currently only set a static size of 300x200 px
  3576     * var options = {
  3577     *   width: '300px',
  3578     *   height: '200px'
  3579     * };
  3580     *
  3581     * // In the global name space Chartist we call the Line function to initialize a line chart. As a first parameter we pass in a selector where we would like to get our chart created. Second parameter is the actual data object and as a third parameter we pass in our options
  3582     * new Chartist.Line('.ct-chart', data, options);
  3583     *
  3584     * @example
  3585     * // Use specific interpolation function with configuration from the Chartist.Interpolation module
  3586     *
  3587     * var chart = new Chartist.Line('.ct-chart', {
  3588     *   labels: [1, 2, 3, 4, 5],
  3589     *   series: [
  3590     *     [1, 1, 8, 1, 7]
  3591     *   ]
  3592     * }, {
  3593     *   lineSmooth: Chartist.Interpolation.cardinal({
  3594     *     tension: 0.2
  3595     *   })
  3596     * });
  3597     *
  3598     * @example
  3599     * // Create a line chart with responsive options
  3600     *
  3601     * var data = {
  3602     *   // A labels array that can contain any sort of values
  3603     *   labels: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
  3604     *   // Our series array that contains series objects or in this case series data arrays
  3605     *   series: [
  3606     *     [5, 2, 4, 2, 0]
  3607     *   ]
  3608     * };
  3609     *
  3610     * // In addition to the regular options we specify responsive option overrides that will override the default configutation based on the matching media queries.
  3611     * var responsiveOptions = [
  3612     *   ['screen and (min-width: 641px) and (max-width: 1024px)', {
  3613     *     showPoint: false,
  3614     *     axisX: {
  3615     *       labelInterpolationFnc: function(value) {
  3616     *         // Will return Mon, Tue, Wed etc. on medium screens
  3617     *         return value.slice(0, 3);
  3618     *       }
  3619     *     }
  3620     *   }],
  3621     *   ['screen and (max-width: 640px)', {
  3622     *     showLine: false,
  3623     *     axisX: {
  3624     *       labelInterpolationFnc: function(value) {
  3625     *         // Will return M, T, W etc. on small screens
  3626     *         return value[0];
  3627     *       }
  3628     *     }
  3629     *   }]
  3630     * ];
  3631     *
  3632     * new Chartist.Line('.ct-chart', data, null, responsiveOptions);
  3633     *
  3634     */
  3635    function Line(query, data, options, responsiveOptions) {
  3636      Chartist.Line.super.constructor.call(this,
  3637        query,
  3638        data,
  3639        defaultOptions,
  3640        Chartist.extend({}, defaultOptions, options),
  3641        responsiveOptions);
  3642    }
  3643  
  3644    // Creating line chart type in Chartist namespace
  3645    Chartist.Line = Chartist.Base.extend({
  3646      constructor: Line,
  3647      createChart: createChart
  3648    });
  3649  
  3650  }(window, document, Chartist));
  3651  ;/**
  3652   * The bar chart module of Chartist that can be used to draw unipolar or bipolar bar and grouped bar charts.
  3653   *
  3654   * @module Chartist.Bar
  3655   */
  3656  /* global Chartist */
  3657  (function(window, document, Chartist){
  3658    'use strict';
  3659  
  3660    /**
  3661     * Default options in bar charts. Expand the code view to see a detailed list of options with comments.
  3662     *
  3663     * @memberof Chartist.Bar
  3664     */
  3665    var defaultOptions = {
  3666      // Options for X-Axis
  3667      axisX: {
  3668        // The offset of the chart drawing area to the border of the container
  3669        offset: 30,
  3670        // Position where labels are placed. Can be set to `start` or `end` where `start` is equivalent to left or top on vertical axis and `end` is equivalent to right or bottom on horizontal axis.
  3671        position: 'end',
  3672        // Allows you to correct label positioning on this axis by positive or negative x and y offset.
  3673        labelOffset: {
  3674          x: 0,
  3675          y: 0
  3676        },
  3677        // If labels should be shown or not
  3678        showLabel: true,
  3679        // If the axis grid should be drawn or not
  3680        showGrid: true,
  3681        // Interpolation function that allows you to intercept the value from the axis label
  3682        labelInterpolationFnc: Chartist.noop,
  3683        // This value specifies the minimum width in pixel of the scale steps
  3684        scaleMinSpace: 30,
  3685        // Use only integer values (whole numbers) for the scale steps
  3686        onlyInteger: false
  3687      },
  3688      // Options for Y-Axis
  3689      axisY: {
  3690        // The offset of the chart drawing area to the border of the container
  3691        offset: 40,
  3692        // Position where labels are placed. Can be set to `start` or `end` where `start` is equivalent to left or top on vertical axis and `end` is equivalent to right or bottom on horizontal axis.
  3693        position: 'start',
  3694        // Allows you to correct label positioning on this axis by positive or negative x and y offset.
  3695        labelOffset: {
  3696          x: 0,
  3697          y: 0
  3698        },
  3699        // If labels should be shown or not
  3700        showLabel: true,
  3701        // If the axis grid should be drawn or not
  3702        showGrid: true,
  3703        // Interpolation function that allows you to intercept the value from the axis label
  3704        labelInterpolationFnc: Chartist.noop,
  3705        // This value specifies the minimum height in pixel of the scale steps
  3706        scaleMinSpace: 20,
  3707        // Use only integer values (whole numbers) for the scale steps
  3708        onlyInteger: false
  3709      },
  3710      // Specify a fixed width for the chart as a string (i.e. '100px' or '50%')
  3711      width: undefined,
  3712      // Specify a fixed height for the chart as a string (i.e. '100px' or '50%')
  3713      height: undefined,
  3714      // Overriding the natural high of the chart allows you to zoom in or limit the charts highest displayed value
  3715      high: undefined,
  3716      // Overriding the natural low of the chart allows you to zoom in or limit the charts lowest displayed value
  3717      low: undefined,
  3718      // Unless low/high are explicitly set, bar chart will be centered at zero by default. Set referenceValue to null to auto scale.
  3719      referenceValue: 0,
  3720      // Padding of the chart drawing area to the container element and labels as a number or padding object {top: 5, right: 5, bottom: 5, left: 5}
  3721      chartPadding: {
  3722        top: 15,
  3723        right: 15,
  3724        bottom: 5,
  3725        left: 10
  3726      },
  3727      // Specify the distance in pixel of bars in a group
  3728      seriesBarDistance: 15,
  3729      // If set to true this property will cause the series bars to be stacked. Check the `stackMode` option for further stacking options.
  3730      stackBars: false,
  3731      // If set to 'overlap' this property will force the stacked bars to draw from the zero line.
  3732      // If set to 'accumulate' this property will form a total for each series point. This will also influence the y-axis and the overall bounds of the chart. In stacked mode the seriesBarDistance property will have no effect.
  3733      stackMode: 'accumulate',
  3734      // Inverts the axes of the bar chart in order to draw a horizontal bar chart. Be aware that you also need to invert your axis settings as the Y Axis will now display the labels and the X Axis the values.
  3735      horizontalBars: false,
  3736      // If set to true then each bar will represent a series and the data array is expected to be a one dimensional array of data values rather than a series array of series. This is useful if the bar chart should represent a profile rather than some data over time.
  3737      distributeSeries: false,
  3738      // If true the whole data is reversed including labels, the series order as well as the whole series data arrays.
  3739      reverseData: false,
  3740      // If the bar chart should add a background fill to the .ct-grids group.
  3741      showGridBackground: false,
  3742      // Override the class names that get used to generate the SVG structure of the chart
  3743      classNames: {
  3744        chart: 'ct-chart-bar',
  3745        horizontalBars: 'ct-horizontal-bars',
  3746        label: 'ct-label',
  3747        labelGroup: 'ct-labels',
  3748        series: 'ct-series',
  3749        bar: 'ct-bar',
  3750        grid: 'ct-grid',
  3751        gridGroup: 'ct-grids',
  3752        gridBackground: 'ct-grid-background',
  3753        vertical: 'ct-vertical',
  3754        horizontal: 'ct-horizontal',
  3755        start: 'ct-start',
  3756        end: 'ct-end'
  3757      }
  3758    };
  3759  
  3760    /**
  3761     * Creates a new chart
  3762     *
  3763     */
  3764    function createChart(options) {
  3765      var data;
  3766      var highLow;
  3767  
  3768      if(options.distributeSeries) {
  3769        data = Chartist.normalizeData(this.data, options.reverseData, options.horizontalBars ? 'x' : 'y');
  3770        data.normalized.series = data.normalized.series.map(function(value) {
  3771          return [value];
  3772        });
  3773      } else {
  3774        data = Chartist.normalizeData(this.data, options.reverseData, options.horizontalBars ? 'x' : 'y');
  3775      }
  3776  
  3777      // Create new svg element
  3778      this.svg = Chartist.createSvg(
  3779        this.container,
  3780        options.width,
  3781        options.height,
  3782        options.classNames.chart + (options.horizontalBars ? ' ' + options.classNames.horizontalBars : '')
  3783      );
  3784  
  3785      // Drawing groups in correct order
  3786      var gridGroup = this.svg.elem('g').addClass(options.classNames.gridGroup);
  3787      var seriesGroup = this.svg.elem('g');
  3788      var labelGroup = this.svg.elem('g').addClass(options.classNames.labelGroup);
  3789  
  3790      if(options.stackBars && data.normalized.series.length !== 0) {
  3791  
  3792        // If stacked bars we need to calculate the high low from stacked values from each series
  3793        var serialSums = Chartist.serialMap(data.normalized.series, function serialSums() {
  3794          return Array.prototype.slice.call(arguments).map(function(value) {
  3795            return value;
  3796          }).reduce(function(prev, curr) {
  3797            return {
  3798              x: prev.x + (curr && curr.x) || 0,
  3799              y: prev.y + (curr && curr.y) || 0
  3800            };
  3801          }, {x: 0, y: 0});
  3802        });
  3803  
  3804        highLow = Chartist.getHighLow([serialSums], options, options.horizontalBars ? 'x' : 'y');
  3805  
  3806      } else {
  3807  
  3808        highLow = Chartist.getHighLow(data.normalized.series, options, options.horizontalBars ? 'x' : 'y');
  3809      }
  3810  
  3811      // Overrides of high / low from settings
  3812      highLow.high = +options.high || (options.high === 0 ? 0 : highLow.high);
  3813      highLow.low = +options.low || (options.low === 0 ? 0 : highLow.low);
  3814  
  3815      var chartRect = Chartist.createChartRect(this.svg, options, defaultOptions.padding);
  3816  
  3817      var valueAxis,
  3818        labelAxisTicks,
  3819        labelAxis,
  3820        axisX,
  3821        axisY;
  3822  
  3823      // We need to set step count based on some options combinations
  3824      if(options.distributeSeries && options.stackBars) {
  3825        // If distributed series are enabled and bars need to be stacked, we'll only have one bar and therefore should
  3826        // use only the first label for the step axis
  3827        labelAxisTicks = data.normalized.labels.slice(0, 1);
  3828      } else {
  3829        // If distributed series are enabled but stacked bars aren't, we should use the series labels
  3830        // If we are drawing a regular bar chart with two dimensional series data, we just use the labels array
  3831        // as the bars are normalized
  3832        labelAxisTicks = data.normalized.labels;
  3833      }
  3834  
  3835      // Set labelAxis and valueAxis based on the horizontalBars setting. This setting will flip the axes if necessary.
  3836      if(options.horizontalBars) {
  3837        if(options.axisX.type === undefined) {
  3838          valueAxis = axisX = new Chartist.AutoScaleAxis(Chartist.Axis.units.x, data.normalized.series, chartRect, Chartist.extend({}, options.axisX, {
  3839            highLow: highLow,
  3840            referenceValue: 0
  3841          }));
  3842        } else {
  3843          valueAxis = axisX = options.axisX.type.call(Chartist, Chartist.Axis.units.x, data.normalized.series, chartRect, Chartist.extend({}, options.axisX, {
  3844            highLow: highLow,
  3845            referenceValue: 0
  3846          }));
  3847        }
  3848  
  3849        if(options.axisY.type === undefined) {
  3850          labelAxis = axisY = new Chartist.StepAxis(Chartist.Axis.units.y, data.normalized.series, chartRect, {
  3851            ticks: labelAxisTicks
  3852          });
  3853        } else {
  3854          labelAxis = axisY = options.axisY.type.call(Chartist, Chartist.Axis.units.y, data.normalized.series, chartRect, options.axisY);
  3855        }
  3856      } else {
  3857        if(options.axisX.type === undefined) {
  3858          labelAxis = axisX = new Chartist.StepAxis(Chartist.Axis.units.x, data.normalized.series, chartRect, {
  3859            ticks: labelAxisTicks
  3860          });
  3861        } else {
  3862          labelAxis = axisX = options.axisX.type.call(Chartist, Chartist.Axis.units.x, data.normalized.series, chartRect, options.axisX);
  3863        }
  3864  
  3865        if(options.axisY.type === undefined) {
  3866          valueAxis = axisY = new Chartist.AutoScaleAxis(Chartist.Axis.units.y, data.normalized.series, chartRect, Chartist.extend({}, options.axisY, {
  3867            highLow: highLow,
  3868            referenceValue: 0
  3869          }));
  3870        } else {
  3871          valueAxis = axisY = options.axisY.type.call(Chartist, Chartist.Axis.units.y, data.normalized.series, chartRect, Chartist.extend({}, options.axisY, {
  3872            highLow: highLow,
  3873            referenceValue: 0
  3874          }));
  3875        }
  3876      }
  3877  
  3878      // Projected 0 point
  3879      var zeroPoint = options.horizontalBars ? (chartRect.x1 + valueAxis.projectValue(0)) : (chartRect.y1 - valueAxis.projectValue(0));
  3880      // Used to track the screen coordinates of stacked bars
  3881      var stackedBarValues = [];
  3882  
  3883      labelAxis.createGridAndLabels(gridGroup, labelGroup, this.supportsForeignObject, options, this.eventEmitter);
  3884      valueAxis.createGridAndLabels(gridGroup, labelGroup, this.supportsForeignObject, options, this.eventEmitter);
  3885  
  3886      if (options.showGridBackground) {
  3887        Chartist.createGridBackground(gridGroup, chartRect, options.classNames.gridBackground, this.eventEmitter);
  3888      }
  3889  
  3890      // Draw the series
  3891      data.raw.series.forEach(function(series, seriesIndex) {
  3892        // Calculating bi-polar value of index for seriesOffset. For i = 0..4 biPol will be -1.5, -0.5, 0.5, 1.5 etc.
  3893        var biPol = seriesIndex - (data.raw.series.length - 1) / 2;
  3894        // Half of the period width between vertical grid lines used to position bars
  3895        var periodHalfLength;
  3896        // Current series SVG element
  3897        var seriesElement;
  3898  
  3899        // We need to set periodHalfLength based on some options combinations
  3900        if(options.distributeSeries && !options.stackBars) {
  3901          // If distributed series are enabled but stacked bars aren't, we need to use the length of the normaizedData array
  3902          // which is the series count and divide by 2
  3903          periodHalfLength = labelAxis.axisLength / data.normalized.series.length / 2;
  3904        } else if(options.distributeSeries && options.stackBars) {
  3905          // If distributed series and stacked bars are enabled we'll only get one bar so we should just divide the axis
  3906          // length by 2
  3907          periodHalfLength = labelAxis.axisLength / 2;
  3908        } else {
  3909          // On regular bar charts we should just use the series length
  3910          periodHalfLength = labelAxis.axisLength / data.normalized.series[seriesIndex].length / 2;
  3911        }
  3912  
  3913        // Adding the series group to the series element
  3914        seriesElement = seriesGroup.elem('g');
  3915  
  3916        // Write attributes to series group element. If series name or meta is undefined the attributes will not be written
  3917        seriesElement.attr({
  3918          'ct:series-name': series.name,
  3919          'ct:meta': Chartist.serialize(series.meta)
  3920        });
  3921  
  3922        // Use series class from series data or if not set generate one
  3923        seriesElement.addClass([
  3924          options.classNames.series,
  3925          (series.className || options.classNames.series + '-' + Chartist.alphaNumerate(seriesIndex))
  3926        ].join(' '));
  3927  
  3928        data.normalized.series[seriesIndex].forEach(function(value, valueIndex) {
  3929          var projected,
  3930            bar,
  3931            previousStack,
  3932            labelAxisValueIndex;
  3933  
  3934          // We need to set labelAxisValueIndex based on some options combinations
  3935          if(options.distributeSeries && !options.stackBars) {
  3936            // If distributed series are enabled but stacked bars aren't, we can use the seriesIndex for later projection
  3937            // on the step axis for label positioning
  3938            labelAxisValueIndex = seriesIndex;
  3939          } else if(options.distributeSeries && options.stackBars) {
  3940            // If distributed series and stacked bars are enabled, we will only get one bar and therefore always use
  3941            // 0 for projection on the label step axis
  3942            labelAxisValueIndex = 0;
  3943          } else {
  3944            // On regular bar charts we just use the value index to project on the label step axis
  3945            labelAxisValueIndex = valueIndex;
  3946          }
  3947  
  3948          // We need to transform coordinates differently based on the chart layout
  3949          if(options.horizontalBars) {
  3950            projected = {
  3951              x: chartRect.x1 + valueAxis.projectValue(value && value.x ? value.x : 0, valueIndex, data.normalized.series[seriesIndex]),
  3952              y: chartRect.y1 - labelAxis.projectValue(value && value.y ? value.y : 0, labelAxisValueIndex, data.normalized.series[seriesIndex])
  3953            };
  3954          } else {
  3955            projected = {
  3956              x: chartRect.x1 + labelAxis.projectValue(value && value.x ? value.x : 0, labelAxisValueIndex, data.normalized.series[seriesIndex]),
  3957              y: chartRect.y1 - valueAxis.projectValue(value && value.y ? value.y : 0, valueIndex, data.normalized.series[seriesIndex])
  3958            }
  3959          }
  3960  
  3961          // If the label axis is a step based axis we will offset the bar into the middle of between two steps using
  3962          // the periodHalfLength value. Also we do arrange the different series so that they align up to each other using
  3963          // the seriesBarDistance. If we don't have a step axis, the bar positions can be chosen freely so we should not
  3964          // add any automated positioning.
  3965          if(labelAxis instanceof Chartist.StepAxis) {
  3966            // Offset to center bar between grid lines, but only if the step axis is not stretched
  3967            if(!labelAxis.options.stretch) {
  3968              projected[labelAxis.units.pos] += periodHalfLength * (options.horizontalBars ? -1 : 1);
  3969            }
  3970            // Using bi-polar offset for multiple series if no stacked bars or series distribution is used
  3971            projected[labelAxis.units.pos] += (options.stackBars || options.distributeSeries) ? 0 : biPol * options.seriesBarDistance * (options.horizontalBars ? -1 : 1);
  3972          }
  3973  
  3974          // Enter value in stacked bar values used to remember previous screen value for stacking up bars
  3975          previousStack = stackedBarValues[valueIndex] || zeroPoint;
  3976          stackedBarValues[valueIndex] = previousStack - (zeroPoint - projected[labelAxis.counterUnits.pos]);
  3977  
  3978          // Skip if value is undefined
  3979          if(value === undefined) {
  3980            return;
  3981          }
  3982  
  3983          var positions = {};
  3984          positions[labelAxis.units.pos + '1'] = projected[labelAxis.units.pos];
  3985          positions[labelAxis.units.pos + '2'] = projected[labelAxis.units.pos];
  3986  
  3987          if(options.stackBars && (options.stackMode === 'accumulate' || !options.stackMode)) {
  3988            // Stack mode: accumulate (default)
  3989            // If bars are stacked we use the stackedBarValues reference and otherwise base all bars off the zero line
  3990            // We want backwards compatibility, so the expected fallback without the 'stackMode' option
  3991            // to be the original behaviour (accumulate)
  3992            positions[labelAxis.counterUnits.pos + '1'] = previousStack;
  3993            positions[labelAxis.counterUnits.pos + '2'] = stackedBarValues[valueIndex];
  3994          } else {
  3995            // Draw from the zero line normally
  3996            // This is also the same code for Stack mode: overlap
  3997            positions[labelAxis.counterUnits.pos + '1'] = zeroPoint;
  3998            positions[labelAxis.counterUnits.pos + '2'] = projected[labelAxis.counterUnits.pos];
  3999          }
  4000  
  4001          // Limit x and y so that they are within the chart rect
  4002          positions.x1 = Math.min(Math.max(positions.x1, chartRect.x1), chartRect.x2);
  4003          positions.x2 = Math.min(Math.max(positions.x2, chartRect.x1), chartRect.x2);
  4004          positions.y1 = Math.min(Math.max(positions.y1, chartRect.y2), chartRect.y1);
  4005          positions.y2 = Math.min(Math.max(positions.y2, chartRect.y2), chartRect.y1);
  4006  
  4007          var metaData = Chartist.getMetaData(series, valueIndex);
  4008  
  4009          // Create bar element
  4010          bar = seriesElement.elem('line', positions, options.classNames.bar).attr({
  4011            'ct:value': [value.x, value.y].filter(Chartist.isNumeric).join(','),
  4012            'ct:meta': Chartist.serialize(metaData)
  4013          });
  4014  
  4015          this.eventEmitter.emit('draw', Chartist.extend({
  4016            type: 'bar',
  4017            value: value,
  4018            index: valueIndex,
  4019            meta: metaData,
  4020            series: series,
  4021            seriesIndex: seriesIndex,
  4022            axisX: axisX,
  4023            axisY: axisY,
  4024            chartRect: chartRect,
  4025            group: seriesElement,
  4026            element: bar
  4027          }, positions));
  4028        }.bind(this));
  4029      }.bind(this));
  4030  
  4031      this.eventEmitter.emit('created', {
  4032        bounds: valueAxis.bounds,
  4033        chartRect: chartRect,
  4034        axisX: axisX,
  4035        axisY: axisY,
  4036        svg: this.svg,
  4037        options: options
  4038      });
  4039    }
  4040  
  4041    /**
  4042     * This method creates a new bar chart and returns API object that you can use for later changes.
  4043     *
  4044     * @memberof Chartist.Bar
  4045     * @param {String|Node} query A selector query string or directly a DOM element
  4046     * @param {Object} data The data object that needs to consist of a labels and a series array
  4047     * @param {Object} [options] The options object with options that override the default options. Check the examples for a detailed list.
  4048     * @param {Array} [responsiveOptions] Specify an array of responsive option arrays which are a media query and options object pair => [[mediaQueryString, optionsObject],[more...]]
  4049     * @return {Object} An object which exposes the API for the created chart
  4050     *
  4051     * @example
  4052     * // Create a simple bar chart
  4053     * var data = {
  4054     *   labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
  4055     *   series: [
  4056     *     [5, 2, 4, 2, 0]
  4057     *   ]
  4058     * };
  4059     *
  4060     * // In the global name space Chartist we call the Bar function to initialize a bar chart. As a first parameter we pass in a selector where we would like to get our chart created and as a second parameter we pass our data object.
  4061     * new Chartist.Bar('.ct-chart', data);
  4062     *
  4063     * @example
  4064     * // This example creates a bipolar grouped bar chart where the boundaries are limitted to -10 and 10
  4065     * new Chartist.Bar('.ct-chart', {
  4066     *   labels: [1, 2, 3, 4, 5, 6, 7],
  4067     *   series: [
  4068     *     [1, 3, 2, -5, -3, 1, -6],
  4069     *     [-5, -2, -4, -1, 2, -3, 1]
  4070     *   ]
  4071     * }, {
  4072     *   seriesBarDistance: 12,
  4073     *   low: -10,
  4074     *   high: 10
  4075     * });
  4076     *
  4077     */
  4078    function Bar(query, data, options, responsiveOptions) {
  4079      Chartist.Bar.super.constructor.call(this,
  4080        query,
  4081        data,
  4082        defaultOptions,
  4083        Chartist.extend({}, defaultOptions, options),
  4084        responsiveOptions);
  4085    }
  4086  
  4087    // Creating bar chart type in Chartist namespace
  4088    Chartist.Bar = Chartist.Base.extend({
  4089      constructor: Bar,
  4090      createChart: createChart
  4091    });
  4092  
  4093  }(window, document, Chartist));
  4094  ;/**
  4095   * The pie chart module of Chartist that can be used to draw pie, donut or gauge charts
  4096   *
  4097   * @module Chartist.Pie
  4098   */
  4099  /* global Chartist */
  4100  (function(window, document, Chartist) {
  4101    'use strict';
  4102  
  4103    /**
  4104     * Default options in line charts. Expand the code view to see a detailed list of options with comments.
  4105     *
  4106     * @memberof Chartist.Pie
  4107     */
  4108    var defaultOptions = {
  4109      // Specify a fixed width for the chart as a string (i.e. '100px' or '50%')
  4110      width: undefined,
  4111      // Specify a fixed height for the chart as a string (i.e. '100px' or '50%')
  4112      height: undefined,
  4113      // Padding of the chart drawing area to the container element and labels as a number or padding object {top: 5, right: 5, bottom: 5, left: 5}
  4114      chartPadding: 5,
  4115      // Override the class names that are used to generate the SVG structure of the chart
  4116      classNames: {
  4117        chartPie: 'ct-chart-pie',
  4118        chartDonut: 'ct-chart-donut',
  4119        series: 'ct-series',
  4120        slicePie: 'ct-slice-pie',
  4121        sliceDonut: 'ct-slice-donut',
  4122        sliceDonutSolid: 'ct-slice-donut-solid',
  4123        label: 'ct-label'
  4124      },
  4125      // The start angle of the pie chart in degrees where 0 points north. A higher value offsets the start angle clockwise.
  4126      startAngle: 0,
  4127      // An optional total you can specify. By specifying a total value, the sum of the values in the series must be this total in order to draw a full pie. You can use this parameter to draw only parts of a pie or gauge charts.
  4128      total: undefined,
  4129      // If specified the donut CSS classes will be used and strokes will be drawn instead of pie slices.
  4130      donut: false,
  4131      // If specified the donut segments will be drawn as shapes instead of strokes.
  4132      donutSolid: false,
  4133      // Specify the donut stroke width, currently done in javascript for convenience. May move to CSS styles in the future.
  4134      // This option can be set as number or string to specify a relative width (i.e. 100 or '30%').
  4135      donutWidth: 60,
  4136      // If a label should be shown or not
  4137      showLabel: true,
  4138      // Label position offset from the standard position which is half distance of the radius. This value can be either positive or negative. Positive values will position the label away from the center.
  4139      labelOffset: 0,
  4140      // This option can be set to 'inside', 'outside' or 'center'. Positioned with 'inside' the labels will be placed on half the distance of the radius to the border of the Pie by respecting the 'labelOffset'. The 'outside' option will place the labels at the border of the pie and 'center' will place the labels in the absolute center point of the chart. The 'center' option only makes sense in conjunction with the 'labelOffset' option.
  4141      labelPosition: 'inside',
  4142      // An interpolation function for the label value
  4143      labelInterpolationFnc: Chartist.noop,
  4144      // Label direction can be 'neutral', 'explode' or 'implode'. The labels anchor will be positioned based on those settings as well as the fact if the labels are on the right or left side of the center of the chart. Usually explode is useful when labels are positioned far away from the center.
  4145      labelDirection: 'neutral',
  4146      // If true the whole data is reversed including labels, the series order as well as the whole series data arrays.
  4147      reverseData: false,
  4148      // If true empty values will be ignored to avoid drawing unncessary slices and labels
  4149      ignoreEmptyValues: false
  4150    };
  4151  
  4152    /**
  4153     * Determines SVG anchor position based on direction and center parameter
  4154     *
  4155     * @param center
  4156     * @param label
  4157     * @param direction
  4158     * @return {string}
  4159     */
  4160    function determineAnchorPosition(center, label, direction) {
  4161      var toTheRight = label.x > center.x;
  4162  
  4163      if(toTheRight && direction === 'explode' ||
  4164        !toTheRight && direction === 'implode') {
  4165        return 'start';
  4166      } else if(toTheRight && direction === 'implode' ||
  4167        !toTheRight && direction === 'explode') {
  4168        return 'end';
  4169      } else {
  4170        return 'middle';
  4171      }
  4172    }
  4173  
  4174    /**
  4175     * Creates the pie chart
  4176     *
  4177     * @param options
  4178     */
  4179    function createChart(options) {
  4180      var data = Chartist.normalizeData(this.data);
  4181      var seriesGroups = [],
  4182        labelsGroup,
  4183        chartRect,
  4184        radius,
  4185        labelRadius,
  4186        totalDataSum,
  4187        startAngle = options.startAngle;
  4188  
  4189      // Create SVG.js draw
  4190      this.svg = Chartist.createSvg(this.container, options.width, options.height,options.donut ? options.classNames.chartDonut : options.classNames.chartPie);
  4191      // Calculate charting rect
  4192      chartRect = Chartist.createChartRect(this.svg, options, defaultOptions.padding);
  4193      // Get biggest circle radius possible within chartRect
  4194      radius = Math.min(chartRect.width() / 2, chartRect.height() / 2);
  4195      // Calculate total of all series to get reference value or use total reference from optional options
  4196      totalDataSum = options.total || data.normalized.series.reduce(function(previousValue, currentValue) {
  4197        return previousValue + currentValue;
  4198      }, 0);
  4199  
  4200      var donutWidth = Chartist.quantity(options.donutWidth);
  4201      if (donutWidth.unit === '%') {
  4202        donutWidth.value *= radius / 100;
  4203      }
  4204  
  4205      // If this is a donut chart we need to adjust our radius to enable strokes to be drawn inside
  4206      // Unfortunately this is not possible with the current SVG Spec
  4207      // See this proposal for more details: http://lists.w3.org/Archives/Public/www-svg/2003Oct/0000.html
  4208      radius -= options.donut && !options.donutSolid ? donutWidth.value / 2  : 0;
  4209  
  4210      // If labelPosition is set to `outside` or a donut chart is drawn then the label position is at the radius,
  4211      // if regular pie chart it's half of the radius
  4212      if(options.labelPosition === 'outside' || options.donut && !options.donutSolid) {
  4213        labelRadius = radius;
  4214      } else if(options.labelPosition === 'center') {
  4215        // If labelPosition is center we start with 0 and will later wait for the labelOffset
  4216        labelRadius = 0;
  4217      } else if(options.donutSolid) {
  4218        labelRadius = radius - donutWidth.value / 2;
  4219      } else {
  4220        // Default option is 'inside' where we use half the radius so the label will be placed in the center of the pie
  4221        // slice
  4222        labelRadius = radius / 2;
  4223      }
  4224      // Add the offset to the labelRadius where a negative offset means closed to the center of the chart
  4225      labelRadius += options.labelOffset;
  4226  
  4227      // Calculate end angle based on total sum and current data value and offset with padding
  4228      var center = {
  4229        x: chartRect.x1 + chartRect.width() / 2,
  4230        y: chartRect.y2 + chartRect.height() / 2
  4231      };
  4232  
  4233      // Check if there is only one non-zero value in the series array.
  4234      var hasSingleValInSeries = data.raw.series.filter(function(val) {
  4235        return val.hasOwnProperty('value') ? val.value !== 0 : val !== 0;
  4236      }).length === 1;
  4237  
  4238      // Creating the series groups
  4239      data.raw.series.forEach(function(series, index) {
  4240        seriesGroups[index] = this.svg.elem('g', null, null);
  4241      }.bind(this));
  4242      //if we need to show labels we create the label group now
  4243      if(options.showLabel) {
  4244        labelsGroup = this.svg.elem('g', null, null);
  4245      }
  4246  
  4247      // Draw the series
  4248      // initialize series groups
  4249      data.raw.series.forEach(function(series, index) {
  4250        // If current value is zero and we are ignoring empty values then skip to next value
  4251        if (data.normalized.series[index] === 0 && options.ignoreEmptyValues) return;
  4252  
  4253        // If the series is an object and contains a name or meta data we add a custom attribute
  4254        seriesGroups[index].attr({
  4255          'ct:series-name': series.name
  4256        });
  4257  
  4258        // Use series class from series data or if not set generate one
  4259        seriesGroups[index].addClass([
  4260          options.classNames.series,
  4261          (series.className || options.classNames.series + '-' + Chartist.alphaNumerate(index))
  4262        ].join(' '));
  4263  
  4264        // If the whole dataset is 0 endAngle should be zero. Can't divide by 0.
  4265        var endAngle = (totalDataSum > 0 ? startAngle + data.normalized.series[index] / totalDataSum * 360 : 0);
  4266  
  4267        // Use slight offset so there are no transparent hairline issues
  4268        var overlappigStartAngle = Math.max(0, startAngle - (index === 0 || hasSingleValInSeries ? 0 : 0.2));
  4269  
  4270        // If we need to draw the arc for all 360 degrees we need to add a hack where we close the circle
  4271        // with Z and use 359.99 degrees
  4272        if(endAngle - overlappigStartAngle >= 359.99) {
  4273          endAngle = overlappigStartAngle + 359.99;
  4274        }
  4275  
  4276        var start = Chartist.polarToCartesian(center.x, center.y, radius, overlappigStartAngle),
  4277          end = Chartist.polarToCartesian(center.x, center.y, radius, endAngle);
  4278  
  4279        var innerStart,
  4280          innerEnd,
  4281          donutSolidRadius;
  4282  
  4283        // Create a new path element for the pie chart. If this isn't a donut chart we should close the path for a correct stroke
  4284        var path = new Chartist.Svg.Path(!options.donut || options.donutSolid)
  4285          .move(end.x, end.y)
  4286          .arc(radius, radius, 0, endAngle - startAngle > 180, 0, start.x, start.y);
  4287  
  4288        // If regular pie chart (no donut) we add a line to the center of the circle for completing the pie
  4289        if(!options.donut) {
  4290          path.line(center.x, center.y);
  4291        } else if (options.donutSolid) {
  4292          donutSolidRadius = radius - donutWidth.value;
  4293          innerStart = Chartist.polarToCartesian(center.x, center.y, donutSolidRadius, startAngle - (index === 0 || hasSingleValInSeries ? 0 : 0.2));
  4294          innerEnd = Chartist.polarToCartesian(center.x, center.y, donutSolidRadius, endAngle);
  4295          path.line(innerStart.x, innerStart.y);
  4296          path.arc(donutSolidRadius, donutSolidRadius, 0, endAngle - startAngle  > 180, 1, innerEnd.x, innerEnd.y);
  4297        }
  4298  
  4299        // Create the SVG path
  4300        // If this is a donut chart we add the donut class, otherwise just a regular slice
  4301        var pathClassName = options.classNames.slicePie;
  4302        if (options.donut) {
  4303          pathClassName = options.classNames.sliceDonut;
  4304          if (options.donutSolid) {
  4305            pathClassName = options.classNames.sliceDonutSolid;
  4306          }
  4307        }
  4308        var pathElement = seriesGroups[index].elem('path', {
  4309          d: path.stringify()
  4310        }, pathClassName);
  4311  
  4312        // Adding the pie series value to the path
  4313        pathElement.attr({
  4314          'ct:value': data.normalized.series[index],
  4315          'ct:meta': Chartist.serialize(series.meta)
  4316        });
  4317  
  4318        // If this is a donut, we add the stroke-width as style attribute
  4319        if(options.donut && !options.donutSolid) {
  4320          pathElement._node.style.strokeWidth = donutWidth.value + 'px';
  4321        }
  4322  
  4323        // Fire off draw event
  4324        this.eventEmitter.emit('draw', {
  4325          type: 'slice',
  4326          value: data.normalized.series[index],
  4327          totalDataSum: totalDataSum,
  4328          index: index,
  4329          meta: series.meta,
  4330          series: series,
  4331          group: seriesGroups[index],
  4332          element: pathElement,
  4333          path: path.clone(),
  4334          center: center,
  4335          radius: radius,
  4336          startAngle: startAngle,
  4337          endAngle: endAngle
  4338        });
  4339  
  4340        // If we need to show labels we need to add the label for this slice now
  4341        if(options.showLabel) {
  4342          var labelPosition;
  4343          if(data.raw.series.length === 1) {
  4344            // If we have only 1 series, we can position the label in the center of the pie
  4345            labelPosition = {
  4346              x: center.x,
  4347              y: center.y
  4348            };
  4349          } else {
  4350            // Position at the labelRadius distance from center and between start and end angle
  4351            labelPosition = Chartist.polarToCartesian(
  4352              center.x,
  4353              center.y,
  4354              labelRadius,
  4355              startAngle + (endAngle - startAngle) / 2
  4356            );
  4357          }
  4358  
  4359          var rawValue;
  4360          if(data.normalized.labels && !Chartist.isFalseyButZero(data.normalized.labels[index])) {
  4361            rawValue = data.normalized.labels[index];
  4362          } else {
  4363            rawValue = data.normalized.series[index];
  4364          }
  4365  
  4366          var interpolatedValue = options.labelInterpolationFnc(rawValue, index);
  4367  
  4368          if(interpolatedValue || interpolatedValue === 0) {
  4369            var labelElement = labelsGroup.elem('text', {
  4370              dx: labelPosition.x,
  4371              dy: labelPosition.y,
  4372              'text-anchor': determineAnchorPosition(center, labelPosition, options.labelDirection)
  4373            }, options.classNames.label).text('' + interpolatedValue);
  4374  
  4375            // Fire off draw event
  4376            this.eventEmitter.emit('draw', {
  4377              type: 'label',
  4378              index: index,
  4379              group: labelsGroup,
  4380              element: labelElement,
  4381              text: '' + interpolatedValue,
  4382              x: labelPosition.x,
  4383              y: labelPosition.y
  4384            });
  4385          }
  4386        }
  4387  
  4388        // Set next startAngle to current endAngle.
  4389        // (except for last slice)
  4390        startAngle = endAngle;
  4391      }.bind(this));
  4392  
  4393      this.eventEmitter.emit('created', {
  4394        chartRect: chartRect,
  4395        svg: this.svg,
  4396        options: options
  4397      });
  4398    }
  4399  
  4400    /**
  4401     * This method creates a new pie chart and returns an object that can be used to redraw the chart.
  4402     *
  4403     * @memberof Chartist.Pie
  4404     * @param {String|Node} query A selector query string or directly a DOM element
  4405     * @param {Object} data The data object in the pie chart needs to have a series property with a one dimensional data array. The values will be normalized against each other and don't necessarily need to be in percentage. The series property can also be an array of value objects that contain a value property and a className property to override the CSS class name for the series group.
  4406     * @param {Object} [options] The options object with options that override the default options. Check the examples for a detailed list.
  4407     * @param {Array} [responsiveOptions] Specify an array of responsive option arrays which are a media query and options object pair => [[mediaQueryString, optionsObject],[more...]]
  4408     * @return {Object} An object with a version and an update method to manually redraw the chart
  4409     *
  4410     * @example
  4411     * // Simple pie chart example with four series
  4412     * new Chartist.Pie('.ct-chart', {
  4413     *   series: [10, 2, 4, 3]
  4414     * });
  4415     *
  4416     * @example
  4417     * // Drawing a donut chart
  4418     * new Chartist.Pie('.ct-chart', {
  4419     *   series: [10, 2, 4, 3]
  4420     * }, {
  4421     *   donut: true
  4422     * });
  4423     *
  4424     * @example
  4425     * // Using donut, startAngle and total to draw a gauge chart
  4426     * new Chartist.Pie('.ct-chart', {
  4427     *   series: [20, 10, 30, 40]
  4428     * }, {
  4429     *   donut: true,
  4430     *   donutWidth: 20,
  4431     *   startAngle: 270,
  4432     *   total: 200
  4433     * });
  4434     *
  4435     * @example
  4436     * // Drawing a pie chart with padding and labels that are outside the pie
  4437     * new Chartist.Pie('.ct-chart', {
  4438     *   series: [20, 10, 30, 40]
  4439     * }, {
  4440     *   chartPadding: 30,
  4441     *   labelOffset: 50,
  4442     *   labelDirection: 'explode'
  4443     * });
  4444     *
  4445     * @example
  4446     * // Overriding the class names for individual series as well as a name and meta data.
  4447     * // The name will be written as ct:series-name attribute and the meta data will be serialized and written
  4448     * // to a ct:meta attribute.
  4449     * new Chartist.Pie('.ct-chart', {
  4450     *   series: [{
  4451     *     value: 20,
  4452     *     name: 'Series 1',
  4453     *     className: 'my-custom-class-one',
  4454     *     meta: 'Meta One'
  4455     *   }, {
  4456     *     value: 10,
  4457     *     name: 'Series 2',
  4458     *     className: 'my-custom-class-two',
  4459     *     meta: 'Meta Two'
  4460     *   }, {
  4461     *     value: 70,
  4462     *     name: 'Series 3',
  4463     *     className: 'my-custom-class-three',
  4464     *     meta: 'Meta Three'
  4465     *   }]
  4466     * });
  4467     */
  4468    function Pie(query, data, options, responsiveOptions) {
  4469      Chartist.Pie.super.constructor.call(this,
  4470        query,
  4471        data,
  4472        defaultOptions,
  4473        Chartist.extend({}, defaultOptions, options),
  4474        responsiveOptions);
  4475    }
  4476  
  4477    // Creating pie chart type in Chartist namespace
  4478    Chartist.Pie = Chartist.Base.extend({
  4479      constructor: Pie,
  4480      createChart: createChart,
  4481      determineAnchorPosition: determineAnchorPosition
  4482    });
  4483  
  4484  }(window, document, Chartist));
  4485  
  4486  return Chartist;
  4487  
  4488  }));