////////////////////////////////////////////////////////////////////////////////
//
// 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 spark.components
{
import flash.events.Event;
import flash.utils.Dictionary;
import mx.collections.ArrayCollection;
import mx.collections.IList;
import mx.collections.ISort;
import mx.core.ClassFactory;
import mx.core.IFactory;
import mx.core.IVisualElementContainer;
import mx.core.mx_internal;
import mx.events.FlexEvent;
import spark.collections.Sort;
import spark.collections.SortField;
import spark.components.calendarClasses.DateAndTimeProvider;
import spark.components.calendarClasses.DateSelectorDisplayMode;
import spark.components.calendarClasses.DateSpinnerItemRenderer;
import spark.components.calendarClasses.YearProvider;
import spark.components.supportClasses.SkinnableComponent;
import spark.events.IndexChangeEvent;
import spark.formatters.DateTimeFormatter;
import spark.formatters.NumberFormatter;
import spark.globalization.supportClasses.CalendarDate;
import spark.globalization.supportClasses.DateTimeFormatterEx;
use namespace mx_internal;
[Exclude(name="textAlign", kind="style")]
//--------------------------------------
// Events
//--------------------------------------
/**
* Dispatched after the selected date has been changed by the user.
*
* @eventType flash.events.Event.CHANGE
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
[Event(name="change", type="flash.events.Event")]
/**
* Dispatched after the selected date has been changed, either
* by the user (i.e. interactively) or programmatically.
*
* @eventType mx.events.FlexEvent.VALUE_COMMIT
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
[Event(name="valueCommit", type="mx.events.FlexEvent")]
//--------------------------------------
// Styles
//--------------------------------------
include "../styles/metadata/StyleableTextFieldTextStyles.as"
/**
* The locale of the component. Controls how dates are formatted, e.g. in what order the fields
* are listed and what additional date-related characters are shown, if any. Uses standard locale
* identifiers as described in Unicode Technical Standard #35. For example "en", "en_US" and "en-US"
* are all English, "ja" is Japanese. If the specified locale is not supported on the platform, "en_US"
* will be used. To determine if a locale is supported, use
* DateTimeFormatter.getAvailableLocaleIDNames()
*
*
The default value is undefined. This property inherits its value from an ancestor; if still
* undefined, it inherits from the global locale
style.
When using the Spark formatters and globalization classes, you can set this style on the root
* application to the value of the LocaleID.DEFAULT
constant.
* Those classes will then use the client operating system's international preferences.
The default value is inherited from the global styles; see the framework defaults.css for * the specific value.
* * @langversion 3.0 * @playerversion AIR 3 * @productversion Flex 4.6 */ [Style(name="accentColor", type="uint", format="Color", inherit="yes")] //-------------------------------------- // Other metadata //-------------------------------------- [IconFile("DateSpinner.png")] /** * The DateSpinner control presents an interface for picking a particular date or time. * *The DateSpinner control can display the date, the time, or the date and time, based on the
* value of the displayMode
property.
The following images shows the three modes of the DateSpinner control:
* ** *
* *The UI for the control is made up of a series of SpinnerList controls wrapped inside * a SpinnerListContainer that show the * currently-selected date. Through touch or mouse interaction, users * can adjust the selected date.
* *The DateSpinnerSkin only defines some sizing properties. * To change the appearance of the DateSpinner control, you typically reskin the underlying * SpinnerListSkin or SpinnerListContainerSkin.
* * @mxmlThe <s:DateSpinner>
tag inherits all of the tag
* attributes of its superclass and adds the following tag attributes:
* <s:DateSpinner * Properties * displayMode="date|time|dateAndTime" * maxDate="null" * minDate="null" * minuteStepSize="1" * selectedDate="" * * Styles * accentColor="0x0099FF" * /> ** * @see spark.components.SpinnerList * @see spark.components.calendarClasses.DateSpinnerItemRenderer * @see spark.skins.mobile.DateSpinnerSkin * * @includeExample examples/DateSpinnerExample.mxml -noswf * * @langversion 3.0 * @playerversion AIR 3 * @productversion Flex 4.6 */ public class DateSpinner extends SkinnableComponent { //-------------------------------------------------------------------------- // // Class constants // //-------------------------------------------------------------------------- /** * Specifies to the
createDateItemList()
method that the list is for showing
* years.
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
protected static const YEAR_ITEM:String = "yearItem";
/**
* Specifies to the createDateItemList()
method that the list is for showing
* months.
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
protected static const MONTH_ITEM:String = "monthItem";
/**
* Specifies to the createDateItemList()
method that the list is for showing
* dates of the month or year.
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
protected static const DATE_ITEM:String = "dateItem";
/**
* Specifies to the createDateItemList()
method that the list is for showing
* hours.
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
protected static const HOUR_ITEM:String = "hourItem";
/**
* Specifies to the createDateItemList()
method that the list is for showing
* minutes.
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
protected static const MINUTE_ITEM:String = "minuteItem";
/**
* Specifies to the createDateItemList()
method that the list is for showing
* meridian options.
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
protected static const MERIDIAN_ITEM:String = "meridianItem";
private static const MS_IN_DAY:Number = 1000 * 60 * 60 * 24;
// choosing January 1980 to guarantee 31 days in the month
private static const JAN1980_IN_MS:Number = 315561660000;
// meridian
private static const AM:String = "am";
private static const PM:String = "pm";
// default min/max date
private static const MIN_DATE_DEFAULT:Date = new Date(DateTimeFormatterEx.MIN_YEAR, 0, 1);
private static const MAX_DATE_DEFAULT:Date = new Date(9999, 11, 31, 23, 59, 59, 999);
// Emphasized constant
mx_internal static const EMPHASIZED_PROPERTY_NAME:String = "_emphasized_";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
public function DateSpinner()
{
super();
}
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
private var dispatchValueCommitEvent:Boolean = false;
private var dispatchChangeEvent:Boolean = false;
private var populateYearDataProvider:Boolean = true;
private var populateMonthDataProvider:Boolean = true;
private var populateDateDataProvider:Boolean = true;
private var populateHourDataProvider:Boolean = true;
private var populateMinuteDataProvider:Boolean = true;
private var populateMeridianDataProvider:Boolean = true;
private var refreshDateTimeFormatter:Boolean = true;
// the internal DateTimeFormatter that provides a set of extended functionalities
private var dateTimeFormatterEx:DateTimeFormatterEx = new DateTimeFormatterEx();
private var dateTimeFormatter:DateTimeFormatter = new DateTimeFormatter();
// the DateTimeFormatterEx that uses MMMEEEd skeleton pattern to identify
// the longest dateList item in DATE_AND_TIME mode
private var dayMonthDateFormatter:DateTimeFormatterEx;
// the NumberFormatter to identify the longest yearList item in DATE mode
private var numberFormatter:NumberFormatter;
// caching the longest digit (the array value) between index and 9, inclusive, and refresh when locale changes
private var longestDigitArray:Array = new Array(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
private var dateObj:Date = new Date();
private var use24HourTime:Boolean;
// store the longest dateList item and updates only when locale changes
private var longestDateItem:Object;
private var longestYearItem:Object;
// controls whether we should snap to or animate to spinner values
private var useAnimationToSetSelectedDate:Boolean = false;
// keep track of which lists are currently animating for programmatic animations
private var listsBeingAnimated:Dictionary = new Dictionary();
//--------------------------------------------------------------------------
//
// Skin parts
//
//--------------------------------------------------------------------------
[SkinPart]
/**
* The default factory for creating SpinnerList interfaces for all fields.
* This is used by the createDateItemList()
method.
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
public var dateItemList:IFactory;
[SkinPart]
/**
* The container for the date part lists.
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
public var listContainer:IVisualElementContainer;
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
/**
* The SpinnerList that shows the year field of the date.
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
protected var yearList:SpinnerList;
/**
* The SpinnerList that shows the month field of the date.
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
protected var monthList:SpinnerList;
/**
* The SpinnerList that shows the date field of the date.
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
protected var dateList:SpinnerList;
/**
* The SpinnerList that shows the hour field of the date.
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
protected var hourList:SpinnerList;
/**
* The SpinnerList that shows the minutes field of the date.
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
protected var minuteList:SpinnerList;
/**
* The SpinnerList that shows the meridian field (AM/PM) of the date.
*
* @langversion 3.0
* @playerversion AIR 3
* @productversion Flex 4.6
*/
protected var meridianList:SpinnerList;
//----------------------------------
// displayMode
//----------------------------------
private var _displayMode:String = DateSelectorDisplayMode.DATE;
private var displayModeChanged:Boolean = false;
[Inspectable(category="General", enumeration="date,time,dateAndTime", defaultValue="date")]
/**
* Mode the DateSpinner is currently using for display. You can set this value to the constants defined in the
* DateSelectorDisplayMode class, or to their string equivalents.
*
* The following table describes the possible values: *
Mode (String equivalent) | Description |
---|---|
DateSelectorDisplayMode.DATE ("date") | Displays the month, day, and year. This is the default mode. |
DateSelectorDisplayMode.TIME ("time") | Displays the day of week, month, day, and time. |
DateSelectorDisplayMode.DATE_AND_TIME ("dateAndTime") | Displays the hours and minutes, and, if the locale supports it, the AM/PM selector. |
Note that the Date object returned by the getter is a copy of * the internal Date, and the one provided to the setter is copied * internally, so modifications to these will not be reflected in the * DateSpinner.
* * @default Dec 31st, 9999 * * @langversion 3.0 * @playerversion AIR 3 * @productversion Flex 4.6 */ public function get maxDate():Date { return new Date(_maxDate.time); } /** * @private */ public function set maxDate(value:Date):void { // don't allow minDate to be outside of the defaults if (value && (value.time < MIN_DATE_DEFAULT.time || value.time > MAX_DATE_DEFAULT.time)) value = null; // ignore if no change if ((_maxDate && value && _maxDate.time == value.time) || (_maxDate == null && value == null)) return; _maxDate = new Date(value != null ? value.time : MAX_DATE_DEFAULT.time); populateYearDataProvider = true; populateDateDataProvider = true; maxDateChanged = true; syncSelectedDate = true; invalidateProperties(); } //---------------------------------- // minDate //---------------------------------- private var _minDate:Date = new Date(MIN_DATE_DEFAULT.time); private var minDateChanged:Boolean = false; /** * Minimum selectable date; only this date and dates after this date are selectable. * *Note that the Date object returned by the getter is a copy of * the internal Date, and the one provided to the setter is copied * internally, so modifications to these will not be reflected in the * DateSpinner.
* *The value of the minDate property's year must be greater than or equal to 1601 because * the DateTimeFormatter class supports only the range from 1601 to 30827.
* * @default January 1st, 1601 * @langversion 3.0 * @playerversion AIR 3 * @productversion Flex 4.6 */ public function get minDate():Date { return new Date(_minDate.time); } /** * @private */ public function set minDate(value:Date):void { // don't allow minDate to be outside of the defaults if (value && (value.time < MIN_DATE_DEFAULT.time || value.time > MAX_DATE_DEFAULT.time)) value = null; // ignore if no change if ((_minDate && value && _minDate.time == value.time) || (_minDate == null && value == null)) return; _minDate = new Date(value != null ? value.time : MIN_DATE_DEFAULT.time); populateYearDataProvider = true; populateDateDataProvider = true; minDateChanged = true; syncSelectedDate = true; invalidateProperties(); } //---------------------------------- // minuteStepSize //---------------------------------- private var _minuteStepSize:int = 1; private var minuteStepSizeChanged:Boolean = false; /** * Minute interval to be used when displaying minutes. Only * applicable in TIME and DATE_AND_TIME modes. Valid values must * be evenly divisible into 60; invalid values will revert to * the default interval of 1. For example, a value of "15" will show * the values 0, 15, 30, 45. * * @default 1 * @langversion 3.0 * @playerversion AIR 3 * @productversion Flex 4.6 */ public function get minuteStepSize():int { return _minuteStepSize; } /** * @private */ public function set minuteStepSize(value:int):void { // adjust for invalid values if (value <= 0 || 60 % value != 0) value = 1; // short-circuit if no change if (value == _minuteStepSize) return; _minuteStepSize = value; minuteStepSizeChanged = true; populateMinuteDataProvider = true; syncSelectedDate = true; invalidateProperties(); } //---------------------------------- // selectedDate //---------------------------------- private var _selectedDate:Date = todayDate; // set to true initially so that lists will be set to right values on creation private var syncSelectedDate:Boolean = true; [Bindable(event="valueCommit")] /** * Date that is currently selected in the DateSpinner control. * *Note that the Date object returned by the getter is a copy of * the internal Date, and the one provided to the setter is copied * internally, so modifications to these will not be reflected in the * DateSpinner. To modify the DateSpinner's date, get and modify the * current selectedDate and then re-set the selectedDate.
* * @default the current date * * @langversion 3.0 * @playerversion AIR 3 * @productversion Flex 4.6 * */ public function get selectedDate():Date { return new Date(_selectedDate.time); } /** * @private */ public function set selectedDate(value:Date):void { // no-op if null; there must always be a selectedDate if (value == null) value = todayDate; // short-circuit if no change if (value.time == _selectedDate.time) return; _selectedDate = new Date(value.time); syncSelectedDate = true; dispatchValueCommitEvent = true; invalidateProperties(); } mx_internal function animateToSelectedDate(value:Date):void { // no-op if null; there must always be a selectedDate if (value == null) value = todayDate; // short-circuit if no change if (value.time == _selectedDate.time) return; selectedDate = value; useAnimationToSetSelectedDate = true; } //---------------------------------- // todayDate //---------------------------------- mx_internal static var _todayDate:Date = null; /** * @private * Function to retrieve the current Date. Provided so that * testing can override by setting the _todayDate variable. */ private static function get todayDate():Date { if (_todayDate != null) { // use a copy, not the original that was passed in return new Date(_todayDate.time); } return new Date(); } //-------------------------------------------------------------------------- // // Overridden methods // //-------------------------------------------------------------------------- /** * @private */ override protected function attachSkin():void { super.attachSkin(); displayModeChanged = true; invalidateProperties(); } /** * @private */ override protected function commitProperties():void { super.commitProperties(); var localeStr:String = getStyle("locale"); if (refreshDateTimeFormatter) { if (localeStr) { dateTimeFormatterEx.setStyle("locale", localeStr); dateTimeFormatter.setStyle("locale", localeStr); } else { dateTimeFormatterEx.clearStyle("locale"); dateTimeFormatter.clearStyle("locale"); } use24HourTime = dateTimeFormatterEx.getUse24HourFlag(); refreshDateTimeFormatter = false; } // stop any animations if we might be resetting the visible values if (syncSelectedDate) stopAllAnimations(); // ================================================== // switch out lists if the display mode changed if (displayModeChanged) { setupDateItemLists(); displayModeChanged = false; syncSelectedDate = true; } // ================================================== // populate the lists with the appropriate data providers populateDateItemLists(localeStr); // ================================================== // correct any integrity violations if (minDateChanged || maxDateChanged || syncSelectedDate || minuteStepSizeChanged) { // check min <= max if (_minDate.time > _maxDate.time) { // note assumption here that we're not using the defaults since they // should always maintain minDate < maxDate integrity // correct min/max dates, one day apart if (!maxDateChanged) _minDate.time = _maxDate.time - MS_IN_DAY; // min date was changed past max else _maxDate.time = _minDate.time + MS_IN_DAY; // max date was changed past min } // make sure there's at least one minuteStepSize between the min and max if ((_maxDate.time - _minDate.time) < (minuteStepSize * 60 * 1000)) _maxDate.time = _minDate.time + (minuteStepSize * 60 * 1000); var origSelectedDate:Date = new Date(_selectedDate.time); // check minDate <= selectedDate <= maxDate if (!_selectedDate || _selectedDate.time < _minDate.time) { _selectedDate = new Date(_minDate.time); } else if (_selectedDate.time > _maxDate.time) { _selectedDate = new Date(_maxDate.time); } minDateChanged = false; maxDateChanged = false; if (minuteStepSizeChanged) { // verify minutes are a multiple of minuteStepSize if ((_selectedDate.minutes % minuteStepSize) != 0) { // adjust to the closest number evenly disible by minuteStepSize _selectedDate.minutes = Math.round(_selectedDate.minutes / minuteStepSize) * minuteStepSize; // last adjustment to make sure we didn't accidentally go outside of bounds if (_selectedDate.time < _minDate.time) _selectedDate.minutes += minuteStepSize; else if (_selectedDate.time > _maxDate.time) _selectedDate.minutes -= minuteStepSize; } minuteStepSizeChanged = false; } if (origSelectedDate.time != _selectedDate.time) dispatchValueCommitEvent = true; disableInvalidSpinnerValues(_selectedDate); } // ================================================== // update selections on the lists if necessary if (syncSelectedDate) { updateListsToSelectedDate(useAnimationToSetSelectedDate); syncSelectedDate = false; if (dispatchChangeEvent || dispatchValueCommitEvent) { // dispatch the events: now or after animation? if (useAnimationToSetSelectedDate) { // we animated the list ourselves; wait for lists // to report VALUE_COMMIT before dispatching our events var numEls:int = listContainer.numElements; var sl:SpinnerList; for (var elIdx:int = 0; elIdx < numEls; elIdx++) { sl = listContainer.getElementAt(elIdx) as SpinnerList; sl.addEventListener(FlexEvent.VALUE_COMMIT, waitForSpinnerListValueCommit_handler); } } else { // dispatch our events immediately dispatchSelectedDateChangedEvents(); } } } // reset flags useAnimationToSetSelectedDate = false; } /** * @private */ override public function styleChanged(styleProp:String):void { super.styleChanged(styleProp); // if locale changed, all date item formats need to be regenerated if (styleProp == "locale") { displayModeChanged = true; // order of lists may be different with new locale populateYearDataProvider = true; populateMonthDataProvider = true; populateDateDataProvider = true; populateHourDataProvider = true; populateMinuteDataProvider = true; populateMeridianDataProvider = true; refreshDateTimeFormatter = true; syncSelectedDate = true; longestDateItem = null; longestYearItem = null; invalidateProperties(); } } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * Create a list object for the specified date part. * * @param datePart Use date part constants, e.g. YEAR_ITEM. * @param itemIndex Index of the date part in the overall list container. * @param itemCount Number of date parts being shown. * * @langversion 3.0 * @playerversion AIR 3 * @productversion Flex 4.6 * * @return A SpinnerList that contains the part. */ protected function createDateItemList(datePart:String, itemIndex:int, itemCount:int):SpinnerList { return SpinnerList(createDynamicPartInstance("dateItemList")); } /** * @private * Sets up the date item lists based on the current mode. Clears pre-existing lists. */ private function setupDateItemLists():void { // an array of the list and position objects that will be sorted by position var fieldPositionObjArray:ArrayCollection = new ArrayCollection(); var listSort:ISort = new Sort(); listSort.fields = [new SortField("position")]; fieldPositionObjArray.sort = listSort; // clean out the container and all existing lists // they may be in different positions, which will affect how they // need to be (re)created cleanContainer(); var fieldPosition:int = 0; var listItem:Object; var tempList:SpinnerList; var numItems:int; // configure the correct lists to use if (displayMode == DateSelectorDisplayMode.TIME || displayMode == DateSelectorDisplayMode.DATE_AND_TIME) { fieldPositionObjArray.addItem(generateFieldPositionObject(HOUR_ITEM, dateTimeFormatterEx.getHourPosition())); fieldPositionObjArray.addItem(generateFieldPositionObject(MINUTE_ITEM, dateTimeFormatterEx.getMinutePosition())); if (displayMode == DateSelectorDisplayMode.DATE_AND_TIME) fieldPositionObjArray.addItem(generateFieldPositionObject(DATE_ITEM, dateTimeFormatterEx.getMonthPosition())); if (!use24HourTime) fieldPositionObjArray.addItem(generateFieldPositionObject(MERIDIAN_ITEM, dateTimeFormatterEx.getAmPmPosition())); // sort fieldPosition objects by position fieldPositionObjArray.refresh(); numItems = fieldPositionObjArray.length; for each (listItem in fieldPositionObjArray) { switch(listItem.dateItem) { case HOUR_ITEM: { hourList = createDateItemList(HOUR_ITEM, fieldPosition++, numItems); tempList = hourList; break; } case MINUTE_ITEM: { minuteList = createDateItemList(MINUTE_ITEM, fieldPosition++, numItems); tempList = minuteList; break; } case MERIDIAN_ITEM: { meridianList = createDateItemList(MERIDIAN_ITEM, fieldPosition++, numItems); tempList = meridianList; break; } case DATE_ITEM: { dateList = createDateItemList(DATE_ITEM, fieldPosition++, numItems); dateList.wrapElements = false; tempList = dateList; break; } } if (tempList && listContainer) { tempList.addEventListener(IndexChangeEvent.CHANGE, dateItemList_changeHandler); listContainer.addElement(tempList); } } } else // default case: DATE mode { fieldPositionObjArray.addItem(generateFieldPositionObject(MONTH_ITEM, dateTimeFormatterEx.getMonthPosition())); fieldPositionObjArray.addItem(generateFieldPositionObject(DATE_ITEM, dateTimeFormatterEx.getDayOfMonthPosition())); fieldPositionObjArray.addItem(generateFieldPositionObject(YEAR_ITEM, dateTimeFormatterEx.getYearPosition())); // sort fieldPosition objects by position fieldPositionObjArray.refresh(); numItems = fieldPositionObjArray.length; for each (listItem in fieldPositionObjArray) { switch(listItem.dateItem) { case MONTH_ITEM: { monthList = createDateItemList(MONTH_ITEM, fieldPosition++, numItems); tempList = monthList; break; } case DATE_ITEM: { dateList = createDateItemList(DATE_ITEM, fieldPosition++, numItems); tempList = dateList; break; } case YEAR_ITEM: { yearList = createDateItemList(YEAR_ITEM, fieldPosition++, numItems); yearList.wrapElements = false; tempList = yearList; break; } } if (tempList && listContainer) { tempList.addEventListener(IndexChangeEvent.CHANGE, dateItemList_changeHandler); listContainer.addElement(tempList); } } } } /** * Populate the currently existing date item lists with correct data providers using the * provided locale to format the date text correctly. */ private function populateDateItemLists(localeStr:String):void { var today:Date = todayDate; // populate lists that are being shown if (yearList && populateYearDataProvider) { yearList.dataProvider = new YearProvider(localeStr, _minDate.fullYear, _maxDate.fullYear, today); // set size to longest string if (!longestYearItem) longestYearItem = findLongestYearItem(); yearList.typicalItem = longestYearItem; alignList(yearList); } if (monthList && populateMonthDataProvider) { monthList.dataProvider = generateMonths(today); // set size monthList.typicalItem = getLongestLabel(monthList.dataProvider); alignList(monthList); } if (dateList && populateDateDataProvider) { if (displayMode == DateSelectorDisplayMode.DATE_AND_TIME) { dateList.dataProvider = new DateAndTimeProvider(localeStr, _minDate, _maxDate, today); // set size to longest string if (!longestDateItem) longestDateItem = findLongestDateItem(); dateList.typicalItem = longestDateItem; } else { dateList.dataProvider = generateMonthOfDates(today); // set size to width of longest visible value dateList.typicalItem = getLongestLabel(dateList.dataProvider); } alignList(dateList); } if (hourList && populateHourDataProvider) { hourList.dataProvider = generateHours(use24HourTime); hourList.typicalItem = getLongestLabel(hourList.dataProvider); alignList(hourList); } if (minuteList && populateMinuteDataProvider) { minuteList.dataProvider = generateMinutes(); minuteList.typicalItem = getLongestLabel(minuteList.dataProvider); alignList(minuteList); } if (meridianList && populateMeridianDataProvider) { var amObject:Object = generateAmPm(AM); var pmObject:Object = generateAmPm(PM); meridianList.dataProvider = new ArrayCollection([amObject, pmObject]); meridianList.typicalItem = getLongestLabel(meridianList.dataProvider); alignList(meridianList); } // reset all flags populateYearDataProvider = false; populateMonthDataProvider = false; populateDateDataProvider = false; populateHourDataProvider = false; populateMinuteDataProvider = false; populateMeridianDataProvider = false; } // set the selected index on the SpinnerList. use animation only if requested private function goToIndex(list:SpinnerList, newIndex:int, useAnimation:Boolean):void { // don't do anything if it's already on that index if (list.selectedIndex == newIndex) return; if (useAnimation) { list.animateToSelectedIndex(newIndex); listsBeingAnimated[list] = true; } else { list.selectedIndex = newIndex; } } // generate objects to populate a SpinnerList with months private function generateMonths(today:Date):IList { var ac:ArrayCollection = new ArrayCollection(); var todayMonth:int = today.getMonth(); var monthNames:Vector.