inputdatepicker.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. // Copyright 2007 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Input Date Picker implementation. Pairs a
  16. * goog.ui.PopupDatePicker with an input element and handles the input from
  17. * either.
  18. *
  19. * @see ../demos/inputdatepicker.html
  20. */
  21. goog.provide('goog.ui.InputDatePicker');
  22. goog.require('goog.date.DateTime');
  23. goog.require('goog.dom');
  24. goog.require('goog.dom.InputType');
  25. goog.require('goog.dom.TagName');
  26. goog.require('goog.i18n.DateTimeParse');
  27. goog.require('goog.string');
  28. goog.require('goog.ui.Component');
  29. goog.require('goog.ui.DatePicker');
  30. /** @suppress {extraRequire} */
  31. goog.require('goog.ui.LabelInput');
  32. goog.require('goog.ui.PopupBase');
  33. goog.require('goog.ui.PopupDatePicker');
  34. /**
  35. * Input date picker widget.
  36. *
  37. * @param {goog.i18n.DateTimeFormat} dateTimeFormatter A formatter instance
  38. * used to format the date picker's date for display in the input element.
  39. * @param {goog.i18n.DateTimeParse} dateTimeParser A parser instance used to
  40. * parse the input element's string as a date to set the picker.
  41. * @param {goog.ui.DatePicker=} opt_datePicker Optional DatePicker. This
  42. * enables the use of a custom date-picker instance.
  43. * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
  44. * @extends {goog.ui.Component}
  45. * @constructor
  46. */
  47. goog.ui.InputDatePicker = function(
  48. dateTimeFormatter, dateTimeParser, opt_datePicker, opt_domHelper) {
  49. goog.ui.Component.call(this, opt_domHelper);
  50. this.dateTimeFormatter_ = dateTimeFormatter;
  51. this.dateTimeParser_ = dateTimeParser;
  52. this.popupDatePicker_ =
  53. new goog.ui.PopupDatePicker(opt_datePicker, opt_domHelper);
  54. this.addChild(this.popupDatePicker_);
  55. this.popupDatePicker_.setAllowAutoFocus(false);
  56. };
  57. goog.inherits(goog.ui.InputDatePicker, goog.ui.Component);
  58. goog.tagUnsealableClass(goog.ui.InputDatePicker);
  59. /**
  60. * Used to format the date picker's date for display in the input element.
  61. * @type {goog.i18n.DateTimeFormat}
  62. * @private
  63. */
  64. goog.ui.InputDatePicker.prototype.dateTimeFormatter_ = null;
  65. /**
  66. * Used to parse the input element's string as a date to set the picker.
  67. * @type {goog.i18n.DateTimeParse}
  68. * @private
  69. */
  70. goog.ui.InputDatePicker.prototype.dateTimeParser_ = null;
  71. /**
  72. * The instance of goog.ui.PopupDatePicker used to pop up and select the date.
  73. * @type {goog.ui.PopupDatePicker}
  74. * @private
  75. */
  76. goog.ui.InputDatePicker.prototype.popupDatePicker_ = null;
  77. /**
  78. * The element that the PopupDatePicker should be parented to. Defaults to the
  79. * body element of the page.
  80. * @type {Element}
  81. * @private
  82. */
  83. goog.ui.InputDatePicker.prototype.popupParentElement_ = null;
  84. /**
  85. * Returns the PopupDatePicker's internal DatePicker instance. This can be
  86. * used to customize the date picker's styling.
  87. *
  88. * @return {goog.ui.DatePicker} The internal DatePicker instance.
  89. */
  90. goog.ui.InputDatePicker.prototype.getDatePicker = function() {
  91. return this.popupDatePicker_.getDatePicker();
  92. };
  93. /**
  94. * Returns the PopupDatePicker instance.
  95. *
  96. * @return {goog.ui.PopupDatePicker} Popup instance.
  97. */
  98. goog.ui.InputDatePicker.prototype.getPopupDatePicker = function() {
  99. return this.popupDatePicker_;
  100. };
  101. /**
  102. * Returns the selected date, if any. Compares the dates from the date picker
  103. * and the input field, causing them to be synced if different.
  104. * @return {goog.date.DateTime} The selected date, if any.
  105. */
  106. goog.ui.InputDatePicker.prototype.getDate = function() {
  107. // The user expectation is that the date be whatever the input shows.
  108. // This method biases towards the input value to conform to that expectation.
  109. var inputDate = this.getInputValueAsDate_();
  110. var pickerDate = this.popupDatePicker_.getDate();
  111. if (inputDate && pickerDate) {
  112. if (!inputDate.equals(pickerDate)) {
  113. this.popupDatePicker_.setDate(inputDate);
  114. }
  115. } else {
  116. this.popupDatePicker_.setDate(null);
  117. }
  118. return inputDate;
  119. };
  120. /**
  121. * Sets the selected date. See goog.ui.PopupDatePicker.setDate().
  122. * @param {goog.date.Date} date The date to set.
  123. */
  124. goog.ui.InputDatePicker.prototype.setDate = function(date) {
  125. this.popupDatePicker_.setDate(date);
  126. };
  127. /**
  128. * Sets the value of the input element. This can be overridden to support
  129. * alternative types of input setting.
  130. *
  131. * @param {string} value The value to set.
  132. */
  133. goog.ui.InputDatePicker.prototype.setInputValue = function(value) {
  134. var el = this.getElement();
  135. if (el.labelInput_) {
  136. var labelInput = /** @type {goog.ui.LabelInput} */ (el.labelInput_);
  137. labelInput.setValue(value);
  138. } else {
  139. el.value = value;
  140. }
  141. };
  142. /**
  143. * Returns the value of the input element. This can be overridden to support
  144. * alternative types of input getting.
  145. *
  146. * @return {string} The input value.
  147. */
  148. goog.ui.InputDatePicker.prototype.getInputValue = function() {
  149. var el = this.getElement();
  150. if (el.labelInput_) {
  151. var labelInput = /** @type {goog.ui.LabelInput} */ (el.labelInput_);
  152. return labelInput.getValue();
  153. } else {
  154. return el.value;
  155. }
  156. };
  157. /**
  158. * Sets the value of the input element from date object.
  159. *
  160. * @param {?goog.date.Date} date The value to set.
  161. * @private
  162. */
  163. goog.ui.InputDatePicker.prototype.setInputValueAsDate_ = function(date) {
  164. this.setInputValue(date ? this.dateTimeFormatter_.format(date) : '');
  165. };
  166. /**
  167. * Gets the input element value and attempts to parse it as a date.
  168. *
  169. * @return {goog.date.DateTime} The date object is returned if the parse
  170. * is successful, null is returned on failure.
  171. * @private
  172. */
  173. goog.ui.InputDatePicker.prototype.getInputValueAsDate_ = function() {
  174. var value = goog.string.trim(this.getInputValue());
  175. if (value) {
  176. var date = new goog.date.DateTime();
  177. // DateTime needed as parse assumes it can call getHours(), getMinutes(),
  178. // etc, on the date if hours and minutes aren't defined.
  179. if (this.dateTimeParser_.strictParse(value, date) > 0) {
  180. // Parser with YYYY format string will interpret 1 as year 1 A.D.
  181. // However, datepicker.setDate() method will change it into 1901.
  182. // Same is true for any other pattern when number entered by user is
  183. // different from number of digits in the pattern. (YY and 1 will be 1AD).
  184. // See i18n/datetimeparse.js
  185. // Conversion happens in goog.date.Date/DateTime constructor
  186. // when it calls new Date(year...). See ui/datepicker.js.
  187. return date;
  188. }
  189. }
  190. return null;
  191. };
  192. /**
  193. * Creates an input element for use with the popup date picker.
  194. * @override
  195. */
  196. goog.ui.InputDatePicker.prototype.createDom = function() {
  197. this.setElementInternal(
  198. this.getDomHelper().createDom(
  199. goog.dom.TagName.INPUT, {'type': goog.dom.InputType.TEXT}));
  200. this.popupDatePicker_.createDom();
  201. };
  202. /**
  203. * Sets the element that the PopupDatePicker should be parented to. If not set,
  204. * defaults to the body element of the page.
  205. * @param {Element} el The element that the PopupDatePicker should be parented
  206. * to.
  207. */
  208. goog.ui.InputDatePicker.prototype.setPopupParentElement = function(el) {
  209. this.popupParentElement_ = el;
  210. };
  211. /** @override */
  212. goog.ui.InputDatePicker.prototype.enterDocument = function() {
  213. // this.popupDatePicker_ has been added as a child even though it isn't really
  214. // a child (since its root element is not within InputDatePicker's DOM tree).
  215. // The PopupDatePicker will have its enterDocument method called as a result
  216. // of calling the superClass's enterDocument method. The PopupDatePicker needs
  217. // to be attached to the document *before* calling enterDocument so that when
  218. // PopupDatePicker decorates its element as a DatePicker, the element will be
  219. // in the document and enterDocument will be called for the DatePicker. Having
  220. // the PopupDatePicker's element in the document before calling enterDocument
  221. // will ensure that the event handlers for DatePicker are attached.
  222. //
  223. // An alternative could be to stop adding popupDatePicker_ as a child and
  224. // instead keep a reference to it and sync some event handlers, etc. but
  225. // appending the element to the document before calling enterDocument is a
  226. // less intrusive option.
  227. //
  228. // See cl/100837907 for more context and the discussion around this decision.
  229. (this.popupParentElement_ || this.getDomHelper().getDocument().body)
  230. .appendChild(this.popupDatePicker_.getElement());
  231. goog.ui.InputDatePicker.superClass_.enterDocument.call(this);
  232. var el = this.getElement();
  233. this.popupDatePicker_.attach(el);
  234. // Set the date picker to have the input's initial value, if any.
  235. this.popupDatePicker_.setDate(this.getInputValueAsDate_());
  236. var handler = this.getHandler();
  237. handler.listen(
  238. this.popupDatePicker_, goog.ui.DatePicker.Events.CHANGE,
  239. this.onDateChanged_);
  240. handler.listen(
  241. this.popupDatePicker_, goog.ui.PopupBase.EventType.SHOW, this.onPopup_);
  242. };
  243. /** @override */
  244. goog.ui.InputDatePicker.prototype.exitDocument = function() {
  245. goog.ui.InputDatePicker.superClass_.exitDocument.call(this);
  246. var el = this.getElement();
  247. this.popupDatePicker_.detach(el);
  248. this.popupDatePicker_.exitDocument();
  249. goog.dom.removeNode(this.popupDatePicker_.getElement());
  250. };
  251. /** @override */
  252. goog.ui.InputDatePicker.prototype.decorateInternal = function(element) {
  253. goog.ui.InputDatePicker.superClass_.decorateInternal.call(this, element);
  254. this.popupDatePicker_.createDom();
  255. };
  256. /** @override */
  257. goog.ui.InputDatePicker.prototype.disposeInternal = function() {
  258. goog.ui.InputDatePicker.superClass_.disposeInternal.call(this);
  259. this.popupDatePicker_.dispose();
  260. this.popupDatePicker_ = null;
  261. this.popupParentElement_ = null;
  262. };
  263. /**
  264. * See goog.ui.PopupDatePicker.showPopup().
  265. * @param {Element} element Reference element for displaying the popup -- popup
  266. * will appear at the bottom-left corner of this element.
  267. */
  268. goog.ui.InputDatePicker.prototype.showForElement = function(element) {
  269. this.popupDatePicker_.showPopup(element);
  270. };
  271. /**
  272. * See goog.ui.PopupDatePicker.hidePopup().
  273. */
  274. goog.ui.InputDatePicker.prototype.hidePopup = function() {
  275. this.popupDatePicker_.hidePopup();
  276. };
  277. /**
  278. * Event handler for popup date picker popup events.
  279. *
  280. * @param {goog.events.Event} e popup event.
  281. * @private
  282. */
  283. goog.ui.InputDatePicker.prototype.onPopup_ = function(e) {
  284. var inputValueAsDate = this.getInputValueAsDate_();
  285. this.setDate(inputValueAsDate);
  286. // don't overwrite the input value with empty date if input is not valid
  287. if (inputValueAsDate) {
  288. this.setInputValueAsDate_(this.getDatePicker().getDate());
  289. }
  290. };
  291. /**
  292. * Event handler for date change events. Called when the date changes.
  293. *
  294. * @param {goog.ui.DatePickerEvent} e Date change event.
  295. * @private
  296. */
  297. goog.ui.InputDatePicker.prototype.onDateChanged_ = function(e) {
  298. this.setInputValueAsDate_(e.date);
  299. };