You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
731 lines
21 KiB
731 lines
21 KiB
/*
|
|
YUI 3.17.2 (build 9c3c78e)
|
|
Copyright 2014 Yahoo! Inc. All rights reserved.
|
|
Licensed under the BSD License.
|
|
http://yuilibrary.com/license/
|
|
*/
|
|
|
|
YUI.add('series-pie', function (Y, NAME) {
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
}
|
|
});
|
|
|
|
|
|
}, '3.17.2', {"requires": ["series-base", "series-plot-util"]});
|
|
|