1005 lines
26 KiB
ActionScript
Executable File
1005 lines
26 KiB
ActionScript
Executable File
/*
|
|
Copyright (c) 2009 Yahoo! Inc. All rights reserved.
|
|
The copyrights embodied in the content of this file are licensed under the BSD (revised) open source license
|
|
*/
|
|
package com.yahoo.astra.fl.charts.axes
|
|
{
|
|
import com.yahoo.astra.fl.charts.series.ISeries;
|
|
import com.yahoo.astra.fl.charts.CartesianChart;
|
|
import com.yahoo.astra.utils.DateUtil;
|
|
import com.yahoo.astra.utils.TimeUnit;
|
|
import fl.core.UIComponent;
|
|
import flash.text.TextFormat;
|
|
|
|
/**
|
|
* An axis type representing a date and time range from minimum to maximum
|
|
* with major and minor divisions.
|
|
*
|
|
* @author Josh Tynjala
|
|
*/
|
|
public class TimeAxis extends BaseAxis implements IAxis, IStackingAxis
|
|
{
|
|
|
|
//--------------------------------------
|
|
// Static Properties
|
|
//--------------------------------------
|
|
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
private static const TIME_UNITS:Array = [TimeUnit.MILLISECONDS, TimeUnit.SECONDS, TimeUnit.MINUTES, TimeUnit.HOURS, TimeUnit.DAY, TimeUnit.MONTH, TimeUnit.YEAR];
|
|
|
|
//--------------------------------------
|
|
// Constructor
|
|
//--------------------------------------
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
public function TimeAxis()
|
|
{
|
|
super();
|
|
}
|
|
|
|
//--------------------------------------
|
|
// Properties
|
|
//--------------------------------------
|
|
|
|
protected var positionMultiplier:Number = 0;
|
|
|
|
/**
|
|
* @private
|
|
* Storage for the minimum value.
|
|
*/
|
|
private var _minimum:Date;
|
|
|
|
/**
|
|
* @private
|
|
* Indicates whether the minimum bound is user-defined or generated by the axis.
|
|
*/
|
|
private var _minimumSetByUser:Boolean = false;
|
|
|
|
/**
|
|
* The minimum value displayed on the axis.
|
|
*/
|
|
public function get minimum():Date
|
|
{
|
|
return this._minimum;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
public function set minimum(value:Date):void
|
|
{
|
|
this._minimum = value;
|
|
this._minimumSetByUser = value != null;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Storage for the maximum value.
|
|
*/
|
|
private var _maximum:Date;
|
|
|
|
/**
|
|
* @private
|
|
* Indicates whether the maximum bound is user-defined or generated by the axis.
|
|
*/
|
|
private var _maximumSetByUser:Boolean = false;
|
|
|
|
/**
|
|
* The maximum value displayed on the axis.
|
|
*/
|
|
public function get maximum():Date
|
|
{
|
|
return this._maximum;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
public function set maximum(value:Date):void
|
|
{
|
|
this._maximum = value;
|
|
this._maximumSetByUser = value != null;
|
|
}
|
|
|
|
//-- Units
|
|
|
|
/**
|
|
* @private
|
|
* Storage for the major unit.
|
|
*/
|
|
private var _majorUnit:int = 1;
|
|
|
|
/**
|
|
* @private
|
|
* Indicates whether the major unit is user-defined or generated by the axis.
|
|
*/
|
|
private var _majorUnitSetByUser:Boolean = false;
|
|
|
|
/**
|
|
* The major unit at which new lines are drawn.
|
|
*/
|
|
public function get majorUnit():Number
|
|
{
|
|
return this._majorUnit;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
public function set majorUnit(value:Number):void
|
|
{
|
|
this._majorUnit = value;
|
|
this._majorUnitSetByUser = !isNaN(value);
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Storage for the majorTimeUnit property.
|
|
*/
|
|
private var _majorTimeUnit:String = TimeUnit.MONTH;
|
|
|
|
/**
|
|
* @private
|
|
* Indicates whether the major time unit is user-defined or generated by the axis.
|
|
*/
|
|
private var _majorTimeUnitSetByUser:Boolean = false;
|
|
|
|
/**
|
|
* Combined with majorUnit, determines the amount of time between major ticks and labels.
|
|
*
|
|
* @see com.yahoo.astra.fl.charts.TimeUnit;
|
|
*/
|
|
public function get majorTimeUnit():String
|
|
{
|
|
return this._majorTimeUnit;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
public function set majorTimeUnit(value:String):void
|
|
{
|
|
this._majorTimeUnit = value;
|
|
this._majorTimeUnitSetByUser = value != null;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Storage for the minor unit.
|
|
*/
|
|
private var _minorUnit:int = 1;
|
|
|
|
/**
|
|
* @private
|
|
* Indicates whether the minor unit is user-defined or generated by the axis.
|
|
*/
|
|
private var _minorUnitSetByUser:Boolean = false;
|
|
|
|
/**
|
|
* The minor unit at which new lines are drawn.
|
|
*/
|
|
public function get minorUnit():Number
|
|
{
|
|
return this._minorUnit;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
public function set minorUnit(value:Number):void
|
|
{
|
|
this._minorUnit = value;
|
|
this._minorUnitSetByUser = !isNaN(value);
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Storage for the minorTimeUnit property.
|
|
*/
|
|
private var _minorTimeUnit:String = TimeUnit.MONTH;
|
|
|
|
/**
|
|
* @private
|
|
* Indicates whether the minor time unit is user-defined or generated by the axis.
|
|
*/
|
|
private var _minorTimeUnitSetByUser:Boolean = false;
|
|
|
|
/**
|
|
* Combined with minorUnit, determines the amount of time between minor ticks.
|
|
*
|
|
* @see com.yahoo.astra.fl.charts.TimeUnit;
|
|
*/
|
|
public function get minorTimeUnit():String
|
|
{
|
|
return this._minorTimeUnit;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
public function set minorTimeUnit(value:String):void
|
|
{
|
|
this._minorTimeUnit = value;
|
|
this._minorTimeUnitSetByUser = value != null;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Storage for the stackingEnabled property.
|
|
*/
|
|
private var _stackingEnabled:Boolean = false;
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function get stackingEnabled():Boolean
|
|
{
|
|
return this._stackingEnabled;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
public function set stackingEnabled(value:Boolean):void
|
|
{
|
|
this._stackingEnabled = value;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Storage for the snapToUnits property.
|
|
*/
|
|
private var _snapToUnits:Boolean = true;
|
|
|
|
/**
|
|
* If true, the labels, ticks, gridlines, and other objects will snap to
|
|
* the nearest major or minor unit. If false, their position will be based
|
|
* on the minimum value.
|
|
*/
|
|
public function get snapToUnits():Boolean
|
|
{
|
|
return this._snapToUnits;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
public function set snapToUnits(value:Boolean):void
|
|
{
|
|
this._snapToUnits = value;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
private var _dataMinimum:Date;
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
private var _dataMaximum:Date;
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
private var _numLabels:Number;
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
private var _numLabelsSetByUser:Boolean = false;
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function get numLabels():Number
|
|
{
|
|
return _numLabels;
|
|
}
|
|
|
|
/**
|
|
* @private (setter)
|
|
*/
|
|
public function set numLabels(value:Number):void
|
|
{
|
|
if(_numLabelsSetByUser) return;
|
|
_numLabels = value;
|
|
_numLabelsSetByUser = true;
|
|
_majorUnitSetByUser = false;
|
|
_minorUnitSetByUser = false;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Holds value for idealPixels
|
|
*/
|
|
private var _idealPixels:Number = 60;
|
|
|
|
/**
|
|
* Desired distance between majorUnits. Used to calculate the major unit
|
|
* when unspecified and <code>calculateByLabelSize</code> is set to false.
|
|
*/
|
|
public function get idealPixels():Number
|
|
{
|
|
return _idealPixels;
|
|
}
|
|
|
|
/**
|
|
* @private (setter)
|
|
*/
|
|
public function set idealPixels(value:Number):void
|
|
{
|
|
_idealPixels = value;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Holds value for calculateByLabelSize
|
|
*/
|
|
private var _calculateByLabelSize:Boolean = false;
|
|
|
|
/**
|
|
* Indicates whether to use the maximum size of an axis label
|
|
* when calculating the majorUnit.
|
|
*/
|
|
public function get calculateByLabelSize():Boolean
|
|
{
|
|
return _calculateByLabelSize;
|
|
}
|
|
|
|
/**
|
|
* @private (setter)
|
|
*/
|
|
public function set calculateByLabelSize(value:Boolean):void
|
|
{
|
|
_calculateByLabelSize = value;
|
|
}
|
|
|
|
//--------------------------------------
|
|
// Public Methods
|
|
//--------------------------------------
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function stack(top:Object, ...rest:Array):Object
|
|
{
|
|
var value:Number = this.valueToNumber(top);
|
|
var restCount:int = rest.length;
|
|
for(var i:int = 0; i < restCount; i++)
|
|
{
|
|
value += this.valueToNumber(rest[i]);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function updateScale():void
|
|
{
|
|
this.resetScale();
|
|
this.calculatePositionMultiplier();
|
|
|
|
(this.renderer as ICartesianAxisRenderer).majorUnitSetByUser = this._majorUnitSetByUser;
|
|
this.renderer.ticks = this.createAxisData(this.majorUnit, this.majorTimeUnit);
|
|
this.renderer.minorTicks = this.createAxisData(this.minorUnit, this.minorTimeUnit, false);
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function valueToLocal(value:Object):Number
|
|
{
|
|
var numericValue:Number = this.valueToNumber(value);
|
|
|
|
var position:Number = (numericValue - this.minimum.valueOf()) * this.positionMultiplier;
|
|
if(this.reverse)
|
|
{
|
|
position = this.renderer.length - position;
|
|
}
|
|
|
|
//the vertical axis has its origin on the bottom
|
|
if(this.renderer is ICartesianAxisRenderer && ICartesianAxisRenderer(this.renderer).orientation == AxisOrientation.VERTICAL)
|
|
{
|
|
position = this.renderer.length - position;
|
|
}
|
|
|
|
return position;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
override public function valueToLabel(value:Object):String
|
|
{
|
|
var text:String = value.toString();
|
|
if(this.labelFunction != null)
|
|
{
|
|
var numericValue:Number = this.valueToNumber(value);
|
|
text = this.labelFunction(new Date(numericValue), this.majorTimeUnit);
|
|
}
|
|
if(text == null)
|
|
{
|
|
text = "";
|
|
}
|
|
return text;
|
|
}
|
|
|
|
//--------------------------------------
|
|
// Protected Methods
|
|
//--------------------------------------
|
|
|
|
/**
|
|
* @private
|
|
* Converts one of the accepted values to a Number that can be
|
|
* used for position calculation.
|
|
*/
|
|
protected function valueToNumber(value:Object):Number
|
|
{
|
|
var convertedValue:Number = 0;
|
|
if(value is Date)
|
|
{
|
|
convertedValue = (value as Date).valueOf();
|
|
}
|
|
else if(!(value is Number))
|
|
{
|
|
convertedValue = new Date(value.toString()).valueOf();
|
|
}
|
|
else
|
|
{
|
|
convertedValue = value as Number;
|
|
}
|
|
return convertedValue;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Calculates the best scale.
|
|
*/
|
|
protected function resetScale():void
|
|
{
|
|
if(!this._minimumSetByUser)
|
|
{
|
|
this._minimum = new Date(this._dataMinimum.valueOf());
|
|
}
|
|
|
|
if(!this._maximumSetByUser)
|
|
{
|
|
this._maximum = new Date(this._dataMaximum.valueOf());
|
|
}
|
|
|
|
this.checkMinLessThanMax();
|
|
this.calculateMajorUnit();
|
|
this.calculateMinorUnit();
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Generates AxisData objects for use by the axis renderer.
|
|
*/
|
|
protected function createAxisData(unit:Number, timeUnit:String, isMajorUnit:Boolean = true):Array
|
|
{
|
|
if(unit <= 0)
|
|
{
|
|
return [];
|
|
}
|
|
|
|
var data:Array = [];
|
|
var displayedMaximum:Boolean = false;
|
|
var displayedMinimum:Boolean = false;
|
|
var date:Date = new Date(this.minimum.valueOf());
|
|
var itemCount:int = 0;
|
|
while(date.valueOf() <= this.maximum.valueOf())
|
|
{
|
|
date = new Date(this.minimum.valueOf());
|
|
if(itemCount > 0)
|
|
{
|
|
var unitValue:Number = itemCount * unit;
|
|
date = this.updateDate(date, timeUnit, unitValue, this.snapToUnits);
|
|
}
|
|
|
|
//stop at the maximum value.
|
|
if(date.valueOf() > this.maximum.valueOf())
|
|
{
|
|
if(!this._majorUnitSetByUser && this.calculateByLabelSize) break;
|
|
date = new Date(this.maximum.valueOf());
|
|
}
|
|
//because Flash UIComponents round the position to the nearest pixel, we need to do the same.
|
|
var position:Number = Math.round(this.valueToLocal(date));
|
|
var label:String = this.valueToLabel(date);
|
|
var axisData:AxisData = new AxisData(position, date, label);
|
|
data.push(axisData);
|
|
itemCount++;
|
|
|
|
if(date.valueOf() == this.maximum.valueOf())
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function getMaxLabel():String
|
|
{
|
|
var maxAbbrevDate:String = this.valueToLabel(new Date(2008, 4, 30));
|
|
var maxFullDate:String = this.valueToLabel(new Date(2009, 8, 30));
|
|
var maxDate:String = maxAbbrevDate.length > maxFullDate.length ? maxAbbrevDate : maxFullDate;
|
|
return maxDate;
|
|
}
|
|
|
|
//--------------------------------------
|
|
// Private Methods
|
|
//--------------------------------------
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
private function updateDate(date:Date, timeUnit:String, unitValue:Number, snapToUnits:Boolean):Date
|
|
{
|
|
switch(timeUnit)
|
|
{
|
|
case TimeUnit.YEAR:
|
|
date.fullYear += unitValue;
|
|
if(snapToUnits)
|
|
{
|
|
date.month = 0;
|
|
date.date = 1;
|
|
date.hours = 0;
|
|
date.minutes = 0;
|
|
date.seconds = 0;
|
|
date.milliseconds = 0;
|
|
}
|
|
break;
|
|
case TimeUnit.MONTH:
|
|
date.month += unitValue;
|
|
if(snapToUnits)
|
|
{
|
|
date.date = 1;
|
|
date.hours = 0;
|
|
date.minutes = 0;
|
|
date.seconds = 0;
|
|
date.milliseconds = 0;
|
|
}
|
|
break;
|
|
case TimeUnit.DAY:
|
|
date.date += unitValue;
|
|
if(snapToUnits)
|
|
{
|
|
date.hours = 0;
|
|
date.minutes = 0;
|
|
date.seconds = 0;
|
|
date.milliseconds = 0;
|
|
}
|
|
break;
|
|
case TimeUnit.HOURS:
|
|
date.hours += unitValue;
|
|
if(snapToUnits)
|
|
{
|
|
date.minutes = 0;
|
|
date.seconds = 0;
|
|
date.milliseconds = 0;
|
|
}
|
|
break;
|
|
case TimeUnit.MINUTES:
|
|
date.minutes += unitValue;
|
|
if(snapToUnits)
|
|
{
|
|
date.seconds = 0;
|
|
date.milliseconds = 0;
|
|
}
|
|
break;
|
|
case TimeUnit.SECONDS:
|
|
date.seconds += unitValue;
|
|
if(snapToUnits)
|
|
{
|
|
date.milliseconds = 0;
|
|
}
|
|
break;
|
|
case TimeUnit.MILLISECONDS:
|
|
date.milliseconds += unitValue;
|
|
break;
|
|
}
|
|
return date;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Swaps the minimum and maximum values, if needed.
|
|
*/
|
|
private function checkMinLessThanMax():void
|
|
{
|
|
if(this._minimum.valueOf() > this._maximum.valueOf())
|
|
{
|
|
var temp:Date = this._minimum;
|
|
this._minimum = this._maximum;
|
|
this._maximum = temp;
|
|
|
|
//be sure to swap these flags too!
|
|
var temp2:Boolean = this._minimumSetByUser;
|
|
this._minimumSetByUser = this._maximumSetByUser;
|
|
this._maximumSetByUser = temp2;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Determines the best major unit.
|
|
*/
|
|
private function calculateMajorUnit():void
|
|
{
|
|
if(!this._majorTimeUnitSetByUser)
|
|
{
|
|
//ballpark it
|
|
var dayCount:Number = DateUtil.countDays(this.minimum, this.maximum);
|
|
var yearCount:Number = DateUtil.getDateDifferenceByTimeUnit(this.minimum, this.maximum, TimeUnit.YEAR);
|
|
var monthCount:Number = DateUtil.getDateDifferenceByTimeUnit(this.minimum, this.maximum, TimeUnit.MONTH);
|
|
var hourCount:Number = dayCount * 24;
|
|
var minuteCount:Number = hourCount * 60;
|
|
var secondCount:Number = minuteCount * 60;
|
|
|
|
if(yearCount >= 1) this._majorTimeUnit = TimeUnit.YEAR;
|
|
else if(monthCount >= 1) this._majorTimeUnit = TimeUnit.MONTH;
|
|
else if(dayCount >= 1) this._majorTimeUnit = TimeUnit.DAY;
|
|
else if(hourCount >= 1) this._majorTimeUnit = TimeUnit.HOURS;
|
|
else if(minuteCount >= 1) this.majorTimeUnit = TimeUnit.MINUTES;
|
|
else if(secondCount >= 1) this.majorTimeUnit = TimeUnit.SECONDS;
|
|
else this.majorTimeUnit = TimeUnit.MILLISECONDS;
|
|
}
|
|
|
|
if(this._majorUnitSetByUser)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.calculateMaximumAndMinimum();
|
|
|
|
var chart:CartesianChart = this.chart as CartesianChart;
|
|
var labelSpacing:Number = 0;
|
|
var overflow:Number = 0;
|
|
var approxLabelDistance:Number = this.idealPixels;
|
|
if(this.calculateByLabelSize)
|
|
{
|
|
var rotation:Number;
|
|
//Check to see if this axis is horizontal. Since the width of labels will be variable, we will need to apply a different alogrithm to determine the majorUnit.
|
|
if(chart.horizontalAxis == this)
|
|
{
|
|
//extract the approximate width of the labels by getting the textWidth of the maximum date when rendered by the label function with the textFormat of the renderer.
|
|
approxLabelDistance = this.maxLabelWidth;
|
|
rotation = chart.getHorizontalAxisStyle("rotation") as Number;
|
|
if(rotation >= 0)
|
|
{
|
|
if(!isNaN(chart.horizontalAxisLabelData.rightLabelOffset)) overflow += chart.horizontalAxisLabelData.rightLabelOffset as Number;
|
|
}
|
|
if(rotation <= 0)
|
|
{
|
|
if(!isNaN(chart.horizontalAxisLabelData.leftLabelOffset)) overflow += chart.horizontalAxisLabelData.leftLabelOffset as Number;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
approxLabelDistance = this.maxLabelHeight;
|
|
rotation = chart.getVerticalAxisStyle("rotation") as Number;
|
|
if(!isNaN(chart.verticalAxisLabelData.topLabelOffset)) overflow = chart.verticalAxisLabelData.topLabelOffset as Number;
|
|
}
|
|
labelSpacing = this.labelSpacing;
|
|
approxLabelDistance += (labelSpacing*2);
|
|
}
|
|
|
|
var dateDifference:Number = Math.round(DateUtil.getDateDifferenceByTimeUnit(this.minimum, this.maximum, this.majorTimeUnit));
|
|
var tempMajorUnit:Number = 0;
|
|
|
|
var maxLabels:Number = Math.floor(((this.renderer.length + overflow) - labelSpacing)/approxLabelDistance);
|
|
|
|
//If set by user, use specified number of labels unless its too many
|
|
if(this._numLabelsSetByUser)
|
|
{
|
|
maxLabels = Math.min(maxLabels, this.numLabels);
|
|
}
|
|
|
|
tempMajorUnit = dateDifference/maxLabels;
|
|
tempMajorUnit = Math.ceil(tempMajorUnit);
|
|
|
|
if(tempMajorUnit > Math.round(dateDifference/2)) tempMajorUnit = dateDifference;
|
|
this._majorUnit = tempMajorUnit;
|
|
|
|
if(dateDifference%tempMajorUnit != 0 && this.calculateByLabelSize)
|
|
{
|
|
var len:Number = Math.min(tempMajorUnit, ((dateDifference/2)-tempMajorUnit));
|
|
for(var i:int = 0;i < len; i++)
|
|
{
|
|
tempMajorUnit++;
|
|
if(dateDifference%tempMajorUnit == 0)
|
|
{
|
|
this._majorUnit = tempMajorUnit;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Determines the best minor unit.
|
|
*/
|
|
private function calculateMinorUnit():void
|
|
{
|
|
if(!this._minorTimeUnitSetByUser)
|
|
{
|
|
//if the numeric part of the major unit is 1, we want to move
|
|
//the time part of the minor unit to a interval lower than the major.
|
|
//...unless the user has set the minor unit. this is a weird case
|
|
//that shouldn't happen, but it might.
|
|
//in that case, we go with the standard behavior where major unit
|
|
//and minor unit are the same.
|
|
if(!this._minorUnitSetByUser && this._majorUnit == 1)
|
|
{
|
|
var index:int = TIME_UNITS.indexOf(this._majorTimeUnit);
|
|
if(index > 0) this._minorTimeUnit = TIME_UNITS[index - 1];
|
|
}
|
|
else this._minorTimeUnit = this._majorTimeUnit;
|
|
}
|
|
|
|
if(this._minorUnitSetByUser)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if(this.majorTimeUnit == this.minorTimeUnit && this._majorUnit != 1)
|
|
{
|
|
if(this._majorUnit % 2 == 0)
|
|
{
|
|
this._minorUnit = this._majorUnit / 2;
|
|
}
|
|
else if(this._majorUnit % 3 == 0)
|
|
{
|
|
this._minorUnit = this._majorUnit / 3;
|
|
}
|
|
else this._minorUnit = 0;
|
|
}
|
|
else
|
|
{
|
|
//in this case, we know that the time portion of the minor
|
|
//unit is a smaller interval than the major unit.
|
|
switch(this._minorTimeUnit)
|
|
{
|
|
case TimeUnit.MONTH:
|
|
this._minorUnit = 6;
|
|
break;
|
|
|
|
//no perfect half-way point for number of days in a month
|
|
//so use the default of zero
|
|
/*case TimeUnit.DAY:
|
|
break;*/
|
|
|
|
case TimeUnit.HOURS:
|
|
this._minorUnit = 12;
|
|
break;
|
|
|
|
case TimeUnit.MINUTES:
|
|
this._minorUnit = 30;
|
|
break;
|
|
|
|
case TimeUnit.SECONDS:
|
|
this._minorUnit = 30;
|
|
break;
|
|
|
|
default:
|
|
this._minorUnit = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Determines the best time unit.
|
|
*/
|
|
private function calculateTimeUnitSize(timeUnit:String):Number
|
|
{
|
|
switch(timeUnit)
|
|
{
|
|
case TimeUnit.YEAR:
|
|
var year:Date = new Date(1970, 11, 31, 16);
|
|
return year.valueOf();
|
|
|
|
case TimeUnit.MONTH:
|
|
var month:Date = new Date(1970, 0, 31, 16);
|
|
return month.valueOf();
|
|
|
|
case TimeUnit.DAY:
|
|
var day:Date = new Date(1970, 0, 1, 16);
|
|
return day.valueOf();
|
|
|
|
case TimeUnit.HOURS:
|
|
var hour:Date = new Date(1969, 11, 31, 17);
|
|
return hour.valueOf();
|
|
|
|
case TimeUnit.MINUTES:
|
|
var minute:Date = new Date(1969, 11, 31, 16, 1);
|
|
return minute.valueOf();
|
|
|
|
case TimeUnit.SECONDS:
|
|
var second:Date = new Date(1969, 11, 31, 16, 0, 1);
|
|
return second.valueOf();
|
|
|
|
default: //millisecond
|
|
return 1;
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Using the major time unit, and the current minimum and maximum, generate
|
|
* the ideal minimum and maximum.
|
|
*/
|
|
private function calculateMaximumAndMinimum():void
|
|
{
|
|
switch(this.majorTimeUnit)
|
|
{
|
|
case TimeUnit.YEAR:
|
|
{
|
|
if(!this._minimumSetByUser)
|
|
{
|
|
this._minimum = new Date(this._minimum.fullYear, 0);
|
|
}
|
|
|
|
if(!this._maximumSetByUser)
|
|
{
|
|
var beginningOfYear:Date = new Date(this._maximum.fullYear, 0);
|
|
//don't change the maximum if it is the exact beginning of the year
|
|
if(beginningOfYear.valueOf() != this._maximum.valueOf())
|
|
this._maximum = new Date(this._maximum.fullYear + 1, 0);
|
|
}
|
|
break;
|
|
}
|
|
case TimeUnit.MONTH:
|
|
{
|
|
if(!this._minimumSetByUser)
|
|
this._minimum = new Date(this._minimum.fullYear, this._minimum.month);
|
|
|
|
if(!this._maximumSetByUser)
|
|
{
|
|
var beginningOfMonth:Date = new Date(this._maximum.fullYear, this._maximum.month);
|
|
//don't change the maximum if it is the exact beginning of the month
|
|
if(beginningOfMonth.valueOf() != this._maximum.valueOf())
|
|
this._maximum = new Date(this._maximum.fullYear, this._maximum.month + 1);
|
|
}
|
|
break;
|
|
}
|
|
case TimeUnit.DAY:
|
|
{
|
|
if(!this._minimumSetByUser)
|
|
this._minimum = new Date(this._minimum.fullYear, this._minimum.month, this._minimum.date);
|
|
|
|
if(!this._maximumSetByUser)
|
|
{
|
|
var beginningOfDay:Date = new Date(this._maximum.fullYear, this._maximum.month, this._maximum.date);
|
|
//don't change the maximum if it is the exact beginning of the day
|
|
if(beginningOfDay.valueOf() != this._maximum.valueOf())
|
|
this._maximum = new Date(this._maximum.fullYear, this._maximum.month, this._maximum.date + 1);
|
|
}
|
|
break;
|
|
}
|
|
case TimeUnit.HOURS:
|
|
{
|
|
if(!this._minimumSetByUser)
|
|
this._minimum = new Date(this._minimum.fullYear, this._minimum.month, this._minimum.date, this._minimum.hours);
|
|
|
|
if(!this._maximumSetByUser)
|
|
{
|
|
var beginningOfHour:Date = new Date(this._maximum.fullYear, this._maximum.month, this._maximum.date, this._maximum.hours);
|
|
//don't change the maximum if it is the exact beginning of the day
|
|
if(beginningOfHour.valueOf() != this._maximum.valueOf())
|
|
this._maximum = new Date(this._maximum.fullYear, this._maximum.month, this._maximum.date, this._maximum.hours + 1);
|
|
}
|
|
break;
|
|
}
|
|
case TimeUnit.MINUTES:
|
|
{
|
|
if(!this._minimumSetByUser)
|
|
this._minimum = new Date(this._minimum.fullYear, this._minimum.month, this._minimum.date, this._minimum.hours, this._minimum.minutes);
|
|
|
|
if(!this._maximumSetByUser)
|
|
{
|
|
var beginningOfMinute:Date = new Date(this._maximum.fullYear, this._maximum.month, this._maximum.date, this._maximum.hours, this._maximum.minutes);
|
|
//don't change the maximum if it is the exact beginning of the day
|
|
if(beginningOfMinute.valueOf() != this._maximum.valueOf())
|
|
this._maximum = new Date(this._maximum.fullYear, this._maximum.month, this._maximum.date, this._maximum.hours, this._maximum.minutes + 1);
|
|
}
|
|
break;
|
|
}
|
|
case TimeUnit.SECONDS:
|
|
{
|
|
if(!this._minimumSetByUser)
|
|
this._minimum = new Date(this._minimum.fullYear, this._minimum.month, this._minimum.date, this._minimum.hours, this._minimum.minutes, this._minimum.seconds);
|
|
|
|
if(!this._maximumSetByUser)
|
|
{
|
|
var beginningOfSecond:Date = new Date(this._maximum.fullYear, this._maximum.month, this._maximum.date, this._maximum.hours, this._maximum.minutes, this._maximum.seconds);
|
|
//don't change the maximum if it is the exact beginning of the day
|
|
if(beginningOfSecond.valueOf() != this._maximum.valueOf())
|
|
this._maximum = new Date(this._maximum.fullYear, this._maximum.month, this._maximum.date, this._maximum.hours, this._maximum.minutes, this._maximum.seconds + 1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
* Calculates the multiplier used to convert a data point to an actual position
|
|
* on the axis.
|
|
*/
|
|
private function calculatePositionMultiplier():void
|
|
{
|
|
var range:Number = this.maximum.valueOf() - this.minimum.valueOf();
|
|
if(range == 0)
|
|
{
|
|
this.positionMultiplier = 0;
|
|
return;
|
|
}
|
|
this.positionMultiplier = this.renderer.length / range;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
override protected function parseDataProvider():void
|
|
{
|
|
var seriesCount:int = this.dataProvider.length;
|
|
var min:Number = NaN;
|
|
var max:Number = NaN;
|
|
for(var i:int = 0; i < seriesCount; i++)
|
|
{
|
|
var series:ISeries = ISeries(this.dataProvider[i]);
|
|
var seriesLength:int = series.length;
|
|
for(var j:int = 0; j < seriesLength; j++)
|
|
{
|
|
var item:Object = series.dataProvider[j];
|
|
var value:Object = this.chart.itemToAxisValue(series, j, this);
|
|
var numericValue:Number = this.valueToNumber(value);
|
|
|
|
if(isNaN(min))
|
|
{
|
|
min = numericValue;
|
|
}
|
|
else
|
|
{
|
|
min = Math.min(min, numericValue);
|
|
}
|
|
if(isNaN(max))
|
|
{
|
|
max = numericValue;
|
|
}
|
|
else
|
|
{
|
|
max = Math.max(max, numericValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
//bad data. show yesterday through today.
|
|
if(isNaN(min) || isNaN(max))
|
|
{
|
|
var today:Date = new Date();
|
|
max = today.valueOf();
|
|
today.setDate(today.getDate() - 1);
|
|
min = today.valueOf();
|
|
}
|
|
|
|
this._dataMinimum = new Date(min);
|
|
this._dataMaximum = new Date(max);
|
|
}
|
|
}
|
|
} |