webgui/www/extras/yui/as-src/charts/Charts.as
JT Smith 20f8df1291 upgrading to YUI 2.6
data tables are going to need some work yet, but the other stuff seems to be working 100%
2008-10-22 23:53:29 +00:00

1353 lines
40 KiB
ActionScript

package
{
import com.adobe.serialization.json.JSON;
import com.yahoo.astra.fl.charts.*;
import com.yahoo.astra.fl.charts.events.ChartEvent;
import com.yahoo.astra.fl.charts.legend.Legend;
import com.yahoo.astra.fl.charts.series.*;
import com.yahoo.astra.fl.charts.skins.*;
import com.yahoo.astra.fl.utils.UIComponentUtil;
import com.yahoo.astra.utils.InstanceFactory;
import com.yahoo.astra.utils.JavaScriptUtil;
import com.yahoo.yui.LoggerCategory;
import com.yahoo.yui.YUIAdapter;
import com.yahoo.yui.charts.*;
import fl.core.UIComponent;
import flash.display.DisplayObject;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.ErrorEvent;
import flash.events.MouseEvent;
import flash.external.ExternalInterface;
import flash.text.TextFormat;
import flash.utils.getDefinitionByName;
import flash.utils.getQualifiedClassName;
[SWF(backgroundColor=0xffffff)]
/**
* A wrapper for the Astra Charts components to allow them to be used by the YUI library.
*
* @author Josh Tynjala
*/
public class Charts extends YUIAdapter
{
//--------------------------------------
// Constructor
//--------------------------------------
/**
* Constructor.
*/
public function Charts()
{
super();
}
//--------------------------------------
// Properties
//--------------------------------------
/**
* @private
* A reference to the chart instance.
*/
protected var chart:Chart;
/**
* @private
* The type of the chart specified by setType().
*/
protected var type:String;
/**
* @private
* A reference to the legend instance.
*/
protected var legend:Legend;
/**
* @private
* Storage for the legendDisplay property.
*/
protected var _legendDisplay:String = "none";
/**
* @private
* Specifies the location of the legend, or "none" if the legend
* is not to be displayed.
*/
public function get legendDisplay():String
{
return this._legendDisplay;
}
/**
* @private
*/
public function set legendDisplay(value:String):void
{
this._legendDisplay = value;
this.refreshComponentSize();
}
/**
* @private
* Storage for the spacing property.
*/
protected var _spacing:Number = 6;
/**
* @private
* The spacing between the chart and other objects, such as the legend.
*/
public function get spacing():Number
{
return this._spacing;
}
/**
* @private
*/
public function set spacing(value:Number):void
{
this._spacing = value;
this.refreshComponentSize();
}
/**
* @private
* Storage for the padding property.
*/
protected var _padding:Number = 10;
/**
* @private
* The padding around the chart, in pixels.
*/
public function get padding():Number
{
return this._padding;
}
/**
* @private
*/
public function set padding(value:Number):void
{
this._padding = value;
}
/**
* @private
*/
protected var backgroundAndBorder:BackgroundAndBorder;
/**
* @private
*/
override protected function get component():DisplayObject
{
//why do I have to do this? it's not ambiguous!
return super.component;
}
/**
* @private
*/
override protected function set component(value:DisplayObject):void
{
this.chart = Chart(value);
super.component = value;
}
//--------------------------------------
// Public Methods
//--------------------------------------
/**
* Creates a chart instance based on the specified type.
*/
public function setType(value:String):void
{
if(this.chart)
{
this.removeChild(this.chart);
this.chart.removeEventListener(ChartEvent.ITEM_CLICK, chartItemEventHandler);
this.chart.removeEventListener(ChartEvent.ITEM_DOUBLE_CLICK, chartItemEventHandler);
this.chart.removeEventListener(ChartEvent.ITEM_ROLL_OUT, chartItemEventHandler);
this.chart.removeEventListener(ChartEvent.ITEM_ROLL_OVER, chartItemEventHandler);
this.chart.removeEventListener(MouseEvent.MOUSE_DOWN, chartItemExtraEventHandler);
}
this.type = value;
var ChartType:Class = ChartSerializer.getType(this.type);
var chart:Chart = new ChartType();
chart.setStyle("contentPadding", 0);
chart.setStyle("backgroundSkin", Sprite);
var backgroundFactory:InstanceFactory = this.createBorderBackgroundFactory();
backgroundFactory.properties.fillColor = 0xffffff;
backgroundFactory.properties.fillAlpha = 0.9;
backgroundFactory.properties.borderWeight = 1;
backgroundFactory.properties.borderColor = 0x000000;
chart.setStyle("dataTipBackgroundSkin", backgroundFactory);
chart.setStyle("seriesMarkerSkins", []);
this.addChildAt(chart, 1);
this.component = chart;
this.chart.addEventListener(ChartEvent.ITEM_CLICK, chartItemEventHandler, false, 0, true);
this.chart.addEventListener(ChartEvent.ITEM_DOUBLE_CLICK, chartItemEventHandler, false, 0, true);
this.chart.addEventListener(ChartEvent.ITEM_ROLL_OUT, chartItemEventHandler, false, 0, true);
this.chart.addEventListener(ChartEvent.ITEM_ROLL_OVER, chartItemEventHandler, false, 0, true);
this.chart.addEventListener(MouseEvent.MOUSE_DOWN, chartItemExtraEventHandler, false, 0, true);
this.chart.legend = this.legend;
this.log("Type set to \"" + this.type + "\"");
}
public function setDataProvider(value:Array):void
{
var dataProvider:Array = [];
var seriesCount:int = value.length;
var seriesStyles:Array = [];
for(var i:int = 0; i < seriesCount; i++)
{
var dataFromJavaScript:Object = value[i];
var currentData:ISeries = this.chart.dataProvider[i] as ISeries;
var seriesType:Class = SeriesSerializer.shortNameToSeriesType(dataFromJavaScript.type ? dataFromJavaScript.type : this.type);
var series:ISeries;
if(currentData && getDefinitionByName(getQualifiedClassName(currentData)) == seriesType)
{
//reuse the series if possible because we want animation
series = SeriesSerializer.readSeries(dataFromJavaScript, currentData);
}
else
{
series = SeriesSerializer.readSeries(dataFromJavaScript);
}
dataProvider[i] = series;
//this is where we parse the individual series styles, and we convert them
//to the format the chart actually expects.
//we fill in with defaults for styles that have not been specified
if(dataFromJavaScript.style)
{
seriesStyles.push(dataFromJavaScript.style);
}
else seriesStyles.push(null);
}
this.log("Displaying " + seriesCount + " series.");
//set data provider and new styles
this.chart.dataProvider = dataProvider;
//make sures the series are created!
this.chart.drawNow();
//set the styles for the series
this.setSeriesStyles(seriesStyles);
this.refreshComponentSize();
}
/**
* Returns the category names.
*/
public function getCategoryNames():Array
{
var categoryChart:ICategoryChart = this.chart as ICategoryChart;
if(categoryChart)
{
return categoryChart.categoryNames;
}
var shortName:String = ChartSerializer.getShortName(getQualifiedClassName(this.chart));
this.log("Cannot find categoryNames on a chart of type " + shortName, LoggerCategory.WARN);
return null;
}
/**
* Sets the category names used if the data requires a category axis.
* This field should be used if the data does not define the category
* values directly.
*/
public function setCategoryNames(value:Array):void
{
var categoryChart:ICategoryChart = this.chart as ICategoryChart;
if(categoryChart)
{
categoryChart.categoryNames = value;
}
else
{
var shortName:String = ChartSerializer.getShortName(getQualifiedClassName(this.chart));
this.log("Unable to set categoryNames on a chart of type " + shortName, LoggerCategory.WARN);
}
}
/**
* Returns the field used in complex objects to access data to be
* displayed on the PieChart.
*/
public function getDataField():String
{
var pieChart:PieChart = this.chart as PieChart;
if(pieChart)
{
return pieChart.dataField;
}
var shortName:String = ChartSerializer.getShortName(getQualifiedClassName(this.chart));
this.log("Unable to find dataField on a chart of type " + shortName, LoggerCategory.WARN);
return null;
}
/**
* Sets the field used in complex objects to access data to be displayed
* on the PieChart.
*/
public function setDataField(value:String):void
{
var pieChart:PieChart = this.chart as PieChart;
if(pieChart)
{
pieChart.dataField = value;
}
else
{
var shortName:String = ChartSerializer.getShortName(getQualifiedClassName(this.chart));
this.log("Unable to set dataField on a chart of type " + shortName, LoggerCategory.WARN);
}
}
/**
* Returns the field used in complex objects to access categories to be
* displayed on the PieChart.
*/
public function getCategoryField():String
{
var pieChart:PieChart = this.chart as PieChart;
if(pieChart)
{
return pieChart.categoryField;
}
var shortName:String = ChartSerializer.getShortName(getQualifiedClassName(this.chart));
this.log("Unable to find categoryField on a chart of type " + shortName, LoggerCategory.WARN);
return null;
}
/**
* Sets the field used in complex objects to access categories to be displayed
* on the PieChart.
*/
public function setCategoryField(value:String):void
{
var pieChart:PieChart = this.chart as PieChart;
if(pieChart)
{
pieChart.categoryField = value;
}
else
{
var shortName:String = ChartSerializer.getShortName(getQualifiedClassName(this.chart));
this.log("Unable to set categoryField on a chart of type " + shortName, LoggerCategory.WARN);
}
}
/**
* Returns the field used in complex objects to access data to be
* displayed on the horizontal axis.
*/
public function getHorizontalField():String
{
var cartesianChart:CartesianChart = this.chart as CartesianChart;
if(cartesianChart)
{
return cartesianChart.horizontalField;
}
var shortName:String = ChartSerializer.getShortName(getQualifiedClassName(this.chart));
this.log("Unable to find horizontalField on a chart of type " + shortName, LoggerCategory.WARN);
return null;
}
/**
* Sets the field used in complex objects to access data to be displayed
* on the horizontal axis. If the input data is XML, and the field is an
* attribute, be sure to include the "@" symbol at the beginning of the
* field name.
*/
public function setHorizontalField(value:String):void
{
var cartesianChart:CartesianChart = this.chart as CartesianChart;
if(cartesianChart)
{
cartesianChart.horizontalField = value;
}
else
{
var shortName:String = ChartSerializer.getShortName(getQualifiedClassName(this.chart));
this.log("Unable to set horizontalField on a chart of type " + shortName, LoggerCategory.WARN);
}
}
/**
* Returns the field used in complex objects to access data to be
* displayed on the vertical axis.
*/
public function getVerticalField():String
{
var cartesianChart:CartesianChart = this.chart as CartesianChart;
if(cartesianChart)
{
return cartesianChart.verticalField;
}
var shortName:String = ChartSerializer.getShortName(getQualifiedClassName(this.chart));
this.log("Unable to find verticalField on a chart of type " + shortName, LoggerCategory.WARN);
return null;
}
/**
* Sets the field used in complex objects to access data to be displayed
* on the vertical axis. If the input data is XML, and the field is an
* attribute, be sure to include the "@" symbol at the beginning of the
* field name.
*/
public function setVerticalField(value:String):void
{
var cartesianChart:CartesianChart = this.chart as CartesianChart;
if(cartesianChart)
{
cartesianChart.verticalField = value;
}
else
{
var shortName:String = ChartSerializer.getShortName(getQualifiedClassName(this.chart));
this.log("Unable to set verticalField on a chart of type " + shortName, LoggerCategory.WARN);
}
}
/**
* Returns the title displayed next to the vertical axis.
*/
public function getHorizontalAxisTitle():String
{
var cartesianChart:CartesianChart = this.chart as CartesianChart;
if(cartesianChart)
{
return cartesianChart.horizontalAxisTitle;
}
var shortName:String = ChartSerializer.getShortName(getQualifiedClassName(this.chart));
this.log("Unable to find horizontalAxisTitle on a chart of type " + shortName, LoggerCategory.WARN);
return null;
}
/**
* Sets the title displayed next to the horizontal axis.
*/
public function setHorizontalAxisTitle(value:String):void
{
var cartesianChart:CartesianChart = this.chart as CartesianChart;
if(cartesianChart)
{
cartesianChart.horizontalAxisTitle = value;
}
else
{
var shortName:String = ChartSerializer.getShortName(getQualifiedClassName(this.chart));
this.log("Unable to set horizontalAxisTitle on a chart of type " + shortName, LoggerCategory.WARN);
}
}
/**
* Returns the title displayed next to the vertical axis.
*/
public function getVerticalAxisTitle():String
{
var cartesianChart:CartesianChart = this.chart as CartesianChart;
if(cartesianChart)
{
return cartesianChart.verticalAxisTitle;
}
var shortName:String = ChartSerializer.getShortName(getQualifiedClassName(this.chart));
this.log("Unable to find verticalAxisTitle on a chart of type " + shortName, LoggerCategory.WARN);
return null;
}
/**
* Sets the title displayed next to the vertical axis.
*/
public function setVerticalAxisTitle(value:String):void
{
var cartesianChart:CartesianChart = this.chart as CartesianChart;
if(cartesianChart)
{
cartesianChart.verticalAxisTitle = value;
}
else
{
var shortName:String = ChartSerializer.getShortName(getQualifiedClassName(this.chart));
this.log("Unable to set verticalAxisTitle on a chart of type " + shortName, LoggerCategory.WARN);
}
}
/**
* Updates the horizontal axis with a new type.
*/
public function setHorizontalAxis(value:Object):void
{
var cartesianChart:CartesianChart = this.chart as CartesianChart;
if(cartesianChart)
{
cartesianChart.horizontalAxis = AxisSerializer.readAxis(value);
}
else
{
var shortName:String = ChartSerializer.getShortName(getQualifiedClassName(this.chart));
this.log("Unable to set horizontalAxis on a chart of type " + shortName, LoggerCategory.WARN);
}
}
/**
* Updates the vertical axis with a new type.
*/
public function setVerticalAxis(value:Object):void
{
var cartesianChart:CartesianChart = this.chart as CartesianChart;
if(cartesianChart)
{
cartesianChart.verticalAxis = AxisSerializer.readAxis(value);
}
else
{
var shortName:String = ChartSerializer.getShortName(getQualifiedClassName(this.chart));
this.log("Unable to set verticalAxis on a chart of type " + shortName, LoggerCategory.WARN);
}
}
/**
* Sets the JavaScript function to call to generate the chart's data tip.
*/
public function setDataTipFunction(value:String):void
{
var delegate:Object = {dataTipFunction: JavaScriptUtil.createCallbackFunction(value).callback};
delegate.callback = function(item:Object, index:int, series:ISeries):String
{
return delegate.dataTipFunction(item, index, SeriesSerializer.writeSeries(series));
}
this.chart.dataTipFunction = delegate.callback;
}
/**
* Accepts a JSON-encoded set of styles for the chart itself.
* Flash Player versions below 9.0.60 don't encode ExternalInterface
* calls correctly!
*/
public function setStyles(styles:String):void
{
if(!styles) return;
var parsedStyles:Object = JSON.decode(styles);
for(var styleName:String in parsedStyles)
{
this.setStyle(styleName, parsedStyles[styleName], false);
}
}
public function setStyle(name:String, value:Object, json:Boolean = true):void
{
if(json && value)
{
//by default, we assume it's json data, only setStyles will send false
value = JSON.decode(value as String);
}
var needsSizingRefresh:Boolean = false;
switch(name)
{
case "padding":
this.padding = value as Number;
needsSizingRefresh = true;
break;
case "spacing":
this.spacing = value as Number;
needsSizingRefresh = true;
break;
case "animationEnabled":
this.chart.setStyle("animationEnabled", value);
break;
case "border":
if(value.color != null)
{
this.backgroundAndBorder.borderColor = this.parseColor(value.color);
}
if(value.size != null)
{
this.backgroundAndBorder.borderWeight = value.size;
needsSizingRefresh = true;
}
break;
case "background":
if(value.color != null)
{
this.backgroundAndBorder.fillColor = this.parseColor(value.color);
}
if(value.image)
{
this.backgroundAndBorder.image = value.image;
}
if(value.alpha != null)
{
this.backgroundAndBorder.fillAlpha = value.alpha;
}
if(value.mode)
{
this.backgroundAndBorder.imageMode = value.mode;
}
break;
case "font":
var textFormat:TextFormat = TextFormatSerializer.readTextFormat(value);
this.chart.setStyle("textFormat", textFormat);
break;
case "dataTip":
this.setDataTipStyles(value);
break;
case "xAxis":
this.setAxisStyles(value, "horizontal");
break;
case "yAxis":
this.setAxisStyles(value, "vertical");
break;
case "legend":
this.setLegendStyles(value);
break;
default:
this.log("Unknown style: " + name, LoggerCategory.WARN);
}
if(needsSizingRefresh)
{
this.refreshComponentSize();
}
}
public function setSeriesStyles(styles:Array):void
{
var defaultSeriesColors:Array =
[0x00b8bf, 0x8dd5e7, 0xedff9f, 0xffa928, 0xc0fff6, 0xd00050,
0xc6c6c6, 0xc3eafb, 0xfcffad, 0xcfff83, 0x444444, 0x4d95dd,
0xb8ebff, 0x60558f, 0x737d7e, 0xa64d9a, 0x8e9a9b, 0x803e77];
//will be filled based on the defaults or the series style definition, if present.
var seriesColors:Array = [];
var seriesCount:int = Math.min(this.chart.dataProvider.length, styles.length);
for(var i:int = 0; i < seriesCount; i++)
{
var series:ISeries = ISeries(this.chart.dataProvider[i]);
var style:Object = styles[i];
if(style)
{
style = JSON.decode(style as String);
}
//defaults
var defaultColors:Array = defaultSeriesColors.concat();
if(series is PieSeries)
{
defaultColors = [defaultColors];
}
var defaultSize:Number = 10;
if(series is ColumnSeries || series is BarSeries)
{
defaultSize = 20;
}
var defaultSkin:Object = RectangleSkin;
if(series is LineSeries)
{
defaultSkin = CircleSkin;
}
else if(series is PieSeries)
{
defaultSkin = [defaultSkin];
}
//initialize styles with defaults
var color:Object = defaultColors[i % defaultColors.length];
var skin:Object = defaultSkin;
var mode:Object = "repeat";
if(style)
{
for(var styleName:String in style)
{
switch(styleName)
{
case "images":
if(!(series is PieSeries))
{
this.log(styleName + " style is only supported by series of type 'pie'.", LoggerCategory.WARN);
break;
}
var images:Array = style.images as Array;
var imageCount:int = images.length;
for(var j:int = 0; j < imageCount; j++)
{
images[j] = this.createMarkerSkin(String(images[j]), series);
}
skin = images;
break;
case "image":
skin = this.createMarkerSkin(style.image, series);
if(!(series is LineSeries))
{
skin.properties.fillColor = color;
skin.properties.fillAlpha = 1;
}
break;
case "mode":
mode = style.mode;
break;
case "colors":
if(!(series is PieSeries))
{
this.log(styleName + " style is only supported by series of type 'pie'.", LoggerCategory.WARN);
break;
}
var colors:Array = style.colors;
var colorCount:int = colors.length;
for(j = 0; j < colorCount; j++)
{
colors[j] = this.parseColor(colors[j]);
}
color = colors;
break;
case "color":
color = this.parseColor(style.color);
if(skin is InstanceFactory && !(series is LineSeries))
{
skin.properties.fillColor = color;
skin.properties.fillAlpha = 1;
}
break;
case "size":
UIComponent(series).setStyle("markerSize", style.size);
break;
case "alpha":
UIComponent(series).setStyle("markerAlpha", style.alpha);
break;
case "showAreaFill": //LineSeries only
if(!(series is LineSeries))
{
this.log("The style " + styleName + " is only supported by series of type 'line'.", LoggerCategory.WARN);
}
UIComponent(series).setStyle("showAreaFill", style.showAreaFill);
break;
case "areaFillAlpha": //LineSeries only
if(!(series is LineSeries))
{
this.log("The style " + styleName + " is only supported by series of type 'line'.", LoggerCategory.WARN);
}
UIComponent(series).setStyle("areaFillAlpha", style.areaFillAlpha);
break;
case "lineSize": //LineSeries only
if(!(series is LineSeries))
{
this.log("The style " + styleName + " is only supported by series of type 'line'.", LoggerCategory.WARN);
}
UIComponent(series).setStyle("lineWeight", style.lineSize);
break;
case "connectPoints": //LineSeries only
if(!(series is LineSeries))
{
this.log("The style " + styleName + " is only supported by series of type 'line'.", LoggerCategory.WARN);
}
UIComponent(series).setStyle("connectPoints", style.connectPoints);
break;
case "connectDiscontinuousPoints": //LineSeries only
if(!(series is LineSeries))
{
this.log("The style " + styleName + " is only supported by series of type 'line'.", LoggerCategory.WARN);
}
UIComponent(series).setStyle("connectDiscontinuousPoints", style.connectDiscontinuousPoints);
break;
case "discontinuousDashLength": //LineSeries only
if(!(series is LineSeries))
{
this.log("The style " + styleName + " is only supported by series of type 'line'.", LoggerCategory.WARN);
}
UIComponent(series).setStyle("discontinuousDashLength", style.discontinuousDashLength);
break;
case "showLabels": //PieSeries only
if(!(series is PieSeries))
{
this.log("The style " + styleName + " is only supported by series of type 'pie'.", LoggerCategory.WARN);
}
UIComponent(series).setStyle("showLabels", style.showLabels);
break;
case "hideOverlappingLabels": //PieSeries only
if(!(series is PieSeries))
{
this.log("The style " + styleName + " is only supported by series of type 'pie'.", LoggerCategory.WARN);
}
UIComponent(series).setStyle("hideOverlappingLabels", style.showLabels);
break;
case "font": //PieSeries only
if(!(series is PieSeries))
{
this.log("The style " + styleName + " is only supported by series of type 'pie'.", LoggerCategory.WARN);
}
UIComponent(series).setStyle("textFormat", TextFormatSerializer.readTextFormat(style.font))
break;
default:
this.log("Unknown series style: " + styleName, LoggerCategory.WARN);
}
}
}
if(mode)
{
if(skin is InstanceFactory)
{
skin.properties.imageMode = mode;
}
else if(skin is Array)
{
var skinCount:int = (skin as Array).length;
for(j = 0; j < skinCount; j++)
{
var subSkin:InstanceFactory = skin[j] as InstanceFactory;
if(subSkin)
{
subSkin.properties.imageMode = mode;
}
}
}
}
if(series is PieSeries)
{
PieSeries(series).setStyle("markerSkins", skin);
}
else UIComponent(series).setStyle("markerSkin", skin);
seriesColors[i] = color;
}
this.chart.setStyle("seriesColors", seriesColors);
this.chart.drawNow();
}
//--------------------------------------
// Protected Methods
//--------------------------------------
/**
* @private (protected)
* Initialize the functions that may be called by JavaScript through ExternalInterface.
*/
override protected function initializeComponent():void
{
super.initializeComponent();
try
{
ExternalInterface.addCallback("setType", setType);
ExternalInterface.addCallback("setStyle", setStyle);
ExternalInterface.addCallback("setStyles", setStyles);
ExternalInterface.addCallback("setSeriesStyles", setSeriesStyles);
ExternalInterface.addCallback("setDataProvider", setDataProvider);
ExternalInterface.addCallback("getCategoryNames", getCategoryNames);
ExternalInterface.addCallback("setCategoryNames", setCategoryNames);
ExternalInterface.addCallback("setDataTipFunction", setDataTipFunction);
ExternalInterface.addCallback("getCategoryNames", getCategoryNames);
ExternalInterface.addCallback("setCategoryNames", setCategoryNames);
//CartesianChart
ExternalInterface.addCallback("getHorizontalField", getHorizontalField);
ExternalInterface.addCallback("setHorizontalField", setHorizontalField);
ExternalInterface.addCallback("getVerticalField", getVerticalField);
ExternalInterface.addCallback("setVerticalField", setVerticalField);
ExternalInterface.addCallback("setHorizontalAxis", setHorizontalAxis);
ExternalInterface.addCallback("setVerticalAxis", setVerticalAxis);
//PieChart
ExternalInterface.addCallback("getDataField", getDataField);
ExternalInterface.addCallback("setDataField", setDataField);
ExternalInterface.addCallback("getCategoryField", getCategoryField);
ExternalInterface.addCallback("setCategoryField", setCategoryField);
}
catch(error:SecurityError)
{
//do nothing. it will be caught by the YUIAdapter.
}
this.backgroundAndBorder = new BackgroundAndBorder();
this.backgroundAndBorder.width = this.stage.stageWidth;
this.backgroundAndBorder.height = this.stage.stageHeight;
this.backgroundAndBorder.addEventListener(ErrorEvent.ERROR, backgroundErrorHandler);
this.addChild(this.backgroundAndBorder);
this.legend = new Legend();
this.legend.setStyle("backgroundSkin", Shape);
this.addChild(this.legend);
}
/**
* @private (protected)
* Since Chart is a Flash CS3 component, we should call drawNow() to be sure it updates properly.
*/
override protected function refreshComponentSize():void
{
super.refreshComponentSize();
if(this.backgroundAndBorder)
{
this.backgroundAndBorder.width = this.stage.stageWidth;
this.backgroundAndBorder.height = this.stage.stageHeight;
this.backgroundAndBorder.drawNow();
}
if(this.chart)
{
this.chart.x = this.chart.y = this.backgroundAndBorder.borderWeight + this.padding;
this.chart.width -= 2 * (this.backgroundAndBorder.borderWeight + this.padding);
this.chart.height -= 2 * (this.backgroundAndBorder.borderWeight + this.padding);
if(this.legend && this.legendDisplay != "none")
{
this.legend.visible = true;
//we need to draw because the legend resizes itself
this.legend.drawNow();
if(this.legendDisplay == "left" || this.legendDisplay == "right")
{
if(this.legendDisplay == "left")
{
this.legend.x = this.backgroundAndBorder.borderWeight + this.padding;
this.chart.x = this.legend.x + this.legend.width + this.spacing;
}
else //right
{
this.legend.x = this.stage.stageWidth - this.backgroundAndBorder.borderWeight - this.legend.width - padding;
}
//center vertically
this.legend.y = Math.max(0, (this.stage.stageHeight - this.legend.height) / 2);
this.chart.width -= (this.legend.width + this.spacing);
}
else //top or bottom
{
if(this.legendDisplay == "top")
{
this.legend.y = this.backgroundAndBorder.borderWeight + this.padding;
this.chart.y = this.legend.y + this.legend.height + this.spacing;
}
else //bottom
{
this.legend.y = this.stage.stageHeight - this.backgroundAndBorder.borderWeight - this.legend.height - padding;
}
//center horizontally
this.legend.x = Math.max(0, (this.stage.stageWidth - this.legend.width) / 2);
this.chart.height -= (this.legend.height + this.spacing);
}
//we disable animation temporarily because we don't want the markers
//sliding around because the legend resized
if(this.legend && this.legendDisplay != "none")
{
var oldAnimationEnabled:Boolean = UIComponentUtil.getStyleValue(this.chart, "animationEnabled");
this.chart.setStyle("animationEnabled", false);
this.chart.drawNow();
this.chart.setStyle("animationEnabled", oldAnimationEnabled);
}
}
else
{
this.legend.visible = false;
}
this.chart.drawNow();
}
}
/**
* @private (protected)
* Logs errors for the background image loading.
*/
protected function backgroundErrorHandler(event:ErrorEvent):void
{
this.log(event.text, LoggerCategory.ERROR);
}
/**
* @private (protected)
*
* Receives chart item mouse events and passes them out to JavaScript.
*/
protected function chartItemEventHandler(event:ChartEvent):void
{
var type:String = event.type.replace("Roll", "Mouse");
type += "Event";
var seriesIndex:int = (this.chart.dataProvider as Array).indexOf(event.series);
var itemEvent:Object = {type: type, seriesIndex: seriesIndex, index: event.index, item: event.item, x: this.mouseX, y: this.mouseY};
this.dispatchEventToJavaScript(itemEvent);
}
private var _lastMouseItem:ISeriesItemRenderer;
protected function chartItemExtraEventHandler(event:MouseEvent):void
{
var dragEventType:String = "itemDragEvent";
var renderer:ISeriesItemRenderer = this._lastMouseItem;
this._lastMouseItem = null;
if(event.type == MouseEvent.MOUSE_DOWN)
{
//crawl up until we get to the chart or an item renderer
var displayObject:DisplayObject = event.target as DisplayObject;
while(!(displayObject is ISeriesItemRenderer) && !(displayObject is Chart))
{
displayObject = displayObject.parent;
}
if(displayObject is ISeriesItemRenderer)
{
renderer = ISeriesItemRenderer(displayObject);
this.stage.addEventListener(MouseEvent.MOUSE_MOVE, chartItemExtraEventHandler);
this.stage.addEventListener(MouseEvent.MOUSE_UP, chartItemExtraEventHandler);
}
else
{
renderer = null;
}
dragEventType = "itemDragStartEvent";
}
else if(event.type == MouseEvent.MOUSE_UP)
{
dragEventType = "itemDragEndEvent";
this.stage.removeEventListener(MouseEvent.MOUSE_MOVE, chartItemExtraEventHandler);
this.stage.removeEventListener(MouseEvent.MOUSE_UP, chartItemExtraEventHandler);
}
//if we've found an item renderer, dispatch the event
if(renderer is ISeriesItemRenderer)
{
var seriesIndex:int = (this.chart.dataProvider as Array).indexOf(renderer.series);
var itemIndex:int = renderer.series.dataProvider.indexOf(renderer.data)
var itemEvent:Object = {type: dragEventType, seriesIndex: seriesIndex, index: itemIndex, item: renderer.data, x: this.mouseX, y: this.mouseY};
this.dispatchEventToJavaScript(itemEvent);
this._lastMouseItem = renderer;
}
}
protected function setDataTipStyles(styles:Object):void
{
var contentPadding:Number = 6;
if(styles.padding >= 0)
{
contentPadding = styles.padding;
}
if(styles.border || styles.background)
{
//defaults
var backgroundFactory:InstanceFactory = this.createBorderBackgroundFactory();
backgroundFactory.properties.fillColor = 0xffffff;
backgroundFactory.properties.fillAlpha = 0.9;
backgroundFactory.properties.borderWeight = 1;
backgroundFactory.properties.borderColor = 0x000000;
var border:Object = styles.border;
if(border)
{
if(border.color != null)
{
backgroundFactory.properties.borderColor = this.parseColor(border.color)
}
if(border.size != null)
{
backgroundFactory.properties.borderWeight = border.size;
contentPadding += border.size;
}
}
var background:Object = styles.background;
if(background)
{
if(background.color != null)
{
backgroundFactory.properties.fillColor = this.parseColor(background.color);
}
if(background.image)
{
backgroundFactory.properties.image = background.image;
}
if(background.alpha != null)
{
backgroundFactory.properties.fillAlpha = background.alpha;
}
if(background.mode)
{
backgroundFactory.properties.imageMode = background.mode;
}
}
this.chart.setStyle("dataTipBackgroundSkin", backgroundFactory);
}
this.chart.setStyle("dataTipContentPadding", contentPadding);
if(styles.font)
{
var textFormat:TextFormat = TextFormatSerializer.readTextFormat(styles.font);
this.chart.setStyle("dataTipTextFormat", textFormat);
}
}
protected function setAxisStyles(styles:Object, axisName:String):void
{
if(styles.color != null)
{
this.chart.setStyle(axisName + "AxisColor", this.parseColor(styles.color));
}
if(styles.size != null)
{
this.chart.setStyle(axisName + "AxisWeight", styles.size);
}
if(styles.showLabels != null)
{
this.chart.setStyle("show" + axisName.substr(0, 1).toUpperCase() + axisName.substr(1) + "AxisLabels", styles.showLabels);
}
if(styles.hideOverlappingLabels != null)
{
this.chart.setStyle(axisName.substr(0, 1).toUpperCase() + axisName.substr(1) + "AxisHideOverlappingLabels", styles.hideOverlappingLabels);
}
if(styles.majorGridLines)
{
var majorGridLines:Object = styles.majorGridLines;
if(majorGridLines.color != null)
{
this.chart.setStyle(axisName + "AxisGridLineColor", this.parseColor(majorGridLines.color));
}
if(majorGridLines.size != null)
{
this.chart.setStyle(axisName + "AxisGridLineWeight", majorGridLines.size);
this.chart.setStyle("show" + axisName.substr(0, 1).toUpperCase() + axisName.substr(1) + "AxisGridLines", majorGridLines.size > 0);
}
}
if(styles.minorGridLines)
{
var minorGridLines:Object = styles.minorGridLines;
if(minorGridLines.color != null)
{
this.chart.setStyle(axisName + "AxisMinorGridLineColor", this.parseColor(minorGridLines.color));
}
if(minorGridLines.size != null)
{
this.chart.setStyle(axisName + "AxisMinorGridLineWeight", minorGridLines.size);
this.chart.setStyle("show" + axisName.substr(0, 1).toUpperCase() + axisName.substr(1) + "AxisMinorGridLines", minorGridLines.size > 0);
}
}
if(styles.majorTicks)
{
var majorTicks:Object = styles.majorTicks;
if(majorTicks.color != null)
{
this.chart.setStyle(axisName + "AxisTickColor", this.parseColor(majorTicks.color));
}
if(majorTicks.size != null)
{
this.chart.setStyle(axisName + "AxisTickWeight", majorTicks.size);
}
if(majorTicks.length != null)
{
this.chart.setStyle(axisName + "AxisTickLength", majorTicks.length);
}
if(majorTicks.display)
{
this.chart.setStyle("show" + axisName.substr(0, 1).toUpperCase() + axisName.substr(1) + "AxisTicks", majorTicks.display != "none");
if(majorTicks.display != "none")
{
this.chart.setStyle(axisName + "AxisTickPosition", majorTicks.display);
}
}
}
if(styles.minorTicks)
{
var minorTicks:Object = styles.minorTicks;
if(minorTicks.color != null)
{
this.chart.setStyle(axisName + "AxisMinorTickColor", this.parseColor(minorTicks.color));
}
if(minorTicks.size != null)
{
this.chart.setStyle(axisName + "AxisMinorTickWeight", minorTicks.size);
}
if(minorTicks.length != null)
{
this.chart.setStyle(axisName + "AxisMinorTickLength", minorTicks.length);
}
if(minorTicks.display)
{
this.chart.setStyle("show" + axisName.substr(0, 1).toUpperCase() + axisName.substr(1) + "AxisMinorTicks", minorTicks.display != "none");
if(minorTicks.display != "none")
{
this.chart.setStyle(axisName + "AxisMinorTickPosition", minorTicks.display);
}
}
}
}
protected function setLegendStyles(styles:Object):void
{
if(styles.font)
{
var textFormat:TextFormat = TextFormatSerializer.readTextFormat(styles.font);
this.legend.setStyle("textFormat", textFormat);
}
if(styles.spacing != null)
{
this.legend.setStyle("gap", styles.spacing);
}
if(styles.display)
{
switch(styles.display)
{
case "left":
case "right":
this.legend.setStyle("direction", "vertical");
break;
default: //top, bottom
this.legend.setStyle("direction", "horizontal");
}
this.legendDisplay = styles.display;
}
var contentPadding:Number = 6;
if(styles.padding != null)
{
contentPadding = styles.padding;
}
if(styles.border || styles.background)
{
var backgroundFactory:InstanceFactory = this.createBorderBackgroundFactory();
var border:Object = styles.border;
if(border)
{
if(border.color != null)
{
backgroundFactory.properties.borderColor = this.parseColor(border.color)
}
if(border.size != null)
{
backgroundFactory.properties.borderWeight = border.size;
contentPadding += border.size;
}
}
var background:Object = styles.background;
if(background)
{
if(background.color != null)
{
backgroundFactory.properties.fillColor = this.parseColor(background.color);
}
if(background.image)
{
backgroundFactory.properties.image = background.image;
}
if(background.alpha != null)
{
backgroundFactory.properties.fillAlpha = background.alpha;
}
if(background.mode)
{
backgroundFactory.properties.imageMode = background.mode;
}
}
this.legend.setStyle("backgroundSkin", backgroundFactory);
}
this.legend.setStyle("contentPadding", contentPadding);
}
//--------------------------------------
// Private Methods
//--------------------------------------
/**
* @private
* Creates a pseudo-class to instantiate a BackgroundAndBorder object.
*/
private function createBorderBackgroundFactory():InstanceFactory
{
var factory:InstanceFactory = new InstanceFactory(BackgroundAndBorder);
factory.properties =
{
fillColor: 0xffffff,
fillAlpha: 1,
borderColor: 0x000000,
borderWeight: 0,
image: null,
imageMode: BackgroundImageMode.REPEAT
};
factory.methods =
{
addEventListener: [ErrorEvent.ERROR, backgroundLoadErrorHandler, false, 0, true]
};
return factory;
}
private function backgroundLoadErrorHandler(event:ErrorEvent):void
{
this.log(event.text, LoggerCategory.ERROR);
}
private function createMarkerSkin(imagePath:String, series:ISeries):InstanceFactory
{
//a simple UILoader would be enough, but we want tiling
var skin:InstanceFactory = this.createBorderBackgroundFactory();
//turn off the border
skin.properties.borderWeight = 0;
//turn off the fill
skin.properties.fillAlpha = 0;
skin.properties.image = imagePath;
skin.properties.imageMode = BackgroundImageMode.REPEAT;
if(series is LineSeries)
{
//make points fit to size and maintain their aspect ratio
skin.properties.imageMode = BackgroundImageMode.STRETCH_AND_MAINTAIN_ASPECT_RATIO;
}
return skin;
}
private function parseColor(value:Object):uint
{
if(!(value is Number))
{
var valueAsString:String = value.toString().replace("#", "");
if(valueAsString.indexOf("0x") != 0)
{
valueAsString = "0x" + valueAsString;
}
return parseInt(String(valueAsString), 16);
}
return uint(value);
}
}
}