Source: html-legend.js

/**
 * htmlLegend is a attachable widget that can be added to other dc charts to render horizontal/vertical legend
 * labels.
 * @class htmlLegend
 * @memberof dc
 * @example
 * chart.legend(dc.htmlLegend().container(legendContainerElement).horizontal(false))
 * @returns {dc.htmlLegend}
 */
dc.htmlLegend = function () {
    var _legend = {},
        _htmlLegendDivCssClass = 'dc-html-legend',
        _legendItemCssClassHorizontal = 'dc-legend-item-horizontal',
        _legendItemCssClassVertical = 'dc-legend-item-vertical',
        _parent,
        _container,
        _legendText = dc.pluck('name'),
        _maxItems,
        _horizontal = false,
        _legendItemClass,
        _highlightSelected = false;

    _legend.parent = function (p) {
        if (!arguments.length) {
            return _parent;
        }
        _parent = p;
        return _legend;
    };

    _legend.render = function () {
        var _defaultLegendItemCssClass = _horizontal ?  _legendItemCssClassHorizontal : _legendItemCssClassVertical;
        _container.select('div.dc-html-legend').remove();

        var _l = _container.append('div').attr('class', _htmlLegendDivCssClass);
        _l.attr('style', 'max-width:' + _container.nodes()[0].style.width);

        var legendables = _parent.legendables();
        var filters = _parent.filters();

        if (_maxItems !== undefined) {
            legendables = legendables.slice(0, _maxItems);
        }

        var legendItemClassName = _legendItemClass ? _legendItemClass : _defaultLegendItemCssClass;

        var itemEnter = _l.selectAll('div.' + legendItemClassName)
            .data(legendables).enter()
            .append('div')
            .classed(legendItemClassName, true)
            .on('mouseover', _parent.legendHighlight)
            .on('mouseout', _parent.legendReset)
            .on('click', _parent.legendToggle);

        if (_highlightSelected) {
            itemEnter.classed(dc.constants.SELECTED_CLASS, function (d) {
                return filters.indexOf(d.name) !== -1;
            });
        }

        itemEnter.append('span')
            .attr('class', 'dc-legend-item-color')
            .style('background-color', dc.pluck('color'));

        itemEnter.append('span')
            .attr('class', 'dc-legend-item-label')
            .attr('title', _legendText)
            .text(_legendText);
    };

    /**
     * Set the container selector for the legend widget. Required.
     * @method container
     * @memberof dc.htmlLegend
     * @instance
     * @param {String} [container]
     * @return {String|dc.htmlLegend}
     **/
    _legend.container = function (container) {
        if (!arguments.length) {
            return _container;
        }
        _container = d3.select(container);
        return _legend;
    };

    /**
     * This can be optionally used to override class for legenditem and just use this class style.
     * This is helpful for overriding the style of a particular chart rather than overriding
     * the style for all charts.
     *
     * Setting this will disable the highlighting of selected items also.
     * @method legendItemClass
     * @memberof dc.htmlLegend
     * @instance
     * @param {String} [legendItemClass]
     * @return {String|dc.htmlLegend}
     **/
    _legend.legendItemClass = function (legendItemClass) {
        if (!arguments.length) {
            return _legendItemClass;
        }
        _legendItemClass = legendItemClass;
        return _legend;
    };

    /**
     * This can be optionally used to enable highlighting legends for the selections/filters for the
     * chart.
     * @method highlightSelected
     * @memberof dc.htmlLegend
     * @instance
     * @param {String} [highlightSelected]
     * @return {String|dc.htmlLegend}
     **/
    _legend.highlightSelected = function (highlightSelected) {
        if (!arguments.length) {
            return _highlightSelected;
        }
        _highlightSelected = highlightSelected;
        return _legend;
    };

    /**
     * Display the legend horizontally instead of vertically
     * @method horizontal
     * @memberof dc.htmlLegend
     * @instance
     * @param {String} [horizontal]
     * @return {String|dc.htmlLegend}
     **/
    _legend.horizontal = function (horizontal) {
        if (!arguments.length) {
            return _horizontal;
        }
        _horizontal = horizontal;
        return _legend;
    };

    /**
     * Set or get the legend text function. The legend widget uses this function to render the legend
     * text for each item. If no function is specified the legend widget will display the names
     * associated with each group.
     * @method legendText
     * @memberof dc.htmlLegend
     * @instance
     * @param  {Function} [legendText]
     * @returns {Function|dc.htmlLegend}
     * @example
     * // default legendText
     * legend.legendText(dc.pluck('name'))
     *
     * // create numbered legend items
     * chart.legend(dc.htmlLegend().legendText(function(d, i) { return i + '. ' + d.name; }))
     *
     * // create legend displaying group counts
     * chart.legend(dc.htmlLegend().legendText(function(d) { return d.name + ': ' d.data; }))
     **/
    _legend.legendText = function (legendText) {
        if (!arguments.length) {
            return _legendText;
        }
        _legendText = legendText;
        return _legend;
    };

    /**
     * Maximum number of legend items to display
     * @method maxItems
     * @memberof dc.htmlLegend
     * @instance
     * @param  {Number} [maxItems]
     * @return {dc.htmlLegend}
     */
    _legend.maxItems = function (maxItems) {
        if (!arguments.length) {
            return _maxItems;
        }
        _maxItems = dc.utils.isNumber(maxItems) ? maxItems : undefined;
        return _legend;
    };

    return _legend;
};