blob: b8ac741ba0776a908381298af983b4b393642927 [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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.controls
{
import flash.display.DisplayObject;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.text.TextLineMetrics;
import flash.ui.Keyboard;
import flash.utils.describeType;
import flash.utils.getQualifiedClassName;
import mx.core.IFlexDisplayObject;
import mx.core.IFlexModuleFactory;
import mx.core.IFontContextComponent;
import mx.core.IUITextField;
import mx.core.UIComponent;
import mx.core.UITextField;
import mx.core.mx_internal;
import mx.events.CalendarLayoutChangeEvent;
import mx.events.DateChooserEvent;
import mx.events.DateChooserEventDetail;
import mx.managers.IFocusManagerComponent;
import mx.skins.halo.DateChooserIndicator;
import mx.styles.ISimpleStyleClient;
use namespace mx_internal;
//--------------------------------------
// Styles
//--------------------------------------
include "../styles/metadata/GapStyles.as"
include "../styles/metadata/LeadingStyle.as"
include "../styles/metadata/PaddingStyles.as"
include "../styles/metadata/TextStyles.as"
/**
* Number of pixels between the component's top border
* and the top edge of its content area.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="paddingTop", type="Number", format="Length", inherit="no")]
/**
* Number of pixels between the component's bottom border
* and the bottom edge of its content area.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="paddingBottom", type="Number", format="Length", inherit="no")]
/**
* Name of the skin for the <code>rollOverIndicator</code>.
* It can be customized to some other shape other than rectangular.
* If you want to change just the color,
* use the <code>rollOverColor</code> instead.
* The default value is the DateChooserRollOverIndicator class.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="rollOverIndicatorSkin", type="Class", inherit="no")]
/**
* Name of the skin for <code>selectionIndicator</code>.
* It can customized to some other shape other than rectangular.
* If one just needs to change color,
* use the <code>selectionColor</code> instead.
* The default value is the DateChooserSelectionIndicator class.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="selectionIndicatorSkin", type="Class", inherit="no")]
/**
* Color of the background of today's date.
* The default value is <code>0x818181</code>.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="todayColor", type="uint", format="Color", inherit="yes")]
/**
* Name of the skin for <code>todayIndicator</code> style property. It can be customized
* to some other shape other than rectangular. If you
* want to change just the color, use the <code>todayColor</code> style property instead.
* The default value is the DateChooserTodayIndicator class.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="todayIndicatorSkin", type="Class", inherit="no")]
/**
* Name of the style sheet definition to configure the appearence of the current day's
* numeric text, which is highlighted
* in the control when the <code>showToday</code> property is <code>true</code>.
* Specify a "color" style to change the font color.
* If omitted, the current day text inherits
* the text styles of the control.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="todayStyleName", type="String", inherit="no")]
/**
* Name of the style sheet definition to configure the weekday names of
* the control. If omitted, the weekday names inherit the text
* styles of the control.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
[Style(name="weekDayStyleName", type="String", inherit="no")]
//--------------------------------------
// Other metadata
//--------------------------------------
[ExcludeClass]
[ResourceBundle("controls")]
/**
* @private
* The CalendarLayout class handles the layout of the date grid in a month.
* CalendarLayout can be extended to develop DateControls with
* single month display control or side-by-side month displays.
*
* @see mx.styles.StyleManager
*/
public class CalendarLayout extends UIComponent
implements IFocusManagerComponent, IFontContextComponent
{
include "../core/Version.as";
//--------------------------------------------------------------------------
//
// Constructor
//
//--------------------------------------------------------------------------
/**
* Constructor.
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
public function CalendarLayout()
{
super();
addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
}
private static const START_END:int = 1;
private static const START_ONLY:int = 2;
private static const END_ONLY:int = 3;
private static const SINGLE_DATE:int = 4;
//--------------------------------------------------------------------------
//
// Variables
//
//--------------------------------------------------------------------------
/**
* @private
*/
private var todayRow:int = -1;
/**
* @private
*/
private var todayColumn:int = -1;
/**
* @private
*/
private var enabledDaysInMonth:Array = [];
/**
* @private
*/
private var disabledRangeMode:Array;
/**
* @private
*/
private var cellHeight:Number = 14;
/**
* @private
*/
private var cellWidth:Number = 14;
/**
* @private
*/
private var yOffset:Number = -1;
/**
* @private
*/
mx_internal var dayBlocksArray:Array = [];
/**
* @private
*/
private var disabledArrays:Array = []; // An Array of Arrays
/**
* @private
*/
private var todaysLabelReference:IUITextField = null;
/**
* @private
*/
private var selectedMonthYearChanged:Boolean = false;
/**
* @private
*/
private var todayIndicator:IFlexDisplayObject;
/**
* @private
*/
private var selectionIndicator:Array = [];
/**
* @private
*/
private var rollOverIndicator:IFlexDisplayObject;
/**
* @private
*/
private var selectedRangeCount:int = 0;
/**
* @private
*/
private var lastSelectedDate:Date;
/**
* @private
*/
private var rangeStartDate:Date = null;
/**
* @private
*/
mx_internal var selRangeMode:int = START_END;
//--------------------------------------------------------------------------
//
// Overridden properties
//
//--------------------------------------------------------------------------
//----------------------------------
// enabled
//----------------------------------
/**
* @private
* Storage for the displayedYear property.
*/
private var _enabled:Boolean = true;
/**
* @private
*/
private var enabledChanged:Boolean = false;
[Inspectable(category="General", defaultValue="true")]
/**
* @private
*/
override public function get enabled():Boolean
{
return _enabled;
}
/**
* @private
*/
override public function set enabled(value:Boolean):void
{
super.enabled = value;
_enabled = value;
enabledChanged = true;
invalidateProperties();
}
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
//----------------------------------
// allowDisjointSelection
//----------------------------------
/**
* @private
* Storage for the allowDisjointSelection property.
*/
private var _allowDisjointSelection:Boolean = true;
[Inspectable(category="General", defaultValue="true")]
/**
* @private
*/
public function get allowDisjointSelection():Boolean
{
return _allowDisjointSelection;
}
/**
* @private
*/
public function set allowDisjointSelection(value:Boolean):void
{
_allowDisjointSelection = value;
}
//----------------------------------
// allowMultipleSelection
//----------------------------------
/**
* @private
* Storage for the allowMultipleSelection property.
*/
private var _allowMultipleSelection:Boolean = false;
[Inspectable(category="General", defaultValue="false")]
/**
* @private
*/
public function get allowMultipleSelection():Boolean
{
return _allowMultipleSelection;
}
/**
* @private
*/
public function set allowMultipleSelection(value:Boolean):void
{
_allowMultipleSelection = value;
}
//----------------------------------
// dayNames
//----------------------------------
/**
* @private
* Storage for the dayNames property.
*/
private var _dayNames:Array;
/**
* @private
*/
private var dayNamesChanged:Boolean = false;
/**
* @private
*/
private var dayNamesOverride:Array;
[Inspectable(category="Other", arrayType="String", defaultValue="null")]
/**
* @private
*/
public function get dayNames():Array
{
return _dayNames;
}
/**
* @private
*/
public function set dayNames(value:Array):void
{
dayNamesOverride = value;
_dayNames = value != null ?
value :
resourceManager.getStringArray(
"controls", "dayNamesShortest");
// _dayNames will be null if there are no resources.
_dayNames = _dayNames ? _dayNames.slice(0) : null;
dayNamesChanged = true;
invalidateProperties();
invalidateSize();
invalidateDisplayList();
}
//----------------------------------
// disabledDays
//----------------------------------
/**
* @private
* Storage for the disabledDays property.
*/
private var _disabledDays:Array = [];
[Inspectable(category="General", arrayType="int")]
/**
* @private
*/
public function get disabledDays():Array
{
var result:Array = [];
for (var i:int = 0, k:int = 0; i < _disabledDays.length; i++)
{
if (_disabledDays[i] >= 0 && _disabledDays[i] <= 6)
{
result[k] = _disabledDays[i];
k++;
}
}
return result;
}
/**
* @private
*/
public function set disabledDays(value:Array):void
{
_disabledDays = value.slice(0);
selectedMonthYearChanged = true;
invalidateProperties();
// This removes the disabled ranges/days from the selected ranges.
var selRange:Array = selectedRanges;
}
//----------------------------------
// disabledRanges
//----------------------------------
/**
* @private
* Storage for the disabledRanges property.
*/
private var _disabledRanges:Array = [];
[Inspectable(category="General", arrayType="Object")]
/**
* @private
*/
public function get disabledRanges():Array
{
return _disabledRanges.slice(0);
}
/**
* @private
*/
public function set disabledRanges(value:Array):void
{
_disabledRanges = value.slice(0);
disabledRangeMode = [];
for (var i:int = 0; i < _disabledRanges.length; i++)
{
if (_disabledRanges[i] is Date)
{
disabledRangeMode[i] = SINGLE_DATE;
_disabledRanges[i] = new Date(value[i].getFullYear(),
value[i].getMonth(),
value[i].getDate());
}
else if (_disabledRanges[i] is Object)
{
_disabledRanges[i] = {};
_disabledRanges[i] = value[i];
if (!_disabledRanges[i].rangeStart &&
_disabledRanges[i].rangeEnd)
{
disabledRangeMode[i] = END_ONLY;
}
else if (_disabledRanges[i].rangeStart &&
!_disabledRanges[i].rangeEnd)
{
disabledRangeMode[i] = START_ONLY;
}
else if (_disabledRanges[i].rangeStart &&
_disabledRanges[i].rangeEnd)
{
disabledRangeMode[i] = START_END;
}
}
}
selectedMonthYearChanged = true;
invalidateProperties();
// To remove the disabled ranges/days from the selected ranges.
var selRange:Array=selectedRanges;
}
//----------------------------------
// displayedMonth
//----------------------------------
/**
* @private
* Storage for the displayedMonth property.
*/
private var _displayedMonth:int = (new Date()).getMonth();
/**
* @private
* Holds the proposed value of displayedMonth until it can be verified in commitProperties
*/
private var _proposedDisplayedMonth:int = -1;
[Inspectable(category="General", defaultValue="0")]
/**
* @private
*/
public function get displayedMonth():int
{
return _proposedDisplayedMonth == -1 ? _displayedMonth : _proposedDisplayedMonth;
}
/**
* @private
*/
public function set displayedMonth(value:int):void
{
if (value < 0 || value > 11)
return;
if (value == _proposedDisplayedMonth)
return;
_proposedDisplayedMonth = value;
selectedMonthYearChanged = true;
invalidateProperties();
}
//----------------------------------
// displayedYear
//----------------------------------
/**
* @private
* Storage for the displayedYear property.
*/
private var _displayedYear:int = (new Date()).getFullYear();
/**
* @private
* Holds the proposed value of displayedYear until it can be verified in commitProperties
*/
private var _proposedDisplayedYear:int = -1;
[Inspectable(category="General", defaultValue="2014")]
/**
* @private
*/
public function get displayedYear():int
{
return _proposedDisplayedYear == -1 ? _displayedYear : _proposedDisplayedYear;
}
/**
* @private
*/
public function set displayedYear(value:int):void
{
if (value <= 0)
return;
if (value == _displayedYear)
return;
_proposedDisplayedYear = value;
selectedMonthYearChanged = true;
invalidateProperties();
}
//----------------------------------
// firstDayOfWeek
//----------------------------------
/**
* @private
* Storage for the firstDayOfWeek property.
*/
private var _firstDayOfWeek:int = 0; // Sunday
[Inspectable(category="General", defaultValue="0")]
/**
* @private
*/
public function get firstDayOfWeek():int
{
return _firstDayOfWeek;
}
/**
* @private
*/
public function set firstDayOfWeek(value:int):void
{
if (value < 0 || value > 6)
return;
if (value == _firstDayOfWeek)
return;
_firstDayOfWeek = value;
dayNamesChanged = true;
selectedMonthYearChanged = true;
invalidateProperties();
}
//----------------------------------
// fontContext
//----------------------------------
/**
* @private
*/
public function get fontContext():IFlexModuleFactory
{
return moduleFactory;
}
/**
* @private
*/
public function set fontContext(moduleFactory:IFlexModuleFactory):void
{
this.moduleFactory = moduleFactory;
}
//----------------------------------
// selectableRange
//----------------------------------
/**
* @private
* Storage for the selectableRange property.
*/
private var _selectableRange:Object = null;
[Inspectable(category="General")]
/**
* @private
*/
public function get selectableRange():Object
{
return _selectableRange;
}
/**
* @private
*/
public function set selectableRange(value:Object):void
{
if (!value)
{
_selectableRange = null;
selectedMonthYearChanged = true;
invalidateProperties();
return;
}
var todaysDate:Date = new Date();
var todaysMonth:int = todaysDate.getMonth();
var todaysYear:int = todaysDate.getFullYear();
var callMonth:int;
var callYear:int;
if (value is Date)
{
selRangeMode = SINGLE_DATE;
_selectableRange = new Date(value.getFullYear(),
value.getMonth(),
value.getDate());
callMonth = value.getMonth();
callYear = value.getFullYear();
}
else if (value is Object)
{
_selectableRange = {};
if (!value.rangeStart && value.rangeEnd)
{
selRangeMode = END_ONLY;
_selectableRange.rangeEnd = value.rangeEnd;
if (todaysYear <= _selectableRange.rangeEnd.getFullYear())
{
if (todaysMonth >= _selectableRange.rangeEnd.getMonth())
{
callMonth = _selectableRange.rangeEnd.getMonth();
callYear = _selectableRange.rangeEnd.getFullYear();
}
else if (todaysMonth <=
_selectableRange.rangeEnd.getMonth())
{
callMonth = todaysMonth;
callYear = todaysYear;
}
}
else if (todaysYear > _selectableRange.rangeEnd.getFullYear())
{
callMonth = _selectableRange.rangeEnd.getMonth();
callYear = _selectableRange.rangeEnd.getFullYear();
}
}
else if (!value.rangeEnd && value.rangeStart)
{
selRangeMode = START_ONLY;
_selectableRange.rangeStart = value.rangeStart;
if (todaysYear >= _selectableRange.rangeStart.getFullYear())
{
if (todaysMonth <= _selectableRange.rangeStart.getMonth())
{
callMonth = _selectableRange.rangeStart.getMonth();
callYear = _selectableRange.rangeStart.getFullYear();
}
else if (todaysMonth >=
_selectableRange.rangeStart.getMonth())
{
callMonth = todaysMonth;
callYear = todaysYear;
}
}
else if (todaysYear < _selectableRange.rangeStart.getFullYear())
{
callMonth = _selectableRange.rangeStart.getMonth();
callYear = _selectableRange.rangeStart.getFullYear();
}
}
else if (value.rangeStart && value.rangeEnd)
{
selRangeMode = START_END;
_selectableRange.rangeStart = value.rangeStart;
_selectableRange.rangeEnd = value.rangeEnd;
if (todaysDate >= _selectableRange.rangeStart &&
todaysDate <= _selectableRange.rangeEnd)
{
callMonth = todaysMonth;
callYear = todaysYear;
}
else if (todaysDate < _selectableRange.rangeStart)
{
callMonth = _selectableRange.rangeStart.getMonth();
callYear = _selectableRange.rangeStart.getFullYear();
}
else if (todaysDate > _selectableRange.rangeEnd)
{
callMonth = _selectableRange.rangeEnd.getMonth();
callYear = _selectableRange.rangeEnd.getFullYear();
}
}
}
_displayedMonth = callMonth;
_displayedYear = callYear;
selectedMonthYearChanged = true;
invalidateProperties();
// This removes the non-selectable ranges from the selected ranges.
var selRange:Array = selectedRanges;
}
//----------------------------------
// selectedRanges
//----------------------------------
/**
* @private
* Storage for the selectableRange property.
*/
private var _selectedRanges:Array = [];
[Inspectable(category="General", arrayType="Object")]
/**
* @private
*/
public function get selectedRanges():Array
{
if (_selectableRange)
{
switch (selRangeMode)
{
case START_END:
{
removeRangeFromSelection(null, addSubtractDays(_selectableRange.rangeStart, -1));
removeRangeFromSelection(addSubtractDays(_selectableRange.rangeEnd, +1), null);
break;
}
case START_ONLY:
case END_ONLY:
{
removeRangeFromSelection(addSubtractDays(_selectableRange.rangeStart, -1),
addSubtractDays(_selectableRange.rangeEnd, +1));
break;
}
case SINGLE_DATE:
{
removeDayFromSelection(_selectableRange as Date);
break;
}
}
}
var i:int;
for (i = 0; i < _disabledRanges.length; i++)
{
switch (disabledRangeMode[i])
{
case START_END:
case START_ONLY:
case END_ONLY:
{
removeRangeFromSelection(_disabledRanges[i].rangeStart, _disabledRanges[i].rangeEnd);
break;
}
case SINGLE_DATE:
{
removeDayFromSelection(_disabledRanges[i]);
break;
}
}
}
if (_disabledDays.length > 0 && selectedRangeCount)
{
var minDate:Date = _selectedRanges[0].rangeStart;
var maxDate:Date = _selectedRanges[0].rangeEnd;
for (i = 1; i < selectedRangeCount; i++)
{
if (minDate > _selectedRanges[i].rangeStart)
minDate = _selectedRanges[i].rangeStart;
if (maxDate < _selectedRanges[i].rangeEnd)
maxDate = _selectedRanges[i].rangeEnd;
}
for (i = 0; i < _disabledDays.length; i++)
{
var tempDate:Date = minDate;
var dayOffset:int = _disabledDays[i] - tempDate.getDay();
if (dayOffset < 0)
dayOffset += 7;
tempDate = addSubtractDays(tempDate, dayOffset);
while (tempDate < maxDate)
{
removeDayFromSelection(tempDate);
tempDate = addSubtractDays(tempDate, 7);
}
}
}
_selectedRanges.length = selectedRangeCount;
return _selectedRanges;
}
/**
* @private
*/
public function set selectedRanges(value:Array):void
{
_selectedRanges = value;
selectedRangeCount = _selectedRanges.length;
setSelectedIndicators();
}
//----------------------------------
// showToday
//----------------------------------
/**
* @private
* Storage for the showToday property.
*/
private var _showToday:Boolean = true;
[Inspectable(category="General", defaultValue="true")]
/**
* @private
*/
public function get showToday():Boolean
{
return _showToday;
}
/**
* @private
*/
public function set showToday(value:Boolean):void
{
if (_showToday != value)
_showToday = value;
selectedMonthYearChanged = true;
invalidateProperties();
}
//----------------------------------
// selectedDate
//----------------------------------
[Inspectable(category="General", defaultValue="null")]
/**
* @private
*/
public function get selectedDate():Date
{
return selectedRangeCount ? _selectedRanges[0].rangeStart : null;
}
/**
* @private
*/
public function set selectedDate(value:Date):void
{
selectedRangeCount = 0;
if (value && !checkDateIsDisabled(value))
{
addToSelected(value);
_displayedMonth = value.getMonth();
_displayedYear = value.getFullYear();
selectedMonthYearChanged = true;
invalidateProperties();
}
else
{
setSelectedIndicators();
}
}
//--------------------------------------------------------------------------
//
// Overridden methods
//
//--------------------------------------------------------------------------
/*
A note about DayBlocks:
dayBlock0..dayBlock6 are skins for the 7 grid columns.
They are dynamically created instances of the "DayBlock" symbol.
Three additional properties are set when each dayBlock is created
in createChildren():
columnIndex:int (0-6)
keeps track of where this dayBlock is in the grid
selectedArray:Array
7 elements, one for each row
selectedArray[rowIndex] is true
if that row in the dayBlock is selected
disabledArray:Array
7 elements, one for each row
disabledArray[rowIndex] is true
if that row in the dayBlock is disabled
Additional properties are created dynamically to hold the skins
for selected and disabled rows in the dayBlock:
selectedSkin0..selectedSkin6
disabledSkin0..disabledSkin6
*/
/**
* @private
* Everything that gets initially created now exists.
* Some things, such as the selection skins and disabled skins,
* are created later as they are needed.
* Set the "S", "M", etc. labels in the first row.
*
* Set the day-number labels ("1".."31") in the other rows.
* This method also displays the selection skins,
* the disabled skins, and the "today" indicator.
*/
override protected function createChildren():void
{
super.createChildren();
var labelPosition:Number = 0;
createDayLabels(-1);
createTodayIndicator(0);
if (!rollOverIndicator)
{
var rollOverIndicatorClass:Class = getStyle("rollOverIndicatorSkin");
if (!rollOverIndicatorClass)
rollOverIndicatorClass = DateChooserIndicator;
rollOverIndicator = IFlexDisplayObject(new rollOverIndicatorClass());
// too lazy to make an interface for this.
if (isDateChooserIndicator(rollOverIndicator))
Object(rollOverIndicator).indicatorColor = "rollOverColor";
if (rollOverIndicator is ISimpleStyleClient)
ISimpleStyleClient(rollOverIndicator).styleName = this;
addChildAt(DisplayObject(rollOverIndicator), 0);
rollOverIndicator.visible = false;
}
// Wait for all of the properties to be set before calling setSelectedMonthAndYear
dayNamesChanged = true;
selectedMonthYearChanged = true;
}
/**
* @private
*/
override protected function commitProperties():void
{
super.commitProperties();
if (hasFontContextChanged() && todayIndicator != null)
{
// Re-create the children so we can display
// the embedded font from the new font context.
removeSelectionIndicators();
var childIndex:int = getChildIndex(DisplayObject(todayIndicator));
removeTodayIndicator();
createTodayIndicator(childIndex);
childIndex = getChildIndex(dayBlocksArray[0][0]);
removeDayLabels();
createDayLabels(childIndex);
enabledChanged = true;
dayNamesChanged = true;
selectedMonthYearChanged = true;
}
if (enabledChanged)
{
enabledChanged = false;
for (var o:int = 0; o < 7; o++)
{
for (var r:int = 0; r < 7; r++)
{
dayBlocksArray[o][r].enabled = _enabled;
disabledArrays[o][r] = _enabled;
}
}
if (!_enabled)
{
if (todayIndicator)
todayIndicator.alpha = 0.3;
// Remove the mouse event listeners
removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
removeEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
removeEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
}
else
{
if (todayIndicator)
todayIndicator.alpha = 1.0;
selectedMonthYearChanged = true;
// Restore the mouse event listeners
addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
}
}
if (dayNamesChanged)
{
dayNamesChanged = false;
drawDayNames();
}
if (selectedMonthYearChanged)
{
selectedMonthYearChanged = false;
var proposedDate:Date =
new Date(_proposedDisplayedYear == -1 ? _displayedYear : _proposedDisplayedYear,
_proposedDisplayedMonth == -1 ? _displayedMonth : _proposedDisplayedMonth);
if (isDateInRange(proposedDate, _selectableRange, selRangeMode, true))
{
setSelectedMonthAndYear();
}
_proposedDisplayedYear = -1;
_proposedDisplayedMonth = -1;
}
}
/**
* @private
*/
override protected function measure():void
{
super.measure();
var verticalGap:Number = getStyle("verticalGap");
var horizontalGap:Number = getStyle("horizontalGap");
var paddingLeft:Number = getStyle("paddingLeft");
var paddingRight:Number = getStyle("paddingRight");
var paddingBottom:Number = getStyle("paddingBottom");
var paddingTop:Number = getStyle("paddingTop");
var lineMetrics:TextLineMetrics;
cellWidth = 0;
cellHeight = 0;
for (var dayOfWeek:int = 0; dayOfWeek < 7; dayOfWeek++)
{
// dayNames will be null if there are no resources.
var dayName:String = dayNames ? dayNames[dayOfWeek] : "";
lineMetrics = measureText(dayName);
if (lineMetrics.width > cellWidth)
cellWidth = lineMetrics.width;
if (lineMetrics.height > cellHeight)
cellHeight = lineMetrics.height;
}
lineMetrics = measureText("30");
if (lineMetrics.width > cellWidth)
cellWidth = lineMetrics.width;
if (lineMetrics.height > cellHeight)
cellHeight = lineMetrics.height;
measuredWidth = paddingLeft + horizontalGap * 6 +
cellWidth * 7 + paddingRight;
measuredHeight = verticalGap * 6 + cellHeight * 7 +
paddingBottom + paddingTop;
measuredMinWidth = cellWidth * 7;
measuredMinHeight = cellHeight * 7;
}
/**
* @private
*/
override protected function updateDisplayList(unscaledWidth:Number,
unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
//var verticalGap:Number = getStyle("verticalGap");
//var horizontalGap:Number = getStyle("horizontalGap");
var paddingLeft:Number = getStyle("paddingLeft");
var paddingRight:Number = getStyle("paddingRight");
var paddingBottom:Number = getStyle("paddingBottom");
var paddingTop:Number = getStyle("paddingTop");
var blockX:Number = paddingLeft;
// Bug 134794, clip height/width so that RTE's are not thrown
cellWidth = Math.max((unscaledWidth - (paddingLeft + paddingRight))/7, 4);
cellHeight = Math.max((unscaledHeight - paddingBottom)/7, 4);
var labelPosition:Number = paddingTop;
rollOverIndicator.setActualSize(cellWidth, cellHeight);
todayIndicator.setActualSize(cellWidth, cellHeight);
for (var columnIndex:int = 0; columnIndex < 7; columnIndex++)
{
// Remember the height of the cells if not set by user.
// Create the 7 labels within each DayBlock.
// The first row in each column displays a day name string, such as "Sun".
// The other six rows displays day numbers in the range 1-31.
for (var rowIndex:int = 0; rowIndex < 7; rowIndex++)
{
var label:IUITextField = dayBlocksArray[columnIndex][rowIndex];
if (rowIndex == 0)
labelPosition = paddingTop;
else
labelPosition += cellHeight;
label.setActualSize(cellWidth, cellHeight);
label.move(blockX, labelPosition);
if (selectionIndicator[columnIndex][rowIndex])
{
selectionIndicator[columnIndex][rowIndex].setActualSize(cellWidth, cellHeight);
selectionIndicator[columnIndex][rowIndex].move(blockX, labelPosition + yOffset);
}
//label.width = cellWidth;
//label.height = cellHeight;
//label.x = blockX;
//label.y = labelPosition;
}
blockX += cellWidth;
}
drawDayNames();
setSelectedMonthAndYear();
}
/**
* @private
*/
override public function styleChanged(styleProp:String):void
{
var allStyles:Boolean = !styleProp || styleProp == "styleName";
if (allStyles || styleProp == "todayStyleName")
{
selectedMonthYearChanged = true;
invalidateProperties();
}
if (allStyles || styleProp == "weekDayStyleName")
{
var weekDayStyleName:Object = getStyle("weekDayStyleName");
if (!weekDayStyleName)
weekDayStyleName = this;
if (dayBlocksArray)
{
for (var i:int = 0; i < 7; i++)
{
// Set the styleName on the top row of day name labels
if (dayBlocksArray[i] && dayBlocksArray[i][0])
dayBlocksArray[i][0].styleName = weekDayStyleName;
}
}
}
super.styleChanged(styleProp);
}
/**
* @private
*/
override protected function resourcesChanged():void
{
super.resourcesChanged();
dayNames = dayNamesOverride;
}
//--------------------------------------------------------------------------
//
// Methods
//
//--------------------------------------------------------------------------
/**
* @private
* Creates the day labels and adds them as children of this component.
*
* @param childIndex The index of where to add the children.
* If -1, the text fields are appended to the end of the list.
*/
mx_internal function createDayLabels(childIndex:int):void
{
var weekDayStyleName:Object = getStyle("weekDayStyleName");
// Remember the height of the cells if not set by user.
// Create the 7 labels within each DayBlock.
// The first row in each column displays a day name string,
// such as "Sun".
// The other six rows displays day numbers in the range 1-31.
// Calendar days
for (var columnIndex:int = 0; columnIndex < 7; columnIndex++)
{
dayBlocksArray[columnIndex] = [];
selectionIndicator[columnIndex] = [];
for (var rowIndex:int = 0; rowIndex < 7; rowIndex++)
{
var label:IUITextField =
dayBlocksArray[columnIndex][rowIndex] =
IUITextField(createInFontContext(UITextField));
label.selectable = false;
label.ignorePadding = true;
if (childIndex == -1)
addChild(DisplayObject(label));
else
addChildAt(DisplayObject(label), childIndex++);
if (rowIndex == 0)
{
label.styleName = weekDayStyleName ?
weekDayStyleName :
this;
}
else
{
label.styleName = this;
}
}
disabledArrays[columnIndex] = new Array(7);
}
}
/**
* @private
* Removes the day labels from this component.
*/
mx_internal function removeDayLabels():void
{
for (var columnIndex:int = 0; columnIndex < 7; columnIndex++)
{
for (var rowIndex:int = 0; rowIndex < 7; rowIndex++)
{
removeChild(dayBlocksArray[columnIndex][rowIndex]);
dayBlocksArray[columnIndex][rowIndex] = null;
}
}
}
/**
* @private
* @param childIndex The index of where to add the child.
*/
mx_internal function createTodayIndicator(childIndex:int):void
{
if (!todayIndicator)
{
var todayIndicatorClass:Class = getStyle("todayIndicatorSkin");
if (!todayIndicatorClass)
todayIndicatorClass = DateChooserIndicator;
todayIndicator = IFlexDisplayObject(new todayIndicatorClass());
if (isDateChooserIndicator(todayIndicator))
{
Object(todayIndicator).indicatorColor =
"todayColor";
}
if (todayIndicator is ISimpleStyleClient)
ISimpleStyleClient(todayIndicator).styleName = this;
addChildAt(DisplayObject(todayIndicator), childIndex);
todayIndicator.visible = false;
}
}
/**
* @private
*/
mx_internal function removeTodayIndicator():void
{
if (todayIndicator)
{
removeChild(DisplayObject(todayIndicator));
todayIndicator = null;
}
}
/**
* @private
*/
mx_internal function drawDayNames():void
{
for (var columnIndex:int = 0; columnIndex < 7; columnIndex++)
{
var dayOfWeek:int = (columnIndex + firstDayOfWeek) % 7;
// dayNames will be null if there are no resources.
var dayName:String = dayNames ? dayNames[dayOfWeek] : "";
dayBlocksArray[columnIndex][0].text = dayName;
}
}
/**
* @private
*/
mx_internal function setSelectedMonthAndYear(monthVal:int = -1, yearVal:int = -1):void
{
// This lengthy method updates the UI to display a specified month
// and year. In particular, it updates the day numbers (1-31) in the grid.
// It does NOT update the day names ("Sun", "Mon", etc.),
// since these do not change when the month and year change.
//
// It also takes care of displaying days that are disabled or selected.
// Instances of the skins cal_monthDayDisabled (for disabled days) and
// cal_monthDaySelected (for selected days) get created as they
// are required, to minimize initialization time.
var dayNumber:int; // 1 - 31
var columnIndex:int; // 0 - 6
var rowIndex:int; // 1 - 6
var i:int;
var displayTodayInCurrentMonth:Boolean = false;
var displayDate:Date = null;
// When another method needs to redraw the UI without changing the
// currently selected month and year (because the firstDayOfWeek
// property changed, for example) it calls setSelectedMonthAndYear()
// with no arguments. Therefore we need to handle undefined arguments.
var newMonth:int = monthVal == -1 ? displayedMonth : monthVal;
var newYear:int = yearVal == -1 ? displayedYear : yearVal;
// Determine where in the grid the 1st of the month should appear,
// how many days are in the month, and today's date.
enabledDaysInMonth = [];
var offset:int = getOffsetOfMonth(newYear, newMonth);
var daysInMonth:int = getNumberOfDaysInMonth(newYear, newMonth);
// Determine whether this month contains today.
var today:Date = new Date();
var currentMonthContainsToday:Boolean = (today.getFullYear() == newYear && today.getMonth() == newMonth);
// Set up the days (if any) in row 1 that come from the previous month.
var previousMonth:int = Math.max(newMonth - 1,0);
var previousMonthDate:Date = new Date(newYear, previousMonth, 1);
dayNumber = getNumberOfDaysInMonth(previousMonthDate.getFullYear(), previousMonthDate.getMonth());
rowIndex = 1;
for (columnIndex = 0; columnIndex < offset; columnIndex++)
{
dayBlocksArray[columnIndex][rowIndex].text = "";
// Disable the day.
disabledArrays[columnIndex][rowIndex] = true;
removeSelectionIndicator(columnIndex,rowIndex);
}
// Set up the days of the new month.
for (dayNumber = 1; dayNumber <= daysInMonth; dayNumber++)
{
var cellDate:Date = new Date(newYear, newMonth, dayNumber);
i = offset + dayNumber - 1;
columnIndex = i % 7;
rowIndex = 1 + Math.floor(i / 7);
var todayLabel:IUITextField = dayBlocksArray[columnIndex][rowIndex];//this["dayBlock" + columnIndex+"label" + rowIndex];
todayLabel.text = dayNumber.toString();
// Enable the day.
if (_enabled)
{
disabledArrays[columnIndex][rowIndex] = false;
todayLabel.enabled = true;
}
if (!todayLabel.styleName)
todayLabel.styleName = this;
// Some of these days may be selected.
// One of these days may be today's date.
if (currentMonthContainsToday && (cellDate.getDate() == today.getDate()) && _showToday)
{
todayRow = rowIndex;
todayColumn = columnIndex;
displayTodayInCurrentMonth = true;
todayIndicator.visible = _showToday;
todayLabel.styleName = getStyle("todayStyleName");
todayIndicator.move(todayLabel.x, todayLabel.y + yOffset); // Don't trigger layout
todaysLabelReference = todayLabel;
}
else
{
if (!displayTodayInCurrentMonth)
{
if (todaysLabelReference)
{
todaysLabelReference.styleName = this;
//todaysLabelReference.styleName = this;
}
todayIndicator.visible = false;
}
}
var cellString:String;
// Selectable Range
// set up the selectable Range: type: Object/ Date Object
// Object Attrib: rangeStart[Date Object], rangeEnd[Date Object]
// 1 :: if both attribs are defined
// 2 :: If only rangeStart is defined: All dates after the specified date are enabled
// 3 :: if only rangeEnd is defined: All dates before ths specified date are enabled
// 4 :: If selectable Range param is a Date Object, then only that day has to be defined
if (_selectableRange)
{
if (!isDateInRange(cellDate, _selectableRange, selRangeMode))
{
todayLabel.enabled = false;
disabledArrays[columnIndex][rowIndex] = true;
}
}
// Disabled Ranges
// set up the disabledRanges: type: Array
// Array can contain an Object || Date Object.
// Attrib for Object: rangeStart[Date Object], rangeEnd[Date Object]
// "start"::All dates after the specified date are disabled, including the startDate
// "end"::All dates before ths specified date are disabled, including the end Date
// "date"::Only that day has to be disabled
// "normal"::range is disabled including the start and end date
if (_disabledRanges.length > 0)
{
for (var dRanges:int = 0; dRanges < _disabledRanges.length; dRanges++)
{
if (isDateInRange(cellDate, _disabledRanges[dRanges], disabledRangeMode[dRanges]))
{
todayLabel.enabled = false;
disabledArrays[columnIndex][rowIndex] = true;
}
}
}
var valToPush:Object = {};
if (todayLabel.enabled)
{
valToPush.name = todayLabel.name;
valToPush.text = todayLabel.text;
valToPush.x = todayLabel.x;
valToPush.y = todayLabel.y;
}
enabledDaysInMonth.push(valToPush);
}
// Set up the days (if any) at the end of the grid
// that come from the following month.
dayNumber = 1;
for (i = offset + daysInMonth; i < 42; i++)
{
columnIndex = i % 7;
rowIndex = 1 + Math.floor(i / 7);
dayBlocksArray[columnIndex][rowIndex].text = "";
// Disable the day.
disabledArrays[columnIndex][rowIndex] = true;
removeSelectionIndicator(columnIndex,rowIndex);
}
if (_disabledDays.length>0)
{
for (i = 0; i < _disabledDays.length; i++)
{
if (_disabledDays[i] >= 0 && _disabledDays[i] <= 6 && _disabledDays[i] != -1)
{
columnIndex = ((7 + _disabledDays[i] - _firstDayOfWeek) % 7);
for (rowIndex = 1; rowIndex < 7; rowIndex++)
{
// Disable the day.
disabledArrays[columnIndex][rowIndex] = true;
var tempCalcDate:Number = Number(dayBlocksArray[columnIndex][rowIndex].text);
var tempOffset:Number;
if (!isNaN(tempCalcDate))
{
tempOffset = offset + tempCalcDate % 7;
enabledDaysInMonth[tempCalcDate-1] = null;
}
dayBlocksArray[columnIndex][rowIndex].enabled = false;
}
}
}
}
_displayedMonth = newMonth;
_displayedYear = newYear;
displayDate = new Date(newYear, newMonth, 1);
todayIndicator.alpha = (todaysLabelReference) ? ((todaysLabelReference.enabled == false) ? 0.3 : 1.0) : 1.0;
setSelectedIndicators();
invalidateDisplayList();
}
/**
* @private
* Called from setSelectedMonthAndYear() to get an Offset of the starting day of the month
*/
mx_internal function getOffsetOfMonth(year:int, month:int):int
{
// Determine whether the 1st of the month is a Sunday, Monday, etc.
// and then determine which column of the grid where it appears.
var first:Date = new Date(year, month, 1);
var offset:int = first.getDay() - _firstDayOfWeek;
return offset < 0 ? offset + 7 : offset;
}
/**
* @private
*/
mx_internal function getNumberOfDaysInMonth(year:int, month:int):int
{
// "Thirty days hath September..."
var n:int;
if (month == 1) // Feb
{
if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) // leap year
n = 29;
else
n = 28;
}
else if (month == 3 || month == 5 || month == 8 || month == 10)
n = 30;
else
n = 31;
return n;
}
/**
* @private
*/
mx_internal function stepDate(deltaY:int, deltaM:int, triggerEvent:Event = null):void
{
var oldYear:int = displayedYear;
var oldMonth:int = displayedMonth;
var newDate:Object = getNewIncrementDate(oldYear, oldMonth, deltaY, deltaM);
var newYear:int = newDate.year;
var newMonth:int = newDate.month;
_displayedMonth = newMonth;
_displayedYear = newYear;
selectedMonthYearChanged = true;
invalidateProperties();
var event:DateChooserEvent = new DateChooserEvent(DateChooserEvent.SCROLL);
event.triggerEvent = triggerEvent;
if (newYear > oldYear)
event.detail = DateChooserEventDetail.NEXT_YEAR;
else if (newYear < oldYear)
event.detail = DateChooserEventDetail.PREVIOUS_YEAR;
else if (newMonth > oldMonth)
event.detail = DateChooserEventDetail.NEXT_MONTH;
else if (newMonth < oldMonth)
event.detail = DateChooserEventDetail.PREVIOUS_MONTH;
dispatchEvent(event);
}
/**
* @private Takes a year and a month as well as an increment year and month value
* and returns a new valid year/month.
*/
static mx_internal function getNewIncrementDate(oldYear:int, oldMonth:int, deltaY:int, deltaM:int):Object
{
var newYear:int = oldYear + deltaY;
var newMonth:int = oldMonth + deltaM;
while (newMonth < 0)
{
newYear--;
newMonth += 12;
}
while (newMonth > 11)
{
newYear++;
newMonth -= 12;
}
return {month: newMonth, year: newYear};
}
/**
* @private
*/
mx_internal function dispatchChangeEvent(triggerEvent:Event = null):void
{
var change:CalendarLayoutChangeEvent =
new CalendarLayoutChangeEvent(CalendarLayoutChangeEvent.CHANGE);
change.newDate = lastSelectedDate;
change.triggerEvent = triggerEvent;
dispatchEvent(change);
}
/**
* @private
*
* Returns true if the date is within the dates specified by the dateRange object.
*/
mx_internal function isDateInRange(value:Date, dateRange:Object, rangeMode:int, ignoreDay:Boolean = false):Boolean
{
var result:Boolean = true;
if (dateRange)
{
if (ignoreDay)
{
var dateRangeCopy:Object = {};
if (dateRange.rangeStart)
{
var startDate:Date = dateRange.rangeStart;
dateRangeCopy.rangeStart = new Date(startDate.fullYear, startDate.month, 1);
}
if (dateRange.rangeEnd)
{
var endDate:Date = dateRange.rangeEnd;
dateRangeCopy.rangeEnd = new Date(endDate.fullYear, endDate.month, getNumberOfDaysInMonth(endDate.fullYear, endDate.month));
}
dateRange = dateRangeCopy;
}
switch (rangeMode)
{
case START_END:
{
if (value < dateRange.rangeStart ||
value > dateRange.rangeEnd)
{
result = false;
}
break;
}
case START_ONLY:
{
if (value < dateRange.rangeStart)
result = false;
break;
}
case END_ONLY:
{
if (value > dateRange.rangeEnd)
result = false;
break;
}
case SINGLE_DATE:
{
if (value > dateRange || value < dateRange)
result = false;
break;
}
default:
{
break;
}
}
}
return result;
}
/**
* @private
*
* Checking for valid dates, months and Years before setting them through API
* Returns true is date is disabled. null date is considered enabled,
* as one can set date to null.
*/
mx_internal function checkDateIsDisabled(value:Date):Boolean
{
if (!value)
return false;
var selectedDateIsDisabled:Boolean = false;
if (_selectableRange)
{
if (!isDateInRange(value, _selectableRange, selRangeMode))
{
selectedDateIsDisabled = true;
}
}
if (_disabledRanges.length > 0)
{
for (var dRanges:int = 0; dRanges < _disabledRanges.length; dRanges++)
{
if (isDateInRange(value, _disabledRanges[dRanges], disabledRangeMode[dRanges]))
{
selectedDateIsDisabled = true;
}
}
}
if (_disabledDays.length > 0)
{
for (var i:int = 0; i < _disabledDays.length; i++)
{
if (value.getDay() == _disabledDays[i])
selectedDateIsDisabled = true;
}
}
return selectedDateIsDisabled;
}
/**
* @private
* Adds the newDate to the list of selected dates.
* If range is true, a range of dates starting from the previous selection is selected.
*/
mx_internal function addToSelected(newDate:Date, range:Boolean = false):void
{
if (selectedRangeCount == 0)
rangeStartDate = null;
lastSelectedDate = newDate;
if (range == false)
{
_selectedRanges[selectedRangeCount] = {};
_selectedRanges[selectedRangeCount].rangeStart =
new Date(newDate);
_selectedRanges[selectedRangeCount].rangeEnd =
_selectedRanges[selectedRangeCount].rangeStart;
selectedRangeCount++;
}
else
{
if (selectedRangeCount == 0)
{
_selectedRanges[0] = {};
_selectedRanges[0].rangeStart = new Date(newDate);
}
else
{
selectedRangeCount = 1;
if (!rangeStartDate)
rangeStartDate = _selectedRanges[0].rangeStart;
_selectedRanges[0].rangeStart = new Date(rangeStartDate);
if (newDate < _selectedRanges[0].rangeStart)
{
_selectedRanges[0].rangeEnd = _selectedRanges[0].rangeStart;
_selectedRanges[0].rangeStart = new Date(newDate);
return;
}
}
_selectedRanges[0].rangeEnd = new Date(newDate);
}
}
/**
* @private
* Increments/decrements a date by 'No. of days'
* specified by amount and returns the new date.
*/
mx_internal function addSubtractDays(value:Date, amount:int):Date
{
var newDate:Date = new Date(value);
if (value)
return new Date(newDate.fullYear, newDate.month, newDate.date + amount);
else
return null;
}
/**
* @private
* Returns true if newDate is selected.
*/
mx_internal function isSelected(newDate:Date):Boolean
{
for (var i:int = 0; i < selectedRangeCount; i++)
{
if (newDate >= _selectedRanges[i].rangeStart &&
newDate <=_selectedRanges[i].rangeEnd)
{
return true;
}
}
return false;
}
/**
* @private
* Removes the range of dates specified by startDate and endDate
* from the selected dates.
*/
mx_internal function removeRangeFromSelection(startDate:Date, endDate:Date):void
{
var rangeEnd:Date;
var rangeStart:Date;
if (endDate < startDate)
return;
for (var n:int = 0; n < selectedRangeCount; n++)
{
rangeStart = _selectedRanges[n].rangeStart;
rangeEnd = _selectedRanges[n].rangeEnd;
// ignore selection range outsie of date range
if (endDate < rangeStart || startDate > rangeEnd)
continue;
// remove selection range inside of date range
if (startDate <= rangeStart && endDate >= rangeEnd)
{
_selectedRanges[n] = null;
}
// split selection range if date range inside selection range
else if (startDate > rangeStart && endDate < rangeEnd)
{
var temp:Date = _selectedRanges[n].rangeEnd;
_selectedRanges[n].rangeEnd = addSubtractDays(startDate, -1);
_selectedRanges[selectedRangeCount] = {};
_selectedRanges[selectedRangeCount].rangeStart = addSubtractDays(endDate, +1);
_selectedRanges[selectedRangeCount].rangeEnd = temp;
selectedRangeCount++;
}
// remove day at start of range
else if (endDate.time == rangeStart.time) {
_selectedRanges[n].rangeStart = addSubtractDays(startDate, +1);
}
// remove day at end of range
else if (startDate.time == rangeEnd.time) {
_selectedRanges[n].rangeEnd = addSubtractDays(endDate, -1);
}
// move selection start date if end overlaps
else if (endDate >= rangeStart)
{
_selectedRanges[n].rangeStart = addSubtractDays(endDate, +1);
}
// move selection end date if start overlaps
else if (startDate <= rangeEnd)
{
_selectedRanges[n].rangeEnd = addSubtractDays(startDate, -1);
}
}
// clean up any removed selections
for (n = selectedRangeCount -1; n >= 0; n--)
{
if (_selectedRanges[n] == null)
{
_selectedRanges.splice(n,1);
selectedRangeCount--;
}
}
}
/**
* @private
* Removes a single date specified by singleDate from the selected dates.
*/
mx_internal function removeDayFromSelection(singleDate:Date):void
{
removeRangeFromSelection(singleDate, singleDate);
}
/**
* @private
* Updates the visible property of all the selected indicators.
* Called when a date range has been selected or deselected.
*/
mx_internal function setSelectedIndicators():void
{
var offset:int = getOffsetOfMonth(displayedYear, displayedMonth);
var daysInMonth:int = getNumberOfDaysInMonth(displayedYear, displayedMonth);
var columnIndex:int; // 0 - 6
var rowIndex:int; // 1 - 6
var i:int;
for (var dayNumber:int = 1; dayNumber <= daysInMonth; dayNumber++)
{
var cellDate:Date = new Date(displayedYear, displayedMonth, dayNumber);
i = offset + dayNumber - 1;
columnIndex = i % 7;
rowIndex = 1 + Math.floor(i / 7);
if (isSelected(cellDate) && disabledArrays[columnIndex][rowIndex] == false)
addSelectionIndicator(columnIndex,rowIndex);
else
removeSelectionIndicator(columnIndex,rowIndex);
}
var today:Date = new Date();
if (isSelected(today))
todayIndicator.alpha = 1.0;
}
/**
* @private
*/
mx_internal function addSelectionIndicator(columnIndex:int, rowIndex:int):void
{
if (!selectionIndicator[columnIndex][rowIndex])
{
var selectionIndicatorClass:Class =
getStyle("selectionIndicatorSkin");
if (!selectionIndicatorClass)
selectionIndicatorClass = DateChooserIndicator;
selectionIndicator[columnIndex][rowIndex] =
IFlexDisplayObject(new selectionIndicatorClass());
if (isDateChooserIndicator(selectionIndicator[columnIndex][rowIndex]))
Object(selectionIndicator[columnIndex][rowIndex]).indicatorColor =
"selectionColor";
if (selectionIndicator[columnIndex][rowIndex] is ISimpleStyleClient)
ISimpleStyleClient(selectionIndicator[columnIndex][rowIndex]).styleName = this;
addChildAt(DisplayObject(selectionIndicator[columnIndex][rowIndex]), 0);
var selCell:IUITextField = dayBlocksArray[columnIndex][rowIndex];
selectionIndicator[columnIndex][rowIndex].move(selCell.x, selCell.y + yOffset);
selectionIndicator[columnIndex][rowIndex].setActualSize(cellWidth, cellHeight);
}
selectionIndicator[columnIndex][rowIndex].visible = true;
}
/**
* @private
*/
mx_internal function removeSelectionIndicator(columnIndex:int,
rowIndex:int):void
{
if (selectionIndicator[columnIndex][rowIndex])
{
removeChild(selectionIndicator[columnIndex][rowIndex]);
selectionIndicator[columnIndex][rowIndex] = null;
}
}
/**
* @private
*
* Removes the selection indicators from this component.
*/
mx_internal function removeSelectionIndicators():void
{
for (var columnIndex:int = 0; columnIndex < 7; columnIndex++)
{
for (var rowIndex:int = 0; rowIndex < 7; rowIndex++)
{
removeSelectionIndicator(columnIndex, rowIndex);
}
}
}
//--------------------------------------------------------------------------
//
// Overridden event handlers
//
//--------------------------------------------------------------------------
/**
* @private
*/
override protected function keyDownHandler(event:KeyboardEvent):void
{
/*
PageUp: Previous Month
PageDown: Next Month
*/
var selChanged:Boolean = false;
var date:int = lastSelectedDate ? lastSelectedDate.getDate() : 1;
/*
calculate Days to move will take the following values:
1: Left
2: Right
3: Up
4: Down
5. Home
6. End
*/
var daysInMonth:int = getNumberOfDaysInMonth(displayedYear, displayedMonth);
// If rtl layout, need to swap LEFT and RIGHT so correct action
// is done.
var keyCode:uint = mapKeycodeForLayoutDirection(event);
for (var i:uint = 0; i < 31; i++)
{
if (keyCode == Keyboard.LEFT)
{
if (date > 1)
{
date--;
selChanged = true;
}
else
return;
}
else if (keyCode == Keyboard.RIGHT)
{
if (date < daysInMonth)
{
date++;
selChanged = true;
}
else
return;
}
else if (keyCode == Keyboard.UP)
{
if (date > 7)
{
date -= 7;
selChanged = true;
}
else
return;
}
else if (keyCode == Keyboard.DOWN)
{
if (date + 7 <= daysInMonth)
{
date += 7;
selChanged = true;
}
else
return;
}
else if (keyCode == Keyboard.HOME)
{
if (i == 0)
date = 1;
else
date++;
selChanged = true;
}
else if (keyCode == Keyboard.END)
{
if (i == 0)
date = daysInMonth;
else
date--;
selChanged = true;
}
else if (lastSelectedDate && event.shiftKey &&
(keyCode == Keyboard.PAGE_UP ||
keyCode == Keyboard.PAGE_DOWN))
{
selChanged = true;
}
else if (lastSelectedDate &&
(keyCode == 189 ||
keyCode == 187)) // for year - and +
{
selChanged = true;
}
if (keyCode >= Keyboard.PAGE_UP &&
keyCode <= Keyboard.DOWN)
{
event.stopPropagation();
}
if (selChanged)
{
var newDate:Date = new Date(displayedYear, displayedMonth, date);
if (checkDateIsDisabled(newDate) && !event.shiftKey)
continue;
if (!(event.shiftKey && _allowMultipleSelection))
selectedRangeCount = 0;
addToSelected(newDate, event.shiftKey && _allowMultipleSelection);
setSelectedIndicators();
dispatchChangeEvent(event);
return;
}
}
}
//--------------------------------------------------------------------------
//
// Event handlers
//
//--------------------------------------------------------------------------
/**
* @private
*/
private function mouseOverHandler(event:MouseEvent):void
{
if (event.relatedObject && event.relatedObject.parent != this)
addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
else
event.stopImmediatePropagation();
}
/**
* @private
*/
private function mouseOutHandler(event:MouseEvent):void
{
if (event.relatedObject && event.relatedObject.parent != this)
{
removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
rollOverIndicator.visible = false;
// If todayColumn and todayRow exist and today is not disabled
if (todayColumn != -1 && todayRow != -1 && !disabledArrays[todayColumn][todayRow])
{
var today:Date = new Date();
if (!isSelected(today))
todayIndicator.alpha = 1.0;
}
}
else
{
event.stopImmediatePropagation();
}
}
/**
* @private
*/
private function mouseMoveHandler(event:MouseEvent):void
{
var paddingLeft:Number = getStyle("paddingLeft");
var paddingTop:Number = getStyle("paddingTop");
var firstColX:Number = dayBlocksArray[0][0].x;
var lastColX:Number = dayBlocksArray[6][0].x;
var firstRowY:Number = dayBlocksArray[6][0].y + cellHeight;
var mousePoint:Point = new Point(event.stageX, event.stageY);
mousePoint = globalToLocal(mousePoint);
var mouseY:Number = mousePoint.y;
var mouseX:Number = mousePoint.x;
if (mouseX < firstColX ||
mouseX > lastColX + cellWidth ||
mouseY < firstRowY)
return;
var rowIndex:int = Math.floor((mouseY-paddingTop) / cellHeight);
var colIndex:int = Math.floor((mouseX-paddingLeft) / cellWidth);
colIndex = Math.min(colIndex, 6);
rowIndex = Math.min(rowIndex, 6);
var selCell:IUITextField = dayBlocksArray[colIndex][rowIndex];
// If it is disabled, we're done.
if (disabledArrays[colIndex][rowIndex] || rowIndex == 0)
return;
if (mouseY >= selCell.y &&
mouseY <= selCell.y + cellHeight &&
mouseX >= selCell.x &&
mouseX <= selCell.x + cellWidth)
{
rollOverIndicator.move(selCell.x, selCell.y + yOffset); // Don't trigger layout
rollOverIndicator.visible = true;
// Don't show rollover if we're over the selected date
if (selectionIndicator[colIndex][rowIndex])
{
rollOverIndicator.visible = false;
}
// Set alpha only if today is not disabled
if (todayColumn != -1 && todayRow != -1 && !disabledArrays[todayColumn][todayRow])
{
var today:Date = new Date();
if (rollOverIndicator.x == todayIndicator.x &&
rollOverIndicator.y == todayIndicator.y)
{
todayIndicator.alpha = 0.6;
}
else if (!isSelected(today))
{
todayIndicator.alpha = 1.0;
}
}
}
}
/**
* @private
*/
private function mouseUpHandler(event:MouseEvent):void
{
var paddingLeft:Number = getStyle("paddingLeft");
var paddingTop:Number = getStyle("paddingTop");
var firstColX:Number = dayBlocksArray[0][0].x;
var lastColX:Number = dayBlocksArray[6][0].x;
var firstRowY:Number = dayBlocksArray[6][0].y + cellHeight;
var mousePoint:Point = new Point(event.stageX, event.stageY);
mousePoint = globalToLocal(mousePoint);
var mouseY:Number = mousePoint.y;
var mouseX:Number = mousePoint.x;
if (mouseX < firstColX &&
mouseX >= lastColX + cellWidth ||
mouseY < firstRowY)
return;
var rowIndex:int = Math.floor((mouseY-paddingTop) / cellHeight);
if (rowIndex <= 0)
return;
rowIndex = Math.min(rowIndex, 6);
var colIndex:int = Math.floor((mouseX-paddingLeft) / cellWidth);
colIndex = Math.min(colIndex, 6);
var selCell:IUITextField = dayBlocksArray[colIndex][rowIndex];//this["dayBlock"+colIndex+"label"+rowIndex];
// If it is disabled, we're done.
if (disabledArrays[colIndex][rowIndex])
return;
if (mouseY >= selCell.y &&
mouseY <= selCell.y + cellHeight &&
mouseX >= selCell.x &&
mouseX <= selCell.x + cellWidth)
{
var newDate:Date = new Date(displayedYear, displayedMonth, int(selCell.text));
if (event.shiftKey && _allowMultipleSelection)
{
addToSelected(newDate,true);
setSelectedIndicators();
}
else
{
var alreadySelected:Boolean = selectionIndicator[colIndex][rowIndex] ? true : false;
if (event.ctrlKey && _allowMultipleSelection && _allowDisjointSelection)
{
if (alreadySelected)
{
removeSelectionIndicator(colIndex, rowIndex);
removeDayFromSelection(newDate);
}
else
{
addSelectionIndicator(colIndex, rowIndex);
addToSelected(newDate);
}
}
else
{
rangeStartDate = null;
if (alreadySelected)
{
if (selectedRangeCount > 1 || (selectedRangeCount == 1 && _selectedRanges[0].rangeStart != _selectedRanges[0].rangeEnd))
{
selectedRangeCount = 0;
addSelectionIndicator(colIndex,rowIndex);
addToSelected(newDate);
setSelectedIndicators();
}
else if (event.ctrlKey)
{
removeSelectionIndicator(colIndex, rowIndex);
removeDayFromSelection(newDate);
}
}
else
{
selectedRangeCount = 0;
addSelectionIndicator(colIndex, rowIndex);
addToSelected(newDate);
setSelectedIndicators();
}
}
}
dispatchChangeEvent(event);
if (todayColumn != -1 && todayRow != -1 && !disabledArrays[todayColumn][todayRow]) // Set alpha only if today is not disabled
{
var todaysDate:Date = new Date();
todayIndicator.alpha = isSelected(todaysDate) ? 0.6 : 1.0;
}
// Hide the rollover indicator if it is the selected cell
if (selectionIndicator[colIndex][rowIndex])
rollOverIndicator.visible = false;
}
}
/**
* We don't use 'is' to prevent dependency issues
*
* @langversion 3.0
* @playerversion Flash 9
* @playerversion AIR 1.1
* @productversion Flex 3
*/
private static var dcis:Object = {};
private static function isDateChooserIndicator(parent:Object):Boolean
{
var s:String = getQualifiedClassName(parent);
if (dcis[s] == 1)
return true;
if (dcis[s] == 0)
return false;
if (s == "mx.skins.halo::DateChooserIndicator")
{
dcis[s] == 1;
return true;
}
var x:XML = describeType(parent);
var xmllist:XMLList = x.extendsClass.(@type == "mx.skins.halo::DateChooserIndicator");
if (xmllist.length() == 0)
{
dcis[s] = 0;
return false;
}
dcis[s] = 1;
return true;
}
}
}