| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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. |
| * |
| * <p>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 <code>labelUnits</code> property.</p> |
| * |
| * <p>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 <code>autoAdjust</code> property to <code>true</code>.</p> |
| * |
| * <p>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</p> |
| * @see mx.charts.chartClasses.IAxis |
| * |
| * @mxml |
| * |
| * <p>The <code><mx:DateTimeAxis></code> tag inherits all the properties |
| * of its parent classes and adds the following properties:</p> |
| * |
| * <pre> |
| * <mx:DateTimeAxis |
| * <strong>Properties</strong> |
| * alignLabelsToUnits="true|false" |
| * dataUnits="milliseconds|seconds|minutes|hours|days|weeks|months|years" |
| * disabledDays="<i>Array; No default</i>" |
| * disabledRanges="<i>Array; No default</i>" |
| * displayLocalTime="<i>false</i>" |
| * interval="<i>Number</i>" |
| * labelUnits="milliseconds|seconds|minutes|hours|days|weeks|months|years" |
| * maximum="<i>Date</i>" |
| * minimum="<i>Date</i>" |
| * minorTickInterval="<i>Number</i>" |
| * minorTickUnits="milliseconds|seconds|minutes|hours|days|weeks|months|years" |
| * /> |
| * </pre> |
| * |
| * @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. |
| * |
| * <p>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.</p> |
| * |
| * <p>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 <code>Date.parse()</code> method.</p> |
| * |
| * The following example uses a data provider that defines a data object in the format { yyyy, mm, dd }. |
| * The method specified by the <code>parseFunction</code> uses these values to create a Date object |
| * that the axis can use. |
| * |
| * <pre> |
| * <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> |
| * </pre> |
| * |
| * @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 <code>dataUnits</code> 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. |
| * <p>When <code>false</code>, 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 <code>true</code>, 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.</p> |
| * |
| * @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 <code>dataUnits</code>. |
| * <p>If, for example, the <code>dataUnits</code> property |
| * is set to <code>"hours"</code>, |
| * and <code>dataInterval</code> 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.</p> |
| * |
| * @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: |
| * <ul> |
| * <li><code>milliseconds</code></li> |
| * <li><code>seconds</code></li> |
| * <li><code>minutes</code></li> |
| * <li><code>hours</code></li> |
| * <li><code>days</code></li> |
| * <li><code>weeks</code></li> |
| * <li><code>months</code></li> |
| * <li><code>years</code></li> |
| * </ul> |
| * |
| * <p>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 <code>dataUnits</code> property |
| * is <code>days</code>, the chart would not render labels |
| * for every hour, no matter what the minmium/maximum range is.</p> |
| * |
| * <p>Secondly, the value of the <code>dataUnits</code> 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 <code>dataUnits</code> property |
| * to determine how wide to render their columns.</p> |
| * |
| * <p>If, for example, your ColumnChart control's horizontal axis |
| * had its <code>labelUnits</code> property set to <code>weeks</code> |
| * and its <code>dataUnits</code> property set to <code>days</code>, |
| * the ColumnChart renders each column at 1/7th the distance |
| * between labels.</p> |
| * |
| * <p>When the <code>dataUnits</code> property is set to <code>null</code>, columnar series render |
| * their columns as days, but the DateTimeAxis chooses |
| * an appropriate unit when it generates the labels.</p> |
| * |
| * @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 <code>[ 0, 6 ]</code> |
| * 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. |
| * |
| * <p>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 <code>rangeStart</code> and <code>rangeEnd</code> 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 <code>rangeStart</code>, |
| * all the dates after the specified date are disabled, |
| * including the <code>rangeStart</code> date. |
| * If you specify only <code>rangeEnd</code>, |
| * all the dates before the specified date are disabled, |
| * including the <code>rangeEnd</code> date. |
| * To disable a single day, use a single Date object specifying a date |
| * in the Array.</p> |
| * |
| * <p>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.</p> |
| * |
| * <p><code>disabledRanges="{[ new Date(2006,0,11), {rangeStart: |
| * new Date(2006,0,23), rangeEnd: new Date(2006,1,10)}, |
| * {rangeStart: new Date(2006,2,1)} ]}"</code></p> |
| * |
| * @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 <code>true</code>, |
| * a DateTimeAxis considers all date values to be in the time zone |
| * of the client machine running the application. |
| * If <code>false</code>, 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 <code>labelUnits</code> |
| * between label values along the axis. |
| * Flex calculates the interval if this property is set to <code>null</code>. |
| * |
| * @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 |
| * (<code>milliseconds</code>, <code>seconds</code>, <code>minutes</code>, <code>hours</code>, <code>days</code>, |
| * <code>weeks</code>, <code>months</code>, or <code>years</code>). |
| * |
| * <p>If the <code>labelUnits</code> property is not set, |
| * the chart does not use any unit smaller than the value |
| * of the <code>dataUnits</code> property to render labels.</p> |
| * |
| * @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 <code>null</code>, 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 <code>null</code>, 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 <code>minorTickUnits</code> |
| * between minor tick marks along the axis. |
| * If this is set to <code>NaN</code>, |
| * the DateTimeAxis calculates it automatically. |
| * |
| * <p>Normally the <code>minorTickInterval</code> property |
| * is automatically set to 1. |
| * If, however, the <code>minorTickUnits</code> property |
| * is the same units as the <code>dataUnits</code> property |
| * (either set explicitly or implicitly calculated), |
| * then the <code>minorTickInterval</code> property |
| * is the maximum of 1, or <code>dataInterval</code>.</p> |
| * |
| * @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 |
| * (<code>milliseconds</code>, <code>seconds</code>, <code>minutes</code>, <code>hours</code>, <code>days</code>, |
| * <code>weeks</code>, <code>months</code>, or <code>years</code>). |
| * |
| * <p>If this property is not set, the chart determines the value |
| * of the <code>minorTickUnits</code> property. |
| * If the label interval is greater than 1, |
| * the <code>minorTickUnits</code> property is set to the value |
| * of the <code>labelUnits</code> property, |
| * and the <code>minorTickInterval</code> property is set to 1. |
| * If the label interval is 1, the <code>minorTickUnits</code> property is |
| * set to the next smaller unit from the <code>labelUnits</code> property. |
| * If set, the <code>minorTickUnits</code> property can never be smaller |
| * than the value of the <code>dataUnits</code> property.</p> |
| * |
| * @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 <code>labelUnits</code>. |
| * If you write a custom DateTimeAxis class, you can override this method |
| * to provide alternate default formatting. |
| * |
| * <p>You do not call this method directly. Instead, Flex calls this method before it |
| * renders the label to get the appropriate String to display.</p> |
| * |
| * @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 <code>labelUnits</code>. |
| * If you write a custom DateTimeAxis class, you can override this method to |
| * provide alternate default formatting. |
| * |
| * <p>You do not call this method directly. Instead, Flex calls this method before it |
| * renders the label to get the appropriate String to display.</p> |
| * |
| * @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 <code>labelUnits</code>. |
| * If you write a custom DateTimeAxis class, you can override this method to provide |
| * alternate default formatting. |
| * |
| * <p>You do not call this method directly. Instead, Flex calls this method before it |
| * renders the label to get the appropriate String to display.</p> |
| * |
| * @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 <code>labelUnits</code>. |
| * If you write a custom DateTimeAxis class, you can override this method |
| * to provide alternate default formatting. |
| * |
| * <p>You do not call this method directly. Instead, Flex calls this method before it |
| * renders the label to get the appropriate String to display.</p> |
| * |
| * @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 <code>labelUnits</code>. |
| * If you write a custom DateTimeAxis class, you can override this method to |
| * provide alternate default formatting. |
| * |
| * <p>You do not call this method directly. Instead, Flex calls this method before it |
| * renders the label to get the appropriate String to display.</p> |
| * |
| * @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 <code>labelUnits</code>. |
| * If you write a custom DateTimeAxis class, you can override this method |
| * to provide alternate default formatting. |
| * |
| * <p>You do not call this method directly. Instead, Flex calls this method before it |
| * renders the label to get the appropriate String to display.</p> |
| * |
| * @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"; |
| } |
| } |
| } |
| |
| } |