//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package mx.charts { import flash.events.Event; import mx.charts.chartClasses.AxisLabelSet; import mx.charts.chartClasses.DataDescription; import mx.charts.chartClasses.DateRangeUtilities; import mx.charts.chartClasses.NumericAxis; import mx.core.mx_internal; use namespace mx_internal; /** * The DateTimeAxis class maps time values evenly * between a minimum and maximum value along a chart axis. * It can plot values represented either as instances of the Date class, * as numeric values representing the number of milliseconds * since the epoch (midnight on January 1, 1970, GMT), * or as String values when you provide a custom parsing function. * *

The DateTimeAxis chooses the most reasonable units * to mark the axis by examining the range between the minimum and maximum * values of the axis. * The Axis chooses the largest unit that generates * a reasonable number of labels for the given range. * You can restrict the set of units the chart considers, * or specify exactly which units it should use, * by setting the labelUnits property.

* *

You can specifiy the minimum and maximum values explicitly, * or let the axis automatically determine them by examining * the values being renderered in the chart. * By default, the DateTimeAxis chooses the smallest possible range * to contain all the values represented in the chart. * Optionally, you can request that the minimum and maximum values * be rounded to whole units * (milliseconds, seconds, minutes, hours, days, weeks, months, years) * by setting the autoAdjust property to true.

* *

You can specify disabled days of a week and disabled ranges of dates * in order to show only working days on the axis but not all days * between minimum and maximum. It also filters data and shows only data corresponding * to working days on the chart

* @see mx.charts.chartClasses.IAxis * * @mxml * *

The <mx:DateTimeAxis> tag inherits all the properties * of its parent classes and adds the following properties:

* *
 *  <mx:DateTimeAxis
 *    Properties
 *    alignLabelsToUnits="true|false"
 *    dataUnits="milliseconds|seconds|minutes|hours|days|weeks|months|years"
 *    disabledDays="Array; No default"
 *    disabledRanges="Array; No default"
 *    displayLocalTime="false"
 *    interval="Number"
 *    labelUnits="milliseconds|seconds|minutes|hours|days|weeks|months|years"
 *    maximum="Date"
 *    minimum="Date"
 *    minorTickInterval="Number"
 *    minorTickUnits="milliseconds|seconds|minutes|hours|days|weeks|months|years"
 *  />
 *  
