Version 3.18.1
Show:

File: charts/js/CartesianChartLegend.js

            /**
             * Adds legend functionality to charts.
             *
             * @module charts
             * @submodule charts-legend
             */
            var TOP = "top",
            RIGHT = "right",
            BOTTOM = "bottom",
            LEFT = "left",
            EXTERNAL = "external",
            HORIZONTAL = "horizontal",
            VERTICAL = "vertical",
            WIDTH = "width",
            HEIGHT = "height",
            POSITION = "position",
            _X = "x",
            _Y = "y",
            PX = "px",
            PieChartLegend,
            LEGEND = {
                setter: function(val)
                {
                    var legend = this.get("legend");
                    if(legend)
                    {
                        legend.destroy(true);
                    }
                    if(val instanceof Y.ChartLegend)
                    {
                        legend = val;
                        legend.set("chart", this);
                    }
                    else
                    {
                        val.chart = this;
                        if(!val.hasOwnProperty("render"))
                        {
                            val.render = this.get("contentBox");
                            val.includeInChartLayout = true;
                        }
                        legend = new Y.ChartLegend(val);
                    }
                    return legend;
                }
            },
            
            /**
             * Contains methods for displaying items horizontally in a legend.
             *
             * @module charts
             * @submodule charts-legend
             * @class HorizontalLegendLayout
             */
            HorizontalLegendLayout = {
                /**
                 * Displays items horizontally in a legend.
                 *
                 * @method _positionLegendItems
                 * @param {Array} items Array of items to display in the legend.
                 * @param {Number} maxWidth The width of the largest item in the legend.
                 * @param {Number} maxHeight The height of the largest item in the legend.
                 * @param {Number} totalWidth The total width of all items in a legend.
                 * @param {Number} totalHeight The total height of all items in a legend.
                 * @param {Number} padding The left, top, right and bottom padding properties for the legend.
                 * @param {Number} horizontalGap The horizontal distance between items in a legend.
                 * @param {Number} verticalGap The vertical distance between items in a legend.
                 * @param {String} hAlign The horizontal alignment of the legend.
                 * @protected
                 */
                _positionLegendItems: function(items, maxWidth, maxHeight, totalWidth, totalHeight, padding, horizontalGap, verticalGap, hAlign)
                {
                    var i = 0,
                        rowIterator = 0,
                        item,
                        node,
                        itemWidth,
                        itemHeight,
                        len,
                        width = this.get("width"),
                        rows,
                        rowsLen,
                        row,
                        totalWidthArray,
                        legendWidth,
                        topHeight = padding.top - verticalGap,
                        limit = width - (padding.left + padding.right),
                        left,
                        top,
                        right,
                        bottom;
                    HorizontalLegendLayout._setRowArrays(items, limit, horizontalGap);
                    rows = HorizontalLegendLayout.rowArray;
                    totalWidthArray = HorizontalLegendLayout.totalWidthArray;
                    rowsLen = rows.length;
                    for(; rowIterator < rowsLen; ++ rowIterator)
                    {
                        topHeight += verticalGap;
                        row = rows[rowIterator];
                        len = row.length;
                        legendWidth =  HorizontalLegendLayout.getStartPoint(width, totalWidthArray[rowIterator], hAlign, padding);
                        for(i = 0; i < len; ++i)
                        {
                            item = row[i];
                            node = item.node;
                            itemWidth = item.width;
                            itemHeight = item.height;
                            item.x = legendWidth;
                            item.y = 0;
                            left = !isNaN(left) ? Math.min(left, legendWidth) : legendWidth;
                            top = !isNaN(top) ? Math.min(top, topHeight) : topHeight;
                            right = !isNaN(right) ? Math.max(legendWidth + itemWidth, right) : legendWidth + itemWidth;
                            bottom = !isNaN(bottom) ? Math.max(topHeight + itemHeight, bottom) : topHeight + itemHeight;
                            node.setStyle("left", legendWidth + PX);
                            node.setStyle("top", topHeight + PX);
                            legendWidth += itemWidth + horizontalGap;
                        }
                        topHeight += item.height;
                    }
                    this._contentRect = {
                        left: left,
                        top: top,
                        right: right,
                        bottom: bottom
                    };
                    if(this.get("includeInChartLayout"))
                    {
                        this.set("height", topHeight + padding.bottom);
                    }
                },
            
                /**
                 * Creates row and total width arrays used for displaying multiple rows of
                 * legend items based on the items, available width and horizontalGap for the legend.
                 *
                 * @method _setRowArrays
                 * @param {Array} items Array of legend items to display in a legend.
                 * @param {Number} limit Total available width for displaying items in a legend.
                 * @param {Number} horizontalGap Horizontal distance between items in a legend.
                 * @protected
                 */
                _setRowArrays: function(items, limit, horizontalGap)
                {
                    var item = items[0],
                        rowArray = [[item]],
                        i = 1,
                        rowIterator = 0,
                        len = items.length,
                        totalWidth = item.width,
                        itemWidth,
                        totalWidthArray = [[totalWidth]];
                    for(; i < len; ++i)
                    {
                        item = items[i];
                        itemWidth = item.width;
                        if((totalWidth + horizontalGap + itemWidth) <= limit)
                        {
                            totalWidth += horizontalGap + itemWidth;
                            rowArray[rowIterator].push(item);
                        }
                        else
                        {
                            totalWidth = horizontalGap + itemWidth;
                            if(rowArray[rowIterator])
                            {
                                rowIterator += 1;
                            }
                            rowArray[rowIterator] = [item];
                        }
                        totalWidthArray[rowIterator] = totalWidth;
                    }
                    HorizontalLegendLayout.rowArray = rowArray;
                    HorizontalLegendLayout.totalWidthArray = totalWidthArray;
                },
            
                /**
                 * Returns the starting x-coordinate for a row of legend items.
                 *
                 * @method getStartPoint
                 * @param {Number} w Width of the legend.
                 * @param {Number} totalWidth Total width of all labels in the row.
                 * @param {String} align Horizontal alignment of items for the legend.
                 * @param {Object} padding Object contain left, top, right and bottom padding properties.
                 * @return Number
                 * @protected
                 */
                getStartPoint: function(w, totalWidth, align, padding)
                {
                    var startPoint;
                    switch(align)
                    {
                        case LEFT :
                            startPoint = padding.left;
                        break;
                        case "center" :
                            startPoint = (w - totalWidth) * 0.5;
                        break;
                        case RIGHT :
                            startPoint = w - totalWidth - padding.right;
                        break;
                    }
                    return startPoint;
                }
            },
            
            /**
             * Contains methods for displaying items vertically in a legend.
             *
             * @module charts
             * @submodule charts-legend
             * @class VerticalLegendLayout
             */
            VerticalLegendLayout = {
                /**
                 * Displays items vertically in a legend.
                 *
                 * @method _positionLegendItems
                 * @param {Array} items Array of items to display in the legend.
                 * @param {Number} maxWidth The width of the largest item in the legend.
                 * @param {Number} maxHeight The height of the largest item in the legend.
                 * @param {Number} totalWidth The total width of all items in a legend.
                 * @param {Number} totalHeight The total height of all items in a legend.
                 * @param {Number} padding The left, top, right and bottom padding properties for the legend.
                 * @param {Number} horizontalGap The horizontal distance between items in a legend.
                 * @param {Number} verticalGap The vertical distance between items in a legend.
                 * @param {String} vAlign The vertical alignment of the legend.
                 * @protected
                 */
                _positionLegendItems: function(items, maxWidth, maxHeight, totalWidth, totalHeight, padding, horizontalGap, verticalGap, vAlign)
                {
                    var i = 0,
                        columnIterator = 0,
                        item,
                        node,
                        itemHeight,
                        itemWidth,
                        len,
                        height = this.get("height"),
                        columns,
                        columnsLen,
                        column,
                        totalHeightArray,
                        legendHeight,
                        leftWidth = padding.left - horizontalGap,
                        legendWidth,
                        limit = height - (padding.top + padding.bottom),
                        left,
                        top,
                        right,
                        bottom;
                    VerticalLegendLayout._setColumnArrays(items, limit, verticalGap);
                    columns = VerticalLegendLayout.columnArray;
                    totalHeightArray = VerticalLegendLayout.totalHeightArray;
                    columnsLen = columns.length;
                    for(; columnIterator < columnsLen; ++ columnIterator)
                    {
                        leftWidth += horizontalGap;
                        column = columns[columnIterator];
                        len = column.length;
                        legendHeight =  VerticalLegendLayout.getStartPoint(height, totalHeightArray[columnIterator], vAlign, padding);
                        legendWidth = 0;
                        for(i = 0; i < len; ++i)
                        {
                            item = column[i];
                            node = item.node;
                            itemHeight = item.height;
                            itemWidth = item.width;
                            item.y = legendHeight;
                            item.x = leftWidth;
                            left = !isNaN(left) ? Math.min(left, leftWidth) : leftWidth;
                            top = !isNaN(top) ? Math.min(top, legendHeight) : legendHeight;
                            right = !isNaN(right) ? Math.max(leftWidth + itemWidth, right) : leftWidth + itemWidth;
                            bottom = !isNaN(bottom) ? Math.max(legendHeight + itemHeight, bottom) : legendHeight + itemHeight;
                            node.setStyle("left", leftWidth + PX);
                            node.setStyle("top", legendHeight + PX);
                            legendHeight += itemHeight + verticalGap;
                            legendWidth = Math.max(legendWidth, item.width);
                        }
                        leftWidth += legendWidth;
                    }
                    this._contentRect = {
                        left: left,
                        top: top,
                        right: right,
                        bottom: bottom
                    };
                    if(this.get("includeInChartLayout"))
                    {
                        this.set("width", leftWidth + padding.right);
                    }
                },
            
                /**
                 * Creates column and total height arrays used for displaying multiple columns of
                 * legend items based on the items, available height and verticalGap for the legend.
                 *
                 * @method _setColumnArrays
                 * @param {Array} items Array of legend items to display in a legend.
                 * @param {Number} limit Total available height for displaying items in a legend.
                 * @param {Number} verticalGap Vertical distance between items in a legend.
                 * @protected
                 */
                _setColumnArrays: function(items, limit, verticalGap)
                {
                    var item = items[0],
                        columnArray = [[item]],
                        i = 1,
                        columnIterator = 0,
                        len = items.length,
                        totalHeight = item.height,
                        itemHeight,
                        totalHeightArray = [[totalHeight]];
                    for(; i < len; ++i)
                    {
                        item = items[i];
                        itemHeight = item.height;
                        if((totalHeight + verticalGap + itemHeight) <= limit)
                        {
                            totalHeight += verticalGap + itemHeight;
                            columnArray[columnIterator].push(item);
                        }
                        else
                        {
                            totalHeight = verticalGap + itemHeight;
                            if(columnArray[columnIterator])
                            {
                                columnIterator += 1;
                            }
                            columnArray[columnIterator] = [item];
                        }
                        totalHeightArray[columnIterator] = totalHeight;
                    }
                    VerticalLegendLayout.columnArray = columnArray;
                    VerticalLegendLayout.totalHeightArray = totalHeightArray;
                },
            
                /**
                 * Returns the starting y-coordinate for a column of legend items.
                 *
                 * @method getStartPoint
                 * @param {Number} h Height of the legend.
                 * @param {Number} totalHeight Total height of all labels in the column.
                 * @param {String} align Vertical alignment of items for the legend.
                 * @param {Object} padding Object contain left, top, right and bottom padding properties.
                 * @return Number
                 * @protected
                 */
                getStartPoint: function(h, totalHeight, align, padding)
                {
                    var startPoint;
                    switch(align)
                    {
                        case TOP :
                            startPoint = padding.top;
                        break;
                        case "middle" :
                            startPoint = (h - totalHeight) * 0.5;
                        break;
                        case BOTTOM :
                            startPoint = h - totalHeight - padding.bottom;
                        break;
                    }
                    return startPoint;
                }
            },
            
            CartesianChartLegend = Y.Base.create("cartesianChartLegend", Y.CartesianChart, [], {
                /**
                 * Redraws and position all the components of the chart instance.
                 *
                 * @method _redraw
                 * @private
                 */
                _redraw: function()
                {
                    if(this._drawing)
                    {
                        this._callLater = true;
                        return;
                    }
                    this._drawing = true;
                    this._callLater = false;
                    var w = this.get("width"),
                        h = this.get("height"),
                        layoutBoxDimensions = this._getLayoutBoxDimensions(),
                        leftPaneWidth = layoutBoxDimensions.left,
                        rightPaneWidth = layoutBoxDimensions.right,
                        topPaneHeight = layoutBoxDimensions.top,
                        bottomPaneHeight = layoutBoxDimensions.bottom,
                        leftAxesCollection = this.get("leftAxesCollection"),
                        rightAxesCollection = this.get("rightAxesCollection"),
                        topAxesCollection = this.get("topAxesCollection"),
                        bottomAxesCollection = this.get("bottomAxesCollection"),
                        i = 0,
                        l,
                        axis,
                        graphOverflow = "visible",
                        graph = this.get("graph"),
                        topOverflow,
                        bottomOverflow,
                        leftOverflow,
                        rightOverflow,
                        graphWidth,
                        graphHeight,
                        graphX,
                        graphY,
                        allowContentOverflow = this.get("allowContentOverflow"),
                        diff,
                        rightAxesXCoords,
                        leftAxesXCoords,
                        topAxesYCoords,
                        bottomAxesYCoords,
                        legend = this.get("legend"),
                        graphRect = {};
            
                    if(leftAxesCollection)
                    {
                        leftAxesXCoords = [];
                        l = leftAxesCollection.length;
                        for(i = l - 1; i > -1; --i)
                        {
                            leftAxesXCoords.unshift(leftPaneWidth);
                            leftPaneWidth += leftAxesCollection[i].get("width");
                        }
                    }
                    if(rightAxesCollection)
                    {
                        rightAxesXCoords = [];
                        l = rightAxesCollection.length;
                        i = 0;
                        for(i = l - 1; i > -1; --i)
                        {
                            rightPaneWidth += rightAxesCollection[i].get("width");
                            rightAxesXCoords.unshift(w - rightPaneWidth);
                        }
                    }
                    if(topAxesCollection)
                    {
                        topAxesYCoords = [];
                        l = topAxesCollection.length;
                        for(i = l - 1; i > -1; --i)
                        {
                            topAxesYCoords.unshift(topPaneHeight);
                            topPaneHeight += topAxesCollection[i].get("height");
                        }
                    }
                    if(bottomAxesCollection)
                    {
                        bottomAxesYCoords = [];
                        l = bottomAxesCollection.length;
                        for(i = l - 1; i > -1; --i)
                        {
                            bottomPaneHeight += bottomAxesCollection[i].get("height");
                            bottomAxesYCoords.unshift(h - bottomPaneHeight);
                        }
                    }
            
                    graphWidth = w - (leftPaneWidth + rightPaneWidth);
                    graphHeight = h - (bottomPaneHeight + topPaneHeight);
                    graphRect.left = leftPaneWidth;
                    graphRect.top = topPaneHeight;
                    graphRect.bottom = h - bottomPaneHeight;
                    graphRect.right = w - rightPaneWidth;
                    if(!allowContentOverflow)
                    {
                        topOverflow = this._getTopOverflow(leftAxesCollection, rightAxesCollection);
                        bottomOverflow = this._getBottomOverflow(leftAxesCollection, rightAxesCollection);
                        leftOverflow = this._getLeftOverflow(bottomAxesCollection, topAxesCollection);
                        rightOverflow = this._getRightOverflow(bottomAxesCollection, topAxesCollection);
            
                        diff = topOverflow - topPaneHeight;
                        if(diff > 0)
                        {
                            graphRect.top = topOverflow;
                            if(topAxesYCoords)
                            {
                                i = 0;
                                l = topAxesYCoords.length;
                                for(; i < l; ++i)
                                {
                                    topAxesYCoords[i] += diff;
                                }
                            }
                        }
            
                        diff = bottomOverflow - bottomPaneHeight;
                        if(diff > 0)
                        {
                            graphRect.bottom = h - bottomOverflow;
                            if(bottomAxesYCoords)
                            {
                                i = 0;
                                l = bottomAxesYCoords.length;
                                for(; i < l; ++i)
                                {
                                    bottomAxesYCoords[i] -= diff;
                                }
                            }
                        }
            
                        diff = leftOverflow - leftPaneWidth;
                        if(diff > 0)
                        {
                            graphRect.left = leftOverflow;
                            if(leftAxesXCoords)
                            {
                                i = 0;
                                l = leftAxesXCoords.length;
                                for(; i < l; ++i)
                                {
                                    leftAxesXCoords[i] += diff;
                                }
                            }
                        }
            
                        diff = rightOverflow - rightPaneWidth;
                        if(diff > 0)
                        {
                            graphRect.right = w - rightOverflow;
                            if(rightAxesXCoords)
                            {
                                i = 0;
                                l = rightAxesXCoords.length;
                                for(; i < l; ++i)
                                {
                                    rightAxesXCoords[i] -= diff;
                                }
                            }
                        }
                    }
                    graphWidth = graphRect.right - graphRect.left;
                    graphHeight = graphRect.bottom - graphRect.top;
                    graphX = graphRect.left;
                    graphY = graphRect.top;
                    if(legend)
                    {
                        if(legend.get("includeInChartLayout"))
                        {
                            switch(legend.get("position"))
                            {
                                case "left" :
                                    legend.set("y", graphY);
                                    legend.set("height", graphHeight);
                                break;
                                case "top" :
                                    legend.set("x", graphX);
                                    legend.set("width", graphWidth);
                                break;
                                case "bottom" :
                                    legend.set("x", graphX);
                                    legend.set("width", graphWidth);
                                break;
                                case "right" :
                                    legend.set("y", graphY);
                                    legend.set("height", graphHeight);
                                break;
                            }
                        }
                    }
                    if(topAxesCollection)
                    {
                        l = topAxesCollection.length;
                        i = 0;
                        for(; i < l; i++)
                        {
                            axis = topAxesCollection[i];
                            if(axis.get("width") !== graphWidth)
                            {
                                axis.set("width", graphWidth);
                            }
                            axis.get("boundingBox").setStyle("left", graphX + PX);
                            axis.get("boundingBox").setStyle("top", topAxesYCoords[i] + PX);
                        }
                        if(axis._hasDataOverflow())
                        {
                            graphOverflow = "hidden";
                        }
                    }
                    if(bottomAxesCollection)
                    {
                        l = bottomAxesCollection.length;
                        i = 0;
                        for(; i < l; i++)
                        {
                            axis = bottomAxesCollection[i];
                            if(axis.get("width") !== graphWidth)
                            {
                                axis.set("width", graphWidth);
                            }
                            axis.get("boundingBox").setStyle("left", graphX + PX);
                            axis.get("boundingBox").setStyle("top", bottomAxesYCoords[i] + PX);
                        }
                        if(axis._hasDataOverflow())
                        {
                            graphOverflow = "hidden";
                        }
                    }
                    if(leftAxesCollection)
                    {
                        l = leftAxesCollection.length;
                        i = 0;
                        for(; i < l; ++i)
                        {
                            axis = leftAxesCollection[i];
                            axis.get("boundingBox").setStyle("top", graphY + PX);
                            axis.get("boundingBox").setStyle("left", leftAxesXCoords[i] + PX);
                            if(axis.get("height") !== graphHeight)
                            {
                                axis.set("height", graphHeight);
                            }
                        }
                        if(axis._hasDataOverflow())
                        {
                            graphOverflow = "hidden";
                        }
                    }
                    if(rightAxesCollection)
                    {
                        l = rightAxesCollection.length;
                        i = 0;
                        for(; i < l; ++i)
                        {
                            axis = rightAxesCollection[i];
                            axis.get("boundingBox").setStyle("top", graphY + PX);
                            axis.get("boundingBox").setStyle("left", rightAxesXCoords[i] + PX);
                            if(axis.get("height") !== graphHeight)
                            {
                                axis.set("height", graphHeight);
                            }
                        }
                        if(axis._hasDataOverflow())
                        {
                            graphOverflow = "hidden";
                        }
                    }
                    this._drawing = false;
                    if(this._callLater)
                    {
                        this._redraw();
                        return;
                    }
                    if(graph)
                    {
                        graph.get("boundingBox").setStyle("left", graphX + PX);
                        graph.get("boundingBox").setStyle("top", graphY + PX);
                        graph.set("width", graphWidth);
                        graph.set("height", graphHeight);
                        graph.get("boundingBox").setStyle("overflow", graphOverflow);
                    }
            
                    if(this._overlay)
                    {
                        this._overlay.setStyle("left", graphX + PX);
                        this._overlay.setStyle("top", graphY + PX);
                        this._overlay.setStyle("width", graphWidth + PX);
                        this._overlay.setStyle("height", graphHeight + PX);
                    }
                },
            
                /**
                 * Positions the legend in a chart and returns the properties of the legend to be used in the
                 * chart's layout algorithm.
                 *
                 * @method _getLayoutDimensions
                 * @return {Object} The left, top, right and bottom values for the legend.
                 * @protected
                 */
                _getLayoutBoxDimensions: function()
                {
                    var box = {
                            top: 0,
                            right: 0,
                            bottom: 0,
                            left: 0
                        },
                        legend = this.get("legend"),
                        position,
                        direction,
                        dimension,
                        size,
                        w = this.get(WIDTH),
                        h = this.get(HEIGHT),
                        gap;
                    if(legend && legend.get("includeInChartLayout"))
                    {
                        gap = legend.get("styles").gap;
                        position = legend.get(POSITION);
                        if(position !== EXTERNAL)
                        {
                            direction = legend.get("direction");
                            dimension = direction === HORIZONTAL ? HEIGHT : WIDTH;
                            size = legend.get(dimension);
                            box[position] = size + gap;
                            switch(position)
                            {
                                case TOP :
                                    legend.set(_Y, 0);
                                break;
                                case BOTTOM :
                                    legend.set(_Y, h - size);
                                break;
                                case RIGHT :
                                    legend.set(_X, w - size);
                                break;
                                case LEFT:
                                    legend.set(_X, 0);
                                break;
                            }
                        }
                    }
                    return box;
                },
            
                /**
                 * Destructor implementation for the CartesianChart class. Calls destroy on all axes, series, legend (if available) and the Graph instance.
                 * Removes the tooltip and overlay HTML elements.
                 *
                 * @method destructor
                 * @protected
                 */
                destructor: function()
                {
                    var legend = this.get("legend");
                    if(legend)
                    {
                        legend.destroy(true);
                    }
                }
            }, {
                ATTRS: {
                    legend: LEGEND
                }
            });
            
            Y.CartesianChart = CartesianChartLegend;