// Copyright 2007 The Closure Library Authors. All Rights Reserved. // // Licensed 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. /** * @fileoverview Input Date Picker implementation. Pairs a * goog.ui.PopupDatePicker with an input element and handles the input from * either. * * @see ../demos/inputdatepicker.html */ goog.provide('goog.ui.InputDatePicker'); goog.require('goog.date.DateTime'); goog.require('goog.dom'); goog.require('goog.dom.InputType'); goog.require('goog.dom.TagName'); goog.require('goog.i18n.DateTimeParse'); goog.require('goog.string'); goog.require('goog.ui.Component'); goog.require('goog.ui.DatePicker'); /** @suppress {extraRequire} */ goog.require('goog.ui.LabelInput'); goog.require('goog.ui.PopupBase'); goog.require('goog.ui.PopupDatePicker'); /** * Input date picker widget. * * @param {goog.i18n.DateTimeFormat} dateTimeFormatter A formatter instance * used to format the date picker's date for display in the input element. * @param {goog.i18n.DateTimeParse} dateTimeParser A parser instance used to * parse the input element's string as a date to set the picker. * @param {goog.ui.DatePicker=} opt_datePicker Optional DatePicker. This * enables the use of a custom date-picker instance. * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. * @extends {goog.ui.Component} * @constructor */ goog.ui.InputDatePicker = function( dateTimeFormatter, dateTimeParser, opt_datePicker, opt_domHelper) { goog.ui.Component.call(this, opt_domHelper); this.dateTimeFormatter_ = dateTimeFormatter; this.dateTimeParser_ = dateTimeParser; this.popupDatePicker_ = new goog.ui.PopupDatePicker(opt_datePicker, opt_domHelper); this.addChild(this.popupDatePicker_); this.popupDatePicker_.setAllowAutoFocus(false); }; goog.inherits(goog.ui.InputDatePicker, goog.ui.Component); goog.tagUnsealableClass(goog.ui.InputDatePicker); /** * Used to format the date picker's date for display in the input element. * @type {goog.i18n.DateTimeFormat} * @private */ goog.ui.InputDatePicker.prototype.dateTimeFormatter_ = null; /** * Used to parse the input element's string as a date to set the picker. * @type {goog.i18n.DateTimeParse} * @private */ goog.ui.InputDatePicker.prototype.dateTimeParser_ = null; /** * The instance of goog.ui.PopupDatePicker used to pop up and select the date. * @type {goog.ui.PopupDatePicker} * @private */ goog.ui.InputDatePicker.prototype.popupDatePicker_ = null; /** * The element that the PopupDatePicker should be parented to. Defaults to the * body element of the page. * @type {Element} * @private */ goog.ui.InputDatePicker.prototype.popupParentElement_ = null; /** * Returns the PopupDatePicker's internal DatePicker instance. This can be * used to customize the date picker's styling. * * @return {goog.ui.DatePicker} The internal DatePicker instance. */ goog.ui.InputDatePicker.prototype.getDatePicker = function() { return this.popupDatePicker_.getDatePicker(); }; /** * Returns the PopupDatePicker instance. * * @return {goog.ui.PopupDatePicker} Popup instance. */ goog.ui.InputDatePicker.prototype.getPopupDatePicker = function() { return this.popupDatePicker_; }; /** * Returns the selected date, if any. Compares the dates from the date picker * and the input field, causing them to be synced if different. * @return {goog.date.DateTime} The selected date, if any. */ goog.ui.InputDatePicker.prototype.getDate = function() { // The user expectation is that the date be whatever the input shows. // This method biases towards the input value to conform to that expectation. var inputDate = this.getInputValueAsDate_(); var pickerDate = this.popupDatePicker_.getDate(); if (inputDate && pickerDate) { if (!inputDate.equals(pickerDate)) { this.popupDatePicker_.setDate(inputDate); } } else { this.popupDatePicker_.setDate(null); } return inputDate; }; /** * Sets the selected date. See goog.ui.PopupDatePicker.setDate(). * @param {goog.date.Date} date The date to set. */ goog.ui.InputDatePicker.prototype.setDate = function(date) { this.popupDatePicker_.setDate(date); }; /** * Sets the value of the input element. This can be overridden to support * alternative types of input setting. * * @param {string} value The value to set. */ goog.ui.InputDatePicker.prototype.setInputValue = function(value) { var el = this.getElement(); if (el.labelInput_) { var labelInput = /** @type {goog.ui.LabelInput} */ (el.labelInput_); labelInput.setValue(value); } else { el.value = value; } }; /** * Returns the value of the input element. This can be overridden to support * alternative types of input getting. * * @return {string} The input value. */ goog.ui.InputDatePicker.prototype.getInputValue = function() { var el = this.getElement(); if (el.labelInput_) { var labelInput = /** @type {goog.ui.LabelInput} */ (el.labelInput_); return labelInput.getValue(); } else { return el.value; } }; /** * Sets the value of the input element from date object. * * @param {?goog.date.Date} date The value to set. * @private */ goog.ui.InputDatePicker.prototype.setInputValueAsDate_ = function(date) { this.setInputValue(date ? this.dateTimeFormatter_.format(date) : ''); }; /** * Gets the input element value and attempts to parse it as a date. * * @return {goog.date.DateTime} The date object is returned if the parse * is successful, null is returned on failure. * @private */ goog.ui.InputDatePicker.prototype.getInputValueAsDate_ = function() { var value = goog.string.trim(this.getInputValue()); if (value) { var date = new goog.date.DateTime(); // DateTime needed as parse assumes it can call getHours(), getMinutes(), // etc, on the date if hours and minutes aren't defined. if (this.dateTimeParser_.strictParse(value, date) > 0) { // Parser with YYYY format string will interpret 1 as year 1 A.D. // However, datepicker.setDate() method will change it into 1901. // Same is true for any other pattern when number entered by user is // different from number of digits in the pattern. (YY and 1 will be 1AD). // See i18n/datetimeparse.js // Conversion happens in goog.date.Date/DateTime constructor // when it calls new Date(year...). See ui/datepicker.js. return date; } } return null; }; /** * Creates an input element for use with the popup date picker. * @override */ goog.ui.InputDatePicker.prototype.createDom = function() { this.setElementInternal( this.getDomHelper().createDom( goog.dom.TagName.INPUT, {'type': goog.dom.InputType.TEXT})); this.popupDatePicker_.createDom(); }; /** * Sets the element that the PopupDatePicker should be parented to. If not set, * defaults to the body element of the page. * @param {Element} el The element that the PopupDatePicker should be parented * to. */ goog.ui.InputDatePicker.prototype.setPopupParentElement = function(el) { this.popupParentElement_ = el; }; /** @override */ goog.ui.InputDatePicker.prototype.enterDocument = function() { // this.popupDatePicker_ has been added as a child even though it isn't really // a child (since its root element is not within InputDatePicker's DOM tree). // The PopupDatePicker will have its enterDocument method called as a result // of calling the superClass's enterDocument method. The PopupDatePicker needs // to be attached to the document *before* calling enterDocument so that when // PopupDatePicker decorates its element as a DatePicker, the element will be // in the document and enterDocument will be called for the DatePicker. Having // the PopupDatePicker's element in the document before calling enterDocument // will ensure that the event handlers for DatePicker are attached. // // An alternative could be to stop adding popupDatePicker_ as a child and // instead keep a reference to it and sync some event handlers, etc. but // appending the element to the document before calling enterDocument is a // less intrusive option. // // See cl/100837907 for more context and the discussion around this decision. (this.popupParentElement_ || this.getDomHelper().getDocument().body) .appendChild(this.popupDatePicker_.getElement()); goog.ui.InputDatePicker.superClass_.enterDocument.call(this); var el = this.getElement(); this.popupDatePicker_.attach(el); // Set the date picker to have the input's initial value, if any. this.popupDatePicker_.setDate(this.getInputValueAsDate_()); var handler = this.getHandler(); handler.listen( this.popupDatePicker_, goog.ui.DatePicker.Events.CHANGE, this.onDateChanged_); handler.listen( this.popupDatePicker_, goog.ui.PopupBase.EventType.SHOW, this.onPopup_); }; /** @override */ goog.ui.InputDatePicker.prototype.exitDocument = function() { goog.ui.InputDatePicker.superClass_.exitDocument.call(this); var el = this.getElement(); this.popupDatePicker_.detach(el); this.popupDatePicker_.exitDocument(); goog.dom.removeNode(this.popupDatePicker_.getElement()); }; /** @override */ goog.ui.InputDatePicker.prototype.decorateInternal = function(element) { goog.ui.InputDatePicker.superClass_.decorateInternal.call(this, element); this.popupDatePicker_.createDom(); }; /** @override */ goog.ui.InputDatePicker.prototype.disposeInternal = function() { goog.ui.InputDatePicker.superClass_.disposeInternal.call(this); this.popupDatePicker_.dispose(); this.popupDatePicker_ = null; this.popupParentElement_ = null; }; /** * See goog.ui.PopupDatePicker.showPopup(). * @param {Element} element Reference element for displaying the popup -- popup * will appear at the bottom-left corner of this element. */ goog.ui.InputDatePicker.prototype.showForElement = function(element) { this.popupDatePicker_.showPopup(element); }; /** * See goog.ui.PopupDatePicker.hidePopup(). */ goog.ui.InputDatePicker.prototype.hidePopup = function() { this.popupDatePicker_.hidePopup(); }; /** * Event handler for popup date picker popup events. * * @param {goog.events.Event} e popup event. * @private */ goog.ui.InputDatePicker.prototype.onPopup_ = function(e) { var inputValueAsDate = this.getInputValueAsDate_(); this.setDate(inputValueAsDate); // don't overwrite the input value with empty date if input is not valid if (inputValueAsDate) { this.setInputValueAsDate_(this.getDatePicker().getDate()); } }; /** * Event handler for date change events. Called when the date changes. * * @param {goog.ui.DatePickerEvent} e Date change event. * @private */ goog.ui.InputDatePicker.prototype.onDateChanged_ = function(e) { this.setInputValueAsDate_(e.date); };