diff -r 322d0feea350 -r 89ef5ed3c48b src/cm/media/js/lib/yui/yui_3.10.3/build/charts-legend/charts-legend.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cm/media/js/lib/yui/yui_3.10.3/build/charts-legend/charts-legend.js Tue Jul 16 14:29:46 2013 +0200 @@ -0,0 +1,1711 @@ +/* +YUI 3.10.3 (build 2fb5187) +Copyright 2013 Yahoo! Inc. All rights reserved. +Licensed under the BSD License. +http://yuilibrary.com/license/ +*/ + +YUI.add('charts-legend', function (Y, NAME) { + +/** + * Adds legend functionality to charts. + * + * @module charts + * @submodule charts-legend + */ +var DOCUMENT = Y.config.doc, +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; + +PieChartLegend = Y.Base.create("pieChartLegend", Y.PieChart, [], { + /** + * Redraws the chart instance. + * + * @method _redraw + * @private + */ + _redraw: function() + { + if(this._drawing) + { + this._callLater = true; + return; + } + this._drawing = true; + this._callLater = false; + var graph = this.get("graph"), + w = this.get("width"), + h = this.get("height"), + graphWidth, + graphHeight, + legend = this.get("legend"), + x = 0, + y = 0, + legendX = 0, + legendY = 0, + legendWidth, + legendHeight, + dimension, + gap, + position, + direction; + if(graph) + { + if(legend) + { + position = legend.get("position"); + direction = legend.get("direction"); + graphWidth = graph.get("width"); + graphHeight = graph.get("height"); + legendWidth = legend.get("width"); + legendHeight = legend.get("height"); + gap = legend.get("styles").gap; + + if((direction === "vertical" && (graphWidth + legendWidth + gap !== w)) || + (direction === "horizontal" && (graphHeight + legendHeight + gap !== h))) + { + switch(legend.get("position")) + { + case LEFT : + dimension = Math.min(w - (legendWidth + gap), h); + legendHeight = h; + x = legendWidth + gap; + legend.set(HEIGHT, legendHeight); + break; + case TOP : + dimension = Math.min(h - (legendHeight + gap), w); + legendWidth = w; + y = legendHeight + gap; + legend.set(WIDTH, legendWidth); + break; + case RIGHT : + dimension = Math.min(w - (legendWidth + gap), h); + legendHeight = h; + legendX = dimension + gap; + legend.set(HEIGHT, legendHeight); + break; + case BOTTOM : + dimension = Math.min(h - (legendHeight + gap), w); + legendWidth = w; + legendY = dimension + gap; + legend.set(WIDTH, legendWidth); + break; + } + graph.set(WIDTH, dimension); + graph.set(HEIGHT, dimension); + } + else + { + switch(legend.get("position")) + { + case LEFT : + x = legendWidth + gap; + break; + case TOP : + y = legendHeight + gap; + break; + case RIGHT : + legendX = graphWidth + gap; + break; + case BOTTOM : + legendY = graphHeight + gap; + break; + } + } + } + else + { + graph.set(_X, 0); + graph.set(_Y, 0); + graph.set(WIDTH, w); + graph.set(HEIGHT, h); + } + } + this._drawing = false; + if(this._callLater) + { + this._redraw(); + return; + } + if(graph) + { + graph.set(_X, x); + graph.set(_Y, y); + } + if(legend) + { + legend.set(_X, legendX); + legend.set(_Y, legendY); + } + } +}, { + ATTRS: { + /** + * The legend for the chart. + * + * @attribute + * @type Legend + */ + legend: LEGEND + } +}); +Y.PieChart = PieChartLegend; +/** + * ChartLegend provides a legend for a chart. + * + * @class ChartLegend + * @module charts + * @submodule charts-legend + * @extends Widget + */ +Y.ChartLegend = Y.Base.create("chartlegend", Y.Widget, [Y.Renderer], { + /** + * Initializes the chart. + * + * @method initializer + * @private + */ + initializer: function() + { + this._items = []; + }, + + /** + * @method renderUI + * @private + */ + renderUI: function() + { + var bb = this.get("boundingBox"), + cb = this.get("contentBox"), + styles = this.get("styles").background, + background = new Y.Rect({ + graphic: cb, + fill: styles.fill, + stroke: styles.border + }); + bb.setStyle("display", "block"); + bb.setStyle("position", "absolute"); + this.set("background", background); + }, + + /** + * @method bindUI + * @private + */ + bindUI: function() + { + this.get("chart").after("seriesCollectionChange", Y.bind(this._updateHandler, this)); + this.get("chart").after("stylesChange", Y.bind(this._updateHandler, this)); + this.after("stylesChange", this._updateHandler); + this.after("positionChange", this._positionChangeHandler); + this.after("widthChange", this._handleSizeChange); + this.after("heightChange", this._handleSizeChange); + }, + + /** + * @method syncUI + * @private + */ + syncUI: function() + { + var w = this.get("width"), + h = this.get("height"); + if(isFinite(w) && isFinite(h) && w > 0 && h > 0) + { + this._drawLegend(); + } + }, + + /** + * Handles changes to legend. + * + * @method _updateHandler + * @param {Object} e Event object + * @private + */ + _updateHandler: function() + { + if(this.get("rendered")) + { + this._drawLegend(); + } + }, + + /** + * Handles position changes. + * + * @method _positionChangeHandler + * @param {Object} e Event object + * @private + */ + _positionChangeHandler: function() + { + var chart = this.get("chart"), + parentNode = this._parentNode; + if(parentNode && ((chart && this.get("includeInChartLayout")))) + { + this.fire("legendRendered"); + } + else if(this.get("rendered")) + { + this._drawLegend(); + } + }, + + /** + * Updates the legend when the size changes. + * + * @method _handleSizeChange + * @param {Object} e Event object. + * @private + */ + _handleSizeChange: function(e) + { + var attrName = e.attrName, + pos = this.get(POSITION), + vert = pos === LEFT || pos === RIGHT, + hor = pos === BOTTOM || pos === TOP; + if((hor && attrName === WIDTH) || (vert && attrName === HEIGHT)) + { + this._drawLegend(); + } + }, + + /** + * Draws the legend + * + * @method _drawLegend + * @private + */ + _drawLegend: function() + { + if(this._drawing) + { + this._callLater = true; + return; + } + this._drawing = true; + this._callLater = false; + if(this.get("includeInChartLayout")) + { + this.get("chart")._itemRenderQueue.unshift(this); + } + var chart = this.get("chart"), + node = this.get("contentBox"), + seriesCollection = chart.get("seriesCollection"), + series, + styles = this.get("styles"), + padding = styles.padding, + itemStyles = styles.item, + seriesStyles, + hSpacing = itemStyles.hSpacing, + vSpacing = itemStyles.vSpacing, + direction = this.get("direction"), + align = direction === "vertical" ? styles.vAlign : styles.hAlign, + marker = styles.marker, + labelStyles = itemStyles.label, + displayName, + layout = this._layout[direction], + i, + len, + isArray, + legendShape, + shape, + shapeClass, + item, + fill, + border, + fillColors, + borderColors, + borderWeight, + items = [], + markerWidth = marker.width, + markerHeight = marker.height, + totalWidth = 0 - hSpacing, + totalHeight = 0 - vSpacing, + maxWidth = 0, + maxHeight = 0, + itemWidth, + itemHeight; + if(marker && marker.shape) + { + legendShape = marker.shape; + } + this._destroyLegendItems(); + if(chart instanceof Y.PieChart) + { + series = seriesCollection[0]; + displayName = series.get("categoryAxis").getDataByKey(series.get("categoryKey")); + seriesStyles = series.get("styles").marker; + fillColors = seriesStyles.fill.colors; + borderColors = seriesStyles.border.colors; + borderWeight = seriesStyles.border.weight; + i = 0; + len = displayName.length; + shape = legendShape || Y.Circle; + isArray = Y.Lang.isArray(shape); + for(; i < len; ++i) + { + shape = isArray ? shape[i] : shape; + fill = { + color: fillColors[i] + }; + border = { + colors: borderColors[i], + weight: borderWeight + }; + displayName = chart.getSeriesItems(series, i).category.value; + item = this._getLegendItem(node, this._getShapeClass(shape), fill, border, labelStyles, markerWidth, markerHeight, displayName); + itemWidth = item.width; + itemHeight = item.height; + maxWidth = Math.max(maxWidth, itemWidth); + maxHeight = Math.max(maxHeight, itemHeight); + totalWidth += itemWidth + hSpacing; + totalHeight += itemHeight + vSpacing; + items.push(item); + } + } + else + { + i = 0; + len = seriesCollection.length; + for(; i < len; ++i) + { + series = seriesCollection[i]; + seriesStyles = this._getStylesBySeriesType(series, shape); + if(!legendShape) + { + shape = seriesStyles.shape; + if(!shape) + { + shape = Y.Circle; + } + } + shapeClass = Y.Lang.isArray(shape) ? shape[i] : shape; + item = this._getLegendItem( + node, + this._getShapeClass(shape), + seriesStyles.fill, + seriesStyles.border, + labelStyles, + markerWidth, + markerHeight, + series.get("valueDisplayName") + ); + itemWidth = item.width; + itemHeight = item.height; + maxWidth = Math.max(maxWidth, itemWidth); + maxHeight = Math.max(maxHeight, itemHeight); + totalWidth += itemWidth + hSpacing; + totalHeight += itemHeight + vSpacing; + items.push(item); + } + } + this._drawing = false; + if(this._callLater) + { + this._drawLegend(); + } + else + { + layout._positionLegendItems.apply( + this, + [items, maxWidth, maxHeight, totalWidth, totalHeight, padding, hSpacing, vSpacing, align] + ); + this._updateBackground(styles); + this.fire("legendRendered"); + } + }, + + /** + * Updates the background for the legend. + * + * @method _updateBackground + * @param {Object} styles Reference to the legend's styles attribute + * @private + */ + _updateBackground: function(styles) + { + var backgroundStyles = styles.background, + contentRect = this._contentRect, + padding = styles.padding, + x = contentRect.left - padding.left, + y = contentRect.top - padding.top, + w = contentRect.right - x + padding.right, + h = contentRect.bottom - y + padding.bottom; + this.get("background").set({ + fill: backgroundStyles.fill, + stroke: backgroundStyles.border, + width: w, + height: h, + x: x, + y: y + }); + }, + + /** + * Retrieves the marker styles based on the type of series. For series that contain a marker, the marker styles are returned. + * + * @method _getStylesBySeriesType + * @param {CartesianSeries | PieSeries} The series in which the style properties will be received. + * @return Object An object containing fill, border and shape information. + * @private + */ + _getStylesBySeriesType: function(series) + { + var styles = series.get("styles"), + color; + if(series instanceof Y.LineSeries || series instanceof Y.StackedLineSeries) + { + styles = series.get("styles").line; + color = styles.color || series._getDefaultColor(series.get("graphOrder"), "line"); + return { + border: { + weight: 1, + color: color + }, + fill: { + color: color + } + }; + } + else if(series instanceof Y.AreaSeries || series instanceof Y.StackedAreaSeries) + { + styles = series.get("styles").area; + color = styles.color || series._getDefaultColor(series.get("graphOrder"), "slice"); + return { + border: { + weight: 1, + color: color + }, + fill: { + color: color + } + }; + } + else + { + styles = series.get("styles").marker; + return { + fill: styles.fill, + + border: { + weight: styles.border.weight, + + color: styles.border.color, + + shape: styles.shape + }, + shape: styles.shape + }; + } + }, + + /** + * Returns a legend item consisting of the following properties: + *
+ *
node
The `Node` containing the legend item elements.
+ *
shape
The `Shape` element for the legend item.
+ *
textNode
The `Node` containing the text>
+ *
text
+ *
+ * + * @method _getLegendItem + * @param {Node} shapeProps Reference to the `node` attribute. + * @param {String | Class} shapeClass The type of shape + * @param {Object} fill Properties for the shape's fill + * @param {Object} border Properties for the shape's border + * @param {String} text String to be rendered as the legend's text + * @param {Number} width Total width of the legend item + * @param {Number} height Total height of the legend item + * @param {HTML | String} text Text for the legendItem + * @return Object + * @private + */ + _getLegendItem: function(node, shapeClass, fill, border, labelStyles, w, h, text) + { + var containerNode = Y.one(DOCUMENT.createElement("div")), + textField = Y.one(DOCUMENT.createElement("span")), + shape, + dimension, + padding, + left, + item, + ShapeClass = shapeClass; + containerNode.setStyle(POSITION, "absolute"); + textField.setStyle(POSITION, "absolute"); + textField.setStyles(labelStyles); + textField.appendChild(DOCUMENT.createTextNode(text)); + containerNode.appendChild(textField); + node.appendChild(containerNode); + dimension = textField.get("offsetHeight"); + padding = dimension - h; + left = w + padding + 2; + textField.setStyle("left", left + PX); + containerNode.setStyle("height", dimension + PX); + containerNode.setStyle("width", (left + textField.get("offsetWidth")) + PX); + shape = new ShapeClass({ + fill: fill, + stroke: border, + width: w, + height: h, + x: padding * 0.5, + y: padding * 0.5, + w: w, + h: h, + graphic: containerNode + }); + textField.setStyle("left", dimension + PX); + item = { + node: containerNode, + width: containerNode.get("offsetWidth"), + height: containerNode.get("offsetHeight"), + shape: shape, + textNode: textField, + text: text + }; + this._items.push(item); + return item; + }, + + /** + * Evaluates and returns correct class for drawing a shape. + * + * @method _getShapeClass + * @return Shape + * @private + */ + _getShapeClass: function() + { + var graphic = this.get("background").get("graphic"); + return graphic._getShapeClass.apply(graphic, arguments); + }, + + /** + * Returns the default hash for the `styles` attribute. + * + * @method _getDefaultStyles + * @return Object + * @protected + */ + _getDefaultStyles: function() + { + var styles = { + padding: { + top: 8, + right: 8, + bottom: 8, + left: 9 + }, + gap: 10, + hAlign: "center", + vAlign: "top", + marker: this._getPlotDefaults(), + item: { + hSpacing: 10, + vSpacing: 5, + label: { + color:"#808080", + fontSize:"85%", + whiteSpace: "nowrap" + } + }, + background: { + shape: "rect", + fill:{ + color:"#faf9f2" + }, + border: { + color:"#dad8c9", + weight: 1 + } + } + }; + return styles; + }, + + /** + * Gets the default values for series that use the utility. This method is used by + * the class' `styles` attribute's getter to get build default values. + * + * @method _getPlotDefaults + * @return Object + * @protected + */ + _getPlotDefaults: function() + { + var defs = { + width: 10, + height: 10 + }; + return defs; + }, + + /** + * Destroys legend items. + * + * @method _destroyLegendItems + * @private + */ + _destroyLegendItems: function() + { + var item; + if(this._items) + { + while(this._items.length > 0) + { + item = this._items.shift(); + item.shape.get("graphic").destroy(); + item.node.empty(); + item.node.destroy(true); + item.node = null; + item = null; + } + } + this._items = []; + }, + + /** + * Maps layout classes. + * + * @property _layout + * @private + */ + _layout: { + vertical: VerticalLegendLayout, + horizontal: HorizontalLegendLayout + }, + + /** + * Destructor implementation ChartLegend class. Removes all items and the Graphic instance from the widget. + * + * @method destructor + * @protected + */ + destructor: function() + { + var background = this.get("background"), + backgroundGraphic; + this._destroyLegendItems(); + if(background) + { + backgroundGraphic = background.get("graphic"); + if(backgroundGraphic) + { + backgroundGraphic.destroy(); + } + else + { + background.destroy(); + } + } + + } +}, { + ATTRS: { + /** + * Indicates whether the chart's contentBox is the parentNode for the legend. + * + * @attribute includeInChartLayout + * @type Boolean + * @private + */ + includeInChartLayout: { + value: false + }, + + /** + * Reference to the `Chart` instance. + * + * @attribute chart + * @type Chart + */ + chart: { + setter: function(val) + { + this.after("legendRendered", Y.bind(val._itemRendered, val)); + return val; + } + }, + + /** + * Indicates the direction in relation of the legend's layout. The `direction` of the legend is determined by its + * `position` value. + * + * @attribute direction + * @type String + */ + direction: { + value: "vertical" + }, + + /** + * Indicates the position and direction of the legend. Possible values are `left`, `top`, `right` and `bottom`. + * Values of `left` and `right` values have a `direction` of `vertical`. Values of `top` and `bottom` values have + * a `direction` of `horizontal`. + * + * @attribute position + * @type String + */ + position: { + lazyAdd: false, + + value: "right", + + setter: function(val) + { + if(val === TOP || val === BOTTOM) + { + this.set("direction", HORIZONTAL); + } + else if(val === LEFT || val === RIGHT) + { + this.set("direction", VERTICAL); + } + return val; + } + }, + + /** + * The width of the legend. Depending on the implementation of the ChartLegend, this value is `readOnly`. + * By default, the legend is included in the layout of the `Chart` that it references. Under this circumstance, + * `width` is always `readOnly`. When the legend is rendered in its own dom element, the `readOnly` status is + * determined by the direction of the legend. If the `position` is `left` or `right` or the `direction` is + * `vertical`, width is `readOnly`. If the position is `top` or `bottom` or the `direction` is `horizontal`, + * width can be explicitly set. If width is not explicitly set, the width will be determined by the width of the + * legend's parent element. + * + * @attribute width + * @type Number + */ + width: { + getter: function() + { + var chart = this.get("chart"), + parentNode = this._parentNode; + if(parentNode) + { + if((chart && this.get("includeInChartLayout")) || this._width) + { + if(!this._width) + { + this._width = 0; + } + return this._width; + } + else + { + return parentNode.get("offsetWidth"); + } + } + return ""; + }, + + setter: function(val) + { + this._width = val; + return val; + } + }, + + /** + * The height of the legend. Depending on the implementation of the ChartLegend, this value is `readOnly`. + * By default, the legend is included in the layout of the `Chart` that it references. Under this circumstance, + * `height` is always `readOnly`. When the legend is rendered in its own dom element, the `readOnly` status is + * determined by the direction of the legend. If the `position` is `top` or `bottom` or the `direction` is + * `horizontal`, height is `readOnly`. If the position is `left` or `right` or the `direction` is `vertical`, + * height can be explicitly set. If height is not explicitly set, the height will be determined by the width of the + * legend's parent element. + * + * @attribute height + * @type Number + */ + height: { + valueFn: "_heightGetter", + + getter: function() + { + var chart = this.get("chart"), + parentNode = this._parentNode; + if(parentNode) + { + if((chart && this.get("includeInChartLayout")) || this._height) + { + if(!this._height) + { + this._height = 0; + } + return this._height; + } + else + { + return parentNode.get("offsetHeight"); + } + } + return ""; + }, + + setter: function(val) + { + this._height = val; + return val; + } + }, + + /** + * Indicates the x position of legend. + * + * @attribute x + * @type Number + * @readOnly + */ + x: { + lazyAdd: false, + + value: 0, + + setter: function(val) + { + var node = this.get("boundingBox"); + if(node) + { + node.setStyle(LEFT, val + PX); + } + return val; + } + }, + + /** + * Indicates the y position of legend. + * + * @attribute y + * @type Number + * @readOnly + */ + y: { + lazyAdd: false, + + value: 0, + + setter: function(val) + { + var node = this.get("boundingBox"); + if(node) + { + node.setStyle(TOP, val + PX); + } + return val; + } + }, + + /** + * Array of items contained in the legend. Each item is an object containing the following properties: + * + *
+ *
node
Node containing text for the legend item.
+ *
marker
Shape for the legend item.
+ *
+ * + * @attribute items + * @type Array + * @readOnly + */ + items: { + getter: function() + { + return this._items; + } + }, + + /** + * Background for the legend. + * + * @attribute background + * @type Rect + */ + background: {} + + /** + * Properties used to display and style the ChartLegend. This attribute is inherited from `Renderer`. + * Below are the default values: + * + *
+ *
gap
Distance, in pixels, between the `ChartLegend` instance and the chart's content. When `ChartLegend` + * is rendered within a `Chart` instance this value is applied.
+ *
hAlign
Defines the horizontal alignment of the `items` in a `ChartLegend` rendered in a horizontal direction. + * This value is applied when the instance's `position` is set to top or bottom. This attribute can be set to left, center + * or right. The default value is center.
+ *
vAlign
Defines the vertical alignment of the `items` in a `ChartLegend` rendered in vertical direction. This + * value is applied when the instance's `position` is set to left or right. The attribute can be set to top, middle or + * bottom. The default value is middle.
+ *
item
Set of style properties applied to the `items` of the `ChartLegend`. + *
+ *
hSpacing
Horizontal distance, in pixels, between legend `items`.
+ *
vSpacing
Vertical distance, in pixels, between legend `items`.
+ *
label
Properties for the text of an `item`. + *
+ *
color
Color of the text. The default values is "#808080".
+ *
fontSize
Font size for the text. The default value is "85%".
+ *
+ *
+ *
marker
Properties for the `item` markers. + *
+ *
width
Specifies the width of the markers.
+ *
height
Specifies the height of the markers.
+ *
+ *
+ *
+ *
+ *
background
Properties for the `ChartLegend` background. + *
+ *
fill
Properties for the background fill. + *
+ *
color
Color for the fill. The default value is "#faf9f2".
+ *
+ *
+ *
border
Properties for the background border. + *
+ *
color
Color for the border. The default value is "#dad8c9".
+ *
weight
Weight of the border. The default values is 1.
+ *
+ *
+ *
+ *
+ *
+ * + * @attribute styles + * @type Object + */ + } +}); + + +}, '3.10.3', {"requires": ["charts-base"]});