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