Version 3.18.1

APIs

  • Begin typing in the search box above to see results.
Show:

File: charts/js/PieSeries.js

 /**
 * Provides functionality for creating a pie series.
 *
 * @module charts
 * @submodule series-pie
 */
 /**
 * PieSeries visualizes data as a circular chart divided into wedges which represent data as a
 * percentage of a whole.
 *
 * @class PieSeries
 * @constructor
 * @extends SeriesBase
 * @uses Plots
 * @param {Object} config (optional) Configuration parameters.
 * @submodule series-pie
 */
 var CONFIG = Y.config,
 DOCUMENT = CONFIG.doc,
 _getClassName = Y.ClassNameManager.getClassName,
 SERIES_MARKER = _getClassName("seriesmarker");
 Y.PieSeries = Y.Base.create("pieSeries", Y.SeriesBase, [Y.Plots], {
 /**
 * Image map used for interactivity when rendered with canvas.
 *
 * @property _map
 * @type HTMLElement
 * @private
 */
 _map: null,
 
 /**
 * Image used for image map when rendered with canvas.
 *
 * @property _image
 * @type HTMLElement
 * @private
 */
 _image: null,
 
 /**
 * Creates or updates the image map when rendered with canvas.
 *
 * @method _setMap
 * @private
 */
 _setMap: function()
 {
 var id = "pieHotSpotMapi_" + Math.round(100000 * Math.random()),
 graph = this.get("graph"),
 graphic,
 cb,
 areaNode;
 if(graph)
 {
 cb = graph.get("contentBox");
 }
 else
 {
 graphic = this.get("graphic");
 cb = graphic.get("node");
 }
 if(this._image)
 {
 cb.removeChild(this._image);
 while(this._areaNodes && this._areaNodes.length > 0)
 {
 areaNode = this._areaNodes.shift();
 this._map.removeChild(areaNode);
 }
 cb.removeChild(this._map);
 }
 this._image = DOCUMENT.createElement("img");
 this._image.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAABCAYAAAD9yd/wAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSB" +
 "JbWFnZVJlYWR5ccllPAAAABJJREFUeNpiZGBgSGPAAgACDAAIkABoFyloZQAAAABJRU5ErkJggg==";
 cb.appendChild(this._image);
 this._image.style.position = "absolute";
 this._image.style.left = "0px";
 this._image.style.top = "0px";
 this._image.setAttribute("usemap", "#" + id);
 this._image.style.zIndex = 3;
 this._image.style.opacity = 0;
 this._image.setAttribute("alt", "imagemap");
 this._map = DOCUMENT.createElement("map");
 cb.appendChild(this._map);
 this._map.setAttribute("name", id);
 this._map.setAttribute("id", id);
 this._areaNodes = [];
 },
 
 /**
 * Storage for `categoryDisplayName` attribute.
 *
 * @property _categoryDisplayName
 * @private
 */
 _categoryDisplayName: null,
 
 /**
 * Storage for `valueDisplayName` attribute.
 *
 * @property _valueDisplayName
 * @private
 */
 _valueDisplayName: null,
 
 /**
 * Adds event listeners.
 *
 * @method addListeners
 * @private
 */
 addListeners: function()
 {
 var categoryAxis = this.get("categoryAxis"),
 valueAxis = this.get("valueAxis");
 if(categoryAxis)
 {
 categoryAxis.after("dataReady", Y.bind(this._categoryDataChangeHandler, this));
 categoryAxis.after("dataUpdate", Y.bind(this._categoryDataChangeHandler, this));
 }
 if(valueAxis)
 {
 valueAxis.after("dataReady", Y.bind(this._valueDataChangeHandler, this));
 valueAxis.after("dataUpdate", Y.bind(this._valueDataChangeHandler, this));
 }
 this.after("categoryAxisChange", this.categoryAxisChangeHandler);
 this.after("valueAxisChange", this.valueAxisChangeHandler);
 this._stylesChangeHandle = this.after("stylesChange", this._updateHandler);
 this._visibleChangeHandle = this.after("visibleChange", this._handleVisibleChange);
 },
 
 /**
 * Draws the series.
 *
 * @method validate
 * @private
 */
 validate: function()
 {
 this.draw();
 this._renderered = true;
 },
 
 /**
 * Event handler for the categoryAxisChange event.
 *
 * @method _categoryAxisChangeHandler
 * @param {Object} e Event object.
 * @private
 */
 _categoryAxisChangeHandler: function()
 {
 var categoryAxis = this.get("categoryAxis");
 categoryAxis.after("dataReady", Y.bind(this._categoryDataChangeHandler, this));
 categoryAxis.after("dataUpdate", Y.bind(this._categoryDataChangeHandler, this));
 },
 
 /**
 * Event handler for the valueAxisChange event.
 *
 * @method _valueAxisChangeHandler
 * @param {Object} e Event object.
 * @private
 */
 _valueAxisChangeHandler: function()
 {
 var valueAxis = this.get("valueAxis");
 valueAxis.after("dataReady", Y.bind(this._valueDataChangeHandler, this));
 valueAxis.after("dataUpdate", Y.bind(this._valueDataChangeHandler, this));
 },
 
 /**
 * Constant used to generate unique id.
 *
 * @property GUID
 * @type String
 * @private
 */
 GUID: "pieseries",
 
 /**
 * Event handler for categoryDataChange event.
 *
 * @method _categoryDataChangeHandler
 * @param {Object} event Event object.
 * @private
 */
 _categoryDataChangeHandler: function()
 {
 if(this._rendered && this.get("categoryKey") && this.get("valueKey"))
 {
 this.draw();
 }
 },
 
 /**
 * Event handler for valueDataChange event.
 *
 * @method _valueDataChangeHandler
 * @param {Object} event Event object.
 * @private
 */
 _valueDataChangeHandler: function()
 {
 if(this._rendered && this.get("categoryKey") && this.get("valueKey"))
 {
 this.draw();
 }
 },
 
 /**
 * Returns the sum of all values for the series.
 *
 * @method getTotalValues
 * @return Number
 */
 getTotalValues: function()
 {
 var total = this.get("valueAxis").getTotalByKey(this.get("valueKey"));
 return total;
 },
 
 /**
 * Draws the series. Overrides the base implementation.
 *
 * @method draw
 * @protected
 */
 draw: function()
 {
 var w = this.get("width"),
 h = this.get("height");
 if(isFinite(w) && isFinite(h) && w > 0 && h > 0)
 {
 this._rendered = true;
 if(this._drawing)
 {
 this._callLater = true;
 return;
 }
 this._drawing = true;
 this._callLater = false;
 this.drawSeries();
 this._drawing = false;
 if(this._callLater)
 {
 this.draw();
 }
 else
 {
 this.fire("drawingComplete");
 }
 }
 },
 
 /**
 * Draws the markers
 *
 * @method drawPlots
 * @protected
 */
 drawPlots: function()
 {
 var values = this.get("valueAxis").getDataByKey(this.get("valueKey")).concat(),
 totalValue = 0,
 itemCount = values.length,
 styles = this.get("styles").marker,
 fillColors = styles.fill.colors,
 fillAlphas = styles.fill.alphas || ["1"],
 borderColors = styles.border.colors,
 borderWeights = [styles.border.weight],
 borderAlphas = [styles.border.alpha],
 tbw = borderWeights.concat(),
 tbc = borderColors.concat(),
 tba = borderAlphas.concat(),
 tfc,
 tfa,
 padding = styles.padding,
 graphic = this.get("graphic"),
 minDimension = Math.min(graphic.get("width"), graphic.get("height")),
 w = minDimension - (padding.left + padding.right),
 h = minDimension - (padding.top + padding.bottom),
 startAngle = -90,
 halfWidth = w / 2,
 halfHeight = h / 2,
 radius = Math.min(halfWidth, halfHeight),
 i = 0,
 value,
 angle = 0,
 lc,
 la,
 lw,
 wedgeStyle,
 marker,
 graphOrder = this.get("graphOrder") || 0,
 isCanvas = Y.Graphic.NAME === "canvasGraphic";
 for(; i < itemCount; ++i)
 {
 value = parseFloat(values[i]);
 
 values.push(value);
 if(!isNaN(value))
 {
 totalValue += value;
 }
 }
 
 tfc = fillColors ? fillColors.concat() : null;
 tfa = fillAlphas ? fillAlphas.concat() : null;
 this._createMarkerCache();
 if(isCanvas)
 {
 this._setMap();
 this._image.width = w;
 this._image.height = h;
 }
 for(i = 0; i < itemCount; i++)
 {
 value = values[i];
 if(totalValue === 0)
 {
 angle = 360 / values.length;
 }
 else
 {
 angle = 360 * (value / totalValue);
 }
 if(tfc && tfc.length < 1)
 {
 tfc = fillColors.concat();
 }
 if(tfa && tfa.length < 1)
 {
 tfa = fillAlphas.concat();
 }
 if(tbw && tbw.length < 1)
 {
 tbw = borderWeights.concat();
 }
 if(tbw && tbc.length < 1)
 {
 tbc = borderColors.concat();
 }
 if(tba && tba.length < 1)
 {
 tba = borderAlphas.concat();
 }
 lw = tbw ? tbw.shift() : null;
 lc = tbc ? tbc.shift() : null;
 la = tba ? tba.shift() : null;
 startAngle += angle;
 wedgeStyle = {
 border: {
 color:lc,
 weight:lw,
 alpha:la
 },
 fill: {
 color:tfc ? tfc.shift() : this._getDefaultColor(i, "slice"),
 alpha:tfa ? tfa.shift() : null
 },
 type: "pieslice",
 arc: angle,
 radius: radius,
 startAngle: startAngle,
 cx: halfWidth,
 cy: halfHeight,
 width: w,
 height: h
 };
 marker = this.getMarker(wedgeStyle, graphOrder, i);
 if(isCanvas)
 {
 this._addHotspot(wedgeStyle, graphOrder, i);
 }
 }
 this._clearMarkerCache();
 },
 
 /**
 * @protected
 *
 * Method used by `styles` setter. Overrides base implementation.
 *
 * @method _setStyles
 * @param {Object} newStyles Hash of properties to update.
 * @return Object
 */
 _setStyles: function(val)
 {
 if(!val.marker)
 {
 val = {marker:val};
 }
 val = this._parseMarkerStyles(val);
 return Y.PieSeries.superclass._mergeStyles.apply(this, [val, this._getDefaultStyles()]);
 },
 
 /**
 * Adds an interactive map when rendering in canvas.
 *
 * @method _addHotspot
 * @param {Object} cfg Object containing data used to draw the hotspot
 * @param {Number} seriesIndex Index of series in the `seriesCollection`.
 * @param {Number} index Index of the marker using the hotspot.
 * @private
 */
 _addHotspot: function(cfg, seriesIndex, index)
 {
 var areaNode = DOCUMENT.createElement("area"),
 i = 1,
 x = cfg.cx,
 y = cfg.cy,
 arc = cfg.arc,
 startAngle = cfg.startAngle - arc,
 endAngle = cfg.startAngle,
 radius = cfg.radius,
 ax = x + Math.cos(startAngle / 180 * Math.PI) * radius,
 ay = y + Math.sin(startAngle / 180 * Math.PI) * radius,
 bx = x + Math.cos(endAngle / 180 * Math.PI) * radius,
 by = y + Math.sin(endAngle / 180 * Math.PI) * radius,
 numPoints = Math.floor(arc/10) - 1,
 divAngle = (arc/(Math.floor(arc/10)) / 180) * Math.PI,
 angleCoord = Math.atan((ay - y)/(ax - x)),
 pts = x + ", " + y + ", " + ax + ", " + ay,
 cosAng,
 sinAng,
 multDivAng;
 for(i = 1; i <= numPoints; ++i)
 {
 multDivAng = divAngle * i;
 cosAng = Math.cos(angleCoord + multDivAng);
 sinAng = Math.sin(angleCoord + multDivAng);
 if(startAngle <= 90)
 {
 pts += ", " + (x + (radius * Math.cos(angleCoord + (divAngle * i))));
 pts += ", " + (y + (radius * Math.sin(angleCoord + (divAngle * i))));
 }
 else
 {
 pts += ", " + (x - (radius * Math.cos(angleCoord + (divAngle * i))));
 pts += ", " + (y - (radius * Math.sin(angleCoord + (divAngle * i))));
 }
 }
 pts += ", " + bx + ", " + by;
 pts += ", " + x + ", " + y;
 this._map.appendChild(areaNode);
 areaNode.setAttribute("class", SERIES_MARKER);
 areaNode.setAttribute("id", "hotSpot_" + seriesIndex + "_" + index);
 areaNode.setAttribute("shape", "polygon");
 areaNode.setAttribute("coords", pts);
 this._areaNodes.push(areaNode);
 
 },
 
 /**
 * Resizes and positions markers based on a mouse interaction.
 *
 * @method updateMarkerState
 * @param {String} type state of the marker
 * @param {Number} i index of the marker
 * @protected
 */
 updateMarkerState: function(type, i)
 {
 if(this._markers[i])
 {
 var state = this._getState(type),
 markerStyles,
 indexStyles,
 marker = this._markers[i],
 styles = this.get("styles").marker;
 markerStyles = state === "off" || !styles[state] ? styles : styles[state];
 indexStyles = this._mergeStyles(markerStyles, {});
 indexStyles.fill.color = indexStyles.fill.colors[i % indexStyles.fill.colors.length];
 indexStyles.fill.alpha = indexStyles.fill.alphas[i % indexStyles.fill.alphas.length];
 marker.set(indexStyles);
 }
 },
 
 /**
 * Creates a shape to be used as a marker.
 *
 * @method _createMarker
 * @param {Object} styles Hash of style properties.
 * @return Shape
 * @private
 */
 _createMarker: function(styles)
 {
 var graphic = this.get("graphic"),
 marker,
 cfg = this._copyObject(styles);
 marker = graphic.addShape(cfg);
 marker.addClass(SERIES_MARKER);
 return marker;
 },
 
 /**
 * Creates a cache of markers for reuse.
 *
 * @method _createMarkerCache
 * @private
 */
 _clearMarkerCache: function()
 {
 var len = this._markerCache.length,
 i = 0,
 marker;
 for(; i < len; ++i)
 {
 marker = this._markerCache[i];
 if(marker)
 {
 marker.destroy();
 }
 }
 this._markerCache = [];
 },
 
 /**
 * Gets the default style values for the markers.
 *
 * @method _getPlotDefaults
 * @return Object
 * @private
 */
 _getPlotDefaults: function()
 {
 var defs = {
 padding:{
 top: 0,
 left: 0,
 right: 0,
 bottom: 0
 },
 fill:{
 alphas:["1"]
 },
 border: {
 weight: 0,
 alpha: 1
 }
 };
 defs.fill.colors = this._defaultSliceColors;
 defs.border.colors = this._defaultBorderColors;
 return defs;
 }
 }, {
 ATTRS: {
 /**
 * Read-only attribute indicating the type of series.
 *
 * @attribute type
 * @type String
 * @default pie
 */
 type: {
 value: "pie"
 },
 
 /**
 * Order of this instance of this `type`.
 *
 * @attribute order
 * @type Number
 */
 order: {},
 
 /**
 * Reference to the `Graph` in which the series is drawn into.
 *
 * @attribute graph
 * @type Graph
 */
 graph: {},
 
 /**
 * Reference to the `Axis` instance used for assigning
 * category values to the graph.
 *
 * @attribute categoryAxis
 * @type Axis
 */
 categoryAxis: {
 value: null,
 
 validator: function(value)
 {
 return value !== this.get("categoryAxis");
 }
 },
 
 /**
 * Reference to the `Axis` instance used for assigning
 * series values to the graph.
 *
 * @attribute categoryAxis
 * @type Axis
 */
 valueAxis: {
 value: null,
 
 validator: function(value)
 {
 return value !== this.get("valueAxis");
 }
 },
 
 /**
 * Indicates which array to from the hash of value arrays in
 * the category `Axis` instance.
 *
 * @attribute categoryKey
 * @type String
 */
 categoryKey: {
 value: null,
 
 validator: function(value)
 {
 return value !== this.get("categoryKey");
 }
 },
 /**
 * Indicates which array to from the hash of value arrays in
 * the value `Axis` instance.
 *
 * @attribute valueKey
 * @type String
 */
 valueKey: {
 value: null,
 
 validator: function(value)
 {
 return value !== this.get("valueKey");
 }
 },
 
 /**
 * Name used for for displaying category data
 *
 * @attribute categoryDisplayName
 * @type String
 */
 categoryDisplayName: {
 setter: function(val)
 {
 this._categoryDisplayName = val;
 return val;
 },
 
 getter: function()
 {
 return this._categoryDisplayName || this.get("categoryKey");
 }
 },
 
 /**
 * Name used for for displaying value data
 *
 * @attribute valueDisplayName
 * @type String
 */
 valueDisplayName: {
 setter: function(val)
 {
 this._valueDisplayName = val;
 return val;
 },
 
 getter: function()
 {
 return this._valueDisplayName || this.get("valueKey");
 }
 },
 
 /**
 * @attribute slices
 * @type Array
 * @private
 */
 slices: null
 
 /**
 * Style properties used for drawing markers. This attribute is inherited from `MarkerSeries`. Below are the default
 * values:
 * <dl>
 * <dt>fill</dt><dd>A hash containing the following values:
 * <dl>
 * <dt>colors</dt><dd>An array of colors to be used for the marker fills. The color for each marker is
 * retrieved from the array below:<br/>
 * `["#66007f", "#a86f41", "#295454", "#996ab2", "#e8cdb7", "#90bdbd","#000000","#c3b8ca", "#968373", "#678585"]`
 * </dd>
 * <dt>alphas</dt><dd>An array of alpha references (Number from 0 to 1) indicating the opacity of each marker
 * fill. The default value is [1].</dd>
 * </dl>
 * </dd>
 * <dt>border</dt><dd>A hash containing the following values:
 * <dl>
 * <dt>color</dt><dd>An array of colors to be used for the marker borders. The color for each marker is
 * retrieved from the array below:<br/>
 * `["#205096", "#b38206", "#000000", "#94001e", "#9d6fa0", "#e55b00", "#5e85c9", "#adab9e", "#6ac291", "#006457"]`
 * <dt>alpha</dt><dd>Number from 0 to 1 indicating the opacity of the marker border. The default value is 1.</dd>
 * <dt>weight</dt><dd>Number indicating the width of the border. The default value is 1.</dd>
 * </dl>
 * </dd>
 * <dt>over</dt><dd>hash containing styles for markers when highlighted by a `mouseover` event. The default
 * values for each style is null. When an over style is not set, the non-over value will be used. For example,
 * the default value for `marker.over.fill.color` is equivalent to `marker.fill.color`.</dd>
 * </dl>
 *
 * @attribute styles
 * @type Object
 */
 }
 });
 
 

AltStyle によって変換されたページ (->オリジナル) /