commit extjs-2.2.1
[extjs.git] / source / widgets / form / DateField.js
1 /*\r
2  * Ext JS Library 2.2.1\r
3  * Copyright(c) 2006-2009, Ext JS, LLC.\r
4  * licensing@extjs.com\r
5  * \r
6  * http://extjs.com/license\r
7  */\r
8 \r
9 /**\r
10  * @class Ext.form.DateField\r
11  * @extends Ext.form.TriggerField\r
12  * Provides a date input field with a {@link Ext.DatePicker} dropdown and automatic date validation.\r
13 * @constructor\r
14 * Create a new DateField\r
15 * @param {Object} config\r
16  */\r
17 Ext.form.DateField = Ext.extend(Ext.form.TriggerField,  {\r
18     /**\r
19      * @cfg {String} format\r
20      * The default date format string which can be overriden for localization support.  The format must be\r
21      * valid according to {@link Date#parseDate} (defaults to 'm/d/Y').\r
22      */\r
23     format : "m/d/Y",\r
24     /**\r
25      * @cfg {String} altFormats\r
26      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined\r
27      * format (defaults to 'm/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d').\r
28      */\r
29     altFormats : "m/d/Y|n/j/Y|n/j/y|m/j/y|n/d/y|m/j/Y|n/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d|Y-m-d",\r
30     /**\r
31      * @cfg {String} disabledDaysText\r
32      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')\r
33      */\r
34     disabledDaysText : "Disabled",\r
35     /**\r
36      * @cfg {String} disabledDatesText\r
37      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')\r
38      */\r
39     disabledDatesText : "Disabled",\r
40     /**\r
41      * @cfg {String} minText\r
42      * The error text to display when the date in the cell is before minValue (defaults to\r
43      * 'The date in this field must be after {minValue}').\r
44      */\r
45     minText : "The date in this field must be equal to or after {0}",\r
46     /**\r
47      * @cfg {String} maxText\r
48      * The error text to display when the date in the cell is after maxValue (defaults to\r
49      * 'The date in this field must be before {maxValue}').\r
50      */\r
51     maxText : "The date in this field must be equal to or before {0}",\r
52     /**\r
53      * @cfg {String} invalidText\r
54      * The error text to display when the date in the field is invalid (defaults to\r
55      * '{value} is not a valid date - it must be in the format {format}').\r
56      */\r
57     invalidText : "{0} is not a valid date - it must be in the format {1}",\r
58     /**\r
59      * @cfg {String} triggerClass\r
60      * An additional CSS class used to style the trigger button.  The trigger will always get the\r
61      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'\r
62      * which displays a calendar icon).\r
63      */\r
64     triggerClass : 'x-form-date-trigger',\r
65     /**\r
66      * @cfg {Boolean} showToday\r
67      * False to hide the footer area of the DatePicker containing the Today button and disable the keyboard\r
68      * handler for spacebar that selects the current date (defaults to true).\r
69      */\r
70     showToday : true,\r
71     /**\r
72      * @cfg {Date/String} minValue\r
73      * The minimum allowed date. Can be either a Javascript date object or a string date in a\r
74      * valid format (defaults to null).\r
75      */\r
76     /**\r
77      * @cfg {Date/String} maxValue\r
78      * The maximum allowed date. Can be either a Javascript date object or a string date in a\r
79      * valid format (defaults to null).\r
80      */\r
81     /**\r
82      * @cfg {Array} disabledDays\r
83      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).\r
84      */\r
85     /**\r
86      * @cfg {Array} disabledDates\r
87      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular\r
88      * expression so they are very powerful. Some examples:\r
89      * <ul>\r
90      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>\r
91      * <li>["03/08", "09/16"] would disable those days for every year</li>\r
92      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>\r
93      * <li>["03/../2006"] would disable every day in March 2006</li>\r
94      * <li>["^03"] would disable every day in every March</li>\r
95      * </ul>\r
96      * Note that the format of the dates included in the array should exactly match the {@link #format} config.\r
97      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to\r
98      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].\r
99      */\r
100     /**\r
101      * @cfg {String/Object} autoCreate\r
102      * A DomHelper element spec, or true for a default element spec (defaults to\r
103      * {tag: "input", type: "text", size: "10", autocomplete: "off"})\r
104      */\r
105 \r
106     // private\r
107     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},\r
108 \r
109     initComponent : function(){\r
110         Ext.form.DateField.superclass.initComponent.call(this);\r
111         \r
112         this.addEvents(\r
113             /**\r
114              * @event select\r
115              * Fires when a date is selected via the date picker.\r
116              * @param {Ext.form.DateField} this\r
117              * @param {Date} date The date that was selected\r
118              */\r
119             'select'\r
120         );\r
121         \r
122         if(typeof this.minValue == "string"){\r
123             this.minValue = this.parseDate(this.minValue);\r
124         }\r
125         if(typeof this.maxValue == "string"){\r
126             this.maxValue = this.parseDate(this.maxValue);\r
127         }\r
128         this.disabledDatesRE = null;\r
129         this.initDisabledDays();\r
130     },\r
131 \r
132     // private\r
133     initDisabledDays : function(){\r
134         if(this.disabledDates){\r
135             var dd = this.disabledDates;\r
136             var re = "(?:";\r
137             for(var i = 0; i < dd.length; i++){\r
138                 re += dd[i];\r
139                 if(i != dd.length-1) re += "|";\r
140             }\r
141             this.disabledDatesRE = new RegExp(re + ")");\r
142         }\r
143     },\r
144 \r
145     /**\r
146      * Replaces any existing disabled dates with new values and refreshes the DatePicker.\r
147      * @param {Array} disabledDates An array of date strings (see the {@link #disabledDates} config\r
148      * for details on supported values) used to disable a pattern of dates.\r
149      */\r
150     setDisabledDates : function(dd){\r
151         this.disabledDates = dd;\r
152         this.initDisabledDays();\r
153         if(this.menu){\r
154             this.menu.picker.setDisabledDates(this.disabledDatesRE);\r
155         }\r
156     },\r
157 \r
158     /**\r
159      * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the DatePicker.\r
160      * @param {Array} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config\r
161      * for details on supported values.\r
162      */\r
163     setDisabledDays : function(dd){\r
164         this.disabledDays = dd;\r
165         if(this.menu){\r
166             this.menu.picker.setDisabledDays(dd);\r
167         }\r
168     },\r
169 \r
170     /**\r
171      * Replaces any existing {@link #minValue} with the new value and refreshes the DatePicker.\r
172      * @param {Date} value The minimum date that can be selected\r
173      */\r
174     setMinValue : function(dt){\r
175         this.minValue = (typeof dt == "string" ? this.parseDate(dt) : dt);\r
176         if(this.menu){\r
177             this.menu.picker.setMinDate(this.minValue);\r
178         }\r
179     },\r
180 \r
181     /**\r
182      * Replaces any existing {@link #maxValue} with the new value and refreshes the DatePicker.\r
183      * @param {Date} value The maximum date that can be selected\r
184      */\r
185     setMaxValue : function(dt){\r
186         this.maxValue = (typeof dt == "string" ? this.parseDate(dt) : dt);\r
187         if(this.menu){\r
188             this.menu.picker.setMaxDate(this.maxValue);\r
189         }\r
190     },\r
191 \r
192     // private\r
193     validateValue : function(value){\r
194         value = this.formatDate(value);\r
195         if(!Ext.form.DateField.superclass.validateValue.call(this, value)){\r
196             return false;\r
197         }\r
198         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid\r
199              return true;\r
200         }\r
201         var svalue = value;\r
202         value = this.parseDate(value);\r
203         if(!value){\r
204             this.markInvalid(String.format(this.invalidText, svalue, this.format));\r
205             return false;\r
206         }\r
207         var time = value.getTime();\r
208         if(this.minValue && time < this.minValue.getTime()){\r
209             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));\r
210             return false;\r
211         }\r
212         if(this.maxValue && time > this.maxValue.getTime()){\r
213             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));\r
214             return false;\r
215         }\r
216         if(this.disabledDays){\r
217             var day = value.getDay();\r
218             for(var i = 0; i < this.disabledDays.length; i++) {\r
219                 if(day === this.disabledDays[i]){\r
220                     this.markInvalid(this.disabledDaysText);\r
221                     return false;\r
222                 }\r
223             }\r
224         }\r
225         var fvalue = this.formatDate(value);\r
226         if(this.disabledDatesRE && this.disabledDatesRE.test(fvalue)){\r
227             this.markInvalid(String.format(this.disabledDatesText, fvalue));\r
228             return false;\r
229         }\r
230         return true;\r
231     },\r
232 \r
233     // private\r
234     // Provides logic to override the default TriggerField.validateBlur which just returns true\r
235     validateBlur : function(){\r
236         return !this.menu || !this.menu.isVisible();\r
237     },\r
238 \r
239     /**\r
240      * Returns the current date value of the date field.\r
241      * @return {Date} The date value\r
242      */\r
243     getValue : function(){\r
244         return this.parseDate(Ext.form.DateField.superclass.getValue.call(this)) || "";\r
245     },\r
246 \r
247     /**\r
248      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid\r
249      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}\r
250      * (the default format used is "m/d/Y").\r
251      * <br />Usage:\r
252      * <pre><code>\r
253 //All of these calls set the same date value (May 4, 2006)\r
254 \r
255 //Pass a date object:\r
256 var dt = new Date('5/4/2006');\r
257 dateField.setValue(dt);\r
258 \r
259 //Pass a date string (default format):\r
260 dateField.setValue('05/04/2006');\r
261 \r
262 //Pass a date string (custom format):\r
263 dateField.format = 'Y-m-d';\r
264 dateField.setValue('2006-05-04');\r
265 </code></pre>\r
266      * @param {String/Date} date The date or valid date string\r
267      */\r
268     setValue : function(date){\r
269         Ext.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));\r
270     },\r
271 \r
272     // private\r
273     parseDate : function(value){\r
274         if(!value || Ext.isDate(value)){\r
275             return value;\r
276         }\r
277         var v = Date.parseDate(value, this.format);\r
278         if(!v && this.altFormats){\r
279             if(!this.altFormatsArray){\r
280                 this.altFormatsArray = this.altFormats.split("|");\r
281             }\r
282             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){\r
283                 v = Date.parseDate(value, this.altFormatsArray[i]);\r
284             }\r
285         }\r
286         return v;\r
287     },\r
288 \r
289     // private\r
290     onDestroy : function(){\r
291         if(this.menu) {\r
292             this.menu.destroy();\r
293         }\r
294         if(this.wrap){\r
295             this.wrap.remove();\r
296         }\r
297         Ext.form.DateField.superclass.onDestroy.call(this);\r
298     },\r
299 \r
300     // private\r
301     formatDate : function(date){\r
302         return Ext.isDate(date) ? date.dateFormat(this.format) : date;\r
303     },\r
304 \r
305     // private\r
306     menuListeners : {\r
307         select: function(m, d){\r
308             this.setValue(d);\r
309             this.fireEvent('select', this, d);\r
310         },\r
311         show : function(){ // retain focus styling\r
312             this.onFocus();\r
313         },\r
314         hide : function(){\r
315             this.focus.defer(10, this);\r
316             var ml = this.menuListeners;\r
317             this.menu.un("select", ml.select,  this);\r
318             this.menu.un("show", ml.show,  this);\r
319             this.menu.un("hide", ml.hide,  this);\r
320         }\r
321     },\r
322 \r
323     /**\r
324      * @method onTriggerClick\r
325      * @hide\r
326      */\r
327     // private\r
328     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker\r
329     onTriggerClick : function(){\r
330         if(this.disabled){\r
331             return;\r
332         }\r
333         if(this.menu == null){\r
334             this.menu = new Ext.menu.DateMenu();\r
335         }\r
336         Ext.apply(this.menu.picker,  {\r
337             minDate : this.minValue,\r
338             maxDate : this.maxValue,\r
339             disabledDatesRE : this.disabledDatesRE,\r
340             disabledDatesText : this.disabledDatesText,\r
341             disabledDays : this.disabledDays,\r
342             disabledDaysText : this.disabledDaysText,\r
343             format : this.format,\r
344             showToday : this.showToday,\r
345             minText : String.format(this.minText, this.formatDate(this.minValue)),\r
346             maxText : String.format(this.maxText, this.formatDate(this.maxValue))\r
347         });\r
348         this.menu.on(Ext.apply({}, this.menuListeners, {\r
349             scope:this\r
350         }));\r
351         this.menu.picker.setValue(this.getValue() || new Date());\r
352         this.menu.show(this.el, "tl-bl?");\r
353     },\r
354 \r
355     // private\r
356     beforeBlur : function(){\r
357         var v = this.parseDate(this.getRawValue());\r
358         if(v){\r
359             this.setValue(v);\r
360         }\r
361     }\r
362 \r
363     /**\r
364      * @cfg {Boolean} grow @hide\r
365      */\r
366     /**\r
367      * @cfg {Number} growMin @hide\r
368      */\r
369     /**\r
370      * @cfg {Number} growMax @hide\r
371      */\r
372     /**\r
373      * @hide\r
374      * @method autoSize\r
375      */\r
376 });\r
377 Ext.reg('datefield', Ext.form.DateField);