All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scaffold.libs_as.feathers.controls.DateTimeSpinner.as Maven / Gradle / Ivy

/*
Feathers
Copyright 2012-2015 Bowler Hat LLC. All Rights Reserved.

This program is free software. You can redistribute and/or modify it in
accordance with the terms of the accompanying license agreement.
*/
package feathers.controls
{
	import feathers.controls.renderers.DefaultListItemRenderer;
	import feathers.core.FeathersControl;
	import feathers.data.ListCollection;
	import feathers.layout.HorizontalAlign;
	import feathers.layout.HorizontalLayout;
	import feathers.layout.VerticalAlign;
	import feathers.skins.IStyleProvider;
	import feathers.utils.math.roundDownToNearest;
	import feathers.utils.math.roundUpToNearest;

	import flash.globalization.DateTimeFormatter;
	import flash.globalization.DateTimeNameStyle;
	import flash.globalization.DateTimeStyle;
	import flash.globalization.LocaleID;

	import starling.events.Event;

	/**
	 * Dispatched when the spinner's value changes.
	 *
	 * 

The properties of the event object have the following values:

* * * * * * *
PropertyValue
bubblesfalse
currentTargetThe Object that defines the * event listener that handles the event. For example, if you use * myButton.addEventListener() to register an event listener, * myButton is the value of the currentTarget.
datanull
targetThe Object that dispatched the event; * it is not always the Object listening for the event. Use the * currentTarget property to always access the Object * listening for the event.
* * @eventType starling.events.Event.CHANGE */ [Event(name="change",type="starling.events.Event")] /** * A set of SpinnerList components that allow you to select the * date, the time, or the date and time. * *

The following example sets the date spinner's range and listens for * when the value changes:

* * * var spinner:DateTimeSpinner = new DateTimeSpinner(); * spinner.editingMode = DateTimeSpinner.EDITING_MODE_DATE; * spinner.minimum = new Date(1970, 0, 1); * spinner.maximum = new Date(2050, 11, 31); * spinner.value = new Date(); * spinner.addEventListener( Event.CHANGE, spinner_changeHandler ); * this.addChild( spinner ); * *

Beta Component: This is a new component, and its APIs * may need some changes between now and the next version of Feathers to * account for overlooked requirements or other issues. Upgrading to future * versions of Feathers may involve manual changes to your code that uses * this component. The * Feathers deprecation policy * will not go into effect until this component's status is upgraded from * beta to stable.

* * @see ../../../help/date-time-spinner.html How to use the Feathers DateTimeSpinner component */ public class DateTimeSpinner extends FeathersControl { /** * The default name to use with lists. * * @see feathers.core.FeathersControl#styleNameList */ public static const DEFAULT_CHILD_STYLE_NAME_LIST:String = "feathers-date-time-spinner-list"; /** * The DateTimeSpinner will allow both the date and the * time to be edited. * * @see #editingMode */ public static const EDITING_MODE_DATE_AND_TIME:String = "dateAndTime"; /** * The DateTimeSpinner will allow only the time to be * edited. * * @see #editingMode */ public static const EDITING_MODE_TIME:String = "time"; /** * The DateTimeSpinner will allow only the date to be * edited. * * @see #editingMode */ public static const EDITING_MODE_DATE:String = "date"; /** * @private */ private static const MS_PER_DAY:int = 86400000; /** * @private */ private static const MIN_MONTH_VALUE:int = 0; /** * @private */ private static const MAX_MONTH_VALUE:int = 11; /** * @private */ private static const MIN_DATE_VALUE:int = 1; /** * @private */ private static const MAX_DATE_VALUE:int = 31; /** * @private */ private static const MIN_HOURS_VALUE:int = 0; /** * @private */ private static const MAX_HOURS_VALUE_12HOURS:int = 11; /** * @private */ private static const MAX_HOURS_VALUE_24HOURS:int = 23; /** * @private */ private static const MIN_MINUTES_VALUE:int = 0; /** * @private */ private static const MAX_MINUTES_VALUE:int = 59; /** * @private */ private static const HELPER_DATE:Date = new Date(); /** * @private */ private static const DAYS_IN_MONTH:Vector. = new []; /** * @private */ protected static const INVALIDATION_FLAG_LOCALE:String = "locale"; /** * @private */ protected static const INVALIDATION_FLAG_EDITING_MODE:String = "editingMode"; /** * @private */ protected static const INVALIDATION_FLAG_PENDING_SCROLL:String = "pendingScroll"; /** * @private */ protected static const INVALIDATION_FLAG_SPINNER_LIST_FACTORY:String = "spinnerListFactory"; /** * The default IStyleProvider for all DateTimeSpinner * components. * * @default null * @see feathers.core.FeathersControl#styleProvider */ public static var globalStyleProvider:IStyleProvider; /** * @private */ protected static function defaultListFactory():SpinnerList { return new SpinnerList(); } /** * Constructor. */ public function DateTimeSpinner() { super(); if(DAYS_IN_MONTH.length === 0) { HELPER_DATE.setFullYear(2015); //this is pretty arbitrary for(var i:int = MIN_MONTH_VALUE; i <= MAX_MONTH_VALUE; i++) { //subtract one date from the 1st of next month to figure out //the last date of the current month HELPER_DATE.setMonth(i + 1, -1); DAYS_IN_MONTH[i] = HELPER_DATE.date + 1; } DAYS_IN_MONTH.fixed = true; } } /** * The value added to the styleNameList of the lists. This * variable is protected so that sub-classes can customize * the list style name in their constructors instead of using the * default style name defined by DEFAULT_CHILD_STYLE_NAME_LIST. * *

To customize the list style name without subclassing, see * customListStyleName.

* * @see #customListStyleName * @see feathers.core.FeathersControl#styleNameList */ protected var listStyleName:String = DEFAULT_CHILD_STYLE_NAME_LIST; /** * @private */ protected var monthsList:SpinnerList; /** * @private */ protected var datesList:SpinnerList; /** * @private */ protected var yearsList:SpinnerList; /** * @private */ protected var dateAndTimeDatesList:SpinnerList; /** * @private */ protected var hoursList:SpinnerList; /** * @private */ protected var minutesList:SpinnerList; /** * @private */ protected var meridiemList:SpinnerList; /** * @private */ protected var listGroup:LayoutGroup; /** * @private */ override protected function get defaultStyleProvider():IStyleProvider { return DateTimeSpinner.globalStyleProvider; } /** * @private */ protected var _locale:String = LocaleID.DEFAULT; /** * The locale used to display the date. Supports values defined by * Unicode Technical Standard #35, such as "en_US", * "fr_FR" or "ru_RU". * * @default flash.globalization.LocaleID.DEFAULT * * @see http://unicode.org/reports/tr35/ Unicode Technical Standard #35 */ public function get locale():String { return this._locale; } /** * @private */ public function set locale(value:String):void { if(this._locale == value) { return; } this._locale = value; this._formatter = null; this.invalidate(INVALIDATION_FLAG_LOCALE); } /** * @private */ protected var _value:Date; /** * The value of the date time spinner, between the minimum and maximum. * *

In the following example, the value is changed to a date:

* * * stepper.minimum = new Date(1970, 0, 1); * stepper.maximum = new Date(2050, 11, 31); * stepper.value = new Date(1995, 2, 7); * * @default 0 * * @see #minimum * @see #maximum * @see #event:change */ public function get value():Date { return this._value; } /** * @private */ public function set value(value:Date):void { var time:Number = value.time; if(this._minimum && this._minimum.time > time) { time = this._minimum.time; } if(this._maximum && this._maximum.time < time) { time = this._maximum.time; } if(this._value && this._value.time === time) { return; } this._value = new Date(time); this.invalidate(INVALIDATION_FLAG_DATA); } /** * @private */ protected var _minimum:Date; /** * The date time spinner's value will not go lower than the minimum. * *

In the following example, the minimum is changed:

* * * spinner.minimum = new Date(1970, 0, 1); * * @see #value * @see #maximum */ public function get minimum():Date { return this._minimum; } /** * @private */ public function set minimum(value:Date):void { if(this._minimum == value) { return; } this._minimum = value; this.invalidate(INVALIDATION_FLAG_DATA); } /** * @private */ protected var _maximum:Date; /** * The date time spinner's value will not go higher than the maximum. * *

In the following example, the maximum is changed:

* * * spinner.maximum = new Date(2050, 11, 31); * * @see #value * @see #minimum */ public function get maximum():Date { return this._maximum; } /** * @private */ public function set maximum(value:Date):void { if(this._maximum == value) { return; } this._maximum = value; this.invalidate(INVALIDATION_FLAG_DATA); } /** * @private */ protected var _minuteStep:int = 1; /** * In the list that allows selection of minutes, customizes the number * of minutes between each item. For instance, one might choose 15 or * 30 minute increments. * *

In the following example, the spinner uses 15 minute increments:

* * * spinner.minuteStep = 15; * * @default 1 */ public function get minuteStep():int { return this._minuteStep; } /** * @private */ public function set minuteStep(value:int):void { if(60 % value !== 0) { throw new ArgumentError("minuteStep must evenly divide into 60."); } if(this._minuteStep == value) { return; } this._minuteStep = value; this.invalidate(INVALIDATION_FLAG_DATA); } /** * @private */ protected var _editingMode:String = EDITING_MODE_DATE_AND_TIME; /** * Determines which parts of the Date value may be edited. * * @default DateTimeSpinner.EDITING_MODE_DATE_AND_TIME * * @see #EDITING_MODE_DATE_AND_TIME * @see #EDITING_MODE_DATE * @see #EDITING_MODE_TIME */ public function get editingMode():String { return this._editingMode; } /** * @private */ public function set editingMode(value:String):void { if(this._editingMode == value) { return; } this._editingMode = value; this.invalidate(INVALIDATION_FLAG_EDITING_MODE); } /** * @private */ protected var _formatter:DateTimeFormatter; /** * @private */ protected var _localeMonthNames:Vector.; /** * @private */ protected var _localeWeekdayNames:Vector.; /** * @private */ protected var _ignoreListChanges:Boolean = false; /** * @private */ protected var _monthFirst:Boolean = true; /** * @private */ protected var _showMeridiem:Boolean = true; /** * @private */ protected var _lastMeridiemValue:int = 0; /** * @private */ protected var _listMinYear:int; /** * @private */ protected var _listMaxYear:int; /** * @private */ protected var _minYear:int; /** * @private */ protected var _maxYear:int; /** * @private */ protected var _minMonth:int; /** * @private */ protected var _maxMonth:int; /** * @private */ protected var _minDate:int; /** * @private */ protected var _maxDate:int; /** * @private */ protected var _minHours:int; /** * @private */ protected var _maxHours:int; /** * @private */ protected var _minMinute:int; /** * @private */ protected var _maxMinute:int; /** * @private */ protected var _scrollDuration:Number = 0.5; /** * The duration, in seconds, of the animation when the * scrollToDate() function is called, or when an invalid * date is selected. * *

In the following example, the duration of the animation that * changes the page when thrown is set to 250 milliseconds:

* * * spinner.scrollDuration = 0.25; * * @default 0.5 * * @see #scrollToDate() */ public function get scrollDuration():Number { return this._scrollDuration; } /** * @private */ public function set scrollDuration(value:Number):void { this._scrollDuration = value; } /** * @private */ protected var _itemRendererFactory:Function; /** * A function used to instantiate the date time spinner's item renderer * sub-components. A single factory will be shared by all * SpinnerList sub-components displayed by the * DateTimeSpinner. The item renderers must be instances of * DefaultListItemRenderer. This factory can be used to * change properties of the item renderer sub-components when they are * first created. For instance, if you are skinning Feathers components * without a theme, you might use this factory to style the item * renderer sub-components. * *

The factory should have the following function signature:

*
function():DefaultListItemRenderer
* *

In the following example, the date time spinner uses a custom item * renderer factory:

* * * spinner.itemRendererFactory = function():DefaultListItemRenderer * { * var itemRenderer:DefaultListItemRenderer = new DefaultListItemRenderer(); * // set properties * return itemRenderer; * }; * * @default null * * @see feathers.controls.renderers.DefaultListItemRenderer * @see #itemRendererFactory */ public function get itemRendererFactory():Function { return this._itemRendererFactory; } /** * @private */ public function set itemRendererFactory(value:Function):void { if(this._itemRendererFactory === value) { return; } this._itemRendererFactory = value; this.invalidate(INVALIDATION_FLAG_SPINNER_LIST_FACTORY); } /** * @private */ protected var _listFactory:Function; /** * A function used to instantiate the date time spinner's list * sub-components. The lists must be instances of * SpinnerList. This factory can be used to change * properties of the list sub-components when they are first created. * For instance, if you are skinning Feathers components without a * theme, you might use this factory to style the list sub-components. * *

Warning: The itemRendererFactory * property of the SpinnerList should not be set in the * listFactory. Instead, set the * itemRendererFactory property of the * DateTimeSpinner.

* *

The factory should have the following function signature:

*
function():SpinnerList
* *

In the following example, the date time spinner uses a custom list * factory:

* * * spinner.listFactory = function():SpinnerList * { * var list:SpinnerList = new SpinnerList(); * // set properties * return list; * }; * * @default null * * @see feathers.controls.SpinnerList * @see #itemRendererFactory */ public function get listFactory():Function { return this._listFactory; } /** * @private */ public function set listFactory(value:Function):void { if(this._listFactory == value) { return; } this._listFactory = value; this.invalidate(INVALIDATION_FLAG_TEXT_RENDERER); } /** * @private */ protected var _customListStyleName:String; /** * A style name to add to the date time spinner's list sub-components. * Typically used by a theme to provide different styles to different * date time spinners. * *

In the following example, a custom list style name is passed to * the date time spijnner:

* * * spinner.customListStyleName = "my-custom-spinner-list"; * *

In your theme, you can target this sub-component style name to * provide different styles than the default:

* * * getStyleProviderForClass( SpinnerList ).setFunctionForStyleName( "my-custom-spinner-list", setCustomSpinnerListStyles ); * * @default null * * @see #DEFAULT_CHILD_STYLE_NAME_LIST * @see feathers.core.FeathersControl#styleNameList * @see #listFactory */ public function get customListStyleName():String { return this._customListStyleName; } /** * @private */ public function set customListStyleName(value:String):void { if(this._customListStyleName == value) { return; } this._customListStyleName = value; this.invalidate(INVALIDATION_FLAG_SPINNER_LIST_FACTORY); } /** * @private */ protected var _lastValidate:Date; /** * @private */ protected var _todayLabel:String = null; /** * If not null, and the editingMode property * is set to DateTimeSpinner.EDITING_MODE_DATE_AND_TIME the * date matching today's current date will display this label instead * of the date. * *

In the following example, the label for today is set:

* * * spinner.todayLabel = "Today"; * * @default null */ public function get todayLabel():String { return this._todayLabel; } /** * @private */ public function set todayLabel(value:String):void { if(this._todayLabel == value) { return; } this._todayLabel = value; this.invalidate(INVALIDATION_FLAG_DATA); } /** * @private */ protected var _amString:String; /** * @private */ protected var _pmString:String; /** * @private */ protected var pendingScrollToDate:Date; /** * @private */ protected var pendingScrollDuration:Number; /** * After the next validation, animates the scroll positions of the lists * to a specific date. If the animationDuration argument is * NaN (the default value), the value of the * scrollDuration property is used instead. The duration is * measured in seconds. * *

In the following example, we scroll to a specific date with * animation of 1.5 seconds:

* * * spinner.scrollToDate( new Date(2016, 0, 1), 1.5 ); * * @see #scrollDuration */ public function scrollToDate(date:Date, animationDuration:Number = NaN):void { if(this.pendingScrollToDate && this.pendingScrollToDate.time === date.time && this.pendingScrollDuration === animationDuration) { return; } this.pendingScrollToDate = date; this.pendingScrollDuration = animationDuration; this.invalidate(INVALIDATION_FLAG_PENDING_SCROLL); } /** * @private */ override protected function initialize():void { if(!this.listGroup) { var groupLayout:HorizontalLayout = new HorizontalLayout(); groupLayout.horizontalAlign = HorizontalAlign.CENTER; groupLayout.verticalAlign = VerticalAlign.JUSTIFY; this.listGroup = new LayoutGroup(); this.listGroup.layout = groupLayout; this.addChild(this.listGroup); } } /** * @private */ override protected function draw():void { var editingModeInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_EDITING_MODE); var localeInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_LOCALE); var dataInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_DATA); var pendingScrollInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_PENDING_SCROLL); var spinnerListFactoryInvalid:Boolean = this.isInvalid(INVALIDATION_FLAG_SPINNER_LIST_FACTORY); if(this._todayLabel) { this._lastValidate = new Date(); } if(localeInvalid || editingModeInvalid) { this.refreshLocale(); } if(localeInvalid || editingModeInvalid || spinnerListFactoryInvalid) { this.refreshLists(editingModeInvalid || spinnerListFactoryInvalid); } if(localeInvalid || editingModeInvalid || dataInvalid || spinnerListFactoryInvalid) { this.useDefaultsIfNeeded(); this.refreshValidRanges(); this.refreshSelection(); } this.autoSizeIfNeeded(); this.listGroup.width = this.actualWidth; this.listGroup.height = this.actualHeight; this.listGroup.validate(); if(pendingScrollInvalid) { this.handlePendingScroll(); } } /** * @private */ protected function autoSizeIfNeeded():Boolean { var needsWidth:Boolean = this._explicitWidth !== this._explicitWidth; //isNaN var needsHeight:Boolean = this._explicitHeight !== this._explicitHeight; //isNaN if(!needsWidth && !needsHeight) { return false; } this.listGroup.width = this._explicitWidth; this.listGroup.height = this._explicitHeight; this.listGroup.validate(); return this.setSizeInternal(this.listGroup.width, this.listGroup.height, false); } /** * @private */ protected function refreshLists(createNewLists:Boolean):void { if(createNewLists) { this.createYearList(); this.createMonthList(); this.createDateList(); this.createHourList(); this.createMinuteList(); this.createMeridiemList(); this.createDateAndTimeDateList(); } if(this._editingMode == EDITING_MODE_DATE) { //does this locale show the month or the date first? if(this._monthFirst) { this.listGroup.setChildIndex(this.monthsList, 0); } else { this.listGroup.setChildIndex(this.datesList, 0); } } } /** * @private */ protected function createYearList():void { if(this.yearsList) { this.listGroup.removeChild(this.yearsList, true); this.yearsList = null; } if(this._editingMode !== EDITING_MODE_DATE) { return; } var listFactory:Function = (this._listFactory !== null) ? this._listFactory : defaultListFactory; this.yearsList = SpinnerList(listFactory()); var listStyleName:String = (this._customListStyleName !== null) ? this._customListStyleName : this.listStyleName; this.yearsList.styleNameList.add(listStyleName); //we'll set the data provider later, when we know what range //of years we need this.yearsList.itemRendererFactory = this.yearsListItemRendererFactory; this.yearsList.addEventListener(Event.CHANGE, yearsList_changeHandler); this.listGroup.addChild(this.yearsList); } /** * @private */ protected function createMonthList():void { if(this.monthsList) { this.listGroup.removeChild(this.monthsList, true); this.monthsList = null; } if(this._editingMode !== EDITING_MODE_DATE) { return; } var listFactory:Function = (this._listFactory !== null) ? this._listFactory : defaultListFactory; this.monthsList = SpinnerList(listFactory()); var listStyleName:String = (this._customListStyleName !== null) ? this._customListStyleName : this.listStyleName; this.monthsList.styleNameList.add(listStyleName); var monthsRange:IntegerRange = new IntegerRange(MIN_MONTH_VALUE, MAX_MONTH_VALUE, 1); var monthsCollection:ListCollection = new ListCollection(monthsRange); monthsCollection.dataDescriptor = new IntegerRangeDataDescriptor(); this.monthsList.dataProvider = monthsCollection; this.monthsList.itemRendererFactory = this.monthsListItemRendererFactory; this.monthsList.addEventListener(Event.CHANGE, monthsList_changeHandler); this.listGroup.addChildAt(this.monthsList, 0); } /** * @private */ protected function createDateList():void { if(this.datesList) { this.listGroup.removeChild(this.datesList, true); this.datesList = null; } if(this._editingMode !== EDITING_MODE_DATE) { return; } var listFactory:Function = (this._listFactory !== null) ? this._listFactory : defaultListFactory; this.datesList = SpinnerList(listFactory()); var listStyleName:String = (this._customListStyleName !== null) ? this._customListStyleName : this.listStyleName; this.datesList.styleNameList.add(listStyleName); var datesRange:IntegerRange = new IntegerRange(MIN_DATE_VALUE, MAX_DATE_VALUE, 1); var datesCollection:ListCollection = new ListCollection(datesRange); datesCollection.dataDescriptor = new IntegerRangeDataDescriptor(); this.datesList.dataProvider = datesCollection; this.datesList.itemRendererFactory = this.datesListItemRendererFactory; this.datesList.addEventListener(Event.CHANGE, datesList_changeHandler); this.listGroup.addChildAt(this.datesList, 0); } /** * @private */ protected function createHourList():void { if(this.hoursList) { this.listGroup.removeChild(this.hoursList, true); this.hoursList = null; } if(this._editingMode === EDITING_MODE_DATE) { return; } var listFactory:Function = (this._listFactory !== null) ? this._listFactory : defaultListFactory; this.hoursList = SpinnerList(listFactory()); var listStyleName:String = (this._customListStyleName !== null) ? this._customListStyleName : this.listStyleName; this.hoursList.styleNameList.add(listStyleName); this.hoursList.itemRendererFactory = this.hoursListItemRendererFactory; this.hoursList.addEventListener(Event.CHANGE, hoursList_changeHandler); this.listGroup.addChild(this.hoursList); } /** * @private */ protected function createMinuteList():void { if(this.minutesList) { this.listGroup.removeChild(this.minutesList, true); this.minutesList = null; } if(this._editingMode === EDITING_MODE_DATE) { return; } var listFactory:Function = (this._listFactory !== null) ? this._listFactory : defaultListFactory; this.minutesList = SpinnerList(listFactory()); var listStyleName:String = (this._customListStyleName !== null) ? this._customListStyleName : this.listStyleName; this.minutesList.styleNameList.add(listStyleName); var minutesRange:IntegerRange = new IntegerRange(MIN_MINUTES_VALUE, MAX_MINUTES_VALUE, this._minuteStep); var minutesCollection:ListCollection = new ListCollection(minutesRange); minutesCollection.dataDescriptor = new IntegerRangeDataDescriptor(); this.minutesList.dataProvider = minutesCollection; this.minutesList.itemRendererFactory = this.minutesListItemRendererFactory; this.minutesList.addEventListener(Event.CHANGE, minutesList_changeHandler); this.listGroup.addChild(this.minutesList); } /** * @private */ protected function createMeridiemList():void { if(this.meridiemList) { this.listGroup.removeChild(this.meridiemList, true); this.meridiemList = null; } if(this._editingMode === EDITING_MODE_DATE || !this._showMeridiem) { return; } var listFactory:Function = (this._listFactory !== null) ? this._listFactory : defaultListFactory; this.meridiemList = SpinnerList(listFactory()); var listStyleName:String = (this._customListStyleName !== null) ? this._customListStyleName : this.listStyleName; this.meridiemList.styleNameList.add(listStyleName); this.meridiemList.itemRendererFactory = this.meridiemListItemRendererFactory; this.meridiemList.addEventListener(Event.CHANGE, meridiemList_changeHandler); this.listGroup.addChild(this.meridiemList); } /** * @private */ protected function createDateAndTimeDateList():void { if(this.dateAndTimeDatesList) { this.listGroup.removeChild(this.dateAndTimeDatesList, true); this.dateAndTimeDatesList = null; } if(this._editingMode !== EDITING_MODE_DATE_AND_TIME) { return; } var listFactory:Function = (this._listFactory !== null) ? this._listFactory : defaultListFactory; this.dateAndTimeDatesList = SpinnerList(listFactory()); var listStyleName:String = (this._customListStyleName !== null) ? this._customListStyleName : this.listStyleName; this.dateAndTimeDatesList.styleNameList.add(listStyleName); this.dateAndTimeDatesList.itemRendererFactory = this.dateAndTimeDatesListItemRendererFactory; this.dateAndTimeDatesList.addEventListener(Event.CHANGE, dateAndTimeDatesList_changeHandler); this.listGroup.addChildAt(this.dateAndTimeDatesList, 0); } /** * @private */ protected function refreshLocale():void { if(!this._formatter || this._formatter.requestedLocaleIDName != this._locale) { this._formatter = new DateTimeFormatter(this._locale, DateTimeStyle.SHORT, DateTimeStyle.SHORT); var dateTimePattern:String = this._formatter.getDateTimePattern(); //figure out if month or date should be displayed first var monthIndex:int = dateTimePattern.indexOf("M"); var dateIndex:int = dateTimePattern.indexOf("d"); this._monthFirst = monthIndex < dateIndex; //figure out if this locale uses am/pm or 24-hour format this._showMeridiem = dateTimePattern.indexOf("a") >= 0; if(this._showMeridiem) { this._formatter.setDateTimePattern("a"); HELPER_DATE.setHours(1); //different locales have different names for am and pm //as an example, see zh_CN this._amString = this._formatter.format(HELPER_DATE); HELPER_DATE.setHours(13); this._pmString = this._formatter.format(HELPER_DATE); this._formatter.setDateTimePattern(dateTimePattern); } } if(this._editingMode === EDITING_MODE_DATE) { this._localeMonthNames = this._formatter.getMonthNames(DateTimeNameStyle.FULL); this._localeWeekdayNames = null; } else if(this._editingMode === EDITING_MODE_DATE_AND_TIME) { this._localeMonthNames = this._formatter.getMonthNames(DateTimeNameStyle.SHORT_ABBREVIATION); this._localeWeekdayNames = this._formatter.getWeekdayNames(DateTimeNameStyle.LONG_ABBREVIATION); } else //time only { this._localeMonthNames = null; this._localeWeekdayNames = null; } } /** * @private */ protected function refreshSelection():void { var oldIgnoreListChanges:Boolean = this._ignoreListChanges; this._ignoreListChanges = true; if(this._editingMode === EDITING_MODE_DATE) { var yearsCollection:ListCollection = this.yearsList.dataProvider; if(yearsCollection) { var yearRange:IntegerRange = IntegerRange(yearsCollection.data); if(yearRange.minimum !== this._listMinYear || yearRange.minimum !== this._listMinYear) { yearRange.minimum = this._listMinYear; yearRange.maximum = this._listMinYear; yearsCollection.data = null; yearsCollection.data = yearRange; } } else { yearRange = new IntegerRange(this._listMinYear, this._listMaxYear, 1); yearsCollection = new ListCollection(yearRange); yearsCollection.dataDescriptor = new IntegerRangeDataDescriptor(); this.yearsList.dataProvider = yearsCollection; } } else //time only or both date and time { var totalMS:Number = this._maximum.time - this._minimum.time; var totalDays:int = totalMS / MS_PER_DAY; if(this._editingMode === EDITING_MODE_DATE_AND_TIME) { var dateAndTimeDatesCollection:ListCollection = this.dateAndTimeDatesList.dataProvider; if(dateAndTimeDatesCollection) { var datesRange:IntegerRange = IntegerRange(dateAndTimeDatesCollection.data); if(datesRange.maximum !== totalDays) { datesRange.maximum = totalDays; dateAndTimeDatesCollection.data = null; dateAndTimeDatesCollection.data = datesRange; } } else { datesRange = new IntegerRange(0, totalDays, 1); dateAndTimeDatesCollection = new ListCollection(datesRange); dateAndTimeDatesCollection.dataDescriptor = new IntegerRangeDataDescriptor(); this.dateAndTimeDatesList.dataProvider = dateAndTimeDatesCollection; } } var hoursMinimum:Number = MIN_HOURS_VALUE; var hoursMaximum:Number = this._showMeridiem ? MAX_HOURS_VALUE_12HOURS : MAX_HOURS_VALUE_24HOURS; var hoursCollection:ListCollection = this.hoursList.dataProvider; if(hoursCollection) { var hoursRange:IntegerRange = IntegerRange(hoursCollection.data); if(hoursRange.minimum !== hoursMinimum || hoursRange.maximum !== hoursMaximum) { hoursRange.minimum = hoursMinimum; hoursRange.maximum = hoursMaximum; hoursCollection.data = null; hoursCollection.data = datesRange; } } else { hoursRange = new IntegerRange(hoursMinimum, hoursMaximum, 1); hoursCollection = new ListCollection(hoursRange); hoursCollection.dataDescriptor = new IntegerRangeDataDescriptor(); this.hoursList.dataProvider = hoursCollection; } if(this._showMeridiem && !this.meridiemList.dataProvider) { this.meridiemList.dataProvider = new ListCollection(new [this._amString, this._pmString]); } } if(this.monthsList && !this.monthsList.isScrolling) { this.monthsList.selectedItem = this._value.month; } if(this.datesList && !this.datesList.isScrolling) { this.datesList.selectedItem = this._value.date; } if(this.yearsList && !this.yearsList.isScrolling) { this.yearsList.selectedItem = this._value.fullYear; } if(this.dateAndTimeDatesList) { this.dateAndTimeDatesList.selectedIndex = (this._value.time - this._minimum.time) / MS_PER_DAY; } if(this.hoursList) { if(this._showMeridiem) { this.hoursList.selectedIndex = this._value.hours % 12; } else { this.hoursList.selectedIndex = this._value.hours; } } if(this.minutesList) { this.minutesList.selectedItem = this._value.minutes; } if(this.meridiemList) { this.meridiemList.selectedIndex = (this._value.hours <= MAX_HOURS_VALUE_12HOURS) ? 0 : 1; } this._ignoreListChanges = oldIgnoreListChanges; } /** * @private */ protected function refreshValidRanges():void { var oldMinYear:int = this._minYear; var oldMaxYear:int = this._maxYear; var oldMinMonth:int = this._minMonth; var oldMaxMonth:int = this._maxMonth; var oldMinDate:int = this._minDate; var oldMaxDate:int = this._maxDate; var oldMinHours:int = this._minHours; var oldMaxHours:int = this._maxHours; var oldMinMinutes:int = this._minMinute; var oldMaxMinutes:int = this._maxMinute; var currentYear:int = this._value.fullYear; var currentMonth:int = this._value.month; var currentDate:int = this._value.date; var currentHours:int = this._value.hours; this._minYear = this._minimum.fullYear; this._maxYear = this._maximum.fullYear; if(currentYear === this._minYear) { this._minMonth = this._minimum.month; } else { this._minMonth = MIN_MONTH_VALUE; } if(currentYear === this._maxYear) { this._maxMonth = this._maximum.month; } else { this._maxMonth = MAX_MONTH_VALUE; } if(currentYear === this._minYear && currentMonth === this._minimum.month) { this._minDate = this._minimum.date; } else { this._minDate = MIN_DATE_VALUE; } if(currentYear === this._maxYear && currentMonth === this._maximum.month) { this._maxDate = this._maximum.date; } else { if(currentMonth === 1) //february has a variable number of days { //subtract one date from march 1st to figure out the last //date of february for the specified year HELPER_DATE.setFullYear(currentYear, currentMonth + 1, -1); this._maxDate = HELPER_DATE.date + 1; } else //all other months have been pre-calculated { this._maxDate = DAYS_IN_MONTH[currentMonth]; } } if(this._editingMode === EDITING_MODE_DATE_AND_TIME) { if(currentYear === this._minYear && currentMonth === this._minimum.month && currentDate === this._minimum.date) { this._minHours = this._minimum.hours; } else { this._minHours = MIN_HOURS_VALUE; } if(currentYear === this._maxYear && currentMonth === this._maximum.month && currentDate === this._maximum.date) { this._maxHours = this._maximum.hours; } else { this._maxHours = MAX_HOURS_VALUE_24HOURS; } if(currentYear === this._minYear && currentMonth === this._minimum.month && currentDate === this._minimum.date && currentHours === this._minimum.hours) { this._minMinute = this._minimum.minutes; } else { this._minMinute = MIN_MINUTES_VALUE; } if(currentYear === this._maxYear && currentMonth === this._maximum.month && currentDate === this._maximum.date && currentHours === this._maximum.hours) { this._maxMinute = this._maximum.minutes; } else { this._maxMinute = MAX_MINUTES_VALUE; } } else //time { this._minHours = this._minimum.hours; this._maxHours = this._maximum.hours; if(currentHours === this._minHours) { this._minMinute = this._minimum.minutes; } else { this._minMinute = MIN_MINUTES_VALUE; } if(currentHours === this._maxHours) { this._maxMinute = this._maximum.minutes; } else { this._maxMinute = MAX_MINUTES_VALUE; } } //the item renderers in the lists may need to be enabled or disabled //after the ranges change, so we need to call updateAll() on the //collections var yearsCollection:ListCollection = this.yearsList ? this.yearsList.dataProvider : null; if(yearsCollection && (oldMinYear !== this._minYear || oldMaxYear !== this._maxYear)) { //we need to ensure that the item renderers are enabled yearsCollection.updateAll(); } var monthsCollection:ListCollection = this.monthsList ? this.monthsList.dataProvider : null; if(monthsCollection && (oldMinMonth !== this._minMonth || oldMaxMonth !== this._maxMonth)) { monthsCollection.updateAll(); } var datesCollection:ListCollection = this.datesList ? this.datesList.dataProvider : null; if(datesCollection && (oldMinDate !== this._minDate || oldMaxDate !== this._maxDate)) { datesCollection.updateAll(); } var dateAndTimeDatesCollection:ListCollection = this.dateAndTimeDatesList ? this.dateAndTimeDatesList.dataProvider : null; if(dateAndTimeDatesCollection && (oldMinYear !== this._minYear || oldMaxYear !== this._maxYear || oldMinMonth !== this._minMonth || oldMaxMonth !== this._maxMonth || oldMinDate !== this._minDate || oldMaxDate !== this._maxDate)) { dateAndTimeDatesCollection.updateAll(); } var hoursCollection:ListCollection = this.hoursList ? this.hoursList.dataProvider : null; if(hoursCollection && (oldMinHours !== this._minHours || oldMaxHours !== this._maxHours || (this._showMeridiem && this._lastMeridiemValue !== this.meridiemList.selectedIndex))) { hoursCollection.updateAll(); } var minutesCollection:ListCollection = this.minutesList ? this.minutesList.dataProvider : null; if(minutesCollection && (oldMinMinutes !== this._minMinute || oldMaxMinutes!== this._maxMinute)) { minutesCollection.updateAll(); } if(this._showMeridiem) { this._lastMeridiemValue = (this._value.hours <= MAX_HOURS_VALUE_12HOURS) ? 0 : 1; } } /** * @private */ protected function useDefaultsIfNeeded():void { if(!this._value) { //if we don't have a value, try to use today's date this._value = new Date(); //but if there's an existing range, keep the value in there if(this._minimum && this._value.time < this._minimum.time) { this._value.time = this._minimum.time; } else if(this._maximum && this._value.time > this._maximum.time) { this._value.time = this._maximum.time; } } if(this._minimum) { //we want to be able to see years outside the range between //minimum and maximum, even if we cannot select them. otherwise, //it'll look weird to loop back to the beginning or end. if(this._editingMode === EDITING_MODE_DATE_AND_TIME) { //in this editing mode, the date is only controlled by one //spinner list, that increments by day. we shouldn't need to //go back more than a year. this._listMinYear = this._minimum.fullYear - 1; } else { //in this editing mode, the year, month, and date are //selected separately, so we should have a bigger range this._listMinYear = this._minimum.fullYear - 10; } } //if there's no minimum, we need to generate something that is //arbitrary, but acceptable for most needs else if(this._editingMode === EDITING_MODE_DATE_AND_TIME) { //in this editing mode, the date is only controlled by one //spinner list, that increments by day. we shouldn't need to //go back more than a year. HELPER_DATE.time = this._value.time; this._listMinYear = HELPER_DATE.fullYear - 1; this._minimum = new Date(this._listMinYear, MIN_MONTH_VALUE, MIN_DATE_VALUE, MIN_HOURS_VALUE, MIN_MINUTES_VALUE); } else { //in this editing mode, the year, month, and date are //selected separately, so we should have a bigger range HELPER_DATE.time = this._value.time; //goes back 100 years, rounded down to the nearest half century //for 2015, this would give us 1900 //for 2065, this would give us 1950 this._listMinYear = roundDownToNearest(HELPER_DATE.fullYear - 100, 50); this._minimum = new Date(this._listMinYear, MIN_MONTH_VALUE, MIN_DATE_VALUE, MIN_HOURS_VALUE, MIN_MINUTES_VALUE); } if(this._maximum) { if(this._editingMode === EDITING_MODE_DATE_AND_TIME) { this._listMaxYear = this._maximum.fullYear + 1; } else { this._listMaxYear = this._maximum.fullYear + 10; } } else if(this._editingMode === EDITING_MODE_DATE_AND_TIME) { HELPER_DATE.time = this._minimum.time; this._listMaxYear = HELPER_DATE.fullYear + 1; this._maximum = new Date(this._listMaxYear, MAX_MONTH_VALUE, DAYS_IN_MONTH[MAX_MONTH_VALUE], MAX_HOURS_VALUE_24HOURS, MAX_MINUTES_VALUE); } else // date { //for 2015, this would give us 2150 //for 2065, this would give us 2200 HELPER_DATE.time = this._value.time; this._listMaxYear = roundUpToNearest(HELPER_DATE.fullYear + 100, 50); this._maximum = new Date(this._listMaxYear, MAX_MONTH_VALUE, DAYS_IN_MONTH[MAX_MONTH_VALUE], MAX_HOURS_VALUE_24HOURS, MAX_MINUTES_VALUE); } } /** * @private */ protected function handlePendingScroll():void { if(!this.pendingScrollToDate) { return; } var duration:Number = this.pendingScrollDuration; if(duration !== duration) //isNaN { duration = this._scrollDuration; } if(this.yearsList) { var year:int = this.pendingScrollToDate.fullYear; if(this.yearsList.selectedItem !== year) { var yearRange:IntegerRange = IntegerRange(this.yearsList.dataProvider.data); this.yearsList.scrollToDisplayIndex(year - yearRange.minimum, duration); } } if(this.monthsList) { var month:int = this.pendingScrollToDate.month; if(this.monthsList.selectedItem !== month) { this.monthsList.scrollToDisplayIndex(month, duration); } } if(this.datesList) { var date:int = this.pendingScrollToDate.date; if(this.datesList.selectedItem !== date) { this.datesList.scrollToDisplayIndex(date - 1, duration); } } if(this.dateAndTimeDatesList) { var dateIndex:int = (this.pendingScrollToDate.time - this._minimum.time) / MS_PER_DAY; if(this.dateAndTimeDatesList.selectedIndex !== dateIndex) { this.dateAndTimeDatesList.scrollToDisplayIndex(dateIndex, duration); } } if(this.hoursList) { var hours:int = this.pendingScrollToDate.hours; if(this._showMeridiem) { hours %= 12; } if(this.hoursList.selectedItem !== hours) { this.hoursList.scrollToDisplayIndex(hours, duration); } } if(this.minutesList) { var minutes:int = this.pendingScrollToDate.minutes; if(this.minutesList.selectedItem !== minutes) { this.minutesList.scrollToDisplayIndex(minutes, duration); } } if(this.meridiemList) { var index:int = (this.pendingScrollToDate.hours < MAX_HOURS_VALUE_12HOURS) ? 0 : 1; if(this.meridiemList.selectedIndex !== index) { this.meridiemList.scrollToDisplayIndex(index, duration); } } } /** * @private */ protected function meridiemListItemRendererFactory():DefaultListItemRenderer { if(this._itemRendererFactory !== null) { var itemRenderer:DefaultListItemRenderer = DefaultListItemRenderer(this._itemRendererFactory()); } else { itemRenderer = new DefaultListItemRenderer(); } return itemRenderer; } /** * @private */ protected function minutesListItemRendererFactory():DefaultListItemRenderer { if(this._itemRendererFactory !== null) { var itemRenderer:DefaultListItemRenderer = DefaultListItemRenderer(this._itemRendererFactory()); } else { itemRenderer = new DefaultListItemRenderer(); } itemRenderer.labelFunction = formatMinutes; itemRenderer.enabledFunction = isMinuteEnabled; itemRenderer.itemHasEnabled = true; return itemRenderer; } /** * @private */ protected function hoursListItemRendererFactory():DefaultListItemRenderer { if(this._itemRendererFactory !== null) { var itemRenderer:DefaultListItemRenderer = DefaultListItemRenderer(this._itemRendererFactory()); } else { itemRenderer = new DefaultListItemRenderer(); } itemRenderer.labelFunction = formatHours; itemRenderer.enabledFunction = isHourEnabled; itemRenderer.itemHasEnabled = true; return itemRenderer; } /** * @private */ protected function dateAndTimeDatesListItemRendererFactory():DefaultListItemRenderer { if(this._itemRendererFactory !== null) { var itemRenderer:DefaultListItemRenderer = DefaultListItemRenderer(this._itemRendererFactory()); } else { itemRenderer = new DefaultListItemRenderer(); } itemRenderer.labelFunction = formatDateAndTimeDate; itemRenderer.accessoryLabelFunction = formatDateAndTimeWeekday; return itemRenderer; } /** * @private */ protected function monthsListItemRendererFactory():DefaultListItemRenderer { if(this._itemRendererFactory !== null) { var itemRenderer:DefaultListItemRenderer = DefaultListItemRenderer(this._itemRendererFactory()); } else { itemRenderer = new DefaultListItemRenderer(); } itemRenderer.labelFunction = formatMonthName; itemRenderer.enabledFunction = isMonthEnabled; itemRenderer.itemHasEnabled = true; return itemRenderer; } /** * @private */ protected function datesListItemRendererFactory():DefaultListItemRenderer { if(this._itemRendererFactory !== null) { var itemRenderer:DefaultListItemRenderer = DefaultListItemRenderer(this._itemRendererFactory()); } else { itemRenderer = new DefaultListItemRenderer(); } itemRenderer.enabledFunction = isDateEnabled; itemRenderer.itemHasEnabled = true; return itemRenderer; } /** * @private */ protected function yearsListItemRendererFactory():DefaultListItemRenderer { if(this._itemRendererFactory !== null) { var itemRenderer:DefaultListItemRenderer = DefaultListItemRenderer(this._itemRendererFactory()); } else { itemRenderer = new DefaultListItemRenderer(); } itemRenderer.enabledFunction = isYearEnabled; itemRenderer.itemHasEnabled = true; return itemRenderer; } /** * @private */ protected function isMonthEnabled(month:int):Boolean { return month >= this._minMonth && month <= this._maxMonth; } /** * @private */ protected function isYearEnabled(year:int):Boolean { return year >= this._minYear && year <= this._maxYear; } /** * @private */ protected function isDateEnabled(date:int):Boolean { return date >= this._minDate && date <= this._maxDate; } /** * @private */ protected function isHourEnabled(hour:int):Boolean { if(this._showMeridiem && this.meridiemList.selectedIndex !== 0) { hour += 12; } return hour >= this._minHours && hour <= this._maxHours; } /** * @private */ protected function isMinuteEnabled(minute:int):Boolean { return minute >= this._minMinute && minute <= this._maxMinute; } /** * @private */ protected function formatHours(item:int):String { if(this._showMeridiem) { if(item === 0) { item = 12; } return item.toString(); } var hours:String = item.toString(); if(hours.length < 2) { hours = "0" + hours; } return hours; } /** * @private */ protected function formatMinutes(item:int):String { var minutes:String = item.toString(); if(minutes.length < 2) { minutes = "0" + minutes; } return minutes; } /** * @private */ protected function formatDateAndTimeWeekday(item:int):String { HELPER_DATE.setTime(this._minimum.time); HELPER_DATE.setDate(HELPER_DATE.date + item); if(this._todayLabel) { //_lastValidate will be updated once per validation when //scrolling, which is better than creating many duplicate Date //objects in this function if(HELPER_DATE.fullYear === this._lastValidate.fullYear && HELPER_DATE.month === this._lastValidate.month && HELPER_DATE.date === this._lastValidate.date) { return ""; } } return this._localeWeekdayNames[HELPER_DATE.day] as String; } /** * @private */ protected function formatDateAndTimeDate(item:int):String { HELPER_DATE.setTime(this._minimum.time); HELPER_DATE.setDate(HELPER_DATE.date + item); if(this._todayLabel) { //_lastValidate will be updated once per validation when //scrolling, which is better than creating many duplicate Date //objects in this function if(HELPER_DATE.fullYear === this._lastValidate.fullYear && HELPER_DATE.month === this._lastValidate.month && HELPER_DATE.date === this._lastValidate.date) { return this._todayLabel; } } var monthName:String = this._localeMonthNames[HELPER_DATE.month] as String; if(this._monthFirst) { return monthName + " " + HELPER_DATE.date; } return HELPER_DATE.date + " " + monthName; } /** * @private */ protected function formatMonthName(item:int):String { return this._localeMonthNames[item] as String; } /** * @private */ protected function validateNewValue():void { var currentTime:Number = this._value.time; var minimumTime:Number = this._minimum.time; var maximumTime:Number = this._maximum.time; var needsToScroll:Boolean = false; if(currentTime < minimumTime) { needsToScroll = true; this._value.setTime(minimumTime); } else if(currentTime > maximumTime) { needsToScroll = true; this._value.setTime(maximumTime); } if(needsToScroll) { this.scrollToDate(this._value); } } /** * @private */ protected function updateHoursFromLists():void { var hours:int = this.hoursList.selectedItem as int; if(this.meridiemList && this.meridiemList.selectedIndex === 1) { hours += 12; } this._value.setHours(hours); } /** * @private */ protected function monthsList_changeHandler(event:Event):void { if(this._ignoreListChanges) { return; } var month:int = this.monthsList.selectedItem as int; this._value.setMonth(month); this.validateNewValue(); this.refreshValidRanges(); this.dispatchEventWith(Event.CHANGE); } /** * @private */ protected function datesList_changeHandler(event:Event):void { if(this._ignoreListChanges) { return; } var date:int = this.datesList.selectedItem as int; this._value.setDate(date); this.validateNewValue(); this.refreshValidRanges(); this.dispatchEventWith(Event.CHANGE); } /** * @private */ protected function yearsList_changeHandler(event:Event):void { if(this._ignoreListChanges) { return; } var year:int = this.yearsList.selectedItem as int; this._value.setFullYear(year); this.validateNewValue(); this.refreshValidRanges(); this.dispatchEventWith(Event.CHANGE); } /** * @private */ protected function dateAndTimeDatesList_changeHandler(event:Event):void { if(this._ignoreListChanges) { return; } this._value.setFullYear(this._minimum.fullYear, this._minimum.month, this._minimum.date + this.dateAndTimeDatesList.selectedIndex); this.validateNewValue(); this.refreshValidRanges(); this.dispatchEventWith(Event.CHANGE); } /** * @private */ protected function hoursList_changeHandler(event:Event):void { if(this._ignoreListChanges) { return; } this.updateHoursFromLists(); this.validateNewValue(); this.refreshValidRanges(); this.dispatchEventWith(Event.CHANGE); } /** * @private */ protected function minutesList_changeHandler(event:Event):void { if(this._ignoreListChanges) { return; } var minutes:int = this.minutesList.selectedItem as int; this._value.setMinutes(minutes); this.validateNewValue(); this.refreshValidRanges(); this.dispatchEventWith(Event.CHANGE); } /** * @private */ protected function meridiemList_changeHandler(event:Event):void { if(this._ignoreListChanges) { return; } this.updateHoursFromLists(); this.validateNewValue(); this.refreshValidRanges(); this.dispatchEventWith(Event.CHANGE); } } } import feathers.data.IListCollectionDataDescriptor; class IntegerRange { public function IntegerRange(minimum:int, maximum:int, step:int = 1) { this.minimum = minimum; this.maximum = maximum; this.step = step; } public var minimum:int; public var maximum:int; public var step:int; } class IntegerRangeDataDescriptor implements IListCollectionDataDescriptor { public function getLength(data:Object):int { var range:IntegerRange = IntegerRange(data); return 1 + int((range.maximum - range.minimum) / range.step); } public function getItemAt(data:Object, index:int):Object { var range:IntegerRange = IntegerRange(data); var maximum:int = range.maximum; var result:int = range.minimum + index * range.step; if(result > maximum) { result = maximum; } return result; } public function setItemAt(data:Object, item:Object, index:int):void { throw "Not implemented"; } public function addItemAt(data:Object, item:Object, index:int):void { throw "Not implemented"; } public function removeItemAt(data:Object, index:int):Object { throw "Not implemented"; } public function getItemIndex(data:Object, item:Object):int { var value:int = item as int; var range:IntegerRange = IntegerRange(data); return Math.ceil((value - range.minimum) / range.step); } public function removeAll(data:Object):void { throw "Not implemented"; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy