Current Path : C:/xampp/htdocs/moodle/lib/yuilib/3.17.2/charts-legend/ |
Current File : C:/xampp/htdocs/moodle/lib/yuilib/3.17.2/charts-legend/charts-legend.js |
/* YUI 3.17.2 (build 9c3c78e) Copyright 2014 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 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: * <dl> * <dt>node</dt><dd>The `Node` containing the legend item elements.</dd> * <dt>shape</dt><dd>The `Shape` element for the legend item.</dd> * <dt>textNode</dt><dd>The `Node` containing the text></dd> * <dt>text</dt><dd></dd> * </dl> * * @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} labelStyles 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 {String} text Text for the legendItem * @return Object * @private */ _getLegendItem: function(node, shapeClass, fill, border, labelStyles, w, h, text) { var containerNode = Y.Node.create("<div>"), textField = Y.Node.create("<span>"), shape, dimension, padding, left, item, ShapeClass = shapeClass; containerNode.setStyle(POSITION, "absolute"); textField.setStyle(POSITION, "absolute"); textField.setStyles(labelStyles); textField.set("text", text); containerNode.appendChild(textField); node.append(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: * * <dl> * <dt>node</dt><dd>Node containing text for the legend item.</dd> * <dt>marker</dt><dd>Shape for the legend item.</dd> * </dl> * * @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: * * <dl> * <dt>gap</dt><dd>Distance, in pixels, between the `ChartLegend` instance and the chart's content. When `ChartLegend` * is rendered within a `Chart` instance this value is applied.</dd> * <dt>hAlign</dt><dd>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.</dd> * <dt>vAlign</dt><dd>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.</dd> * <dt>item</dt><dd>Set of style properties applied to the `items` of the `ChartLegend`. * <dl> * <dt>hSpacing</dt><dd>Horizontal distance, in pixels, between legend `items`.</dd> * <dt>vSpacing</dt><dd>Vertical distance, in pixels, between legend `items`.</dd> * <dt>label</dt><dd>Properties for the text of an `item`. * <dl> * <dt>color</dt><dd>Color of the text. The default values is "#808080".</dd> * <dt>fontSize</dt><dd>Font size for the text. The default value is "85%".</dd> * </dl> * </dd> * <dt>marker</dt><dd>Properties for the `item` markers. * <dl> * <dt>width</dt><dd>Specifies the width of the markers.</dd> * <dt>height</dt><dd>Specifies the height of the markers.</dd> * </dl> * </dd> * </dl> * </dd> * <dt>background</dt><dd>Properties for the `ChartLegend` background. * <dl> * <dt>fill</dt><dd>Properties for the background fill. * <dl> * <dt>color</dt><dd>Color for the fill. The default value is "#faf9f2".</dd> * </dl> * </dd> * <dt>border</dt><dd>Properties for the background border. * <dl> * <dt>color</dt><dd>Color for the border. The default value is "#dad8c9".</dd> * <dt>weight</dt><dd>Weight of the border. The default values is 1.</dd> * </dl> * </dd> * </dl> * </dd> * </dl> * * @attribute styles * @type Object */ } }); }, '3.17.2', {"requires": ["charts-base"]});