* * @includeExample examples/DateTimeAxisExample.mxml * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public class DateTimeAxis extends NumericAxis { include "../core/Version.as"; //-------------------------------------------------------------------------- // // Class constants // //-------------------------------------------------------------------------- /** * @private */ private static const MILLISECONDS_IN_MINUTE:Number = 1000 * 60; /** * @private */ private static const MILLISECONDS_IN_HOUR:Number = 1000 * 60 * 60; /** * @private */ private static const MILLISECONDS_IN_DAY:Number = 1000 * 60 * 60 * 24; /** * @private */ private static const MILLISECONDS_IN_WEEK:Number = 1000 * 60 * 60 * 24 * 7; /** * @private */ private static const MILLISECONDS_IN_MONTH:Number = 1000 * 60 * 60 * 24 * 30; /** * @private */ private static const MILLISECONDS_IN_YEAR:Number = 1000 * 60 * 60 * 24 * 365; /** * @private */ private static const MINIMUM_LABEL_COUNT:Number = 2; /** * @private */ private var UNIT_PROGRESSION:Object = { milliseconds: null, seconds: "milliseconds", minutes: "seconds", hours: "minutes", days: "hours", weeks: "days", months: "weeks", years: "months" }; //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function DateTimeAxis() { super(); baseAtZero = false; autoAdjust = false; updatePropertyAccessors(); } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private */ private static var tmpDate:Date = new Date(); /** * @private */ private var millisecondsP:String; /** * @private */ private var secondsP:String; /** * @private */ private var minutesP:String; /** * @private */ private var hoursP:String; /** * @private */ private var dateP:String; /** * @private */ private var dayP:String; /** * @private */ private var monthP:String; /** * @private */ private var fullYearP:String; /** * @private */ private var dateRangeUtilities:DateRangeUtilities = new DateRangeUtilities(); //-------------------------------------------------------------------------- // // Overridden properties: NumericAxis // //-------------------------------------------------------------------------- //---------------------------------- // parseFunction //---------------------------------- [Inspectable(category="Other")] /** * Specifies a method that customizes the value of the data points. * With this property, you specify a method that accepts a value and * returns a Date object. The Date object is then used in the DateTimeAxis * object of the chart. This lets you provide customizable date input strings * and convert them to Date objects, which Flex can then interpret for use in the DateTimeAxis. * *

Flex passes only one parameter to the parsing method. This parameter is the value of the * data point you specified for the series. Typically, it is a String that represents some form * of a date. You cannot override this parameter or add additional parameters.

* *

This Date object is immediately converted to a numeric value, * so custom parseFunctions can reuse the same Date object * for performance reasons. * By default, the DateTimeAxis uses the string parsing functionality * in the ECMA standard Date.parse() method.

* * The following example uses a data provider that defines a data object in the format { yyyy, mm, dd }. * The method specified by the parseFunction uses these values to create a Date object * that the axis can use. * *
     *  <mx:Script>
     *      import mx.collections.ArrayCollection;
     *      [Bindable] 
     *      public var aapl:ArrayCollection = new ArrayCollection([ 
     *          {date: "2005, 8, 1", close: 42.71},
     *          {date: "2005, 8, 2", close: 42.99},
     *          {date: "2005, 8, 3", close: 44}
     *      ]);
     *      
     *      public function myParseFunction(s:String):Date { 
     *          // Get an array of Strings from the comma-separated String passed in.
     *          var a:Array = s.split(",");
     *  
     *          // Create the new Date object. Note that the month argument is 0-based (with 0 being January).
     *          var newDate:Date = new Date(a[0],a[1]-1,a[2]);
     *          return newDate;
     *      }
     *  </mx:Script>
     *  <mx:LineChart id="mychart" dataProvider="{aapl}" showDataTips="true">
     *      <mx:horizontalAxis>
     *          <mx:DateTimeAxis dataUnits="days" parseFunction="myParseFunction"/>
     *      </mx:horizontalAxis>
     *      <mx:series>
     *          <mx:LineSeries yField="close" xField="date" displayName="AAPL"/>
     *      </mx:series>
     *  </mx:LineChart>
     *  
* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override public function set parseFunction(value:Function):void { super.parseFunction = value; } //---------------------------------- // requiredDescribedFields //---------------------------------- /** * The fields of the DescribeData structure that this axis is interested in. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override protected function get requiredDescribedFields():uint { var fields:uint = DataDescription.REQUIRED_MIN_MAX | DataDescription.REQUIRED_BOUNDED_VALUES; if (_userDataUnits == null) fields |= DataDescription.REQUIRED_MIN_INTERVAL; return fields; } //---------------------------------- // unitSize //---------------------------------- /** * @private * Storage for the unitSize property. */ private var _unitSize:Number = MILLISECONDS_IN_DAY; /** * The width, in pixels, of a single data unit. * The type of a data unit is determined * by the value of the dataUnits property. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override public function get unitSize():Number { return _unitSize; } //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //-------------------------------------- // alignLabelsToUnits //-------------------------------------- /** * @private * Storage for alignLabelsToUnits property */ private var _alignLabelsToUnits:Boolean = true; /** * Determines the placement of labels along the axis. *

When false, the chart always puts a label at the beginning of the axis. For example, * if your labels are every month and your first datapoint is July 14th, your first label * will be on July 14th. When true, Flex first calculates the label units, then labels * the first whole interval of those units. For example, if your first data point was * July 14th, and your label units was months (set explicitly or dynamically calculated), * the first label will be on August 1st.

* * @default true * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get alignLabelsToUnits():Boolean { return _alignLabelsToUnits; } /** * @private */ public function set alignLabelsToUnits(value:Boolean):void { if (value != _alignLabelsToUnits) { _alignLabelsToUnits = value; invalidateCache(); dispatchEvent(new Event("mappingChange")); dispatchEvent(new Event("axisChange")); } } //---------------------------------- // dataInterval //---------------------------------- /** * @private * Storage for the dataInterval property. */ private var _dataInterval:Number = 1; /** * @private */ private var _userDataInterval:Number; [Inspectable] /** * Specifies the interval between data in your chart, * specified in dataUnits. *

If, for example, the dataUnits property * is set to "hours", * and dataInterval property is set to 4, * the chart assumes your data occurs every four hours. * This affects how some series (such as ColumnSeries * and CandlestickSeries) render their data. * It also affects how labels are automatically chosen.

* * @see #dataUnits * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function set dataInterval(value:Number):void { if (isNaN(value)) value = 1; _dataInterval = _userDataInterval = value; if (_userDataUnits != null) _unitSize = toMilli(_dataInterval, _userDataUnits); else _unitSize = MILLISECONDS_IN_DAY; invalidateCache(); dispatchEvent(new Event("mappingChange")); dispatchEvent(new Event("axisChange")); } //---------------------------------- // dataUnits //---------------------------------- /** * @private * Storage for the dataUnits property. */ private var _dataUnits:String = null; /** * @private */ private var _userDataUnits:String = null; [Inspectable(category="General", enumeration="milliseconds,seconds,minutes,hours,days,weeks,months,years", defaultValue="days")] /** * Specifies the units that you expect the data in your chart to represent. * The value can be one of the following: * * *

This value is used in two ways. * First, when choosing appropriate label units, * a DateTimeAxis does not choose any unit smaller * than the units represented by the data. * If the value of the dataUnits property * is days, the chart would not render labels * for every hour, no matter what the minmium/maximum range is.

* *

Secondly, the value of the dataUnits property * is used by some series to affect their rendering. * Specifically, most columnar series * (such as ColumnSeries, BarSeries, CandlestickSeries, and HLOCSeries) * use the value of the dataUnits property * to determine how wide to render their columns.

* *

If, for example, your ColumnChart control's horizontal axis * had its labelUnits property set to weeks * and its dataUnits property set to days, * the ColumnChart renders each column at 1/7th the distance * between labels.

* *

When the dataUnits property is set to null, columnar series render * their columns as days, but the DateTimeAxis chooses * an appropriate unit when it generates the labels.

* * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get dataUnits():String { return _dataUnits; } /** * @private */ public function set dataUnits(value:String):void { _dataUnits = _userDataUnits = value; if (_dataUnits != null) _unitSize = toMilli(_dataInterval, _dataUnits); else _unitSize = MILLISECONDS_IN_DAY; invalidateCache(); dispatchEvent(new Event("mappingChange")); dispatchEvent(new Event("axisChange")); } //-------------------------------------- // disabledDays //-------------------------------------- /** * @private * Storage for the disabledDays property. */ private var _disabledDays:Array /* of int */; [Inspectable(arrayType = "int", category = "General", defaultValue = null)] /** * The days to disable in a week. * All the dates in a month, for the specified day, are disabled. * The elements of this array can have values from 0 (Sunday) to * 6 (Saturday). * For example, a value of [ 0, 6 ] * disables Sunday and Saturday. * * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get disabledDays():Array /* of int */ { return _disabledDays; } /** * @private */ public function set disabledDays(value:Array /* of int */):void { _disabledDays = value; invalidateCache(); dispatchEvent(new Event("mappingChange")); dispatchEvent(new Event("axisChange")); } //---------------------------------- // disabledRanges //---------------------------------- /** * @private * Storage for the disabledRanges property. */ private var _disabledRanges:Array /* of Object */; [Inspectable(arrayType = "Object", category = "General", defaultValue = null)] /** * Disables single and multiple days. * *

This property accepts an Array of objects as a parameter. * Each object in this array is a Date object, specifying a * single day to disable; or an object containing either or both * of the rangeStart and rangeEnd properties, * each of whose values is a Date object. * The value of these properties describes the boundaries * of the date range. * If either is omitted, the range is considered * unbounded in that direction. * If you specify only rangeStart, * all the dates after the specified date are disabled, * including the rangeStart date. * If you specify only rangeEnd, * all the dates before the specified date are disabled, * including the rangeEnd date. * To disable a single day, use a single Date object specifying a date * in the Array.

* *

The following example, disables the following dates: January 11 * 2006, the range January 23 - February 10 2006, and March 1 2006 * and all following dates.

* *

disabledRanges="{[ new Date(2006,0,11), {rangeStart: * new Date(2006,0,23), rangeEnd: new Date(2006,1,10)}, * {rangeStart: new Date(2006,2,1)} ]}"

* * @default [] * * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get disabledRanges():Array /* of Object */ { return _disabledRanges; } /** * @private */ public function set disabledRanges(value:Array /* of Object */):void { _disabledRanges = value; invalidateCache(); dispatchEvent(new Event("mappingChange")); dispatchEvent(new Event("axisChange")); } //---------------------------------- // displayLocalTime //---------------------------------- /** * @private * Storage for the displayLocalTime property. */ private var _displayLocalTime:Boolean = false; [Inspectable(category="General")] /** * When set to true, * a DateTimeAxis considers all date values to be in the time zone * of the client machine running the application. * If false, all values are in Universal Time * (Greenwich Mean Time). * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get displayLocalTime():Boolean { return _displayLocalTime; } /** * @private */ public function set displayLocalTime(value:Boolean):void { _displayLocalTime = value; invalidateCache(); dispatchEvent(new Event("mappingChange")); dispatchEvent(new Event("axisChange")); updatePropertyAccessors(); } //---------------------------------- // interval //---------------------------------- /** * @private * Storage for the interval property. */ private var _interval:Number; [Inspectable(category="General")] /** * Specifies the number of labelUnits * between label values along the axis. * Flex calculates the interval if this property is set to null. * * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get interval():Number { return _interval; } /** * @private */ public function set interval(value:Number):void { _interval = Math.max(1, value); invalidateCache(); dispatchEvent(new Event("mappingChange")); dispatchEvent(new Event("axisChange")); } //---------------------------------- // labelUnits //---------------------------------- /** * @private * Storage for the labelUnits property. */ private var _labelUnits:String; /** * @private */ private var _userLabelUnits:String = null; [Inspectable(category="General", enumeration="milliseconds,seconds,minutes,hours,days,weeks,months,years")] /** * The units that the axis uses to generate labels. * By default, a DateTimeAxis considers all valid units * (milliseconds, seconds, minutes, hours, days, * weeks, months, or years). * *

If the labelUnits property is not set, * the chart does not use any unit smaller than the value * of the dataUnits property to render labels.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get labelUnits():String { return _labelUnits; } /** * @private */ public function set labelUnits(value:String):void { _userLabelUnits = _labelUnits = value; invalidateCache(); dispatchEvent(new Event("mappingChange")); dispatchEvent(new Event("axisChange")); } //---------------------------------- // maximum //---------------------------------- [Inspectable(category="General", defaultValue="null")] /** * Specifies the maximum value for an axis label. * If null, Flex determines the minimum value * from the data in the chart. * * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get maximum():Date { return new Date(computedMaximum); } /** * @private */ public function set maximum(value:Date):void { if (value != null) assignedMaximum = value.getTime(); else assignedMaximum = NaN; invalidateCache(); dispatchEvent(new Event("mappingChange")); dispatchEvent(new Event("axisChange")); } //---------------------------------- // minimum //---------------------------------- [Inspectable(category="General", defaultValue="null")] /** * Specifies the minimum value for an axis label. * If null, Flex determines the minimum value * from the data in the chart. * * @default null * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get minimum():Date { return new Date(computedMinimum); } /** * @private */ public function set minimum(value:Date):void { if (value != null) assignedMinimum = value.getTime(); else assignedMinimum = NaN; invalidateCache(); dispatchEvent(new Event("mappingChange")); dispatchEvent(new Event("axisChange")); } //---------------------------------- // minorTickInterval //---------------------------------- /** * @private */ private var _minorTickInterval:Number; /** * @private */ private var _userMinorTickInterval:Number; [Inspectable(category="General")] /** * Specifies the number of minorTickUnits * between minor tick marks along the axis. * If this is set to NaN, * the DateTimeAxis calculates it automatically. * *

Normally the minorTickInterval property * is automatically set to 1. * If, however, the minorTickUnits property * is the same units as the dataUnits property * (either set explicitly or implicitly calculated), * then the minorTickInterval property * is the maximum of 1, or dataInterval.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get minorTickInterval():Number { return _userMinorTickInterval; } /** * @private */ public function set minorTickInterval(value:Number):void { _userMinorTickInterval = value; invalidateCache(); dispatchEvent(new Event("mappingChange")); dispatchEvent(new Event("axisChange")); } //---------------------------------- // minorTickUnits //---------------------------------- /** * @private * Storage for the minorTickUnits property. */ private var _minorTickUnits:String; /** * @private */ private var _userMinorTickUnits:String = null; [Inspectable(category="General", enumeration="milliseconds,seconds,minutes,hours,days,weeks,months,years")] /** * The units that the Axis considers when generating minor tick marks. * By default, a DateTimeAxis considers all valid units * (milliseconds, seconds, minutes, hours, days, * weeks, months, or years). * *

If this property is not set, the chart determines the value * of the minorTickUnits property. * If the label interval is greater than 1, * the minorTickUnits property is set to the value * of the labelUnits property, * and the minorTickInterval property is set to 1. * If the label interval is 1, the minorTickUnits property is * set to the next smaller unit from the labelUnits property. * If set, the minorTickUnits property can never be smaller * than the value of the dataUnits property.

* * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ public function get minorTickUnits():String { return _minorTickUnits; } /** * @private */ public function set minorTickUnits(value:String):void { _minorTickUnits = _userMinorTickUnits = value; invalidateCache(); dispatchEvent(new Event("mappingChange")); dispatchEvent(new Event("axisChange")); } //-------------------------------------------------------------------------- // // Overridden methods: Numeric Axis // //-------------------------------------------------------------------------- /** * @copy mx.charts.chartClasses.IAxis#transformCache() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override public function transformCache(cache:Array /* of ChartItem */, field:String, convertedField:String):void { update(); var alen:Number = computedMaximum - computedMinimum - dateRangeUtilities.calculateDisabledRange(computedMinimum, computedMaximum);; var n:int = cache.length; var i:int; if (disabledRanges || disabledDays) { var diff:Number = 0; for (i = 0; i < n; i++) { diff = dateRangeUtilities.calculateDisabledRange(computedMinimum,cache[i][field]); if(direction == "inverted") cache[i][convertedField] = 1 - (cache[i][field] - diff - computedMinimum) / alen; else cache[i][convertedField] = (cache[i][field] - diff - computedMinimum) / alen; } } else { var r:Number = computedMaximum - computedMinimum; for (i = 0; i < n; i++) { if(direction == "inverted") cache[i][convertedField] = 1 - (cache[i][field] - computedMinimum) / r; else cache[i][convertedField] = (cache[i][field] - computedMinimum) / r; } } } /** * @private */ override protected function adjustMinMax(minValue:Number, maxValue:Number):void { var interval:Number = _interval; var adjustMin:Boolean = autoAdjust && isNaN(assignedMinimum); var adjustMax:Boolean = autoAdjust && isNaN(assignedMaximum); var delta:Number = minValue - maxValue; var validUnitFound:Boolean = false; var min:Number; var max:Number; var labelInterval:Number = isNaN(_interval) ? 1 : _interval; var units:String; var i:int; var n:int; // First step: Calculate our dataunits. // If the user has explicitly assigned one, then we just // go with that and assume values are set correctly. // If a user specified data units isn't set, then we'll try and // dynamically determine one based on the spacing of the data. // This needs to be calculated before the label units are calculated. // As a rule, we don't auto-choose label units smaller // than the data units. if (_userDataUnits == null) { // We're going to start with the largest possible units // and work down until we find something that works. // Normally, the largest is years. // But if the user has specified a label unit, // we don't want our data until to be larger than that. // For example, if they specify months as the labels, // it would weird to have columns be the entire width of a year. units = "years"; // If we're autogenerating data units, // we'll assume the interval is 1. _dataInterval = 1; if (_userLabelUnits != null && _userLabelUnits != "") units = _userLabelUnits; var descriptions:Array /* of DataDescription */ = dataDescriptions; var minInterval:Number = Infinity; n = descriptions.length; // First, find the smallest interval. for (i = 0; i < n; i++) { interval = descriptions[i].minInterval; if (isNaN(interval)) continue; minInterval = Math.min(interval, minInterval); } // If there's no smallest interval, we need to choose // some sort of default value. We'll assume days. if (minInterval == Infinity || minInterval == 0) { _unitSize = MILLISECONDS_IN_DAY; _dataUnits = "days"; } else { // Start with years, and see if that will work. // 'Work' means that the data size is smaller // than the smallest interval represented in the data. // If that doesn't work, keep reducing the data unit size // until we find one that does. while (units != null) { _unitSize = toMilli(1,units); if (_unitSize <= minInterval) break; units = UNIT_PROGRESSION[units]; } // If we ran out of units, go with day. if (units == null) _unitSize = MILLISECONDS_IN_DAY; else _dataUnits = units; } } else { _dataUnits = _userDataUnits; _dataInterval = isNaN(_userDataInterval) ? 1 : _userDataInterval; } // Now we're going to start looking for the right units // to mark off labels for. // Again, we'll start with the largest possible values, and work // our way down until we find units that generate more than 3 labels. // Unless, of course, the user has specified the label units, // in which case we'll start with what they asked for. units = "years"; if (_userLabelUnits != null && _userLabelUnits != "") units = _userLabelUnits; var lastValidUnits:String = units; var minD:Date = new Date(minValue); var maxD:Date = new Date(maxValue); var minMilli:Number = minValue; var maxMilli:Number = maxValue; while (units != null) { lastValidUnits = units; // We never want our labels to occur more often than our data does. // So if our label units and our data units are the same, // let's make sure that our label interval isn't more often // than our data interval. if (units == _dataUnits) labelInterval = Math.max(labelInterval, _dataInterval); // Now check and see if we'll have enough labels // with the current units. if (adjustMin) { minD.setTime(minValue); roundDateDown(minD,units); minMilli = minD.getTime(); } if (adjustMax) { maxD.setTime(maxValue); roundDateUp(maxD,units); maxMilli = maxD.getTime(); } switch (units) { case "milliseconds": { minMilli = minValue; maxMilli = maxValue; break; } case "seconds": case "hours": case "days": case "minutes": case "years": { min = fromMilli(minD.getTime(), units); max = fromMilli(maxD.getTime(), units); if (max - min >= MINIMUM_LABEL_COUNT * labelInterval) validUnitFound=true; break; } case "weeks": { if (fromMilli(maxMilli - minMilli, "weeks") >= MINIMUM_LABEL_COUNT * labelInterval) { validUnitFound = true; } break; } case "months": { min = minD[monthP] + minD[fullYearP] * 12; max = maxD[monthP] + maxD[fullYearP] * 12; if (max - min >= MINIMUM_LABEL_COUNT * labelInterval) validUnitFound=true; break; } } // Stop when we've found a unit that gives us enough labels. if (validUnitFound) break; // Also stop either if the user has explicitly requested // these label units, or if we've run out of label units. if (units == _userLabelUnits || UNIT_PROGRESSION[units] == null) break; if (units == _dataUnits) { // If the current label units/interval is the same as our // data units/interval, we'll stop... we don't want // wide columns and narrow labels. if (labelInterval <= _dataInterval) break; else { // But if our data units are our label units // and our data interval is smaller than our data interval, // we can always drop down and try again. labelInterval = _dataInterval; } } else { // Drop down a level of specificity and try again. units = UNIT_PROGRESSION[units]; } } _labelUnits = lastValidUnits; if (_userMinorTickUnits != null && _userMinorTickUnits != "") { _minorTickInterval = isNaN(_userMinorTickInterval) ? 1 : _userMinorTickInterval; _minorTickUnits = _userMinorTickUnits; } else if (labelInterval == 1) { _minorTickInterval = 1; _minorTickUnits = _labelUnits; } else { _minorTickUnits = _labelUnits; for (i = 2; i <= labelInterval; i++) { if ((labelInterval % i) == 0) { _minorTickInterval = labelInterval / i; break; } } } invalidateCache(); if (adjustMin) computedMinimum = minMilli; if (adjustMax) computedMaximum = maxMilli; computedInterval = labelInterval; } /** * @private */ override protected function guardMinMax(min:Number, max:Number):Array /* of int */ { if (isNaN(min) || !isFinite(min)) return [ 0, 100 ]; else if (isNaN(max) || !isFinite(max)) return [ min, min + 1 ]; else if (min == max) return [ min, min + 1]; return null; } /** * @copy mx.charts.chartClasses.IAxis#filterCache() * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override public function filterCache(cache:Array /* of ChartItem */, field:String, filteredField:String):void { super.filterCache(cache,field,filteredField); var i:int; var n:int = cache.length; if (disabledRanges || disabledDays) { for (i = 0; i < n; i++) { cache[i][filteredField] = dateRangeUtilities.isDisabled(cache[i][field]) ? NaN : cache[i][field]; } } } /** * @private */ override public function mapCache(cache:Array /* of ChartItem */, field:String, convertedField:String, indexValues:Boolean = false):void { var n:int = cache.length; var i:int; var v:Object; var tmpValue:Number; var parseFunction:Function = this.parseFunction; if (parseFunction != null) { for (i = 0; i < n; i++) { v = cache[i]; var d:Date = parseFunction(v[field]); if (d != null) v[convertedField] = d.getTime(); } } else { for (i = 0; i < n && cache[i][field] == null; i++) { } if (i == n) return; if (cache[i][field] is String) { for (; i < n; i++) { v = cache[i]; if (!v[field]) continue; v[convertedField] = Date.parse(v[field]); } } else if (cache[i][field] is XML || cache[i][field] is XMLList) { v = cache[i]; if (isNaN(Number(v[field].toString()))) { for (; i < n; i++) { v = cache[i]; if (!v[field]) continue; v[convertedField] = Date.parse(v[field].toString()); } } else { for (; i < n; i++) { v = cache[i]; if (!v[field]) continue; v[convertedField] = Number(v[field].toString()); } } } else if (cache[i][field] is Date) { for (; i < n; i++) { v = cache[i]; if (!v[field]) continue; v[convertedField] = v[field].getTime(); } } else { for (; i < n; i++) { v = cache[i]; if (!v[field]) continue; v[convertedField] = v[field]; } } } } /** * @private */ override public function formatForScreen(v:Object):String { var d:Date = tmpDate; if (parseFunction != null) { d = parseFunction(v); } else { if (v is String) d.setTime(Date.parse(v)); else if (v is Date) d = (v as Date); else d.setTime(v); } var f:Function = chooseLabelFunction() return f(d, null, this); } /** * @private */ override protected function buildLabelCache():Boolean { var j:int; dateRangeUtilities.createDisabledRangeSet(disabledDays, disabledRanges, computedMinimum, computedMaximum); var lfunc:Function = chooseLabelFunction(); if (labelCache) return false; var d:Date = new Date(); labelCache = []; var r:Number = computedMaximum - computedMinimum - dateRangeUtilities.calculateDisabledRange(computedMinimum, computedMaximum);; var milliInterval:Number = toMilli(computedInterval, _labelUnits); var labelBase:Number = labelMinimum; var labelTop:Number = labelMaximum + 0.000001 var previousValue:Date = null; var labelDate:Date; var dTime:Number; var diff:Number = 0; var tzo:Number = 0; var firstLabelAdded:Boolean = false; labelDate = new Date(labelBase); if (_alignLabelsToUnits) roundDateUp(labelDate,_labelUnits); labelBase = labelDate.getTime(); switch (_labelUnits) { case "months": { // Start with the first label. // While.... // Add N to the month. // Check the month; if it's not base + N, // then the new month doesn't have enough days... // So roll back to the last day of month base + N // (most likely, by setting day back to 0). // Base each month off the original date, so an adjustment // for short date doesn't affect the following months var nextMonth:Number = labelDate[monthP]; while (labelDate.getTime() <= labelTop) { dTime = labelDate.getTime(); if (disabledDays || disabledRanges) { if (previousValue != null) diff = dateRangeUtilities.getDisabledRange(previousValue.getTime() + 1, dTime, _labelUnits); else diff = dateRangeUtilities.getDisabledRange(computedMinimum, dTime, _labelUnits); if (!(dateRangeUtilities.isDisabled(dTime))) { if(direction == "inverted") labelCache.push(new AxisLabel( 1 - (dTime - computedMinimum - diff) / r, new Date(dTime), lfunc(labelDate, previousValue, this))); else labelCache.push(new AxisLabel( (dTime - computedMinimum - diff) / r, new Date(dTime), lfunc(labelDate, previousValue, this))); } } else if(direction == "inverted") labelCache.push(new AxisLabel( 1 - (dTime - computedMinimum) / r, new Date(dTime), lfunc(labelDate, previousValue, this))); else labelCache.push(new AxisLabel( (dTime - computedMinimum) / r, new Date(dTime), lfunc(labelDate, previousValue, this))); if (previousValue == null) previousValue = new Date(dTime); else previousValue.setTime(dTime); nextMonth += computedInterval; // Init labelDate to N months past labelBase. labelDate.setTime(labelBase); labelDate[monthP] = nextMonth; if (labelDate[monthP] != (nextMonth % 12)) { // If the month isn't what we expected it to be, // it must have wrapped. // Set the date to 0, which will roll it back // to the last date of the previous month, // which is the one we want. labelDate[dateP] = 0; } } break; } case "years": { // Start with the first label. // While.... // Add N to the month // Check the month; if it's not base + N, // then the new month doesn't have enough days... // so roll back to the last day of month base + N // (most likely, by setting day back to 0) // Base each month off the original date, so an adjustment // for short date doesn't affect the following months. var nextYear:Number = labelDate[fullYearP]; while (labelDate.getTime() <= labelTop) { dTime = labelDate.getTime(); if (disabledDays|| disabledRanges) { if (previousValue != null) diff = dateRangeUtilities.getDisabledRange(previousValue.getTime() + 1, dTime, _labelUnits); else diff = dateRangeUtilities.getDisabledRange(computedMinimum, dTime, _labelUnits); if (!(dateRangeUtilities.isDisabled(dTime))) { if(direction == "inverted") labelCache.push(new AxisLabel( 1 - (dTime - computedMinimum - diff) / r, new Date(dTime), lfunc(labelDate, previousValue, this))); else labelCache.push(new AxisLabel( (dTime - computedMinimum - diff) / r, new Date(dTime), lfunc(labelDate, previousValue, this))); } } else { if(direction == "inverted") labelCache.push(new AxisLabel( 1 - (dTime - computedMinimum) / r, new Date(dTime), lfunc(labelDate, previousValue, this))); else labelCache.push(new AxisLabel( (dTime - computedMinimum) / r, new Date(dTime), lfunc(labelDate, previousValue, this))); } if (previousValue == null) previousValue = new Date(dTime); else previousValue.setTime(dTime); nextYear += computedInterval; // Init labelDate to N months past labelBase. labelDate.setTime(labelBase); labelDate[fullYearP] = nextYear; if (labelDate[fullYearP] != nextYear) { // If the month isn't what we expected it to be, // it must have wrapped. // Set the date to 0, which will roll it back // to the last date of the previous month, // which is the one we want. labelDate[dateP] = 0; } } break; } default: { for (var i:Number = labelBase; i <= labelTop;) { d = new Date(i); if (disabledDays|| disabledRanges) { if (previousValue != null) diff = dateRangeUtilities.getDisabledRange(previousValue.getTime() + 1, i, _labelUnits); else diff = dateRangeUtilities.getDisabledRange(computedMinimum, i, _labelUnits); if (!(dateRangeUtilities.isDisabled(i))) { if(direction == "inverted") labelCache.push(new AxisLabel(1 - (i - computedMinimum - diff)/r , d, lfunc(d, previousValue, this))); else labelCache.push(new AxisLabel((i - computedMinimum - diff)/r , d, lfunc(d, previousValue, this))); if (!firstLabelAdded) firstLabelAdded = true; } previousValue = d; if (!firstLabelAdded) i += MILLISECONDS_IN_DAY; else { var tmp:Date = new Date(i); if (_labelUnits == "weeks") { tmp.dateUTC = tmp.dateUTC + 7 * computedInterval; i = tmp.time; } else if (_labelUnits == "hours") { tmp.hoursUTC = tmp.hoursUTC + computedInterval; i = tmp.time; } else if (_labelUnits == "minutes") { tmp.minutesUTC = tmp.minutesUTC + computedInterval; i = tmp.time; } else if (_labelUnits == "seconds") { tmp.secondsUTC = tmp.secondsUTC + computedInterval; i = tmp.time; } else if (_labelUnits == "milliseconds") { tmp.millisecondsUTC = tmp.millisecondsUTC + computedInterval; i = tmp.time; } else { tmp.dateUTC = tmp.dateUTC + computedInterval; i = tmp.time; } } } else { if(direction == "inverted") labelCache.push(new AxisLabel(1 - (i - computedMinimum)/r , d, lfunc(d, previousValue, this))); else labelCache.push(new AxisLabel((i - computedMinimum)/r , d, lfunc(d, previousValue, this))); previousValue = d; tmp = new Date(i); if (_labelUnits == "weeks") { tmp.dateUTC = tmp.dateUTC + 7 * computedInterval; i = tmp.time; } else if (_labelUnits == "hours") { tmp.hoursUTC = tmp.hoursUTC + computedInterval; i = tmp.time; } else if (_labelUnits == "minutes") { tmp.minutesUTC = tmp.minutesUTC + computedInterval; i = tmp.time; } else if (_labelUnits == "seconds") { tmp.secondsUTC = tmp.secondsUTC + computedInterval; i = tmp.time; } else if (_labelUnits == "milliseconds") { tmp.millisecondsUTC = tmp.millisecondsUTC + computedInterval; i = tmp.time; } else { tmp.dateUTC = tmp.dateUTC + computedInterval; i = tmp.time; } } } break; } } return true; } /** * Invoked when an AxisRenderer is unable to cleanly render * the labels without overlap, and would like the Axis object * to reduce the set of labels. * The method is passed the two labels that are overlapping. * * @param intervalStart The start of the interval where labels overlap. * * @param intervalEnd The end of the interval where labels overlap. * * @return A new label set that resolves the overlap by reducing * the number of labels. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override public function reduceLabels(intervalStart:AxisLabel, intervalEnd:AxisLabel):AxisLabelSet { // We need to determine how many labels to skip. var intervalMultiplier:int = 0; switch (_labelUnits) { case "months": { intervalMultiplier= Math.floor( ((intervalEnd.value[fullYearP] * 12 + intervalEnd.value[monthP]) - (intervalStart.value[fullYearP] * 12 + intervalStart.value[monthP])) / computedInterval) + 1; break; } case "years": { intervalMultiplier = Math.floor( (intervalEnd.value[fullYearP] - intervalStart.value[fullYearP]) / computedInterval) + 1; break; } default: { var milliInterval:Number = toMilli(computedInterval, _labelUnits); intervalMultiplier = Math.floor( (intervalEnd.value.getTime() - intervalStart.value.getTime()) / milliInterval) + 1; break; } } var labels:Array /* of AxisLabel */ = []; var newTicks:Array /* of Number */ = []; var newMinorTicks:Array /* of Number */ = []; var i:int; var n:int = labelCache.length; for (i = 0; i < n; i += intervalMultiplier) { labels.push(labelCache[i]); newTicks.push(labelCache[i].position); } if (computedInterval == _minorTickInterval && intervalMultiplier > 1) for (i = intervalMultiplier - 1; i >= 1; i--) { if ((intervalMultiplier % i) == 0) { intervalMultiplier = i; break; } } n = minorTickCache.length; for (i = 0; i < n; i += intervalMultiplier) { newMinorTicks.push(minorTickCache[i]); } var labelSet:AxisLabelSet = new AxisLabelSet(); labelSet.labels = labels; labelSet.minorTicks = newMinorTicks; labelSet.ticks = newTicks; labelSet.accurate = true; return labelSet; } /** * @inheritDoc * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ override protected function buildMinorTickCache():Array /* of Number */ { var cache:Array /* of Number */ = []; var d:Date = new Date(); var r:Number = computedMaximum - computedMinimum - dateRangeUtilities.calculateDisabledRange(computedMinimum, computedMaximum);; var milliInterval:Number = toMilli(_minorTickInterval, _minorTickUnits); var labelBase:Number = labelMinimum; var labelTop:Number = labelMaximum + 0.000001 var previousValue:Date = null; var labelDate:Date; var dTime:Number; var tzo:Number = 0; var disabled:Boolean = false; var diff:Number = 0; var j:int; labelDate = new Date(labelBase); if (_alignLabelsToUnits) roundDateUp(labelDate,_minorTickUnits); labelBase = labelDate.getTime(); switch (_minorTickUnits) { case "months": { // Start with the first label // while.... // Add N to the month // Check the month; if it's not base + N, // then the new month doesn't have enough days... // so roll back to the last day of month base + N // (most likely, by setting day back to 0). // Base each month off the original date, // so an adjustment for short date // doesn't affect the following months. var nextMonth:Number = labelDate[monthP]; while (labelDate.getTime() <= labelTop) { dTime = labelDate.getTime(); if (disabledDays || disabledRanges) { if (previousValue != null) diff = dateRangeUtilities.getDisabledRange(previousValue.getTime() + 1,dTime, _minorTickUnits); else diff = dateRangeUtilities.getDisabledRange(computedMinimum, dTime, _minorTickUnits); if (!(dateRangeUtilities.isDisabled(dTime))) { cache.push((dTime - computedMinimum - diff) / r); } } else cache.push((dTime - computedMinimum) / r); if (previousValue == null) previousValue = new Date(dTime); else previousValue.setTime(dTime); nextMonth += _minorTickInterval; // init labelDate to N months past labelBase labelDate.setTime(labelBase); labelDate[monthP] = nextMonth; if (labelDate[monthP] != (nextMonth % 12)) { // If the month isn't what we expected it to be, // it must have wrapped. // Set the date to 0, which will roll it back // to the last date of the previous month // (which is the one we want). labelDate[dateP] = 0; } } break; } case "years": { // Start with the first label // while.... // Add N to the month. // Check the month; if it's not base + N, // then the new month doesn't have enough days... // so roll back to the last day of month base + N // (most likely, by setting day back to 0). // Base each month off the original date, // so an adjustment for short date // doesn't affect the following months var nextYear:Number = labelDate[fullYearP]; while (labelDate.getTime() <= labelTop) { dTime = labelDate.getTime(); if (disabledDays || disabledRanges) { if (previousValue != null) diff = dateRangeUtilities.getDisabledRange(previousValue.getTime() + 1, dTime, _minorTickUnits); else diff = dateRangeUtilities.getDisabledRange(computedMinimum, dTime, _minorTickUnits); if (!(dateRangeUtilities.isDisabled(dTime))) { cache.push((dTime - computedMinimum - diff)/r); } } else cache.push((dTime - computedMinimum)/r); if (previousValue == null) previousValue = new Date(dTime); else previousValue.setTime(dTime); nextYear += _minorTickInterval; // init labelDate to N months past labelBase labelDate.setTime(labelBase); labelDate[fullYearP] = nextYear; if (labelDate[fullYearP] != nextYear) { // If the month isn't what we expected it to be, // it must have wrapped. // Set the date to 0, which will roll it back // to the last date of the previous month // (which is the one we want). labelDate[dateP] = 0; } } break; } default: { for (var i:Number = labelBase; i <= labelTop;) { d = new Date(i); if (disabledDays|| disabledRanges) { if (previousValue != null) diff = dateRangeUtilities.getDisabledRange(previousValue.getTime() + 1, i, _minorTickUnits); else diff = dateRangeUtilities.getDisabledRange(computedMinimum, i, _minorTickUnits); if (!(dateRangeUtilities.isDisabled(i))) { cache.push((i - computedMinimum - diff)/r); } } else cache.push((i - computedMinimum)/r); previousValue = d; var tmp:Date = new Date(i); if (_minorTickUnits == "weeks") { tmp.dateUTC = tmp.dateUTC + 7 * _minorTickInterval; i = tmp.time; } else if (_minorTickUnits == "hours") { tmp.hoursUTC = tmp.hoursUTC + _minorTickInterval; i = tmp.time; } else if (_minorTickUnits == "minutes") { tmp.minutesUTC = tmp.minutesUTC + _minorTickInterval; i = tmp.time; } else if (_minorTickUnits == "seconds") { tmp.secondsUTC = tmp.secondsUTC + _minorTickInterval; i = tmp.time; } else if (_minorTickUnits == "milliseconds") { tmp.millisecondsUTC = tmp.millisecondsUTC + _minorTickInterval; i = tmp.time; } else { tmp.dateUTC = tmp.dateUTC + _minorTickInterval; i = tmp.time; } } break; } } return cache; } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- private function roundDateUp(d:Date,units:String):void { switch (units) { case "seconds": if (d[millisecondsP] > 0) { d[secondsP] = d[secondsP] + 1; d[millisecondsP] = 0; } break; case "minutes": if (d[secondsP] > 0 || d[millisecondsP] > 0) { d[minutesP] = d[minutesP] + 1; d[secondsP] = 0; d[millisecondsP] = 0; } break; case "hours": if (d[minutesP] > 0 || d[secondsP] > 0 || d[millisecondsP] > 0) { d[hoursP] = d[hoursP] + 1; d[minutesP] = 0; d[secondsP] = 0; d[millisecondsP] = 0; } break; case "days": if (d[hoursP] > 0 || d[minutesP] > 0 || d[secondsP] > 0 || d[millisecondsP] > 0) { d[hoursP] = 0; d[minutesP] = 0; d[secondsP] = 0; d[millisecondsP] = 0; d[dateP] = d[dateP] + 1; } break; d[hoursP] = 0; d[minutesP] = 0; d[secondsP] = 0; d[millisecondsP] = 0; break; case "weeks": d[hoursP] = 0; d[minutesP] = 0; d[secondsP] = 0; d[millisecondsP] = 0; if (d[dayP] != 0) d[dateP] = d[dateP] + (7 - d[dayP]); break; case "months": if (d[dateP] > 1 || d[hoursP] > 0 || d[minutesP] > 0 || d[secondsP] > 0 || d[millisecondsP] > 0) { d[hoursP] = 0; d[minutesP] = 0; d[secondsP] = 0; d[millisecondsP] = 0; d[dateP] = 1; d[monthP] = d[monthP] + 1; } break; case "years": if (d[monthP] > 0 || d[dateP] > 1 || d[hoursP] > 0 || d[minutesP] > 0 || d[secondsP] > 0 || d[millisecondsP] > 0) { d[hoursP] = 0; d[minutesP] = 0; d[secondsP] = 0; d[millisecondsP] = 0; d[dateP] = 1; d[monthP] = 0; d[fullYearP] = d[fullYearP] + 1; } break; } } private function roundDateDown(d:Date,units:String):void { switch (units) { case "seconds": d[secondsP] = 0; break; case "minutes": d[secondsP] = 0; d[millisecondsP] = 0; break; case "hours": d[minutesP] = 0; d[secondsP] = 0; d[millisecondsP] = 0; break; case "days": d[hoursP] = 0; d[minutesP] = 0; d[secondsP] = 0; d[millisecondsP] = 0; break; case "weeks": d[hoursP] = 0; d[minutesP] = 0; d[secondsP] = 0; d[millisecondsP] = 0; if (d[dayP] != 0) d[dateP] = d[dateP] - d[dayP]; break; case "months": d[hoursP] = 0; d[minutesP] = 0; d[secondsP] = 0; d[millisecondsP] = 0; d[dateP] = 1; break; case "years": d[hoursP] = 0; d[minutesP] = 0; d[secondsP] = 0; d[millisecondsP] = 0; d[dateP] = 1; d[monthP] = 0; break; } } /** * The default formatting function used * when the axis renders with year-based labelUnits. * If you write a custom DateTimeAxis class, you can override this method * to provide alternate default formatting. * *

You do not call this method directly. Instead, Flex calls this method before it * renders the label to get the appropriate String to display.

* * @param d The Date object that contains the unit to format. * * @param previousValue The Date object that contains the data point that occurs * prior to the current data point. * * @param axis The DateTimeAxis on which the label is rendered. * * @return The formatted label. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function formatYears(d:Date, previousValue:Date, axis:DateTimeAxis):String { var fy:Number = d[fullYearP]; return fy.toString(); } /** * The default formatting function used * when the axis renders with month-based labelUnits. * If you write a custom DateTimeAxis class, you can override this method to * provide alternate default formatting. * *

You do not call this method directly. Instead, Flex calls this method before it * renders the label to get the appropriate String to display.

* * @param d The Date object that contains the unit to format. * * @param previousValue The Date object that contains the data point that occurs * prior to the current data point. * * @param axis The DateTimeAxis on which the label is rendered. * * @return The formatted label. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function formatMonths(d:Date, previousValue:Date, axis:DateTimeAxis):String { var fy:Number = d[fullYearP]; return (d[monthP] + 1) + "/" + ((fy % 100) < 10 ? "0" + fy % 100 : fy % 100); } /** * The default formatting function used * when the axis renders with day-based labelUnits. * If you write a custom DateTimeAxis class, you can override this method to provide * alternate default formatting. * *

You do not call this method directly. Instead, Flex calls this method before it * renders the label to get the appropriate String to display.

* * @param d The Date object that contains the unit to format. * * @param previousValue The Date object that contains the data point that occurs * prior to the current data point. * * @param axis The DateTimeAxis on which the label is rendered. * * @return The formatted label. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function formatDays(d:Date, previousValue:Date, axis:DateTimeAxis):String { var fy:Number = d[fullYearP]; return (d[monthP] + 1) + "/" + d[dateP] + "/" + ((fy % 100) < 10 ? "0" + fy % 100 : fy % 100); } /** * The default formatting function used * when the axis renders with minute-based labelUnits. * If you write a custom DateTimeAxis class, you can override this method * to provide alternate default formatting. * *

You do not call this method directly. Instead, Flex calls this method before it * renders the label to get the appropriate String to display.

* * @param d The Date object that contains the unit to format. * * @param previousValue The Date object that contains the data point that occurs * prior to the current data point. * * @param axis The DateTimeAxis on which the label is rendered. * * @return The formatted label. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function formatMinutes(d:Date, previousValue:Date, axis:DateTimeAxis):String { return d[hoursP] + ":" + (d[minutesP] < 10 ? "0" + d[minutesP] : d[minutesP]); } /** * The default formatting function used * when the axis renders with second-based labelUnits. * If you write a custom DateTimeAxis class, you can override this method to * provide alternate default formatting. * *

You do not call this method directly. Instead, Flex calls this method before it * renders the label to get the appropriate String to display.

* * @param d The Date object that contains the unit to format. * * @param previousValue The Date object that contains the data point that occurs * prior to the current data point. * * @param axis The DateTimeAxis on which the label is rendered. * * @return The formatted label. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function formatSeconds(d:Date, previousValue:Date, axis:DateTimeAxis):String { return d[hoursP]+ ":" + (d[minutesP] < 10 ? "0" + d[minutesP] : d[minutesP]) + ":" + (d[secondsP] < 10 ? "0" + d[secondsP] : d[secondsP]); } /** * The default formatting function used * when the axis renders with millisecond-based labelUnits. * If you write a custom DateTimeAxis class, you can override this method * to provide alternate default formatting. * *

You do not call this method directly. Instead, Flex calls this method before it * renders the label to get the appropriate String to display.

* * @param d The Date object that contains the unit to format. * * @param previousValue The Date object that contains the data point that occurs * prior to the current data point. * * @param axis The DateTimeAxis on which the label is rendered. * * @return The formatted label. * * @langversion 3.0 * @playerversion Flash 9 * @playerversion AIR 1.1 * @productversion Flex 3 */ protected function formatMilliseconds(d:Date, previousValue:Date, axis:DateTimeAxis):String { var s:String = d[hoursP] + ":" + (d[minutesP] < 10 ? "0" + d[minutesP] : d[minutesP]) + ":" + (d[secondsP] < 10 ? "0" + d[secondsP] : d[secondsP]); var m:String = d[millisecondsP].toString(); if (m.length < 4) { var n:int = m.length; for (var i:int = n; i < 4; i++) { m = "0" + m; } } return s + m; } /** * @private */ private function chooseLabelFunction():Function { if (labelFunction != null) return labelFunction; switch (_labelUnits) { case "years": { return formatYears; } case "months": { return formatMonths; } case "days": case "weeks": { return formatDays; } case "hours": case "minutes": { return formatMinutes; } case "seconds": { return formatSeconds; } case "milliseconds": return formatMilliseconds; break; } return formatDays; } /** * @private */ private function toMilli(v:Number, unit:String):Number { switch (unit) { case "milliseconds": { return v; } case "seconds": { return v * 1000; } case "minutes": { return v * MILLISECONDS_IN_MINUTE; } case "hours": { return v * MILLISECONDS_IN_HOUR; } case "weeks": { return v * MILLISECONDS_IN_WEEK; } case "months": { return v * MILLISECONDS_IN_MONTH; } case "years": { return v * MILLISECONDS_IN_YEAR; } case "days": default: { return v * MILLISECONDS_IN_DAY; } } } /** * @private */ private function fromMilli(v:Number, unit:String):Number { switch (unit) { case "milliseconds": { return v; } case "seconds": { return v / 1000; } case "minutes": { return v / MILLISECONDS_IN_MINUTE; } case "hours": { return v / MILLISECONDS_IN_HOUR; } case "days": { return v / MILLISECONDS_IN_DAY; } case "weeks": { return v / MILLISECONDS_IN_WEEK; } case "months": { return v / MILLISECONDS_IN_MONTH; } case "years": { return v / MILLISECONDS_IN_YEAR; } } return NaN; } /** * @private */ private function updatePropertyAccessors():void { if (_displayLocalTime) { millisecondsP = "milliseconds"; secondsP = "seconds"; minutesP = "minutes"; hoursP = "hours"; dateP = "date"; dayP = "day"; monthP = "month"; fullYearP = "fullYear"; } else { millisecondsP = "millisecondsUTC"; secondsP = "secondsUTC"; minutesP = "minutesUTC"; hoursP = "hoursUTC"; dateP = "dateUTC"; dayP = "dayUTC"; monthP = "monthUTC"; fullYearP = "fullYearUTC"; } } } }