text: /**
   * `dc.baseMixin` is an abstract functional object representing a basic `dc` chart object
   * for all chart and widget implementations. Methods from the {@link #dc.baseMixin dc.baseMixin} are inherited
   * and available on all chart implementations in the `dc` library.
   * @name baseMixin
   * @memberof dc
   * @mixin
   * @param {Object} _chart
   * @returns {dc.baseMixin}
   */
  dc.baseMixin = function (_chart) {
      _chart.__dcFlag__ = dc.utils.uniqueId();
  
      var _dimension;
      var _group;
  
      var _anchor;
      var _root;
      var _svg;
      var _isChild;
  
      var _minWidth = 200;
      var _defaultWidthCalc = function (element) {
          var width = element && element.getBoundingClientRect && element.getBoundingClientRect().width;
          return (width && width > _minWidth) ? width : _minWidth;
      };
      var _widthCalc = _defaultWidthCalc;
  
      var _minHeight = 200;
      var _defaultHeightCalc = function (element) {
          var height = element && element.getBoundingClientRect && element.getBoundingClientRect().height;
          return (height && height > _minHeight) ? height : _minHeight;
      };
      var _heightCalc = _defaultHeightCalc;
      var _width, _height;
      var _useViewBoxResizing = false;
  
      var _keyAccessor = dc.pluck('key');
      var _valueAccessor = dc.pluck('value');
      var _label = dc.pluck('key');
  
      var _ordering = dc.pluck('key');
  
      var _renderLabel = false;
  
      var _title = function (d) {
          return _chart.keyAccessor()(d) + ': ' + _chart.valueAccessor()(d);
      };
      var _renderTitle = true;
      var _controlsUseVisibility = false;
  
      var _transitionDuration = 750;
  
      var _transitionDelay = 0;
  
      var _filterPrinter = dc.printers.filters;
  
      var _mandatoryAttributes = ['dimension', 'group'];
  
      var _chartGroup = dc.constants.DEFAULT_CHART_GROUP;
  
      var _listeners = d3.dispatch(
          'preRender',
          'postRender',
          'preRedraw',
          'postRedraw',
          'filtered',
          'zoomed',
          'renderlet',
          'pretransition');
  
      var _legend;
      var _commitHandler;
  
      var _filters = [];
      var _filterHandler = function (dimension, filters) {
          if (filters.length === 0) {
              dimension.filter(null);
          } else if (filters.length === 1 && !filters[0].isFiltered) {
              // single value and not a function-based filter
              dimension.filterExact(filters[0]);
          } else if (filters.length === 1 && filters[0].filterType === 'RangedFilter') {
              // single range-based filter
              dimension.filterRange(filters[0]);
          } else {
              dimension.filterFunction(function (d) {
                  for (var i = 0; i < filters.length; i++) {
                      var filter = filters[i];
                      if (filter.isFiltered && filter.isFiltered(d)) {
                          return true;
                      } else if (filter <= d && filter >= d) {
                          return true;
                      }
                  }
                  return false;
              });
          }
          return filters;
      };
  
      var _data = function (group) {
          return group.all();
      };
  
      /**
       * Set or get the height attribute of a chart. The height is applied to the SVGElement generated by
       * the chart when rendered (or re-rendered). If a value is given, then it will be used to calculate
       * the new height and the chart returned for method chaining.  The value can either be a numeric, a
       * function, or falsy. If no value is specified then the value of the current height attribute will
       * be returned.
       *
       * By default, without an explicit height being given, the chart will select the width of its
       * anchor element. If that isn't possible it defaults to 200 (provided by the
       * {@link dc.baseMixin#minHeight minHeight} property). Setting the value falsy will return
       * the chart to the default behavior.
       * @method height
       * @memberof dc.baseMixin
       * @instance
       * @see {@link dc.baseMixin#minHeight minHeight}
       * @example
       * // Default height
       * chart.height(function (element) {
       *     var height = element && element.getBoundingClientRect && element.getBoundingClientRect().height;
       *     return (height && height > chart.minHeight()) ? height : chart.minHeight();
       * });
       *
       * chart.height(250); // Set the chart's height to 250px;
       * chart.height(function(anchor) { return doSomethingWith(anchor); }); // set the chart's height with a function
       * chart.height(null); // reset the height to the default auto calculation
       * @param {Number|Function} [height]
       * @returns {Number|dc.baseMixin}
       */
      _chart.height = function (height) {
          if (!arguments.length) {
              if (!dc.utils.isNumber(_height)) {
                  // only calculate once
                  _height = _heightCalc(_root.node());
              }
              return _height;
          }
          _heightCalc = height ? (typeof height === 'function' ? height : dc.utils.constant(height)) : _defaultHeightCalc;
          _height = undefined;
          return _chart;
      };
  
      /**
       * Set or get the width attribute of a chart.
       * @method width
       * @memberof dc.baseMixin
       * @instance
       * @see {@link dc.baseMixin#height height}
       * @see {@link dc.baseMixin#minWidth minWidth}
       * @example
       * // Default width
       * chart.width(function (element) {
       *     var width = element && element.getBoundingClientRect && element.getBoundingClientRect().width;
       *     return (width && width > chart.minWidth()) ? width : chart.minWidth();
       * });
       * @param {Number|Function} [width]
       * @returns {Number|dc.baseMixin}
       */
      _chart.width = function (width) {
          if (!arguments.length) {
              if (!dc.utils.isNumber(_width)) {
                  // only calculate once
                  _width = _widthCalc(_root.node());
              }
              return _width;
          }
          _widthCalc = width ? (typeof width === 'function' ? width : dc.utils.constant(width)) : _defaultWidthCalc;
          _width = undefined;
          return _chart;
      };
  
      /**
       * Set or get the minimum width attribute of a chart. This only has effect when used with the default
       * {@link dc.baseMixin#width width} function.
       * @method minWidth
       * @memberof dc.baseMixin
       * @instance
       * @see {@link dc.baseMixin#width width}
       * @param {Number} [minWidth=200]
       * @returns {Number|dc.baseMixin}
       */
      _chart.minWidth = function (minWidth) {
          if (!arguments.length) {
              return _minWidth;
          }
          _minWidth = minWidth;
          return _chart;
      };
  
      /**
       * Set or get the minimum height attribute of a chart. This only has effect when used with the default
       * {@link dc.baseMixin#height height} function.
       * @method minHeight
       * @memberof dc.baseMixin
       * @instance
       * @see {@link dc.baseMixin#height height}
       * @param {Number} [minHeight=200]
       * @returns {Number|dc.baseMixin}
       */
      _chart.minHeight = function (minHeight) {
          if (!arguments.length) {
              return _minHeight;
          }
          _minHeight = minHeight;
          return _chart;
      };
  
      /**
       * Turn on/off using the SVG
       * {@link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox `viewBox` attribute}.
       * When enabled, `viewBox` will be set on the svg root element instead of `width` and `height`.
       * Requires that the chart aspect ratio be defined using chart.width(w) and chart.height(h).
       *
       * This will maintain the aspect ratio while enabling the chart to resize responsively to the
       * space given to the chart using CSS. For example, the chart can use `width: 100%; height:
       * 100%` or absolute positioning to resize to its parent div.
       *
       * Since the text will be sized as if the chart is drawn according to the width and height, and
       * will be resized if the chart is any other size, you need to set the chart width and height so
       * that the text looks good. In practice, 600x400 seems to work pretty well for most charts.
       *
       * You can see examples of this resizing strategy in the [Chart Resizing
       * Examples](http://dc-js.github.io/dc.js/resizing/); just add `?resize=viewbox` to any of the
       * one-chart examples to enable `useViewBoxResizing`.
       * @method useViewBoxResizing
       * @memberof dc.baseMixin
       * @instance
       * @param {Boolean} [useViewBoxResizing=false]
       * @returns {Boolean|dc.baseMixin}
       */
      _chart.useViewBoxResizing = function (useViewBoxResizing) {
          if (!arguments.length) {
              return _useViewBoxResizing;
          }
          _useViewBoxResizing = useViewBoxResizing;
          return _chart;
      };
  
      /**
       * **mandatory**
       *
       * Set or get the dimension attribute of a chart. In `dc`, a dimension can be any valid
       * {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#dimension crossfilter dimension}
       *
       * If a value is given, then it will be used as the new dimension. If no value is specified then
       * the current dimension will be returned.
       * @method dimension
       * @memberof dc.baseMixin
       * @instance
       * @see {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#dimension crossfilter.dimension}
       * @example
       * var index = crossfilter([]);
       * var dimension = index.dimension(dc.pluck('key'));
       * chart.dimension(dimension);
       * @param {crossfilter.dimension} [dimension]
       * @returns {crossfilter.dimension|dc.baseMixin}
       */
      _chart.dimension = function (dimension) {
          if (!arguments.length) {
              return _dimension;
          }
          _dimension = dimension;
          _chart.expireCache();
          return _chart;
      };
  
      /**
       * Set the data callback or retrieve the chart's data set. The data callback is passed the chart's
       * group and by default will return
       * {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#group_all group.all}.
       * This behavior may be modified to, for instance, return only the top 5 groups.
       * @method data
       * @memberof dc.baseMixin
       * @instance
       * @example
       * // Default data function
       * chart.data(function (group) { return group.all(); });
       *
       * chart.data(function (group) { return group.top(5); });
       * @param {Function} [callback]
       * @returns {*|dc.baseMixin}
       */
      _chart.data = function (callback) {
          if (!arguments.length) {
              return _data.call(_chart, _group);
          }
          _data = typeof callback === 'function' ? callback : dc.utils.constant(callback);
          _chart.expireCache();
          return _chart;
      };
  
      /**
       * **mandatory**
       *
       * Set or get the group attribute of a chart. In `dc` a group is a
       * {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#group-map-reduce crossfilter group}.
       * Usually the group should be created from the particular dimension associated with the same chart. If a value is
       * given, then it will be used as the new group.
       *
       * If no value specified then the current group will be returned.
       * If `name` is specified then it will be used to generate legend label.
       * @method group
       * @memberof dc.baseMixin
       * @instance
       * @see {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#group-map-reduce crossfilter.group}
       * @example
       * var index = crossfilter([]);
       * var dimension = index.dimension(dc.pluck('key'));
       * chart.dimension(dimension);
       * chart.group(dimension.group().reduceSum());
       * @param {crossfilter.group} [group]
       * @param {String} [name]
       * @returns {crossfilter.group|dc.baseMixin}
       */
      _chart.group = function (group, name) {
          if (!arguments.length) {
              return _group;
          }
          _group = group;
          _chart._groupName = name;
          _chart.expireCache();
          return _chart;
      };
  
      /**
       * Get or set an accessor to order ordinal dimensions.  The chart uses
       * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort Array.sort}
       * to sort elements; this accessor returns the value to order on.
       * @method ordering
       * @memberof dc.baseMixin
       * @instance
       * @example
       * // Default ordering accessor
       * _chart.ordering(dc.pluck('key'));
       * @param {Function} [orderFunction]
       * @returns {Function|dc.baseMixin}
       */
      _chart.ordering = function (orderFunction) {
          if (!arguments.length) {
              return _ordering;
          }
          _ordering = orderFunction;
          _chart.expireCache();
          return _chart;
      };
  
      _chart._computeOrderedGroups = function (data) {
          // clone the array before sorting, otherwise Array.sort sorts in-place
          return data.slice().sort(function (a, b) { return _ordering(a) - _ordering(b) });
      };
  
      /**
       * Clear all filters associated with this chart. The same effect can be achieved by calling
       * {@link dc.baseMixin#filter chart.filter(null)}.
       * @method filterAll
       * @memberof dc.baseMixin
       * @instance
       * @returns {dc.baseMixin}
       */
      _chart.filterAll = function () {
          return _chart.filter(null);
      };
  
      /**
       * Execute d3 single selection in the chart's scope using the given selector and return the d3
       * selection.
       *
       * This function is **not chainable** since it does not return a chart instance; however the d3
       * selection result can be chained to d3 function calls.
       * @method select
       * @memberof dc.baseMixin
       * @instance
       * @see {@link https://github.com/d3/d3-selection/blob/master/README.md#select d3.select}
       * @example
       * // Has the same effect as d3.select('#chart-id').select(selector)
       * chart.select(selector)
       * @param {String} sel CSS selector string
       * @returns {d3.selection}
       */
      _chart.select = function (sel) {
          return _root.select(sel);
      };
  
      /**
       * Execute in scope d3 selectAll using the given selector and return d3 selection result.
       *
       * This function is **not chainable** since it does not return a chart instance; however the d3
       * selection result can be chained to d3 function calls.
       * @method selectAll
       * @memberof dc.baseMixin
       * @instance
       * @see {@link https://github.com/d3/d3-selection/blob/master/README.md#selectAll d3.selectAll}
       * @example
       * // Has the same effect as d3.select('#chart-id').selectAll(selector)
       * chart.selectAll(selector)
       * @param {String} sel CSS selector string
       * @returns {d3.selection}
       */
      _chart.selectAll = function (sel) {
          return _root ? _root.selectAll(sel) : null;
      };
  
      /**
       * Set the root SVGElement to either be an existing chart's root; or any valid [d3 single
       * selector](https://github.com/d3/d3-selection/blob/master/README.md#selecting-elements) specifying a dom
       * block element such as a div; or a dom element or d3 selection. Optionally registers the chart
       * within the chartGroup. This class is called internally on chart initialization, but be called
       * again to relocate the chart. However, it will orphan any previously created SVGElements.
       * @method anchor
       * @memberof dc.baseMixin
       * @instance
       * @param {anchorChart|anchorSelector|anchorNode} [parent]
       * @param {String} [chartGroup]
       * @returns {String|node|d3.selection|dc.baseMixin}
       */
      _chart.anchor = function (parent, chartGroup) {
          if (!arguments.length) {
              return _anchor;
          }
          if (dc.instanceOfChart(parent)) {
              _anchor = parent.anchor();
              if (_anchor.children) { // is _anchor a div?
                  _anchor = '#' + parent.anchorName();
              }
              _root = parent.root();
              _isChild = true;
          } else if (parent) {
              if (parent.select && parent.classed) { // detect d3 selection
                  _anchor = parent.node();
              } else {
                  _anchor = parent;
              }
              _root = d3.select(_anchor);
              _root.classed(dc.constants.CHART_CLASS, true);
              dc.registerChart(_chart, chartGroup);
              _isChild = false;
          } else {
              throw new dc.errors.BadArgumentException('parent must be defined');
          }
          _chartGroup = chartGroup;
          return _chart;
      };
  
      /**
       * Returns the DOM id for the chart's anchored location.
       * @method anchorName
       * @memberof dc.baseMixin
       * @instance
       * @returns {String}
       */
      _chart.anchorName = function () {
          var a = _chart.anchor();
          if (a && a.id) {
              return a.id;
          }
          if (a && a.replace) {
              return a.replace('#', '');
          }
          return 'dc-chart' + _chart.chartID();
      };
  
      /**
       * Returns the root element where a chart resides. Usually it will be the parent div element where
       * the SVGElement was created. You can also pass in a new root element however this is usually handled by
       * dc internally. Resetting the root element on a chart outside of dc internals may have
       * unexpected consequences.
       * @method root
       * @memberof dc.baseMixin
       * @instance
       * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement HTMLElement}
       * @param {HTMLElement} [rootElement]
       * @returns {HTMLElement|dc.baseMixin}
       */
      _chart.root = function (rootElement) {
          if (!arguments.length) {
              return _root;
          }
          _root = rootElement;
          return _chart;
      };
  
      /**
       * Returns the top SVGElement for this specific chart. You can also pass in a new SVGElement,
       * however this is usually handled by dc internally. Resetting the SVGElement on a chart outside
       * of dc internals may have unexpected consequences.
       * @method svg
       * @memberof dc.baseMixin
       * @instance
       * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/SVGElement SVGElement}
       * @param {SVGElement|d3.selection} [svgElement]
       * @returns {SVGElement|d3.selection|dc.baseMixin}
       */
      _chart.svg = function (svgElement) {
          if (!arguments.length) {
              return _svg;
          }
          _svg = svgElement;
          return _chart;
      };
  
      /**
       * Remove the chart's SVGElements from the dom and recreate the container SVGElement.
       * @method resetSvg
       * @memberof dc.baseMixin
       * @instance
       * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/SVGElement SVGElement}
       * @returns {SVGElement}
       */
      _chart.resetSvg = function () {
          _chart.select('svg').remove();
          return generateSvg();
      };
  
      function sizeSvg () {
          if (_svg) {
              if (!_useViewBoxResizing) {
                  _svg
                      .attr('width', _chart.width())
                      .attr('height', _chart.height());
              } else if (!_svg.attr('viewBox')) {
                  _svg
                      .attr('viewBox', '0 0 ' + _chart.width() + ' ' + _chart.height());
              }
          }
      }
  
      function generateSvg () {
          _svg = _chart.root().append('svg');
          sizeSvg();
          return _svg;
      }
  
      /**
       * Set or get the filter printer function. The filter printer function is used to generate human
       * friendly text for filter value(s) associated with the chart instance. The text will get shown
       * in the `.filter element; see {@link dc.baseMixin#turnOnControls turnOnControls}.
       *
       * By default dc charts use a default filter printer {@link dc.printers.filters dc.printers.filters}
       * that provides simple printing support for both single value and ranged filters.
       * @method filterPrinter
       * @memberof dc.baseMixin
       * @instance
       * @example
       * // for a chart with an ordinal brush, print the filters in upper case
       * chart.filterPrinter(function(filters) {
       *   return filters.map(function(f) { return f.toUpperCase(); }).join(', ');
       * });
       * // for a chart with a range brush, print the filter as start and extent
       * chart.filterPrinter(function(filters) {
       *   return 'start ' + dc.utils.printSingleValue(filters[0][0]) +
       *     ' extent ' + dc.utils.printSingleValue(filters[0][1] - filters[0][0]);
       * });
       * @param {Function} [filterPrinterFunction=dc.printers.filters]
       * @returns {Function|dc.baseMixin}
       */
      _chart.filterPrinter = function (filterPrinterFunction) {
          if (!arguments.length) {
              return _filterPrinter;
          }
          _filterPrinter = filterPrinterFunction;
          return _chart;
      };
  
      /**
       * If set, use the `visibility` attribute instead of the `display` attribute for showing/hiding
       * chart reset and filter controls, for less disruption to the layout.
       * @method controlsUseVisibility
       * @memberof dc.baseMixin
       * @instance
       * @param {Boolean} [controlsUseVisibility=false]
       * @returns {Boolean|dc.baseMixin}
       **/
      _chart.controlsUseVisibility = function (controlsUseVisibility) {
          if (!arguments.length) {
              return _controlsUseVisibility;
          }
          _controlsUseVisibility = controlsUseVisibility;
          return _chart;
      };
  
      /**
       * Turn on optional control elements within the root element. dc currently supports the
       * following html control elements.
       * * root.selectAll('.reset') - elements are turned on if the chart has an active filter. This type
       * of control element is usually used to store a reset link to allow user to reset filter on a
       * certain chart. This element will be turned off automatically if the filter is cleared.
       * * root.selectAll('.filter') elements are turned on if the chart has an active filter. The text
       * content of this element is then replaced with the current filter value using the filter printer
       * function. This type of element will be turned off automatically if the filter is cleared.
       * @method turnOnControls
       * @memberof dc.baseMixin
       * @instance
       * @returns {dc.baseMixin}
       */
      _chart.turnOnControls = function () {
          if (_root) {
              var attribute = _chart.controlsUseVisibility() ? 'visibility' : 'display';
              _chart.selectAll('.reset').style(attribute, null);
              _chart.selectAll('.filter').text(_filterPrinter(_chart.filters())).style(attribute, null);
          }
          return _chart;
      };
  
      /**
       * Turn off optional control elements within the root element.
       * @method turnOffControls
       * @memberof dc.baseMixin
       * @see {@link dc.baseMixin#turnOnControls turnOnControls}
       * @instance
       * @returns {dc.baseMixin}
       */
      _chart.turnOffControls = function () {
          if (_root) {
              var attribute = _chart.controlsUseVisibility() ? 'visibility' : 'display';
              var value = _chart.controlsUseVisibility() ? 'hidden' : 'none';
              _chart.selectAll('.reset').style(attribute, value);
              _chart.selectAll('.filter').style(attribute, value).text(_chart.filter());
          }
          return _chart;
      };
  
      /**
       * Set or get the animation transition duration (in milliseconds) for this chart instance.
       * @method transitionDuration
       * @memberof dc.baseMixin
       * @instance
       * @param {Number} [duration=750]
       * @returns {Number|dc.baseMixin}
       */
      _chart.transitionDuration = function (duration) {
          if (!arguments.length) {
              return _transitionDuration;
          }
          _transitionDuration = duration;
          return _chart;
      };
  
      /**
       * Set or get the animation transition delay (in milliseconds) for this chart instance.
       * @method transitionDelay
       * @memberof dc.baseMixin
       * @instance
       * @param {Number} [delay=0]
       * @returns {Number|dc.baseMixin}
       */
      _chart.transitionDelay = function (delay) {
          if (!arguments.length) {
              return _transitionDelay;
          }
          _transitionDelay = delay;
          return _chart;
      };
  
      _chart._mandatoryAttributes = function (_) {
          if (!arguments.length) {
              return _mandatoryAttributes;
          }
          _mandatoryAttributes = _;
          return _chart;
      };
  
      function checkForMandatoryAttributes (a) {
          if (!_chart[a] || !_chart[a]()) {
              throw new dc.errors.InvalidStateException('Mandatory attribute chart.' + a +
                  ' is missing on chart[#' + _chart.anchorName() + ']');
          }
      }
  
      /**
       * Invoking this method will force the chart to re-render everything from scratch. Generally it
       * should only be used to render the chart for the first time on the page or if you want to make
       * sure everything is redrawn from scratch instead of relying on the default incremental redrawing
       * behaviour.
       * @method render
       * @memberof dc.baseMixin
       * @instance
       * @returns {dc.baseMixin}
       */
      _chart.render = function () {
          _height = _width = undefined; // force recalculate
          _listeners.call('preRender', _chart, _chart);
  
          if (_mandatoryAttributes) {
              _mandatoryAttributes.forEach(checkForMandatoryAttributes);
          }
  
          var result = _chart._doRender();
  
          if (_legend) {
              _legend.render();
          }
  
          _chart._activateRenderlets('postRender');
  
          return result;
      };
  
      _chart._activateRenderlets = function (event) {
          _listeners.call('pretransition', _chart, _chart);
          if (_chart.transitionDuration() > 0 && _svg) {
              _svg.transition().duration(_chart.transitionDuration()).delay(_chart.transitionDelay())
                  .on('end', function () {
                      _listeners.call('renderlet', _chart, _chart);
                      if (event) {
                          _listeners.call(event, _chart, _chart);
                      }
                  });
          } else {
              _listeners.call('renderlet', _chart, _chart);
              if (event) {
                  _listeners.call(event, _chart, _chart);
              }
          }
      };
  
      /**
       * Calling redraw will cause the chart to re-render data changes incrementally. If there is no
       * change in the underlying data dimension then calling this method will have no effect on the
       * chart. Most chart interaction in dc will automatically trigger this method through internal
       * events (in particular {@link dc.redrawAll dc.redrawAll}); therefore, you only need to
       * manually invoke this function if data is manipulated outside of dc's control (for example if
       * data is loaded in the background using
       * {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#crossfilter_add crossfilter.add}).
       * @method redraw
       * @memberof dc.baseMixin
       * @instance
       * @returns {dc.baseMixin}
       */
      _chart.redraw = function () {
          sizeSvg();
          _listeners.call('preRedraw', _chart, _chart);
  
          var result = _chart._doRedraw();
  
          if (_legend) {
              _legend.render();
          }
  
          _chart._activateRenderlets('postRedraw');
  
          return result;
      };
  
      /**
       * Gets/sets the commit handler. If the chart has a commit handler, the handler will be called when
       * the chart's filters have changed, in order to send the filter data asynchronously to a server.
       *
       * Unlike other functions in dc.js, the commit handler is asynchronous. It takes two arguments:
       * a flag indicating whether this is a render (true) or a redraw (false), and a callback to be
       * triggered once the commit is done. The callback has the standard node.js continuation signature
       * with error first and result second.
       * @method commitHandler
       * @param {Function} commitHandler
       * @memberof dc.baseMixin
       * @instance
       * @returns {dc.baseMixin}
       */
      _chart.commitHandler = function (commitHandler) {
          if (!arguments.length) {
              return _commitHandler;
          }
          _commitHandler = commitHandler;
          return _chart;
      };
  
      /**
       * Redraws all charts in the same group as this chart, typically in reaction to a filter
       * change. If the chart has a {@link dc.baseMixin.commitFilter commitHandler}, it will
       * be executed and waited for.
       * @method redrawGroup
       * @memberof dc.baseMixin
       * @instance
       * @returns {dc.baseMixin}
       */
      _chart.redrawGroup = function () {
          if (_commitHandler) {
              _commitHandler(false, function (error, result) {
                  if (error) {
                      console.log(error);
                  } else {
                      dc.redrawAll(_chart.chartGroup());
                  }
              });
          } else {
              dc.redrawAll(_chart.chartGroup());
          }
          return _chart;
      };
  
      /**
       * Renders all charts in the same group as this chart. If the chart has a
       * {@link dc.baseMixin.commitFilter commitHandler}, it will be executed and waited for
       * @method renderGroup
       * @memberof dc.baseMixin
       * @instance
       * @returns {dc.baseMixin}
       */
      _chart.renderGroup = function () {
          if (_commitHandler) {
              _commitHandler(false, function (error, result) {
                  if (error) {
                      console.log(error);
                  } else {
                      dc.renderAll(_chart.chartGroup());
                  }
              });
          } else {
              dc.renderAll(_chart.chartGroup());
          }
          return _chart;
      };
  
      _chart._invokeFilteredListener = function (f) {
          if (f !== undefined) {
              _listeners.call('filtered', _chart, _chart, f);
          }
      };
  
      _chart._invokeZoomedListener = function () {
          _listeners.call('zoomed', _chart, _chart);
      };
  
      var _hasFilterHandler = function (filters, filter) {
          if (filter === null || typeof(filter) === 'undefined') {
              return filters.length > 0;
          }
          return filters.some(function (f) {
              return filter <= f && filter >= f;
          });
      };
  
      /**
       * Set or get the has-filter handler. The has-filter handler is a function that checks to see if
       * the chart's current filters (first argument) include a specific filter (second argument).  Using a custom has-filter handler allows
       * you to change the way filters are checked for and replaced.
       * @method hasFilterHandler
       * @memberof dc.baseMixin
       * @instance
       * @example
       * // default has-filter handler
       * chart.hasFilterHandler(function (filters, filter) {
       *     if (filter === null || typeof(filter) === 'undefined') {
       *         return filters.length > 0;
       *     }
       *     return filters.some(function (f) {
       *         return filter <= f && filter >= f;
       *     });
       * });
       *
       * // custom filter handler (no-op)
       * chart.hasFilterHandler(function(filters, filter) {
       *     return false;
       * });
       * @param {Function} [hasFilterHandler]
       * @returns {Function|dc.baseMixin}
       */
      _chart.hasFilterHandler = function (hasFilterHandler) {
          if (!arguments.length) {
              return _hasFilterHandler;
          }
          _hasFilterHandler = hasFilterHandler;
          return _chart;
      };
  
      /**
       * Check whether any active filter or a specific filter is associated with particular chart instance.
       * This function is **not chainable**.
       * @method hasFilter
       * @memberof dc.baseMixin
       * @instance
       * @see {@link dc.baseMixin#hasFilterHandler hasFilterHandler}
       * @param {*} [filter]
       * @returns {Boolean}
       */
      _chart.hasFilter = function (filter) {
          return _hasFilterHandler(_filters, filter);
      };
  
      var _removeFilterHandler = function (filters, filter) {
          for (var i = 0; i < filters.length; i++) {
              if (filters[i] <= filter && filters[i] >= filter) {
                  filters.splice(i, 1);
                  break;
              }
          }
          return filters;
      };
  
      /**
       * Set or get the remove filter handler. The remove filter handler is a function that removes a
       * filter from the chart's current filters. Using a custom remove filter handler allows you to
       * change how filters are removed or perform additional work when removing a filter, e.g. when
       * using a filter server other than crossfilter.
       *
       * The handler should return a new or modified array as the result.
       * @method removeFilterHandler
       * @memberof dc.baseMixin
       * @instance
       * @example
       * // default remove filter handler
       * chart.removeFilterHandler(function (filters, filter) {
       *     for (var i = 0; i < filters.length; i++) {
       *         if (filters[i] <= filter && filters[i] >= filter) {
       *             filters.splice(i, 1);
       *             break;
       *         }
       *     }
       *     return filters;
       * });
       *
       * // custom filter handler (no-op)
       * chart.removeFilterHandler(function(filters, filter) {
       *     return filters;
       * });
       * @param {Function} [removeFilterHandler]
       * @returns {Function|dc.baseMixin}
       */
      _chart.removeFilterHandler = function (removeFilterHandler) {
          if (!arguments.length) {
              return _removeFilterHandler;
          }
          _removeFilterHandler = removeFilterHandler;
          return _chart;
      };
  
      var _addFilterHandler = function (filters, filter) {
          filters.push(filter);
          return filters;
      };
  
      /**
       * Set or get the add filter handler. The add filter handler is a function that adds a filter to
       * the chart's filter list. Using a custom add filter handler allows you to change the way filters
       * are added or perform additional work when adding a filter, e.g. when using a filter server other
       * than crossfilter.
       *
       * The handler should return a new or modified array as the result.
       * @method addFilterHandler
       * @memberof dc.baseMixin
       * @instance
       * @example
       * // default add filter handler
       * chart.addFilterHandler(function (filters, filter) {
       *     filters.push(filter);
       *     return filters;
       * });
       *
       * // custom filter handler (no-op)
       * chart.addFilterHandler(function(filters, filter) {
       *     return filters;
       * });
       * @param {Function} [addFilterHandler]
       * @returns {Function|dc.baseMixin}
       */
      _chart.addFilterHandler = function (addFilterHandler) {
          if (!arguments.length) {
              return _addFilterHandler;
          }
          _addFilterHandler = addFilterHandler;
          return _chart;
      };
  
      var _resetFilterHandler = function (filters) {
          return [];
      };
  
      /**
       * Set or get the reset filter handler. The reset filter handler is a function that resets the
       * chart's filter list by returning a new list. Using a custom reset filter handler allows you to
       * change the way filters are reset, or perform additional work when resetting the filters,
       * e.g. when using a filter server other than crossfilter.
       *
       * The handler should return a new or modified array as the result.
       * @method resetFilterHandler
       * @memberof dc.baseMixin
       * @instance
       * @example
       * // default remove filter handler
       * function (filters) {
       *     return [];
       * }
       *
       * // custom filter handler (no-op)
       * chart.resetFilterHandler(function(filters) {
       *     return filters;
       * });
       * @param {Function} [resetFilterHandler]
       * @returns {dc.baseMixin}
       */
      _chart.resetFilterHandler = function (resetFilterHandler) {
          if (!arguments.length) {
              return _resetFilterHandler;
          }
          _resetFilterHandler = resetFilterHandler;
          return _chart;
      };
  
      function applyFilters (filters) {
          if (_chart.dimension() && _chart.dimension().filter) {
              var fs = _filterHandler(_chart.dimension(), filters);
              if (fs) {
                  filters = fs;
              }
          }
          return filters;
      }
  
      /**
       * Replace the chart filter. This is equivalent to calling `chart.filter(null).filter(filter)`
       * but more efficient because the filter is only applied once.
       *
       * @method replaceFilter
       * @memberof dc.baseMixin
       * @instance
       * @param {*} [filter]
       * @returns {dc.baseMixin}
       **/
      _chart.replaceFilter = function (filter) {
          _filters = _resetFilterHandler(_filters);
          _chart.filter(filter);
          return _chart;
      };
  
      /**
       * Filter the chart by the given parameter, or return the current filter if no input parameter
       * is given.
       *
       * The filter parameter can take one of these forms:
       * * A single value: the value will be toggled (added if it is not present in the current
       * filters, removed if it is present)
       * * An array containing a single array of values (`[[value,value,value]]`): each value is
       * toggled
       * * When appropriate for the chart, a {@link dc.filters dc filter object} such as
       *   * {@link dc.filters.RangedFilter `dc.filters.RangedFilter`} for the
       * {@link dc.coordinateGridMixin dc.coordinateGridMixin} charts
       *   * {@link dc.filters.TwoDimensionalFilter `dc.filters.TwoDimensionalFilter`} for the
       * {@link dc.heatMap heat map}
       *   * {@link dc.filters.RangedTwoDimensionalFilter `dc.filters.RangedTwoDimensionalFilter`}
       * for the {@link dc.scatterPlot scatter plot}
       * * `null`: the filter will be reset using the
       * {@link dc.baseMixin#resetFilterHandler resetFilterHandler}
       *
       * Note that this is always a toggle (even when it doesn't make sense for the filter type). If
       * you wish to replace the current filter, either call `chart.filter(null)` first - or it's more
       * efficient to call {@link dc.baseMixin#replaceFilter `chart.replaceFilter(filter)`} instead.
       *
       * Each toggle is executed by checking if the value is already present using the
       * {@link dc.baseMixin#hasFilterHandler hasFilterHandler}; if it is not present, it is added
       * using the {@link dc.baseMixin#addFilterHandler addFilterHandler}; if it is already present,
       * it is removed using the {@link dc.baseMixin#removeFilterHandler removeFilterHandler}.
       *
       * Once the filters array has been updated, the filters are applied to the
       * crossfilter dimension, using the {@link dc.baseMixin#filterHandler filterHandler}.
       *
       * Once you have set the filters, call {@link dc.baseMixin#redrawGroup `chart.redrawGroup()`}
       * (or {@link dc.redrawAll `dc.redrawAll()`}) to redraw the chart's group.
       * @method filter
       * @memberof dc.baseMixin
       * @instance
       * @see {@link dc.baseMixin#addFilterHandler addFilterHandler}
       * @see {@link dc.baseMixin#removeFilterHandler removeFilterHandler}
       * @see {@link dc.baseMixin#resetFilterHandler resetFilterHandler}
       * @see {@link dc.baseMixin#filterHandler filterHandler}
       * @example
       * // filter by a single string
       * chart.filter('Sunday');
       * // filter by a single age
       * chart.filter(18);
       * // filter by a set of states
       * chart.filter([['MA', 'TX', 'ND', 'WA']]);
       * // filter by range -- note the use of dc.filters.RangedFilter, which is different
       * // from the syntax for filtering a crossfilter dimension directly, dimension.filter([15,20])
       * chart.filter(dc.filters.RangedFilter(15,20));
       * @param {*} [filter]
       * @returns {dc.baseMixin}
       */
      _chart.filter = function (filter) {
          if (!arguments.length) {
              return _filters.length > 0 ? _filters[0] : null;
          }
          var filters = _filters;
          if (filter instanceof Array && filter[0] instanceof Array && !filter.isFiltered) {
              // toggle each filter
              filter[0].forEach(function (f) {
                  if (_hasFilterHandler(filters, f)) {
                      filters = _removeFilterHandler(filters, f);
                  } else {
                      filters = _addFilterHandler(filters, f);
                  }
              });
          } else if (filter === null) {
              filters = _resetFilterHandler(filters);
          } else {
              if (_hasFilterHandler(filters, filter)) {
                  filters = _removeFilterHandler(filters, filter);
              } else {
                  filters = _addFilterHandler(filters, filter);
              }
          }
          _filters = applyFilters(filters);
          _chart._invokeFilteredListener(filter);
  
          if (_root !== null && _chart.hasFilter()) {
              _chart.turnOnControls();
          } else {
              _chart.turnOffControls();
          }
  
          return _chart;
      };
  
      /**
       * Returns all current filters. This method does not perform defensive cloning of the internal
       * filter array before returning, therefore any modification of the returned array will effect the
       * chart's internal filter storage.
       * @method filters
       * @memberof dc.baseMixin
       * @instance
       * @returns {Array<*>}
       */
      _chart.filters = function () {
          return _filters;
      };
  
      _chart.highlightSelected = function (e) {
          d3.select(e).classed(dc.constants.SELECTED_CLASS, true);
          d3.select(e).classed(dc.constants.DESELECTED_CLASS, false);
      };
  
      _chart.fadeDeselected = function (e) {
          d3.select(e).classed(dc.constants.SELECTED_CLASS, false);
          d3.select(e).classed(dc.constants.DESELECTED_CLASS, true);
      };
  
      _chart.resetHighlight = function (e) {
          d3.select(e).classed(dc.constants.SELECTED_CLASS, false);
          d3.select(e).classed(dc.constants.DESELECTED_CLASS, false);
      };
  
      /**
       * This function is passed to d3 as the onClick handler for each chart. The default behavior is to
       * filter on the clicked datum (passed to the callback) and redraw the chart group.
       *
       * This function can be replaced in order to change the click behavior (but first look at 
       * @method onClick
       * @memberof dc.baseMixin
       * @instance
       * @example
       * var oldHandler = chart.onClick;
       * chart.onClick = function(datum) {
       *   // use datum.
       * @param {*} datum
       * @return {undefined}
       */
      _chart.onClick = function (datum) {
          var filter = _chart.keyAccessor()(datum);
          dc.events.trigger(function () {
              _chart.filter(filter);
              _chart.redrawGroup();
          });
      };
  
      /**
       * Set or get the filter handler. The filter handler is a function that performs the filter action
       * on a specific dimension. Using a custom filter handler allows you to perform additional logic
       * before or after filtering.
       * @method filterHandler
       * @memberof dc.baseMixin
       * @instance
       * @see {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#dimension_filter crossfilter.dimension.filter}
       * @example
       * // the default filter handler handles all possible cases for the charts in dc.js
       * // you can replace it with something more specialized for your own chart
       * chart.filterHandler(function (dimension, filters) {
       *     if (filters.length === 0) {
       *         // the empty case (no filtering)
       *         dimension.filter(null);
       *     } else if (filters.length === 1 && !filters[0].isFiltered) {
       *         // single value and not a function-based filter
       *         dimension.filterExact(filters[0]);
       *     } else if (filters.length === 1 && filters[0].filterType === 'RangedFilter') {
       *         // single range-based filter
       *         dimension.filterRange(filters[0]);
       *     } else {
       *         // an array of values, or an array of filter objects
       *         dimension.filterFunction(function (d) {
       *             for (var i = 0; i < filters.length; i++) {
       *                 var filter = filters[i];
       *                 if (filter.isFiltered && filter.isFiltered(d)) {
       *                     return true;
       *                 } else if (filter <= d && filter >= d) {
       *                     return true;
       *                 }
       *             }
       *             return false;
       *         });
       *     }
       *     return filters;
       * });
       *
       * // custom filter handler
       * chart.filterHandler(function(dimension, filter){
       *     var newFilter = filter + 10;
       *     dimension.filter(newFilter);
       *     return newFilter; // set the actual filter value to the new value
       * });
       * @param {Function} [filterHandler]
       * @returns {Function|dc.baseMixin}
       */
      _chart.filterHandler = function (filterHandler) {
          if (!arguments.length) {
              return _filterHandler;
          }
          _filterHandler = filterHandler;
          return _chart;
      };
  
      // abstract function stub
      _chart._doRender = function () {
          // do nothing in base, should be overridden by sub-function
          return _chart;
      };
  
      _chart._doRedraw = function () {
          // do nothing in base, should be overridden by sub-function
          return _chart;
      };
  
      _chart.legendables = function () {
          // do nothing in base, should be overridden by sub-function
          return [];
      };
  
      _chart.legendHighlight = function () {
          // do nothing in base, should be overridden by sub-function
      };
  
      _chart.legendReset = function () {
          // do nothing in base, should be overridden by sub-function
      };
  
      _chart.legendToggle = function () {
          // do nothing in base, should be overriden by sub-function
      };
  
      _chart.isLegendableHidden = function () {
          // do nothing in base, should be overridden by sub-function
          return false;
      };
  
      /**
       * Set or get the key accessor function. The key accessor function is used to retrieve the key
       * value from the crossfilter group. Key values are used differently in different charts, for
       * example keys correspond to slices in a pie chart and x axis positions in a grid coordinate chart.
       * @method keyAccessor
       * @memberof dc.baseMixin
       * @instance
       * @example
       * // default key accessor
       * chart.keyAccessor(function(d) { return d.key; });
       * // custom key accessor for a multi-value crossfilter reduction
       * chart.keyAccessor(function(p) { return p.value.absGain; });
       * @param {Function} [keyAccessor]
       * @returns {Function|dc.baseMixin}
       */
      _chart.keyAccessor = function (keyAccessor) {
          if (!arguments.length) {
              return _keyAccessor;
          }
          _keyAccessor = keyAccessor;
          return _chart;
      };
  
      /**
       * Set or get the value accessor function. The value accessor function is used to retrieve the
       * value from the crossfilter group. Group values are used differently in different charts, for
       * example values correspond to slice sizes in a pie chart and y axis positions in a grid
       * coordinate chart.
       * @method valueAccessor
       * @memberof dc.baseMixin
       * @instance
       * @example
       * // default value accessor
       * chart.valueAccessor(function(d) { return d.value; });
       * // custom value accessor for a multi-value crossfilter reduction
       * chart.valueAccessor(function(p) { return p.value.percentageGain; });
       * @param {Function} [valueAccessor]
       * @returns {Function|dc.baseMixin}
       */
      _chart.valueAccessor = function (valueAccessor) {
          if (!arguments.length) {
              return _valueAccessor;
          }
          _valueAccessor = valueAccessor;
          return _chart;
      };
  
      /**
       * Set or get the label function. The chart class will use this function to render labels for each
       * child element in the chart, e.g. slices in a pie chart or bubbles in a bubble chart. Not every
       * chart supports the label function, for example line chart does not use this function
       * at all. By default, enables labels; pass false for the second parameter if this is not desired.
       * @method label
       * @memberof dc.baseMixin
       * @instance
       * @example
       * // default label function just return the key
       * chart.label(function(d) { return d.key; });
       * // label function has access to the standard d3 data binding and can get quite complicated
       * chart.label(function(d) { return d.data.key + '(' + Math.floor(d.data.value / all.value() * 100) + '%)'; });
       * @param {Function} [labelFunction]
       * @param {Boolean} [enableLabels=true]
       * @returns {Function|dc.baseMixin}
       */
      _chart.label = function (labelFunction, enableLabels) {
          if (!arguments.length) {
              return _label;
          }
          _label = labelFunction;
          if ((enableLabels === undefined) || enableLabels) {
              _renderLabel = true;
          }
          return _chart;
      };
  
      /**
       * Turn on/off label rendering
       * @method renderLabel
       * @memberof dc.baseMixin
       * @instance
       * @param {Boolean} [renderLabel=false]
       * @returns {Boolean|dc.baseMixin}
       */
      _chart.renderLabel = function (renderLabel) {
          if (!arguments.length) {
              return _renderLabel;
          }
          _renderLabel = renderLabel;
          return _chart;
      };
  
      /**
       * Set or get the title function. The chart class will use this function to render the SVGElement title
       * (usually interpreted by browser as tooltips) for each child element in the chart, e.g. a slice
       * in a pie chart or a bubble in a bubble chart. Almost every chart supports the title function;
       * however in grid coordinate charts you need to turn off the brush in order to see titles, because
       * otherwise the brush layer will block tooltip triggering.
       * @method title
       * @memberof dc.baseMixin
       * @instance
       * @example
       * // default title function shows "key: value"
       * chart.title(function(d) { return d.key + ': ' + d.value; });
       * // title function has access to the standard d3 data binding and can get quite complicated
       * chart.title(function(p) {
       *    return p.key.getFullYear()
       *        + '\n'
       *        + 'Index Gain: ' + numberFormat(p.value.absGain) + '\n'
       *        + 'Index Gain in Percentage: ' + numberFormat(p.value.percentageGain) + '%\n'
       *        + 'Fluctuation / Index Ratio: ' + numberFormat(p.value.fluctuationPercentage) + '%';
       * });
       * @param {Function} [titleFunction]
       * @returns {Function|dc.baseMixin}
       */
      _chart.title = function (titleFunction) {
          if (!arguments.length) {
              return _title;
          }
          _title = titleFunction;
          return _chart;
      };
  
      /**
       * Turn on/off title rendering, or return the state of the render title flag if no arguments are
       * given.
       * @method renderTitle
       * @memberof dc.baseMixin
       * @instance
       * @param {Boolean} [renderTitle=true]
       * @returns {Boolean|dc.baseMixin}
       */
      _chart.renderTitle = function (renderTitle) {
          if (!arguments.length) {
              return _renderTitle;
          }
          _renderTitle = renderTitle;
          return _chart;
      };
  
      /**
       * A renderlet is similar to an event listener on rendering event. Multiple renderlets can be added
       * to an individual chart.  Each time a chart is rerendered or redrawn the renderlets are invoked
       * right after the chart finishes its transitions, giving you a way to modify the SVGElements.
       * Renderlet functions take the chart instance as the only input parameter and you can
       * use the dc API or use raw d3 to achieve pretty much any effect.
       *
       * Use {@link dc.baseMixin#on on} with a 'renderlet' prefix.
       * Generates a random key for the renderlet, which makes it hard to remove.
       * @method renderlet
       * @memberof dc.baseMixin
       * @instance
       * @deprecated
       * @example
       * // do this instead of .renderlet(function(chart) { ... })
       * chart.on("renderlet", function(chart){
       *     // mix of dc API and d3 manipulation
       *     chart.select('g.y').style('display', 'none');
       *     // its a closure so you can also access other chart variable available in the closure scope
       *     moveChart.filter(chart.filter());
       * });
       * @param {Function} renderletFunction
       * @returns {dc.baseMixin}
       */
      _chart.renderlet = dc.logger.deprecate(function (renderletFunction) {
          _chart.on('renderlet.' + dc.utils.uniqueId(), renderletFunction);
          return _chart;
      }, 'chart.renderlet has been deprecated.  Please use chart.on("renderlet.<renderletKey>", renderletFunction)');
  
      /**
       * Get or set the chart group to which this chart belongs. Chart groups are rendered or redrawn
       * together since it is expected they share the same underlying crossfilter data set.
       * @method chartGroup
       * @memberof dc.baseMixin
       * @instance
       * @param {String} [chartGroup]
       * @returns {String|dc.baseMixin}
       */
      _chart.chartGroup = function (chartGroup) {
          if (!arguments.length) {
              return _chartGroup;
          }
          if (!_isChild) {
              dc.deregisterChart(_chart, _chartGroup);
          }
          _chartGroup = chartGroup;
          if (!_isChild) {
              dc.registerChart(_chart, _chartGroup);
          }
          return _chart;
      };
  
      /**
       * Expire the internal chart cache. dc charts cache some data internally on a per chart basis to
       * speed up rendering and avoid unnecessary calculation; however it might be useful to clear the
       * cache if you have changed state which will affect rendering.  For example, if you invoke
       * {@link https://github.com/crossfilter/crossfilter/wiki/API-Reference#crossfilter_add crossfilter.add}
       * function or reset group or dimension after rendering, it is a good idea to
       * clear the cache to make sure charts are rendered properly.
       * @method expireCache
       * @memberof dc.baseMixin
       * @instance
       * @returns {dc.baseMixin}
       */
      _chart.expireCache = function () {
          // do nothing in base, should be overridden by sub-function
          return _chart;
      };
  
      /**
       * Attach a dc.legend widget to this chart. The legend widget will automatically draw legend labels
       * based on the color setting and names associated with each group.
       * @method legend
       * @memberof dc.baseMixin
       * @instance
       * @example
       * chart.legend(dc.legend().x(400).y(10).itemHeight(13).gap(5))
       * @param {dc.legend} [legend]
       * @returns {dc.legend|dc.baseMixin}
       */
      _chart.legend = function (legend) {
          if (!arguments.length) {
              return _legend;
          }
          _legend = legend;
          _legend.parent(_chart);
          return _chart;
      };
  
      /**
       * Returns the internal numeric ID of the chart.
       * @method chartID
       * @memberof dc.baseMixin
       * @instance
       * @returns {String}
       */
      _chart.chartID = function () {
          return _chart.__dcFlag__;
      };
  
      /**
       * Set chart options using a configuration object. Each key in the object will cause the method of
       * the same name to be called with the value to set that attribute for the chart.
       * @method options
       * @memberof dc.baseMixin
       * @instance
       * @example
       * chart.options({dimension: myDimension, group: myGroup});
       * @param {{}} opts
       * @returns {dc.baseMixin}
       */
      _chart.options = function (opts) {
          var applyOptions = [
              'anchor',
              'group',
              'xAxisLabel',
              'yAxisLabel',
              'stack',
              'title',
              'point',
              'getColor',
              'overlayGeoJson'
          ];
  
          for (var o in opts) {
              if (typeof(_chart[o]) === 'function') {
                  if (opts[o] instanceof Array && applyOptions.indexOf(o) !== -1) {
                      _chart[o].apply(_chart, opts[o]);
                  } else {
                      _chart[o].call(_chart, opts[o]);
                  }
              } else {
                  dc.logger.debug('Not a valid option setter name: ' + o);
              }
          }
          return _chart;
      };
  
      /**
       * All dc chart instance supports the following listeners.
       * Supports the following events:
       * * `renderlet` - This listener function will be invoked after transitions after redraw and render. Replaces the
       * deprecated {@link dc.baseMixin#renderlet renderlet} method.
       * * `pretransition` - Like `.on('renderlet', ...)` but the event is fired before transitions start.
       * * `preRender` - This listener function will be invoked before chart rendering.
       * * `postRender` - This listener function will be invoked after chart finish rendering including
       * all renderlets' logic.
       * * `preRedraw` - This listener function will be invoked before chart redrawing.
       * * `postRedraw` - This listener function will be invoked after chart finish redrawing
       * including all renderlets' logic.
       * * `filtered` - This listener function will be invoked after a filter is applied, added or removed.
       * * `zoomed` - This listener function will be invoked after a zoom is triggered.
       * @method on
       * @memberof dc.baseMixin
       * @instance
       * @see {@link https://github.com/d3/d3-dispatch/blob/master/README.md#dispatch_on d3.dispatch.on}
       * @example
       * .on('renderlet', function(chart, filter){...})
       * .on('pretransition', function(chart, filter){...})
       * .on('preRender', function(chart){...})
       * .on('postRender', function(chart){...})
       * .on('preRedraw', function(chart){...})
       * .on('postRedraw', function(chart){...})
       * .on('filtered', function(chart, filter){...})
       * .on('zoomed', function(chart, filter){...})
       * @param {String} event
       * @param {Function} listener
       * @returns {dc.baseMixin}
       */
      _chart.on = function (event, listener) {
          _listeners.on(event, listener);
          return _chart;
      };
  
      return _chart;
  };
  