<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:mccune="com.dougmccune.controls.*"
xmlns:skins="skins.*"
xmlns:annotations="annotations.*"
creationComplete="createComplete()"
initialize="init()" visible="false" pageTitle="Step 5" viewSourceURL="srcview/index.html">
<mx:Style source="styles/ChartStyles.css" />
<mx:Canvas id="appContainer" width="100%" height="100%" horizontalScrollPolicy="off" verticalScrollPolicy="off">
<mx:Canvas id="mainChartContainer" x="0" y="25" width="{CHART_WIDTH}" height="{MAIN_CHART_HEIGHT}"
horizontalScrollPolicy="off" verticalScrollPolicy="off" borderSides="left right top"
styleName="canvasContainerStyles">
<mx:AreaChart id="mainChart" x="-2" y="0"
showDataTips="false" dataProvider="{mainData}"
width="{CHART_WIDTH}" height="{MAIN_CHART_HEIGHT-20}"
styleName="mainChartStyle"
mouseMove="getChartDataPoint()" mouseOut="chartMouseOut()"
backgroundElements="{backgroundContents}">
<mx:horizontalAxis>
<mx:DateTimeAxis dataUnits="days" parseFunction="dateParse"
labelFunction="formatDateLabel" />
</mx:horizontalAxis>
<mx:horizontalAxisRenderer>
<skins:InnerAxisRenderer axisPosition="right" axisWidth="20"
axisBackgroundAlpha="1" axisBackgroundColor="#CDE2F8"
labelAlign="right"
labelFontSize="10" labelFontColor="#000000" />
</mx:horizontalAxisRenderer>
<mx:verticalAxis><mx:LinearAxis baseAtZero="false" /></mx:verticalAxis>
<mx:verticalAxisRenderer>
<skins:InnerAxisRenderer axisPosition="right" axisWidth="200"
axisBackgroundAlpha="0"
labelFontSize="10" labelFontColor="#000000" />
</mx:verticalAxisRenderer>
<mx:series>
<mx:AreaSeries id="largeSeries" name="close" xField="date" yField="close"
areaFill="{new SolidColor(0xCDE2F8, 0.20)}"
areaStroke="{new Stroke(0x0066DD, 1)}"
fill="{new SolidColor(0x0066DD, .75)}"
radius="2.5" form="segment"
itemRenderer="skins.LineSeriesCustomRenderer"
updateComplete="seriesComplete();" />
</mx:series>
<mx:annotationElements>
<mx:Canvas id="mainChartArea" width="100%" height="100%" buttonMode="true"
mouseDown="setMouseDown(mainChart);"
mouseUp="showAnnotations = true; refreshAnnotations()" />
<mx:Canvas id="annotationCanvas" width="100%" height="100%"
horizontalScrollPolicy="off" verticalScrollPolicy="off"/>
</mx:annotationElements>
</mx:AreaChart>
</mx:Canvas>
<mx:Canvas id="mainChartVolumeContainer"
x="0" y="{mainChartContainer.y + mainChartContainer.height - 2 - 18}"
width="{CHART_WIDTH}" height="{VOLUME_CHART_HEIGHT}"
horizontalScrollPolicy="off" verticalScrollPolicy="off"
borderSides="left right bottom" styleName="canvasContainerStyles">
<mx:CartesianChart id="mainChartVolume"
x="-2" y="0"
dataProvider="{mainData}"
width="{CHART_WIDTH}" height="{VOLUME_CHART_HEIGHT}"
showDataTips="false" backgroundElements="[]"
mouseMove="getChartDataPoint()" mouseOut="chartMouseOut()">
<mx:horizontalAxis>
<mx:DateTimeAxis dataUnits="days" parseFunction="dateParse" />
</mx:horizontalAxis>
<mx:horizontalAxisRenderer>
<mx:AxisRenderer width="0" height="0" styleName="noAxisStyle" />
</mx:horizontalAxisRenderer>
<mx:verticalAxis><mx:LinearAxis baseAtZero="false" /></mx:verticalAxis>
<mx:verticalAxisRenderer>
<mx:AxisRenderer width="0" height="0" styleName="noAxisStyle" />
</mx:verticalAxisRenderer>
<mx:series>
<mx:ColumnSeries id="volumeSeries" name="close" xField="date" yField="volume"
fill="{new SolidColor(0x0066dd, 0.85)}"
maxColumnWidth="3"
itemRenderer="skins.ColumnSeriesCustomRenderer" />
</mx:series>
</mx:CartesianChart>
</mx:Canvas>
<mx:Canvas id="rangeChartContainer"
x="15" y="{mainChartVolumeContainer.y + mainChartVolumeContainer.height - 1}"
width="{CHART_WIDTH - 31}" height="{RANGE_CHART_HEIGHT}"
horizontalScrollPolicy="off" verticalScrollPolicy="off"
styleName="canvasContainerStyles" borderSides="left right" >
<mx:AreaChart x="-2" y="0" id="rangeChart"
showDataTips="false" dataProvider="{rangeData}"
width="{CHART_WIDTH}" height="{RANGE_CHART_HEIGHT}">
<mx:horizontalAxis><mx:DateTimeAxis dataUnits="days" parseFunction="dateParse" /></mx:horizontalAxis>
<mx:horizontalAxisRenderer>
<mx:AxisRenderer styleName="noAxisStyle" />
</mx:horizontalAxisRenderer>
<mx:verticalAxis><mx:LinearAxis baseAtZero="false" /></mx:verticalAxis>
<mx:verticalAxisRenderer>
<mx:AxisRenderer styleName="noAxisStyle" />
</mx:verticalAxisRenderer>
<mx:series>
<mx:AreaSeries id="smallSeries" name="close" xField="date" yField="close"
areaFill="{new SolidColor(0xcde2f8, 0.20)}"
areaStroke="{new Stroke(0x0066dd, 1)}"
form="curve" />
</mx:series>
<mx:annotationElements>
<mx:HDividedBox id="dividedBox" horizontalScrollPolicy="off"
width="100%" height="100%"
liveDragging="true"
dividerRelease="updateIndicatorValuesWithEffect();"
dividerSkin="{blankDividerClass}"
dividerDrag="setDividerDragDate()"
mouseOver="dividedBox.setStyle('dividerSkin', dividerClass);"
mouseOut="dividedBox.setStyle('dividerSkin', blankDividerClass);"
borderSides="bottom top">
<mx:Canvas id="leftBox" backgroundColor="#FFFFFF" backgroundAlpha="0.5" width="50%" height="100%"
borderColor="#333333" borderThickness="1" borderStyle="solid" borderSides="top right" />
<mx:Canvas backgroundColor="#FFFFFF" backgroundAlpha="0" width="50%" height="100%" buttonMode="true"
mouseDown="setMouseDown(rangeChart);" minWidth="{rangeDataRatio * 4}"
mouseUp="showAnnotations = true; refreshAnnotations()" />
<mx:Canvas id="rightBox" backgroundColor="#FFFFFF" backgroundAlpha="0.5" width="0%" height="100%"
borderColor="#333333" borderThickness="1" borderStyle="solid" borderSides="top left" />
</mx:HDividedBox>
</mx:annotationElements>
</mx:AreaChart>
</mx:Canvas>
<mx:Canvas id="rangeSelectorContainer"
x="2" y="{rangeChartContainer.y + rangeChartContainer.height - 13}"
width="{dividedBox.width}" height="25" horizontalScrollPolicy="off">
<skins:GradientBox width="{rangeSelectorContainer.width - 30}" height="12" x="15" y="8"
gradientColors="[#EEEEEE, #999999]"
gradientAlphas="[.5, 0.75]"
gradientRatios="[0, 255]"
gradientAngle="90"
borderColor="#999999" borderStyle="solid" borderThickness="1"
/>
<mx:Button cornerRadius="2" width="14" height="12" x="0" y="8"
click="clickUpdate(-20)" icon="{leftScroll}" />
<mccune:HSlider id="slider" x="14" y="0" height="25" width="{rangeSelectorContainer.width - 28}"
trackHighlightSkin="com.dougmccune.skins.SliderThumbHighlightSkin"
trackSkin="{blankDividerClass}" showDataTip="false"
showTrackHighlight="true"
allowTrackClick="false" allowThumbOverlap="false"
change="updateBox()"
mouseDown="hideAnnotations()"
mouseUp="showAnnotations = true; refreshAnnotations();"
thumbCount="2"
liveDragging="true"
snapInterval="1"
values="{[leftIndicator.x, rightIndicator.x]}"
minimum="0" maximum="{rangeData.length}"
/>
<mx:Button cornerRadius="2" width="14" height="12" x="{slider.width + 13}" y="8"
click="clickUpdate(20)" icon="{rightScroll}" />
</mx:Canvas>
<mx:Canvas x="15" y="{rangeSelectorContainer.y + rangeSelectorContainer.height - 5}"
width="{rangeData.length}" height="25"
horizontalScrollPolicy="off" visible="false" includeInLayout="false" >
<mx:VRule id="leftIndicator" width="1" height="15" y="5" strokeColor="#000000" />
<mx:VRule id="rightIndicator" width="1" height="15" y="5" strokeColor="#000000" />
</mx:Canvas>
<mx:HBox horizontalGap="2" horizontalAlign="left" width="{CHART_WIDTH}" verticalAlign="middle" paddingTop="2">
<mx:Label text="Zoom" fontWeight="bold" />
<mx:LinkButton fontWeight="normal" label="5d"
width="25" height="16"
click="showAnnotations = false; moveSlider(leftIndicator, rightIndicator.x - 5, true);" />
<mx:LinkButton fontWeight="normal" label="1m"
width="25" height="16"
click="showAnnotations = false; moveSlider(leftIndicator, rightIndicator.x - 30, true);" />
<mx:LinkButton fontWeight="normal" label="3m"
width="25" height="16"
click="showAnnotations = false; moveSlider(leftIndicator, rightIndicator.x - 90, true);" />
<mx:LinkButton fontWeight="normal" label="6m"
width="25" height="16"
click="showAnnotations = false; moveSlider(leftIndicator, rightIndicator.x - 182, true);" />
<mx:LinkButton fontWeight="normal" label="Max"
width="30" height="16"
click="showAnnotations = false; moveSlider(leftIndicator, 0, true); moveSlider(rightIndicator, rangeData.length, true);" />
<mx:Spacer width="100%" />
<mx:HBox>
<mx:Label text="{_selectedDate}" />
<mx:Label text="{_selectedClose}" />
<mx:Label text="{_selectedVolume}" />
</mx:HBox>
</mx:HBox>
<mx:VBox x="{CHART_WIDTH + 50}" y="25"
width="300" height="{rangeSelectorContainer.y + rangeSelectorContainer.height - 25}" >
<mx:List id="annotationForm" width="300" dataProvider="{annotationItems}" labelField="letterLabel"
selectable="false" useRollOver="false" focusAlpha="0" selectionEasingFunction="myEasingFunction">
<mx:itemRenderer>
<mx:Component>
<mx:HBox>
<mx:Button width="17" height="17" paddingLeft="0" paddingTop="0" label="{data.letterLabel}"
toggle="true" selected="{data.selected}"
click="{outerDocument.hightlightAnnotation([data.annID, data.date]); data.selected = true;}" />
<mx:Text text="{data.date + ' - ' + data.description}" />
</mx:HBox>
</mx:Component>
</mx:itemRenderer>
</mx:List>
<mx:Form>
<mx:FormItem label="Date">
<mx:DateField id="annDate" showToday="true"
selectableRange="{{rangeStart: dateParse(rangeData.getItemAt(0).date), rangeEnd:dateParse(rangeData.getItemAt(rangeData.length - 1).date)}}" />
</mx:FormItem>
<mx:FormItem label="Description">
<mx:TextArea id="annDescription" />
</mx:FormItem>
<mx:FormItem>
<mx:Button label="Add Annotation"
click="addAnnotation(); annDate.selectedDate = null; annDescription.text = ''" />
</mx:FormItem>
</mx:Form>
</mx:VBox>
</mx:Canvas>
<mx:Array id="backgroundContents">
<mx:GridLines id="backgroundGrid" horizontalStroke="{new Stroke(0xCCCCCC, 1)}" verticalStroke="{new Stroke(0xCCCCCC, 1)}" />
</mx:Array>
<mx:HTTPService id="stockInfo" url="data/adobe_stock_info.xml" result="dataResult(event)" fault="faultResult(event)" resultFormat="object" />
<mx:DateFormatter id="fullDateFormat" formatString="YYYY-MM-DD" />
<mx:DateFormatter id="labelMonthFormatter" formatString="MM/YY" />
<mx:DateFormatter id="labelDayFormatter" formatString="MMM DD" />
<mx:DateFormatter id="labelDefaultFormatter" formatString="EEE MMM D" />
<mx:DateFormatter id="labelSummaryDateFormatter" formatString="EEE MMM DD, YYYY" />
<mx:NumberFormatter id="verticalAxisFormat" precision="1" />
<mx:NumberFormatter id="dollarFormatter" useNegativeSign="true" precision="2" />
<mx:NumberFormatter id="percentageFormatter" useNegativeSign="true" precision="2" />
<mx:NumberFormatter id="volumeFormatter" useThousandsSeparator="true"/>
<mx:Script>
<![CDATA[
import mx.graphics.SolidColor;
import mx.graphics.Stroke;
import mx.controls.Alert;
import mx.collections.ArrayCollection;
import mx.rpc.events.AbstractEvent;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import skins.GradientBox;
import mx.events.DividerEvent;
import mx.events.TweenEvent;
import mx.events.EffectEvent;
import mx.effects.Move;
import mx.effects.easing.Cubic;
import mx.events.SliderEvent;
import vo.AnnotationVO;
import annotations.EventAnnotation;
import mx.collections.Sort;
import mx.collections.SortField;
import mx.utils.UIDUtil;
[Bindable] private var MAIN_CHART_HEIGHT:Number = 350;
[Bindable] private var VOLUME_CHART_HEIGHT:Number = 50;
[Bindable] private var RANGE_CHART_HEIGHT:Number = 80;
[Bindable] private var CHART_WIDTH:Number = 600;
[Bindable] private var mainData:ArrayCollection = new ArrayCollection();
[Bindable] private var rangeData:ArrayCollection = new ArrayCollection();
private var staticLeftBoundary:Number;
private var staticRightBoundary:Number;
private var mouseXRef:Number;
private var rangeDrag:Boolean = false;
private var mainDrag:Boolean = false;
private var rangeDataRatio:Number = 1;
private var updateBoxFromSlider:Boolean = false;
private var allowUpdateComplete:Boolean = true;
[Embed(source="assets/divider.png")] [Bindable] public var dividerClass:Class;
[Embed(source="assets/blank.png")] [Bindable] public var blankDividerClass:Class;
[Embed(source="assets/left_scroll.png")] [Bindable] public var leftScroll:Class;
[Embed(source="assets/right_scroll.png")] [Bindable] public var rightScroll:Class;
[Bindable] private var _selectedDate:String;
[Bindable] private var _selectedClose:String;
[Bindable] private var _selectedVolume:String;
[Bindable] public var annotationItems:ArrayCollection = new ArrayCollection();
private var alphabet:Array = ['A','B','C','D','E','F','G','H','I','J','K',
'L','M','N','O','P','Q','R','S','T','U','V',
'W','X','Y','Z'];
public var showAnnotations:Boolean = true;
/**
* Application creationComplete
*/
private function createComplete():void
{
stockInfo.send();
}
/**
* Called when HTTPService call completes the data load of the XML chart info.
*/
private function dataResult(event:ResultEvent):void
{
var tmpData:ArrayCollection = event.result.root.data;
leftIndicator.x = tmpData.length - 100;
rightIndicator.x = tmpData.length;
rangeData = tmpData;
mainData = new ArrayCollection(rangeData.source);
rangeDataRatio = ((dividedBox.width - 30) / rangeData.length);
}
/**
* If an error occurs loading the XML chart info
*/
private function faultResult(event:FaultEvent):void
{
Alert.show("Error retrieving XML data", "Error");
}
/**
* Called from updateComplete on Main chart series... when data is completely loaded, we set the defaults for the sliders and divider
* boxes etc... filtered to only run once (allowUpdateComplete) when the application first loads
*/
private function seriesComplete():void
{
if(mainData.length > 0 && allowUpdateComplete)
{
allowUpdateComplete = false;
updateBoxFromSlider = true;
updateBox();
callLater(refreshAnnotations);
this.visible = true;
loadSampleAnnotations();
}
}
/**
* Simple parsing function to convert the date strings in our dataset to the equivalent Date object.
*/
private function dateParse(value:String):Date
{
var dateArray:Array = value.split('-');
return new Date(dateArray[0], dateArray[1] - 1, dateArray[2]);
}
/**
* Formats a date object from the DateTimeAxis into a label string
*/
private function formatDateLabel(value:Number, prevValue:Number, axis:DateTimeAxis):String
{
var dateValue:Date = new Date();
dateValue.setTime(value + ((dateValue.timezoneOffset + 60) * 60 * 1000));
switch(axis.labelUnits)
{
case "months":
return labelMonthFormatter.format(dateValue);
break;
case "days":
return labelDayFormatter.format(dateValue);
default:
return labelDefaultFormatter.format(dateValue);
break;
}
}
/**
* Called throughout use to update the mainData range of data that is displayed by slicing the
* range data to the left and right values.
*/
private function updateMainData():void
{
mainData.source = rangeData.source.slice(leftIndicator.x, rightIndicator.x);
refreshAnnotations();
chartMouseOut();
}
/**
* Called from the slider value changes. It is filtered to only change when the slider calling it
* directly. The updateBoxFromSlider value is set to false when the moveSlider function effect is
* playing because the box widths have already been set by the dividerRelease calling
* updateIndicatorValuesWithEffect.
*/
private function updateBox():void
{
if(updateBoxFromSlider)
{
leftBox.width = slider.values[0] * rangeDataRatio;
rightBox.width = dividedBox.width - ( slider.values[1] * rangeDataRatio );
leftIndicator.x = slider.values[0];
rightIndicator.x = slider.values[1];
updateMainData();
}
}
/**
* Updates the range by moving the entire range left or right by a fixed number of units
*/
private function clickUpdate(value:int):void
{
leftIndicator.x += value; rightIndicator.x += value;
slider.dispatchEvent(new SliderEvent('change'));
}
/**
* Called from the divided box dividerRelease. Calls a Move for the left and right Indicator
* x values which has an easing function
* applied.
*/
private function updateIndicatorValuesWithEffect():void
{
hideAnnotations();
moveSlider(leftIndicator, (leftBox.width / rangeDataRatio), false);
moveSlider(rightIndicator, ((dividedBox.width - rightBox.width) / rangeDataRatio), false);
}
/**
* Called from the thumbRelease on the slider instance, as well as creationComplete
* to set the initial range values.
* Updates the left and right indicator x values without the move effect.
*/
private function updateIndicatorsQuietly():void
{
leftIndicator.x = slider.values[0];
rightIndicator.x = slider.values[1];
}
/**
* Moves the left and right indicator x values with an easing transition applied. update
* dictates whether this should update the divided box range measurements (false if we're calling this
* from the divided box release) callbackFunc can be passed to get called when the move is finished.
*/
private function moveSlider(target:VRule, xTo:Number, update:Boolean, callbackFunc:Function = null, ... rest):void
{
var moveIndicator:Move = new Move();
moveIndicator.end();
moveIndicator.easingFunction = Cubic.easeOut;
moveIndicator.duration = 750;
moveIndicator.target = target;
moveIndicator.xTo = xTo;
moveIndicator.addEventListener(EffectEvent.EFFECT_START, function():void {updateBoxFromSlider = update});
moveIndicator.addEventListener(TweenEvent.TWEEN_UPDATE, function():void { mainData.source = rangeData.source.slice(leftIndicator.x, rightIndicator.x);
});
moveIndicator.addEventListener(EffectEvent.EFFECT_END, function():void {updateBoxFromSlider = true;
showAnnotations = true;
callLater(refreshAnnotations);
if(callbackFunc != null) callbackFunc.call(this, rest)});
moveIndicator.play();
}
/**
* Called from range chart or main chart and determines the position of the mouse as well as left
* and right indicators (for static comparison when moving) and adds systemManager events
* to capture mouse movement. The values set here are used in the moveChart function to calculate
* new position differences with start position
*/
private function setMouseDown(theChart:CartesianChart):void
{
if(!(leftIndicator.x == 0 && rightIndicator.x == rangeData.length))
{
hideAnnotations();
mouseXRef = this.mouseX;
staticLeftBoundary = leftIndicator.x;
staticRightBoundary = rightIndicator.x;
if(theChart == mainChart) mainDrag = true;
if(theChart == rangeChart) rangeDrag = true;
this.systemManager.addEventListener(MouseEvent.MOUSE_MOVE, moveChart);
this.systemManager.addEventListener(MouseEvent.MOUSE_UP, stopDragging);
}
}
/**
* Called when systemManager receives mouseUp event. Sets the indicators for which range is
* being dragged to false, and removes the system manager event listeners for drag movement.
*/
private function stopDragging(event:MouseEvent):void
{
if(mainData.length < 2)
{
if(leftIndicator.x == rangeData.length)
{
leftIndicator.x = rangeData.length - 5;
rightIndicator.x = rangeData.length;
}
else if(rightIndicator.x == 0)
{
leftIndicator.x = 0;
rightIndicator.x = 5;
}
updateBox();
}
rangeDrag = false;
mainDrag = false;
this.systemManager.removeEventListener(MouseEvent.MOUSE_MOVE, moveChart);
this.systemManager.removeEventListener(MouseEvent.MOUSE_UP, stopDragging);
}
/**
* Determines which chart instance is being dragged, and updates the left and right indicator x values
*/
private function moveChart(event:MouseEvent):void
{
if(mainDrag)
{
leftIndicator.x = staticLeftBoundary + (mouseXRef - this.mouseX) /
(mainChartArea.width / mainData.length);
rightIndicator.x = staticRightBoundary + (mouseXRef - this.mouseX) /
(mainChartArea.width / mainData.length);
}
else if(rangeDrag)
{
leftIndicator.x = staticLeftBoundary - (mouseXRef - this.mouseX) / rangeDataRatio;
rightIndicator.x = staticRightBoundary - (mouseXRef - this.mouseX) / rangeDataRatio;
}
}
/**
* Finds the DateTimeAxis value (the date) of the mouseover position
*/
private function getChartDataPoint():void
{
if(updateBoxFromSlider)
{
var chartPoint:Object = getChartCoordinates(new Point(mainChart.mouseX, mainChart.mouseY), mainChart);
var formattedDate:String = fullDateFormat.format(new Date(chartPoint.x));
for(var i:int = 0; i < mainData.length; i++)
{
var dataItem:Object = mainData.getItemAt(i);
if(dataItem.date == formattedDate)
{
_selectedDate = labelSummaryDateFormatter.format(dateParse(dataItem.date));
_selectedClose = 'Price: ' + dollarFormatter.format(Number(dataItem.close));
_selectedVolume = 'Vol: ' + volumeFormatter.format(Number(dataItem.volume));
mainChart.series[0].getChildAt(i + 1).showRenderer(true);
mainChartVolume.series[0].getChildAt(i).showRenderer(true);
}
else
{
mainChart.series[0].getChildAt(i + 1).showRenderer(false);
mainChartVolume.series[0].getChildAt(i).showRenderer(false);
}
}
}
}
/**
* Called when cursor is moved off of main chart area. Clears any values that are bound
* to mouseover position, and clears all
* LineSeriesCustomRenderers on the chart that are showing
*/
private function chartMouseOut():void
{
if(mainData.length > 2)
{
for(var i:int = 0; i < mainData.length; i++)
{
try
{
mainChart.series[0].getChildAt(i + 1).showRenderer(false);
mainChartVolume.series[0].getChildAt(i).showRenderer(false);
}
catch(e:Error) {};
}
_selectedDate = labelSummaryDateFormatter.format(dateParse(mainData.getItemAt(0).date)) + ' - ' +
labelSummaryDateFormatter.format(dateParse(mainData.getItemAt(mainData.length - 1).date));
_selectedClose = percentageFormatter.format((Number(mainData.getItemAt(mainData.length - 1).close) /
Number(mainData.getItemAt(0).close) - 1) * 100) + '%';
_selectedVolume = '';
}
else
{
_selectedDate = '';
_selectedClose = '';
_selectedVolume = '';
}
}
/**
* Finds the DateTimeAxis value (the date) of the mouseover position
* invertTransform takes a point in stage space (x and y coordinate) and transforms it into the
* relative point in data space, giving appropriate values along x axis (first item in return array),
* and y axis (second item in return array)
*/
private function getChartCoordinates(thePos:Point, theChart:CartesianChart):Object
{
var tmpArray:Array;
if(theChart.series[0] != null)
{
tmpArray = theChart.series[0].dataTransform.invertTransform(thePos.x, thePos.y);
return {x:tmpArray[0], y:tmpArray[1]};
}
else
{
return null;
}
}
/**
* Updates the date range display to reflect the current position of the divided box drag
*/
private function setDividerDragDate():void
{
var tmpLeftIndex:int = leftBox.width / rangeDataRatio;
var tmpRightIndex:int = ((dividedBox.width - rightBox.width) / rangeDataRatio) - 1;
if(tmpLeftIndex >= 0 && tmpRightIndex <= rangeData.length)
{
_selectedDate = labelSummaryDateFormatter.format(dateParse(rangeData.getItemAt(tmpLeftIndex).date)) + ' - ' +
labelSummaryDateFormatter.format(dateParse(rangeData.getItemAt(tmpRightIndex).date));
_selectedClose = percentageFormatter.format((Number(rangeData.getItemAt(tmpRightIndex).close) /
Number(rangeData.getItemAt(tmpLeftIndex).close) - 1) * 100) + '%';
_selectedVolume = '';
}
}
/**
* Prevents rollover or selection effects in the list control
*/
private function myEasingFunction(t:Number, b:Number, c:Number, d:Number):Number
{
return 0;
}
/**
* Called from form entry for new annotation. Creates an AnnotationVO instance with the form data, adds it to the array collection
* and then sorts to include new annotation date and reset labels correspondingly.
*/
private function addAnnotation():void
{
annotationItems.addItem(new AnnotationVO(UIDUtil.createUID(),
fullDateFormat.format(annDate.selectedDate),
annDescription.text,
false,
''));
var dateSort:Sort = new Sort();
dateSort.fields = [new SortField("date", false, true)];
dateSort.sort(annotationItems.source);
setAnnotationLabels();
annotationItems.refresh();
refreshAnnotations();
}
/**
* Whenever an annotation is added, this method is called to update the alphabetical letter
*/
private function setAnnotationLabels():void
{
for(var i:int = 0; i < annotationItems.length; i++)
{
AnnotationVO(annotationItems[i]).letterLabel = alphabet[i];
}
};
/**
* This method is called from list of annotations when an instance is clicked. It checks to see if the
* annotation date is within the currently viewed range. If not, it finds the appropriate index
* value of the range data to expand to, calls the moveSlider function to move left or right to fit the range
* and after the movement is completed, this function is re-called by the moveSlider end to check again.
* If the item is within the viewable range, we loop through the array of anntations and sets selected to true
* on the target annotation, and selected to false on all other annotations. refreshAnnotations is then
* called to update the annotation flags with the selected value
*/
public function hightlightAnnotation(args:Array):void
{
var targetUID:String = args[0];
var targetDate:String = args[1];
var i:int;
var targetIndex:int;
if(targetDate < mainData.getItemAt(0).date)
{
for(i = 0; i < rangeData.length; i++)
{
if(rangeData.getItemAt(i).date == targetDate)
{
targetIndex = (i >= 10) ? i - 10 : 0;
break;
}
}
showAnnotations = false;
moveSlider(leftIndicator, targetIndex, true, hightlightAnnotation, targetUID, targetDate);
}
else if(targetDate > mainData.getItemAt(mainData.length - 1).date)
{
for(i = 0; i < rangeData.length; i++)
{
if(rangeData.getItemAt(i).date == targetDate)
{
targetIndex = (i <= (rangeData.length - 11)) ? i + 10 : 0;
break;
}
}
showAnnotations = false;
moveSlider(rightIndicator, targetIndex, true, hightlightAnnotation, targetUID, targetDate);
}
else
{
for each(var annListItem:AnnotationVO in annotationItems)
{
if(annListItem.annID == targetUID)
annListItem.selected = true;
else
annListItem.selected = false;
}
annotationItems.refresh();
refreshAnnotations();
}
}
/**
* Loops through the array of annotation objects and sets selected on the target annotation.
* refreshAnnotations is then called to update the annotation flags with the selected value
*/
private function highlightListAnnotation(event:Event):void
{
for each(var annListItem:AnnotationVO in annotationItems)
{
if(annListItem.annID == event.target.annID)
annListItem.selected = true;
else
annListItem.selected = false;
}
refreshAnnotations();
}
/**
* Loops through the array of annotation objects and looks for the data array item in the main chart
* which matches this annotation's date. When found, an EventAnnotation object is created and added to
* the annotationCanvas (on annotationElements property of mainChart) and positioned to the AreaSeries
* renderer instance for the datapoint on the chart.
*/
private function refreshAnnotations():void
{
if(mainData.length > 0)
{
annotationCanvas.removeAllChildren();
if(showAnnotations)
{
var rangeStart:String = mainData.getItemAt(0).date;
var rangeEnd:String = mainData.getItemAt(mainData.length - 1).date;
for each(var annInstance:AnnotationVO in annotationItems)
{
if(annInstance.date > rangeStart && annInstance.date < rangeEnd)
{
for(var i:int = 0; i < mainData.length; i++)
{
var dataItem:Object = mainData.getItemAt(i);
if(dataItem.date == annInstance.date && mainChart.series[0].numChildren >= i + 1)
{
var newAnn:EventAnnotation = new EventAnnotation();
newAnn.letterLabel = annInstance.letterLabel;
newAnn.annID = annInstance.annID;
newAnn.selected = annInstance.selected;
var foundItem:Object = mainChart.series[0].getChildAt(i + 1);
newAnn.x = foundItem.x + 2;
newAnn.y = foundItem.y -32;
newAnn.addEventListener("annotationClicked", highlightListAnnotation);
annotationCanvas.addChild(newAnn);
}
}
}
}
}
}
}
/**
* removes all annotations showing on annotationCanvas and sets the showAnnotations flag to false.
* called when the chart range is being changed by dragging the main or range chart, the divider
* being released and the indicator moveSlider function is called.
*/
private function hideAnnotations():void
{
showAnnotations = false;
annotationCanvas.removeAllChildren();
}
/**
* Sample annotations
*/
private function loadSampleAnnotations():void
{
annotationItems.addItem(new AnnotationVO(UIDUtil.createUID(), '2007-03-15', 'Test Item 1', false, ''));
annotationItems.addItem(new AnnotationVO(UIDUtil.createUID(), '2007-01-03', 'Test Item 2', false, ''));
annotationItems.addItem(new AnnotationVO(UIDUtil.createUID(), '2006-11-29', 'Test Item 3', false, ''));
annotationItems.addItem(new AnnotationVO(UIDUtil.createUID(), '2006-10-19', 'Test Item 4', false, ''));
annotationItems.addItem(new AnnotationVO(UIDUtil.createUID(), '2006-09-12', 'Test Item 5', false, ''));
annotationItems.addItem(new AnnotationVO(UIDUtil.createUID(), '2006-08-08', 'Test Item 6', false, ''));
annotationItems.addItem(new AnnotationVO(UIDUtil.createUID(), '2006-07-14', 'Test Item 7', false, ''));
setAnnotationLabels();
}
]]>
</mx:Script>
</mx:Application>
|