Alloy UI

aui-calendar  1.0.1

 
Filters
AUI.add('aui-calendar-base', function(A) {
/**
 * The Calendar component is a UI control that enables users to choose one or
 * more dates from a graphical calendar presented in a single month or multi
 * month interface. Calendars are generated entirely via script and can be
 * navigated without any page refreshes.
 *
 * @module aui-calendar
 * @submodule aui-calendar-base
 */

var L = A.Lang,
	isString = L.isString,
	isArray = L.isArray,
	isBoolean = L.isBoolean,
	isUndefined = L.isUndefined,
	isNumber = L.isNumber,

	WidgetStdMod = A.WidgetStdMod,

	ACTIVE = 'active',
	BLANK = 'blank',
	BODY_CONTENT = 'bodyContent',
	BOUNDING_BOX = 'boundingBox',
	CALENDAR = 'calendar',
	CIRCLE = 'circle',
	CLEARFIX = 'clearfix',
	CURRENT_DAY = 'currentDay',
	CURRENT_MONTH = 'currentMonth',
	CURRENT_NODE = 'currentNode',
	CURRENT_YEAR = 'currentYear',
	DATES = 'dates',
	DATE_FORMAT = 'dateFormat',
	DAY = 'day',
	DEFAULT = 'default',
	DISABLED = 'disabled',
	DOT = '.',
	FIRST_DAY_OF_WEEK = 'firstDayOfWeek',
	HEADER = 'hd',
	HEADER_CONTENT = 'headerContent',
	HELPER = 'helper',
	HIDDEN = 'hidden',
	HOVER = 'hover',
	ICON = 'icon',
	LOCALE = 'locale',
	MAX_DATE = 'maxDate',
	MIN_DATE = 'minDate',
	MONTH = 'month',
	MONTHDAYS = 'monthdays',
	NEXT = 'next',
	PREV = 'prev',
	SELECT_MULTIPLE_DATES = 'selectMultipleDates',
	SET_VALUE = 'setValue',
	STATE = 'state',
	TITLE = 'title',
	TRIANGLE = 'triangle',
	WEEK = 'week',
	WEEKDAYS = 'weekdays',

	getCN = A.ClassNameManager.getClassName,

	CSS_CALENDAR = getCN(CALENDAR),
	CSS_CALENDAR_DISABLED = getCN(CALENDAR, DISABLED),
	CSS_DAY = getCN(CALENDAR, DAY),
	CSS_DAY_BLANK = getCN(CALENDAR, DAY, BLANK),
	CSS_DAY_HIDDEN = getCN(CALENDAR, DAY, HIDDEN),
	CSS_HEADER = getCN(CALENDAR, HEADER),
	CSS_HELPER_CLEARFIX = getCN(HELPER, CLEARFIX),
	CSS_ICON = getCN(ICON),
	CSS_ICON_CIRCLE_TRIANGLE_L = getCN(ICON, CIRCLE, TRIANGLE, 'l'),
	CSS_ICON_CIRCLE_TRIANGLE_R = getCN(ICON, CIRCLE, TRIANGLE, 'r'),
	CSS_MONTHDAYS = getCN(CALENDAR, MONTHDAYS),
	CSS_NEXT = getCN(CALENDAR, NEXT),
	CSS_PREV = getCN(CALENDAR, PREV),
	CSS_STATE_ACTIVE = getCN(STATE, ACTIVE),
	CSS_STATE_DEFAULT = getCN(STATE, DEFAULT),
	CSS_STATE_HOVER = getCN(STATE, HOVER),
	CSS_TITLE = getCN(CALENDAR, TITLE),
	CSS_WEEK = getCN(CALENDAR, WEEK),
	CSS_WEEKDAYS = getCN(CALENDAR, WEEKDAYS),

	TPL_CALENDAR_HEADER = '<div class="'+[ CSS_HEADER, CSS_STATE_DEFAULT, CSS_HELPER_CLEARFIX ].join(' ')+'">' +
							'<a href="" class="'+[ CSS_ICON, CSS_ICON_CIRCLE_TRIANGLE_L, CSS_PREV ].join(' ')+'">Back</a>'+
							'<a href="" class="'+[ CSS_ICON, CSS_ICON_CIRCLE_TRIANGLE_R, CSS_NEXT ].join(' ')+'">Prev</a>'+
						'</div>',

	TPL_CALENDAR_DAY_BLANK = '<div class="'+[ CSS_DAY_BLANK, CSS_DAY_HIDDEN ].join(' ')+'"></div>',

	TPL_CALENDAR_DAY = '<a href="#" class="'+[ CSS_DAY, CSS_STATE_DEFAULT ].join(' ')+'"></a>',

	TPL_CALENDAR_HEADER_TITLE = '<div class="'+CSS_TITLE+'"></div>',

	TPL_CALENDAR_MONTHDAYS = '<div class="'+[ CSS_MONTHDAYS, CSS_HELPER_CLEARFIX ].join(' ')+'"></div>',

	TPL_CALENDAR_WEEK = '<div class="'+CSS_WEEK+'"></div>',

	TPL_CALENDAR_WEEKDAYS = '<div class="'+[ CSS_WEEKDAYS, CSS_HELPER_CLEARFIX ].join(' ')+'"></div>';

/**
 * <p><img src="assets/images/aui-calendar/main.png"/></p>
 *
 * A base class for Calendar, providing:
 * <ul>
 *    <li>Widget Lifecycle (initializer, renderUI, bindUI, syncUI, destructor)</li>
 *    <li>Setting Configuration Options</li>
 *    <li>Obtaining Selected Dates</li>
 *    <li>Creating International Calendars</li>
 *    <li>Customizing the Calendar</li>
 * </ul>
 * 
 * Quick Example:
 * 
 * <pre><code>var instance = new A.Calendar({
 *  trigger: '#input1',
 *  dates: ['09/14/2009', '09/15/2009'],
 *  dateFormat: '%d/%m/%y %A',
 *  setValue: true,
 *  selectMultipleDates: true
 * }).render();
 * </code></pre>
 *
 * Check the list of <a href="Calendar.html#configattributes">Configuration Attributes</a> available for
 * Calendar.
 * 
 * @class Calendar
 * @param config {Object} Object literal specifying widget configuration properties.
 * @constructor
 * @extends OverlayContext
 */
var Calendar = A.Component.create(
	{
		/**
		 * Static property provides a string to identify the class.
		 *
		 * @property Calendar.NAME
		 * @type String
		 * @static
		 */
		NAME: CALENDAR,

		/**
		 * Static property used to define the default attribute
		 * configuration for the Calendar.
		 *
		 * @property Calendar.ATTRS
		 * @type Object
		 * @static
		 */
		ATTRS: {
			/**
			 * Current day number.
			 *
			 * @attribute currentDay
			 * @default Current day
			 * @type Number
			 */
			currentDay: {
				value: (new Date()).getDate()
			},

			/**
			 * Current month number.
			 *
			 * @attribute currentMonth
			 * @default Current month
			 * @type Number
			 */
			currentMonth: {
				value: (new Date()).getMonth()
			},

			/**
			 * Current year number.
			 *
			 * @attribute currentYear
			 * @default Current year
			 * @type Number
			 */
			currentYear: {
				value: (new Date()).getFullYear()
			},

			/**
			 * Dates which the calendar will show as selected by default.
			 *
			 * @attribute dates
			 * @default Current date
			 * @type Array
			 */
			dates: {
				value: [ new Date() ],
				validator: isArray,
				setter: function(v) {
					return this._setDates(v);
				}
			},

			/**
			 * The default date format string which can be overriden for
	         * localization support. The format must be valid according to
	         * <a href="DataType.Date.html">A.DataType.Date.format</a>.
			 *
			 * @attribute dateFormat
			 * @default %m/%d/%Y
			 * @type String
			 */
			dateFormat: {
				value: '%m/%d/%Y',
				validator: isString
			},

			/**
			 * First day of the week: Sunday is 0, Monday is 1.
			 *
			 * @attribute firstDayOfWeek
			 * @default 0
			 * @type Number
			 */
			firstDayOfWeek: {
				value: 0,
				validator: isNumber
			},

			/**
			 * Minimum allowable date. Values supported by the Date
             * constructor are supported.
			 *
			 * @attribute minDate
			 * @default null
			 * @type Date | String
			 */
			minDate: {
				value: null,
				setter: function(v) {
					return this._setMinMaxDate(v);
				}
			},

			/**
			 * Maximum allowable date. Values supported by the Date
             * constructor are supported.
			 *
			 * @attribute maxDate
			 * @default null
			 * @type String | Date
			 */
			maxDate: {
				value: null,
				setter: function(v) {
					return this._setMinMaxDate(v);
				}
			},

			showOn: {
				value: 'mousedown'
			},

			hideOn: {
				value: 'mousedown'
			},

			/**
			 * Wether accepts to select multiple dates.
			 *
			 * @attribute selectMultipleDates
			 * @default false
			 * @type boolean
			 */
			selectMultipleDates: {
				value: false
			},

			/**
			 * If true set the selected date with the correct
			 * <a href="Calendar.html#config_dateFormat">dateFormat</a> to the
			 * value of the input field which is hosting the Calendar.
			 *
			 * @attribute setValue
			 * @default true
			 * @type boolean
			 */
			setValue: {
				value: true,
				validator: isBoolean
			},

			/**
			 * If true is able to do stacking with another overlays.
			 *
			 * @attribute stack
			 * @default true
			 * @type boolean
			 */
			stack: {
				lazyAdd: false,
				value: true,
				setter: function(v) {
					return this._setStack(v);
				},
				validator: isBoolean
			}
		},

		EXTENDS: A.OverlayContext,

		prototype: {
			/**
			 * Construction logic executed during Calendar instantiation. Lifecycle.
			 *
			 * @method initializer
			 * @protected
			 */
			initializer: function() {
				var instance = this;

				instance.selectedDates = [];
			},

			/**
			 * Create the DOM structure for the Calendar. Lifecycle.
			 *
			 * @method renderUI
			 * @protected
			 */
			renderUI: function() {
				var instance = this;

				Calendar.superclass.renderUI.apply(this, arguments);

				instance._renderCalendar();
				instance._renderWeekDays();
				instance._renderBlankDays();
				instance._renderMonthDays();
			},

			/**
			 * Bind the events on the Calendar UI. Lifecycle.
			 *
			 * @method bindUI
			 * @protected
			 */
			bindUI: function() {
				var instance = this;

				Calendar.superclass.bindUI.apply(this, arguments);

				instance._bindDOMEvents();
				instance._bindDelegateMonthDays();

				instance.after('datesChange', A.bind(instance._afterSetDates, instance));
				// instance.after('currentDayChange', A.bind(instance._syncView, instance));
				instance.after('currentMonthChange', A.bind(instance._syncView, instance));
				instance.after('currentYearChange', A.bind(instance._syncView, instance));
			},

			/**
			 * Sync the Calendar UI. Lifecycle.
			 *
			 * @method syncUI
			 * @protected
			 */
			syncUI: function() {
				var instance = this;

				Calendar.superclass.syncUI.apply(this, arguments);

				instance._syncView();
			},

			/**
			 * Sync Calendar header, days and selected days UI.
			 *
			 * @method _syncView
			 * @protected
			 */
			_syncView: function() {
				var instance = this;
				var currentDay = instance.get(CURRENT_DAY);
				var currentMonth = instance.get(CURRENT_MONTH);
				var currentYear = instance.get(CURRENT_YEAR);

				instance._syncDays()
				instance._syncHeader();
				instance._syncSelectedDays();
			},

			/**
			 * Sync Calendar header UI.
			 *
			 * @method _syncHeader
			 * @protected
			 */
			_syncHeader: function() {
				var instance = this;
				var currentMonth = instance.get(CURRENT_MONTH);
				var currentYear = instance.get(CURRENT_YEAR);

				var title = [ instance._getMonthName(currentMonth), currentYear ].join(' ');

				instance.headerTitleNode.html(title);
			},

			/**
			 * Sync Calendar days UI.
			 *
			 * @method _syncDays
			 * @protected
			 */
			_syncDays: function() {
				var instance = this;
				var daysInMonth = instance.getDaysInMonth();
				var firstWeekDay = instance.getFirstDayOfWeek();
				var currentDate = instance.getCurrentDate();

				instance.monthDays.each(function(monthDayNode, day) {
					if (day >= daysInMonth) {
						// displaying the correct number of days in the current month
						monthDayNode.addClass(CSS_DAY_HIDDEN);
					}
					else {
						monthDayNode.removeClass(CSS_DAY_HIDDEN);
					}

					// restricting date
					currentDate.setDate(day + 1);

					instance._restrictDate(currentDate, monthDayNode);
				});

				instance.blankDays.each(function(blankDayNode, day) {
					var blankDays = (firstWeekDay - instance.get(FIRST_DAY_OF_WEEK) + 7) % 7;

					if (day < blankDays) {
						// show padding days to position the firstWeekDay correctly
						blankDayNode.removeClass(CSS_DAY_HIDDEN);
					}
					else {
						blankDayNode.addClass(CSS_DAY_HIDDEN);
					}
				});
			},

			/**
			 * Sync Calendar selected days UI.
			 *
			 * @method _syncSelectedDays
			 * @protected
			 */
			_syncSelectedDays: function(dates) {
				var instance = this;
				var currentMonth = instance.get(CURRENT_MONTH);
				var currentYear = instance.get(CURRENT_YEAR);

				instance.monthDays.replaceClass(CSS_STATE_ACTIVE, CSS_STATE_DEFAULT);
				instance.monthDays.replaceClass(CSS_STATE_HOVER, CSS_STATE_DEFAULT);

				instance._eachSelectedDate(function(date, index) {
					var canSelectDays = (currentMonth == date.getMonth()) && (currentYear == date.getFullYear());

					if (canSelectDays) {
						var dayNode = instance.monthDays.item( date.getDate() - 1 );

						dayNode.addClass(CSS_STATE_ACTIVE);

						try {
							// focus the last selected date
							// IE doesn't support focus on hidden elements
							dayNode.focus();
						}
						catch (err) {}
					}
				}, dates);
			},

			/**
			 * Render Calendar DOM elements.
			 *
			 * @method _renderCalendar
			 * @protected
			 */
			_renderCalendar: function() {
				var instance = this;
				var boundingBox = instance.get(BOUNDING_BOX);

				/**
				 * Container for house the week days elements.
				 *
				 * @property weekDaysNode
				 * @type Node
				 * @protected
				 */
				instance.weekDaysNode = A.Node.create(TPL_CALENDAR_WEEKDAYS);

				/**
				 * Container for house the month days elements.
				 *
				 * @property monthDaysNode
				 * @type Node
				 * @protected
				 */
				instance.monthDaysNode = A.Node.create(TPL_CALENDAR_MONTHDAYS);

				/**
				 * Header title Node.
				 *
				 * @property headerTitleNode
				 * @type Node
				 * @protected
				 */
				instance.headerTitleNode = A.Node.create(TPL_CALENDAR_HEADER_TITLE);

				/**
				 * This node is the WidgetStdMod.HEADER of the Calendar Overlay.
		         * Container to the
		         * <a href="Calendar.html#property_headertitleNode">headertitleNode</a>.
				 *
				 * @property headerContentNode
				 * @type Node
				 * @protected
				 */
				instance.headerContentNode = A.Node.create(TPL_CALENDAR_HEADER).append(instance.headerTitleNode);

				var bodyContent = A.Node.create('<div></div>');
				bodyContent.append(this.weekDaysNode);
				bodyContent.append(this.monthDaysNode);

				instance.setStdModContent(WidgetStdMod.HEADER, instance.headerContentNode);
				instance.setStdModContent(WidgetStdMod.BODY, bodyContent);

				boundingBox.addClass(CSS_CALENDAR);
			},

			/**
			 * Render Calendar DOM week days elements.
			 *
			 * @method _renderWeekDays
			 * @protected
			 */
			_renderWeekDays: function() {
				var day = 0;
				var instance = this;
				var weekDay = A.Node.create(TPL_CALENDAR_WEEK);
				var firstWeekDay = instance.get(FIRST_DAY_OF_WEEK);

				while(day < 7) {
					var fixedDay = (day + firstWeekDay) % 7;
					var dayName = instance._getDayNameMin(fixedDay);

					instance.weekDaysNode.append(
						weekDay.clone().html(dayName)
					);

					day++;
				}
			},

			/**
			 * Render Calendar DOM blank days elements. Blank days are used to align
		     * with the week day column.
			 *
			 * @method _renderBlankDays
			 * @protected
			 */
			_renderBlankDays: function() {
				var day = 0;
				var instance = this;
				var blankDay = A.Node.create(TPL_CALENDAR_DAY_BLANK);

				while (day++ < 7) {
					instance.monthDaysNode.append(
						blankDay.clone()
					);
				}

				instance.blankDays = instance.monthDaysNode.all(DOT+CSS_DAY_BLANK);
			},

			/**
			 * Render Calendar DOM month days elements.
			 *
			 * @method _renderMonthDays
			 * @protected
			 */
			_renderMonthDays: function() {
				var day = 0;
				var instance = this;
				var monthDay = A.Node.create(TPL_CALENDAR_DAY);

				while (day++ < 31) {
					instance.monthDaysNode.append(
						monthDay.clone().html(day)
					);
				}

				instance.monthDays = instance.monthDaysNode.all(DOT+CSS_DAY);
			},

			/**
			 * Bind DOM events to the UI.
			 *
			 * @method _bindDOMEvents
			 * @private
			 */
			_bindDOMEvents: function() {
				var instance = this;
				var headerContentNode = instance.headerContentNode;
				var boundingBox = instance.get(BOUNDING_BOX);

				var nextIcon = headerContentNode.one(DOT+CSS_ICON_CIRCLE_TRIANGLE_R)
				var prevIcon = headerContentNode.one(DOT+CSS_ICON_CIRCLE_TRIANGLE_L)

				var eventHalt = function(event) {
					event.halt();
				};

				boundingBox.on('click', eventHalt);
				boundingBox.on('mousedown', eventHalt);

				nextIcon.on('mousedown', A.bind(instance._selectNextMonth, instance));
				prevIcon.on('mousedown', A.bind(instance._selectPrevMonth, instance));
			},

			/**
			 * Delegate DOM events to the UI.
			 *
			 * @method _bindDelegateMonthDays
			 * @private
			 */
			_bindDelegateMonthDays: function() {
				var instance = this;
				var boundingBox = instance.get(BOUNDING_BOX);

				boundingBox.delegate('click', A.bind(instance._onClickDays, instance), DOT+CSS_DAY);
				boundingBox.delegate('mouseenter', A.bind(instance._onMouseEnterDays, instance), DOT+CSS_DAY);
				boundingBox.delegate('mouseleave', A.bind(instance._onMouseLeaveDays, instance), DOT+CSS_DAY);
			},

			/**
			 * Check if a date is already selected.
			 *
			 * @method alreadySelected
			 * @param {Date} date Date to be checked.
			 * @return {boolean}
			 */
			alreadySelected: function(date) {
				var instance = this;
				var alreadySelected = false;

				instance._eachSelectedDate(function(d, index) {
					if (instance._compareDates(d, date)) {
						alreadySelected = true;
					}
				});

				return alreadySelected;
			},

			/**
			 * Get the selected dates.
			 *
			 * @method getSelectedDates
			 * @return {Array}
			 */
			getSelectedDates: function() {
				var instance = this;

				return instance.get(DATES);
			},

			/**
			 * Get the selected dates formatted by the
		     * <a href="Calendar.html#config_dateFormat">dateFormat</a>.
			 *
			 * @method getFormattedSelectedDates
			 * @return {Array}
			 */
			getFormattedSelectedDates: function() {
				var instance = this;
				var dates = [];

				instance._eachSelectedDate(function(date) {
					dates.push( instance.formatDate( date, instance.get(DATE_FORMAT) ) );
				});

				return dates;
			},

			/**
			 * Get an Array with selected dates with detailed information (day, month, year).
			 *<pre><code>[{
			 *    year: date.getFullYear(),
			 *    month: date.getMonth(),
			 *    day: date.getDate()
			 * }]</code></pre>
			 *
			 * @method getDetailedSelectedDates
			 * @return {Array}
			 */
			getDetailedSelectedDates: function() {
				var instance = this;
				var dates = [];

				instance._eachSelectedDate(function(date) {
					dates.push({
						year: date.getFullYear(),
						month: date.getMonth(),
						day: date.getDate()
					});
				});

				return dates;
			},

			 /**
			  * Get the locale map containing the respective values for the
		      * <a href="Widget.html#config_locale">locale</a> used.
			  *
			  * <pre><code>A.DataType.Date.Locale['pt-br'] = A.merge(
			  *	A.DataType.Date.Locale['en'], {
			  *		a: ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Fri', 'Sat'],
			  *		A: ['Domingo','Segunda-feira','Ter&ccedil;a-feira','Quarta-feira','Quinta-feira','Sexta-feira','Sabado'],
			  *		b: ['Jan','Fev','Mar','Abr','Mai','Jun', 'Jul','Ago','Set','Out','Nov','Dez'],
			  *		B: ['Janeiro','Fevereiro','Mar&ccedil;o','Abril','Maio','Junho', 'Julho','Agosto','Setembro','Outubro','Novembro','Dezembro'],
			  *		c: '%a %d %b %Y %T %Z',
			  *		p: ['AM', 'PM'],
			  *		P: ['am', 'pm'],
			  *		r: '%I:%M:%S %p',
			  *		x: '%d/%m/%y',
			  *		X: '%T'
			  *	}
			  *);</code></pre>
			  *
			  * @method _getLocaleMap
			  * @protected
			  * @return {Object}
			  */
			_getLocaleMap: function() {
				var instance = this;

				return A.DataType.Date.Locale[ instance.get(LOCALE) ];
			},

			/**
			 * Util method to disable day nodes between
		     * <a href="Calendar.html#config_minDate">minDate</a> and
		     * <a href="Calendar.html#config_maxDate">maxDate</a>.
			 *
			 * @method _restrictDate
			 * @param {Date} currentDate Current date showed on the Calendar.
			 * @param {Node} monthDayNode Day node to be disabled.
			 * @protected
			 */
			_restrictDate: function(currentDate, monthDayNode) {
				var instance = this;
				var maxDate = instance.get(MAX_DATE);
				var minDate = instance.get(MIN_DATE);

				var disablePrev = minDate && (currentDate < minDate);
				var disableNext = maxDate && (currentDate > maxDate);

				if (disablePrev || disableNext) {
					monthDayNode.addClass(CSS_CALENDAR_DISABLED);
				}
				else {
					monthDayNode.removeClass(CSS_CALENDAR_DISABLED);
				}
			},

			/**
			 * Select the current date returned by
		     * <a href="Calendar.html#method_getCurrentDate">getCurrentDate</a>.
			 *
			 * @method _selectDate
			 * @protected
			 */
			_selectDate: function() {
				var instance = this;
				var dates = instance.get(DATES);
				var currentDate = instance.getCurrentDate();

				// if is single selection reset the selected dates
				if (!instance.get(SELECT_MULTIPLE_DATES)) {
					dates = [];
				}

				if (!instance.alreadySelected(currentDate)) {
					dates.push(currentDate);
					instance.set(DATES, dates);
				}
			},

			/**
			 * Remove the passed date from
		     * <a href="Calendar.html#config_dates">dates</a>.
			 *
			 * @method _removeDate
			 * @param {Date} date Date to remove
			 * @protected
			 */
			_removeDate: function(date) {
				var instance = this;
				var dates = instance.get(DATES);

				instance._eachSelectedDate(function(d, index) {
					if (instance._compareDates(d, date)) {
						A.Array.remove(dates, index);
					}
				});

				instance.set(DATES, dates);
			},

		 	/**
		 	 * Loop each date from <a href="Calendar.html#config_dates">dates</a> and
		     * executes a callback.
		 	 *
		 	 * @method _eachSelectedDate
		 	 * @param {function} fn Callback to be executed for each date.
		 	 * @param {Dates} dates Optional dates Array to loop through. If not passed it will use
		     * the <a href="Calendar.html#config_dates">dates</a>.
		 	 * @protected
		 	 */
			_eachSelectedDate: function(fn, dates) {
				var instance = this;

				if (!dates) {
					dates = instance.get(DATES);
				}

				A.Array.each(dates, function() {
					fn.apply(this, arguments);
				});
			},

			/**
			 * Compare two dates.
			 *
			 * @method _compareDates
			 * @param {Date} d1
			 * @param {Date} d2
			 * @protected
			 * @return {boolean}
			 */
			_compareDates: function(d1, d2) {
				return ( d1.getTime() == d2.getTime() );
			},

			/**
			 * Navigate to the next month. Fired from the next icon on the Calendar
		     * header.
			 *
			 * @method _selectNextMonth
			 * @param {EventFacade} event
			 * @protected
			 */
			_selectNextMonth: function(event) {
				var instance = this;

				instance._navigateMonth(+1);

				event.preventDefault();
			},

			/**
			 * Navigate to the previous month. Fired from the previous icon on the
			 * Calendar header.
			 *
			 * @method _selectPrevMonth
			 * @param {EventFacade} event
			 * @protected
			 */
			_selectPrevMonth: function(event) {
				var instance = this;

				instance._navigateMonth(-1);

				event.preventDefault();
			},

			/**
			 * Navigate through months and re-sync the UI.
			 *
			 * @method _navigateMonth
			 * @param {Number} offset Offset of the number of months to navigate.
		     * Could be a positive or a negative offset.
			 * @protected
			 */
			_navigateMonth: function(offset) {
				var instance = this;
				var currentMonth = instance.get(CURRENT_MONTH);
				var currentYear = instance.get(CURRENT_YEAR);

				var date = new Date(currentYear, currentMonth + offset);

				// when navigate by month update the year also
				instance.set(CURRENT_MONTH, date.getMonth());
				instance.set(CURRENT_YEAR, date.getFullYear());
			},

		    /**
		     * Fires after select event.
		     *
		     * @method _afterSetDates
		     * @param {EventFacade} event select custom event
		     * @protected
		     */
			_afterSetDates: function(event) {
				var instance = this;
				var normal = instance.getSelectedDates();
				var formatted = instance.getFormattedSelectedDates();
				var detailed = instance.getDetailedSelectedDates();
				var hasSelected = event.newVal.length;

				instance._syncSelectedDays();

				if (hasSelected) {
					instance.fire('select', {
						date: {
							detailed: detailed,
							formatted: formatted,
							normal: normal
						}
					});

					if (!instance.get(SELECT_MULTIPLE_DATES)) {
						instance.hide();
					}
				}

				if (instance.get(SET_VALUE)) {
					instance.get(CURRENT_NODE).val( formatted.join(',') );
				}
			},

		    /**
		     * Fires on click days elements.
		     *
		     * @method _onClickDays
		     * @param {EventFacade} event
		     * @protected
		     */
			_onClickDays: function(event) {
				var instance = this;
				var target  = event.currentTarget || event.target;
				var day = instance.monthDays.indexOf(target)+1;
				var disabled = target.test(DOT+CSS_CALENDAR_DISABLED);

				if (!disabled) {
					instance.set(CURRENT_DAY, day);

					var currentDate = instance.getCurrentDate();
					var alreadySelected = instance.alreadySelected(currentDate);

					if (alreadySelected) {
						instance._removeDate(currentDate);
					}
					else {
						instance._selectDate();
					}
				}

				event.preventDefault();
			},

		    /**
		     * Fires on mouseenter days elements.
		     *
		     * @method _onMouseEnterDays
		     * @param {EventFacade} event
		     * @protected
		     */
			_onMouseEnterDays: function(event) {
				var instance = this;
				var target  = event.currentTarget || event.target;

				target.replaceClass(CSS_STATE_DEFAULT, CSS_STATE_HOVER);
			},

		    /**
		     * Fires on mouseleave days elements.
		     *
		     * @method _onMouseLeaveDays
		     * @param {EventFacade} event
		     * @protected
		     */
			_onMouseLeaveDays: function(event) {
				var instance = this;
				var target  = event.currentTarget || event.target;

				target.replaceClass(CSS_STATE_HOVER, CSS_STATE_DEFAULT);
			},

			/**
			 * Setter for the <a href="Calendar.html#config_dates">dates</a> attribute.
			 *
			 * @method _setDates
			 * @param {Array} value
			 * @protected
			 * @return {Array}
			 */
			_setDates: function(value) {
				var instance = this;

				A.Array.each(value, function(date, index) {
					if (isString(date)) {
						value[index] = instance.parseDate( date );
					}
				});

				var lastSelectedDate = value[value.length - 1];

				if (lastSelectedDate) {
					// update the current values to the last selected date
					instance.set(CURRENT_DAY, lastSelectedDate.getDate());
					instance.set(CURRENT_MONTH, lastSelectedDate.getMonth());
					instance.set(CURRENT_YEAR, lastSelectedDate.getFullYear());

					instance._syncSelectedDays(value);
				}

				return value;
			},

			/**
			 * Setter for the <a href="Calendar.html#config_maxDates">maxDates</a> or
		     * <a href="Calendar.html#config_mainDates">minDates</a> attributes.
			 *
			 * @method _setMinMaxDate
			 * @param {Date} value
			 * @protected
			 * @return {Date}
			 */
			_setMinMaxDate: function(value) {
				var instance = this;

				if (isString(value)) {
					value = instance.parseDate( value );
				}

				return value;
			},

			/**
			 * Setter for the <a href="Calendar.html#config_stack">stack</a> attribute.
			 *
			 * @method _setStack
			 * @param {boolean} value
			 * @protected
			 * @return {boolean}
			 */
			_setStack: function(value) {
				var instance = this;

				if (value) {
					A.CalendarManager.register(instance);
				}
				else {
					A.CalendarManager.remove(instance);
				}

				return value;
			},

			/**
			 * Get current date.
			 *
			 * @method getCurrentDate
			 * @return {Date}
			 */
			getCurrentDate: function() {
				var instance = this;
				var date = instance._normalizeYearMonth();

				return ( new Date(date.year, date.month, date.day) );
			},

			/**
			 * Get the number of days in the passed year and month.
			 *
			 * @method getDaysInMonth
			 * @param {Number} year Year in the format YYYY.
			 * @param {Number} month 0 for January 11 for December.
			 * @return {Number}
			 */
			getDaysInMonth: function(year, month) {
				var instance = this;
				var date = instance._normalizeYearMonth(year, month);

		        return ( 32 - new Date(date.year, date.month, 32).getDate() );
		    },

			/**
			 * Get the Date for the first day of the passed year and month.
			 *
			 * @method getFirstDate
			 * @param {Number} year Year in the format YYYY.
			 * @param {Number} month 0 for January 11 for December.
			 * @return {Date}
			 */
			getFirstDate: function(year, month) {
				var instance = this;
				var date = instance._normalizeYearMonth(year, month);

				return ( new Date(date.year, date.month, 1) );
			},

			/**
			 * Get the Date for the last day of the passed year and month.
			 *
			 * @method getLastDate
			 * @param {Number} year Year in the format YYYY.
			 * @param {Number} month 0 for January 11 for December.
			 * @return {Date}
			 */
			getLastDate: function(year, month) {
				var instance = this;
				var date = instance._normalizeYearMonth(year, month);
				var daysInMonth = instance.getDaysInMonth(date.month);

				return ( new Date(date.year, date.month, daysInMonth) );
			},

			/**
			 * Get the first day of week of the passed year and month.
			 *
			 * @method getFirstDayOfWeek
			 * @param {Number} year Year in the format YYYY.
			 * @param {Number} month 0 for January 11 for December.
			 * @return {Number}
			 */
			getFirstDayOfWeek: function(year, month) {
				var instance = this;

				return instance.getFirstDate(year, month).getDay();
			},

			/**
			 * Returns an Object with the current day, month and year.
			 *
			 * @method _normalizeYearMonth
			 * @param {Number} year Year in the format YYYY.
			 * @param {Number} month 0 for January 11 for December.
			 * @param {Number} day
			 * @protected
			 * @return {Object}
			 */
			_normalizeYearMonth: function(year, month, day) {
				var instance = this;
				var currentDay = instance.get(CURRENT_DAY);
				var currentMonth = instance.get(CURRENT_MONTH);
				var currentYear = instance.get(CURRENT_YEAR);

				if (isUndefined(day)) {
					day = currentDay;
				}

				if (isUndefined(month)) {
					month = currentMonth;
				}

				if (isUndefined(year)) {
					year = currentYear;
				}

				return { year: year, month: month, day: day };
			},

			/**
			 * Get the day name of the passed weekDay from the locale map.
			 *
			 * @method _getDayName
			 * @param {Number} weekDay
			 * @protected
			 * @return {String}
			 */
			_getDayName: function(weekDay) {
				var instance = this;
				var localeMap = instance._getLocaleMap();

				return localeMap.A[weekDay];
			},

			/**
			 * Get a short day name of the passed weekDay from the locale map.
			 *
			 * @method _getDayNameShort
			 * @param {Number} weekDay
			 * @protected
			 * @return {String}
			 */
			_getDayNameShort: function(weekDay) {
				var instance = this;
				var localeMap = instance._getLocaleMap();

				return localeMap.a[weekDay];
			},

			/**
			 * Get a very short day name of the passed weekDay from the locale map.
			 *
			 * @method _getDayNameMin
			 * @param {Number} weekDay
			 * @protected
			 * @return {String}
			 */
			_getDayNameMin: function(weekDay) {
				var instance = this;
				var name = instance._getDayNameShort(weekDay);

				return name.slice(0, name.length-1);
			},

			/**
			 * Get a month name of the passed month from the locale map.
			 *
			 * @method _getMonthName
			 * @param {Number} month
			 * @protected
			 * @return {String}
			 */
			_getMonthName: function(month) {
				var instance = this;
				var localeMap = instance._getLocaleMap();

				return localeMap.B[month];
			},

			/**
			 * Get a short month name of the passed month from the locale map.
			 *
			 * @method _getMonthNameShort
			 * @param {Number} month
			 * @protected
			 * @return {String}
			 */
			_getMonthNameShort: function(month) {
				var instance = this;
				var localeMap = instance._getLocaleMap();

				return localeMap.b[month];
			},

			/**
			 * Parse a string to a Date object.
			 *
			 * @method parseDate
			 * @param {String} dateString
			 * @return {Date}
			 */
			parseDate: function(dateString) {
				var instance = this;

				return ( dateString ? new Date(dateString) : new Date );
			},

			/**
			 * Format a date with the passed mask. Used on
		     * <a href="Calendar.html#config_dateFormat">dateFormat</a>.
			 *
			 * @method formatDate
			 * @param {Date} date
			 * @param {String} mask See <a href="Calendar.html#config_dateFormat">dateFormat</a>.
			 * @return {String}
			 */
			formatDate: function (date, mask) {
				var instance = this;
				var locale = instance.get(LOCALE);

				return A.DataType.Date.format(date, { format: mask, locale: locale });
			}
		}
	}
);

A.Calendar = Calendar;

/**
 * A base class for CalendarManager:
 *
 * @param config {Object} Object literal specifying widget configuration properties.
 *
 * @class CalendarManager
 * @constructor
 * @extends OverlayManager
 * @static
 */
A.CalendarManager = new A.OverlayManager({
	/**
	 * ZIndex default value passed to the
     * <a href="OverlayManager.html#config_zIndexBase">zIndexBase</a> of
     * <a href="OverlayManager.html">OverlayManager</a>.
	 *
	 * @attribute zIndexBase
	 * @default 1000
	 * @type Number
	 */
	zIndexBase: 1000
});

}, '@VERSION@' ,{requires:['aui-overlay-context','datatype-date','widget-locale'], skinnable:true});
AUI.add('aui-calendar-datepicker-select', function(A) {
/**
 * The DatePickerSelect Utility
 *
 * @module aui-calendar
 * @submodule aui-calendar-datepicker-select
 */

var L = A.Lang,
	isArray = L.isArray,

	nodeSetter = function(v) {
		return A.one(v);
	},

	createSelect = function() {
		return A.Node.create(SELECT_TPL);
	},

	APPEND_ORDER = 'appendOrder',
	BASE_NAME = 'baseName',
	BLANK = '',
	BODY = 'body',
	BOUNDING_BOX = 'boundingBox',
	BUTTON = 'button',
	BUTTONITEM = 'buttonitem',
	BUTTON_NODE = 'buttonNode',
	CALENDAR = 'calendar',
	CLEARFIX = 'clearfix',
	CURRENT_DAY = 'currentDay',
	CURRENT_MONTH = 'currentMonth',
	CURRENT_YEAR = 'currentYear',
	DATA_COMPONENT_ID = 'data-auiComponentID',
	DATEPICKER = 'datepicker',
	DATE_FORMAT = 'dateFormat',
	DAY = 'day',
	DAY_NODE = 'dayNode',
	DAY_NODE_NAME = 'dayNodeName',
	DISPLAY = 'display',
	DOT = '.',
	HELPER = 'helper',
	MAX_DATE = 'maxDate',
	MIN_DATE = 'minDate',
	MONTH = 'month',
	MONTH_NODE = 'monthNode',
	MONTH_NODE_NAME = 'monthNodeName',
	NAME = 'name',
	OPTION = 'option',
	POPULATE_DAY = 'populateDay',
	POPULATE_MONTH = 'populateMonth',
	POPULATE_YEAR = 'populateYear',
	SELECT = 'select',
	SELECTED = 'selected',
	SELECT_WRAPPER_NODE = 'selectWrapperNode',
	SPACE = ' ',
	SRC_NODE = 'srcNode',
	TRIGGER = 'trigger',
	WRAPPER = 'wrapper',
	YEAR = 'year',
	YEAR_NODE = 'yearNode',
	YEAR_NODE_NAME = 'yearNodeName',
	YEAR_RANGE = 'yearRange',

	getCN = A.ClassNameManager.getClassName,

	CSS_BUTTONITEM = getCN(BUTTONITEM),
	CSS_DATEPICKER = getCN(DATEPICKER),
	CSS_DATEPICKER_BUTTON_WRAPPER = getCN(DATEPICKER, BUTTON, WRAPPER),
	CSS_DATEPICKER_DAY = getCN(DATEPICKER, DAY),
	CSS_DATEPICKER_DISPLAY = getCN(DATEPICKER, DISPLAY),
	CSS_DATEPICKER_MONTH = getCN(DATEPICKER, MONTH),
	CSS_DATEPICKER_SELECT_WRAPPER = getCN(DATEPICKER, SELECT, WRAPPER),
	CSS_DATEPICKER_YEAR = getCN(DATEPICKER, YEAR),
	CSS_HELPER_CLEARFIX = getCN(HELPER, CLEARFIX),

	SELECT_TPL = '<select></select>',
	SELECT_OPTION_TPL = '<option></option>',
	SRC_NODE_TPL = '<div></div>',
	WRAPPER_BUTTON_TPL = '<div class="'+ CSS_DATEPICKER_BUTTON_WRAPPER +'"></div>',
	WRAPPER_SELECT_TPL = '<div class='+ CSS_DATEPICKER_SELECT_WRAPPER +'></div>';

/**
 * <p><img src="assets/images/aui-calendar-datepicker-select/main.png"/></p>
 *
 * A base class for DatePickerSelect, providing:
 * <ul>
 *    <li>Widget Lifecycle (initializer, renderUI, bindUI, syncUI, destructor)</li>
 *    <li>Select a date from Calendar to select elements</li>
 * </ul>
 *
 * Quick Example:<br/>
 *
 * <pre><code>var instance = new A.DatePickerSelect({
 *  srcNode: '#srcNodeId',
 *  // locale: 'pt-br',
 *  dateFormat: '%m/%d/%y',
 *  yearRange: [ 1970, 2009 ]
 * }).render();
 * </code></pre>
 *
 * Check the list of <a href="DatePickerSelect.html#configattributes">Configuration Attributes</a> available for
 * DatePickerSelect.
 *
 * @class DatePickerSelect
 * @param config {Object} Object literal specifying widget configuration properties.
 * @constructor
 * @extends Calendar
 */
var DatePickerSelect = A.Component.create(
	{
		/**
		 * Static property provides a string to identify the class.
		 *
		 * @property DatePickerSelect.NAME
		 * @type String
		 * @static
		 */
		NAME: DATEPICKER,

		/**
		 * Static property used to define the default attribute
		 * configuration for the DatePickerSelect.
		 *
		 * @property DatePickerSelect.ATTRS
		 * @type Object
		 * @static
		 */
		ATTRS: {
			/**
			 * The order the selects elements are appended to the
	         * <a href="DatePickerSelect.html#config_srcNode">srcNode</a>.
			 *
			 * @attribute appendOrder
			 * @default [ 'm', 'd', 'y' ]
			 * @type Array
			 */
			appendOrder: {
				value: [ 'm', 'd', 'y' ],
				validator: isArray
			},

			/**
			 * A basename to identify the select elements from this
	         * DatePickerSelect.
			 *
			 * @attribute baseName
			 * @default datepicker
			 * @type String
			 */
			baseName: {
				value: DATEPICKER
			},

			/**
			 * DOM Node to display the button of the DatePickerSelect. If not
             * specified try to query using HTML_PARSER an element inside
             * contentBox which matches <code>aui-buttonitem</code>.
			 *
			 * @attribute buttonNode
			 * @default Generated div element.
			 * @type String
			 */
			buttonNode: {},

			/**
			 * DOM Node to display the day of the DatePickerSelect. If not
             * specified try to query using HTML_PARSER an element inside
             * contentBox which matches <code>aui-datepicker-year</code>.
			 *
			 * @attribute dayNode
			 * @default Generated div element.
			 * @type String | Node
			 */
			dayNode: {
				setter: nodeSetter,
				valueFn: createSelect
			},

			/**
			 * DOM Node to display the month of the DatePickerSelect. If not
             * specified try to query using HTML_PARSER an element inside
             * contentBox which matches <code>aui-datepicker-year</code>.
			 *
			 * @attribute monthNode
			 * @default Generated div element.
			 * @type String | Node
			 */
			monthNode: {
				setter: nodeSetter,
				valueFn: createSelect
			},

			/**
			 * DOM Node to display the year of the DatePickerSelect. If not
             * specified try to query using HTML_PARSER an element inside
             * contentBox which matches <code>aui-datepicker-year</code>.
			 *
			 * @attribute yearNode
			 * @default Generated div element.
			 * @type String | Node
			 */
			yearNode: {
				setter: nodeSetter,
				valueFn: createSelect
			},

			/**
			 * Name attribute used on the
	         * <a href="DatePickerSelect.html#config_dayNode">dayNode</a>.
			 *
			 * @attribute dayNodeName
			 * @default day
			 * @type String
			 */
			dayNodeName: {
				valueFn: function() {
					return this.get(DAY_NODE).get(NAME) || DAY;
				}
			},

			/**
			 * Name attribute used on the
	         * <a href="DatePickerSelect.html#config_monthNode">monthNode</a>.
			 *
			 * @attribute monthNodeName
			 * @default month
			 * @type String
			 */
			monthNodeName: {
				valueFn: function() {
					return this.get(MONTH_NODE).get(NAME) || MONTH;
				}
			},

			/**
			 * DOM Node to display the selects of the DatePickerSelect. If not
             * specified try to query using HTML_PARSER an element inside
             * contentBox which matches <code>aui-datepicker-select-wrapper</code>.
			 *
			 * @attribute selectWrapperNode
			 * @default Generated div element.
			 * @type String
			 */
			selectWrapperNode: {
				valueFn: function() {
					return A.Node.create(WRAPPER_SELECT_TPL);
				}
			},

			/**
			 * Name attribute used on the
	         * <a href="DatePickerSelect.html#config_yearNode">yearNode</a>.
			 *
			 * @attribute yearNodeName
			 * @default year
			 * @type String
			 */
			yearNodeName: {
				valueFn: function() {
					return this.get(YEAR_NODE).get(NAME) || YEAR;
				}
			},

			/**
			 * Trigger element to open the calendar. Inherited from
	         * <a href="OverlayContext.html#config_trigger">OverlayContext</a>.
			 *
			 * @attribute trigger
			 * @default Generated HTLM div element
			 * @type {Node | String}
			 */
			trigger: {
				valueFn: function() {
					return A.Node.create(WRAPPER_BUTTON_TPL);
				}
			},

			/**
			 * If true the Calendar is visible by default after the render phase.
	         * Inherited from
	         * <a href="OverlayContext.html#config_trigger">OverlayContext</a>.
			 *
			 * @attribute visible
			 * @default false
			 * @type boolean
			 */
			visible: {
				value: false
			},

			/**
			 * Year range to be displayed on the year select element. By default
	         * it displays from -10 to +10 years from the current year.
			 *
			 * @attribute yearRange
			 * @default [ year - 10, year + 10 ]
			 * @type Array
			 */
			yearRange: {
				valueFn: function() {
					var year = new Date().getFullYear();

					return [ year - 10, year + 10 ];
				},
				validator: isArray
			},

			/**
			 * Inherited from
	         * <a href="Calendar.html#config_setValue">Calendar</a>.
			 *
			 * @attribute setValue
			 * @default false
			 * @type boolean
			 */
			setValue: {
				value: false
			},

			srcNode: {
				valueFn: function() {
					var srcNode = A.Node.create(SRC_NODE_TPL);

					A.one(BODY).append(srcNode);

					return srcNode;
				}
			},

			/**
			 * If true the select element for the days will be automatic
	         * populated.
			 *
			 * @attribute populateDay
			 * @default true
			 * @type boolean
			 */
			populateDay: {
				value: true
			},

			/**
			 * If true the select element for the month will be automatic
	         * populated.
			 *
			 * @attribute populateMonth
			 * @default true
			 * @type boolean
			 */
			populateMonth: {
				value: true
			},

			/**
			 * If true the select element for the year will be automatic
	         * populated.
			 *
			 * @attribute populateYear
			 * @default true
			 * @type boolean
			 */
			populateYear: {
				value: true
			}
		},

		/**
		 * Object hash, defining how attribute values are to be parsed from
		 * markup contained in the widget's content box.
		 *
		 * @property DatePickerSelect.HTML_PARSER
		 * @type Object
		 * @static
		 */
		HTML_PARSER: {
			buttonNode: DOT+CSS_BUTTONITEM,

			dayNode: DOT+CSS_DATEPICKER_DAY,

			monthNode: DOT+CSS_DATEPICKER_MONTH,

			selectWrapperNode: DOT+CSS_DATEPICKER_SELECT_WRAPPER,

			trigger: DOT+CSS_DATEPICKER_BUTTON_WRAPPER,

			yearNode: DOT+CSS_DATEPICKER_YEAR
		},

		EXTENDS: A.Calendar,

		prototype: {
			/**
			 * Create the DOM structure for the DatePickerSelect. Lifecycle.
			 *
			 * @method renderUI
			 * @protected
			 */
			renderUI: function() {
				var instance = this;

				DatePickerSelect.superclass.renderUI.apply(this, arguments);

				instance._renderElements();
				instance._renderTriggerButton();
			},

			/**
			 * Bind the events on the DatePickerSelect UI. Lifecycle.
			 *
			 * @method bindUI
			 * @protected
			 */
			bindUI: function() {
				var instance = this;

				DatePickerSelect.superclass.bindUI.apply(this, arguments);

				instance.after('datesChange', instance._selectCurrentValues);
				instance.after('currentMonthChange', instance._afterSetCurrentMonth);
				instance.after('disabledChange', instance._afterDisabledChangeDatePicker);

				instance._bindSelectEvents();
			},

			/**
			 * Sync the DatePickerSelect UI. Lifecycle.
			 *
			 * @method syncUI
			 * @protected
			 */
			syncUI: function() {
				var instance = this;

				DatePickerSelect.superclass.syncUI.apply(this, arguments);

				instance._pupulateSelects();
				instance._selectCurrentValues();
			},

			/**
			 * Fired after
		     * <a href="DatePickerSelect.html#config_disabled">disabled</a> is set.
			 *
			 * @method _afterDisabledChangeDatePicker
			 * @param {EventFacade} event
			 * @protected
			 */
			_afterDisabledChangeDatePicker: function(event) {
				var instance = this;

				var disabled = event.newVal;

				instance.get(DAY_NODE).set('disabled', disabled);
				instance.get(MONTH_NODE).set('disabled', disabled);
				instance.get(YEAR_NODE).set('disabled', disabled);
			},

			/*
			* Override the default content box value, since we don't want the srcNode
			* to be the content box for DatePickerSelect.
			*/
			_defaultCB: function() {
				return null;
			},

			/**
			 * Gets an Array with the field elements in the correct order defined
		     * on <a href="DatePickerSelect.html#config_appendOrder">appendOrder</a>.
			 *
			 * @method _getAppendOrder
			 * @protected
			 * @return {Array}
			 */
			_getAppendOrder: function() {
				var instance = this;
				var appendOrder = instance.get(APPEND_ORDER);

				var mapping = {
					d: instance.get(DAY_NODE),
					m: instance.get(MONTH_NODE),
					y: instance.get(YEAR_NODE)
				};

				var firstField = mapping[ appendOrder[0] ];
				var secondField = mapping[ appendOrder[1] ];
				var thirdField = mapping[ appendOrder[2] ];

				var id = instance.get('id');

				firstField.setAttribute(DATA_COMPONENT_ID, id);
				secondField.setAttribute(DATA_COMPONENT_ID, id);
				thirdField.setAttribute(DATA_COMPONENT_ID, id);

				return [ firstField, secondField, thirdField ];
			},

			/**
			 * Render DOM elements for the DatePickerSelect.
			 *
			 * @method _renderElements
			 * @protected
			 */
			_renderElements: function() {
				var instance = this;
				var boundingBox = instance.get(BOUNDING_BOX);
				var srcNode = instance.get(SRC_NODE);

				// re-insert srcNode back into the DOM, it was replaced in _renderBox Widget method
				boundingBox.placeAfter(srcNode);

				var dayNode = instance.get(DAY_NODE);
				var monthNode = instance.get(MONTH_NODE);
				var yearNode = instance.get(YEAR_NODE);

				dayNode.addClass(CSS_DATEPICKER_DAY);
				monthNode.addClass(CSS_DATEPICKER_MONTH);
				yearNode.addClass(CSS_DATEPICKER_YEAR);

				srcNode.addClass(CSS_DATEPICKER);
				srcNode.addClass(CSS_DATEPICKER_DISPLAY);
				srcNode.addClass(CSS_HELPER_CLEARFIX);

				// setting name of the fields
				monthNode.set(NAME, instance.get(MONTH_NODE_NAME));
				yearNode.set(NAME, instance.get(YEAR_NODE_NAME));
				dayNode.set(NAME, instance.get(DAY_NODE_NAME));

				// append elements
				var selectWrapper = instance.get(SELECT_WRAPPER_NODE);
				var orderedFields = instance._getAppendOrder();

				// this textNode is to prevent layout shifting only
				// simulate the default browser space between inputs/selects on re-append
				var textNode = A.one(
					document.createTextNode(SPACE)
				);

				selectWrapper.append(orderedFields[0]);
				selectWrapper.append( textNode.clone() );
				selectWrapper.append(orderedFields[1]);
				selectWrapper.append( textNode );
				selectWrapper.append(orderedFields[2]);

				srcNode.append( selectWrapper );
			},

			/**
			 * Render DOM element for the trigger button of the DatePickerSelect.
			 *
			 * @method _renderTriggerButton
			 * @protected
			 */
			_renderTriggerButton: function() {
				var instance = this;
				var trigger = instance.get(TRIGGER).item(0);
				var srcNode = instance.get(SRC_NODE);

				instance._buttonItem = new A.ButtonItem({
					boundingBox: instance.get(BUTTON_NODE),
					icon: CALENDAR
				});

				srcNode.append(trigger);

				trigger.setAttribute(DATA_COMPONENT_ID, instance.get('id'));

				if ( trigger.test(DOT+CSS_DATEPICKER_BUTTON_WRAPPER) ) {
					// use Button if the user doesn't specify a trigger
					instance._buttonItem.render(trigger);
				}
			},

			/**
			 * Bind events on each select element (change, keypress, etc).
			 *
			 * @method _bindSelectEvents
			 * @protected
			 */
			_bindSelectEvents: function() {
				var instance = this;
				var selects = instance.get(SELECT_WRAPPER_NODE).all(SELECT);

				selects.on('change', A.bind(instance._onSelectChange, instance));
				selects.on('keypress', A.bind(instance._onSelectChange, instance));
			},

			/**
			 * Select the current values for the day, month and year to the respective
		     * input field.
			 *
			 * @method _selectCurrentValues
			 * @protected
			 */
			_selectCurrentValues: function() {
				var instance = this;

				instance._selectCurrentDay();
				instance._selectCurrentMonth();
				instance._selectCurrentYear();
			},

			/**
			 * Select the current day on the respective input field.
			 *
			 * @method _selectCurrentDay
			 * @protected
			 */
			_selectCurrentDay: function() {
				var instance = this;
				var currentDate = instance.getCurrentDate();

				instance.get(DAY_NODE).val(
					String(currentDate.getDate())
				);
			},

			/**
			 * Select the current month on the respective input field.
			 *
			 * @method _selectCurrentMonth
			 * @protected
			 */
			_selectCurrentMonth: function() {
				var instance = this;
				var currentDate = instance.getCurrentDate();

				instance.get(MONTH_NODE).val(
					String(currentDate.getMonth())
				);
			},

			/**
			 * Select the current year on the respective input field.
			 *
			 * @method _selectCurrentYear
			 * @protected
			 */
			_selectCurrentYear: function() {
				var instance = this;
				var currentDate = instance.getCurrentDate();

				instance.get(YEAR_NODE).val(
					String(currentDate.getFullYear())
				);
			},

			/**
			 * Populate each select element with the correct data for the day, month
		     * and year.
			 *
			 * @method _pupulateSelects
			 * @protected
			 */
			_pupulateSelects: function() {
				var instance = this;

				instance._populateDays();
				instance._populateMonths();
				instance._populateYears();

				// restricting dates based on the selects values
				var monthOptions = instance.get(MONTH_NODE).all(OPTION);
				var yearOptions = instance.get(YEAR_NODE).all(OPTION);

				var mLength = monthOptions.size() - 1;
				var yLength = yearOptions.size() - 1;

				var firstMonth = monthOptions.item(0).val();
				var firstYear = yearOptions.item(0).val();
				var lastMonth = monthOptions.item(mLength).val();
				var lastYear = yearOptions.item(yLength).val();

				var maxMonthDays = instance.getDaysInMonth(lastYear, lastMonth);

				var minDate = new Date(firstYear, firstMonth, 1);
				var maxDate = new Date(lastYear, lastMonth, maxMonthDays);

				instance.set(MAX_DATE, maxDate);
				instance.set(MIN_DATE, minDate);
			},

			/**
			 * Populate the year select element with the correct data.
			 *
			 * @method _populateYears
			 * @protected
			 */
			_populateYears: function() {
				var instance = this;
				var yearRange = instance.get(YEAR_RANGE);
				var yearNode = instance.get(YEAR_NODE);

				if (instance.get(POPULATE_YEAR)) {
					instance._populateSelect(yearNode, yearRange[0], yearRange[1]);
				}
			},

			/**
			 * Populate the month select element with the correct data.
			 *
			 * @method _populateMonths
			 * @protected
			 */
			_populateMonths: function() {
				var instance = this;
				var monthNode = instance.get(MONTH_NODE);
				var localeMap = instance._getLocaleMap();
				var monthLabels = localeMap.B;

				if (instance.get(POPULATE_MONTH)) {
					instance._populateSelect(monthNode, 0, (monthLabels.length - 1), monthLabels);
				}
			},

			/**
			 * Populate the day select element with the correct data.
			 *
			 * @method _populateDays
			 * @protected
			 */
			_populateDays: function() {
				var instance = this;
				var dayNode = instance.get(DAY_NODE);
				var daysInMonth = instance.getDaysInMonth();

				if (instance.get(POPULATE_DAY)) {
					instance._populateSelect(dayNode, 1, daysInMonth);
				}
			},

			/**
			 * Populate a select element with the data passed on the params.
			 *
			 * @method _populateSelect
			 * @param {HTMLSelectElement} select Select to be populated
			 * @param {Number} fromIndex Index to start
			 * @param {Number} toIndex Index to end
			 * @param {Object} values Object with labels to be used as content of each
		     * option. Optional.
			 * @protected
			 * @return {String}
			 */
			_populateSelect: function(select, fromIndex, toIndex, labels, values) {
				var i = 0;
				var index = fromIndex;

				select.empty();
				labels = labels || [];
				values = values || [];

				while (index <= toIndex) {
					var value = values[index] || index;
					var label = labels[index] || index;

					A.Node.getDOMNode(select).options[i] = new Option(label, index);

					i++;
					index++;
				}
			},

			/**
			 * Fired on any select change.
			 *
			 * @method _onSelectChange
			 * @param {EventFacade} event
			 * @protected
			 */
			_onSelectChange: function(event) {
				var instance = this;
				var target = event.currentTarget || event.target;
				var monthChanged = target.test(DOT+CSS_DATEPICKER_MONTH);

				var currentDay = instance.get(DAY_NODE).val();
				var currentMonth = instance.get(MONTH_NODE).val();
				var currentYear = instance.get(YEAR_NODE).val();

				instance.set(CURRENT_DAY, currentDay);
				instance.set(CURRENT_MONTH, currentMonth);
				instance.set(CURRENT_YEAR, currentYear);

				if (monthChanged) {
					instance._afterSetCurrentMonth();
				}

				instance._selectDate();
			},

			/**
			 * Fired after
		     * <a href="DatePickerSelect.html#config_currentMonth">currentMonth</a> is set.
			 *
			 * @method _afterSetCurrentMonth
			 * @param {EventFacade} event
			 * @protected
			 */
			_afterSetCurrentMonth: function(event) {
				var instance = this;

				instance._populateDays();
				instance._selectCurrentDay();
			}
		}
	}
);

A.DatePickerSelect = DatePickerSelect;

}, '@VERSION@' ,{requires:['aui-calendar-base','aui-button-item'], skinnable:true});


AUI.add('aui-calendar', function(A){}, '@VERSION@' ,{skinnable:true, use:['aui-calendar-base','aui-calendar-datepicker-select']});