PageRenderTime 27ms CodeModel.GetById 12ms app.highlight 12ms RepoModel.GetById 0ms app.codeStats 0ms

/ext-4.0.7/src/form/field/Date.js

https://bitbucket.org/srogerf/javascript
JavaScript | 558 lines | 244 code | 53 blank | 261 comment | 37 complexity | 055554e3121cef3b95ee276c380c4e5e MD5 | raw file
  1/*
  2
  3This file is part of Ext JS 4
  4
  5Copyright (c) 2011 Sencha Inc
  6
  7Contact:  http://www.sencha.com/contact
  8
  9GNU General Public License Usage
 10This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
 11
 12If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
 13
 14*/
 15/**
 16 * @docauthor Jason Johnston <jason@sencha.com>
 17 *
 18 * Provides a date input field with a {@link Ext.picker.Date date picker} dropdown and automatic date
 19 * validation.
 20 *
 21 * This field recognizes and uses the JavaScript Date object as its main {@link #value} type. In addition,
 22 * it recognizes string values which are parsed according to the {@link #format} and/or {@link #altFormats}
 23 * configs. These may be reconfigured to use date formats appropriate for the user's locale.
 24 *
 25 * The field may be limited to a certain range of dates by using the {@link #minValue}, {@link #maxValue},
 26 * {@link #disabledDays}, and {@link #disabledDates} config parameters. These configurations will be used both
 27 * in the field's validation, and in the date picker dropdown by preventing invalid dates from being selected.
 28 *
 29 * # Example usage
 30 *
 31 *     @example
 32 *     Ext.create('Ext.form.Panel', {
 33 *         renderTo: Ext.getBody(),
 34 *         width: 300,
 35 *         bodyPadding: 10,
 36 *         title: 'Dates',
 37 *         items: [{
 38 *             xtype: 'datefield',
 39 *             anchor: '100%',
 40 *             fieldLabel: 'From',
 41 *             name: 'from_date',
 42 *             maxValue: new Date()  // limited to the current date or prior
 43 *         }, {
 44 *             xtype: 'datefield',
 45 *             anchor: '100%',
 46 *             fieldLabel: 'To',
 47 *             name: 'to_date',
 48 *             value: new Date()  // defaults to today
 49 *         }]
 50 *     });
 51 *
 52 * # Date Formats Examples
 53 *
 54 * This example shows a couple of different date format parsing scenarios. Both use custom date format
 55 * configurations; the first one matches the configured `format` while the second matches the `altFormats`.
 56 *
 57 *     @example
 58 *     Ext.create('Ext.form.Panel', {
 59 *         renderTo: Ext.getBody(),
 60 *         width: 300,
 61 *         bodyPadding: 10,
 62 *         title: 'Dates',
 63 *         items: [{
 64 *             xtype: 'datefield',
 65 *             anchor: '100%',
 66 *             fieldLabel: 'Date',
 67 *             name: 'date',
 68 *             // The value matches the format; will be parsed and displayed using that format.
 69 *             format: 'm d Y',
 70 *             value: '2 4 1978'
 71 *         }, {
 72 *             xtype: 'datefield',
 73 *             anchor: '100%',
 74 *             fieldLabel: 'Date',
 75 *             name: 'date',
 76 *             // The value does not match the format, but does match an altFormat; will be parsed
 77 *             // using the altFormat and displayed using the format.
 78 *             format: 'm d Y',
 79 *             altFormats: 'm,d,Y|m.d.Y',
 80 *             value: '2.4.1978'
 81 *         }]
 82 *     });
 83 */
 84Ext.define('Ext.form.field.Date', {
 85    extend:'Ext.form.field.Picker',
 86    alias: 'widget.datefield',
 87    requires: ['Ext.picker.Date'],
 88    alternateClassName: ['Ext.form.DateField', 'Ext.form.Date'],
 89
 90    /**
 91     * @cfg {String} format
 92     * The default date format string which can be overriden for localization support. The format must be valid
 93     * according to {@link Ext.Date#parse}.
 94     */
 95    format : "m/d/Y",
 96    /**
 97     * @cfg {String} altFormats
 98     * Multiple date formats separated by "|" to try when parsing a user input value and it does not match the defined
 99     * format.
100     */
101    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|n-j|n/j",
102    /**
103     * @cfg {String} disabledDaysText
104     * The tooltip to display when the date falls on a disabled day.
105     */
106    disabledDaysText : "Disabled",
107    /**
108     * @cfg {String} disabledDatesText
109     * The tooltip text to display when the date falls on a disabled date.
110     */
111    disabledDatesText : "Disabled",
112    /**
113     * @cfg {String} minText
114     * The error text to display when the date in the cell is before {@link #minValue}.
115     */
116    minText : "The date in this field must be equal to or after {0}",
117    /**
118     * @cfg {String} maxText
119     * The error text to display when the date in the cell is after {@link #maxValue}.
120     */
121    maxText : "The date in this field must be equal to or before {0}",
122    /**
123     * @cfg {String} invalidText
124     * The error text to display when the date in the field is invalid.
125     */
126    invalidText : "{0} is not a valid date - it must be in the format {1}",
127    /**
128     * @cfg {String} [triggerCls='x-form-date-trigger']
129     * An additional CSS class used to style the trigger button. The trigger will always get the class 'x-form-trigger'
130     * and triggerCls will be **appended** if specified (default class displays a calendar icon).
131     */
132    triggerCls : Ext.baseCSSPrefix + 'form-date-trigger',
133    /**
134     * @cfg {Boolean} showToday
135     * false to hide the footer area of the Date picker containing the Today button and disable the keyboard handler for
136     * spacebar that selects the current date.
137     */
138    showToday : true,
139    /**
140     * @cfg {Date/String} minValue
141     * The minimum allowed date. Can be either a Javascript date object or a string date in a valid format.
142     */
143    /**
144     * @cfg {Date/String} maxValue
145     * The maximum allowed date. Can be either a Javascript date object or a string date in a valid format.
146     */
147    /**
148     * @cfg {Number[]} disabledDays
149     * An array of days to disable, 0 based. Some examples:
150     *
151     *     // disable Sunday and Saturday:
152     *     disabledDays:  [0, 6]
153     *     // disable weekdays:
154     *     disabledDays: [1,2,3,4,5]
155     */
156    /**
157     * @cfg {String[]} disabledDates
158     * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular expression so
159     * they are very powerful. Some examples:
160     *
161     *     // disable these exact dates:
162     *     disabledDates: ["03/08/2003", "09/16/2003"]
163     *     // disable these days for every year:
164     *     disabledDates: ["03/08", "09/16"]
165     *     // only match the beginning (useful if you are using short years):
166     *     disabledDates: ["^03/08"]
167     *     // disable every day in March 2006:
168     *     disabledDates: ["03/../2006"]
169     *     // disable every day in every March:
170     *     disabledDates: ["^03"]
171     *
172     * Note that the format of the dates included in the array should exactly match the {@link #format} config. In order
173     * to support regular expressions, if you are using a {@link #format date format} that has "." in it, you will have
174     * to escape the dot when restricting dates. For example: `["03\\.08\\.03"]`.
175     */
176
177    /**
178     * @cfg {String} submitFormat
179     * The date format string which will be submitted to the server. The format must be valid according to {@link
180     * Ext.Date#parse} (defaults to {@link #format}).
181     */
182
183    // in the absence of a time value, a default value of 12 noon will be used
184    // (note: 12 noon was chosen because it steers well clear of all DST timezone changes)
185    initTime: '12', // 24 hour format
186
187    initTimeFormat: 'H',
188
189    matchFieldWidth: false,
190    /**
191     * @cfg {Number} startDay
192     * Day index at which the week should begin, 0-based (defaults to Sunday)
193     */
194    startDay: 0,
195
196    initComponent : function(){
197        var me = this,
198            isString = Ext.isString,
199            min, max;
200
201        min = me.minValue;
202        max = me.maxValue;
203        if(isString(min)){
204            me.minValue = me.parseDate(min);
205        }
206        if(isString(max)){
207            me.maxValue = me.parseDate(max);
208        }
209        me.disabledDatesRE = null;
210        me.initDisabledDays();
211
212        me.callParent();
213    },
214
215    initValue: function() {
216        var me = this,
217            value = me.value;
218
219        // If a String value was supplied, try to convert it to a proper Date
220        if (Ext.isString(value)) {
221            me.value = me.rawToValue(value);
222        }
223
224        me.callParent();
225    },
226
227    // private
228    initDisabledDays : function(){
229        if(this.disabledDates){
230            var dd = this.disabledDates,
231                len = dd.length - 1,
232                re = "(?:";
233
234            Ext.each(dd, function(d, i){
235                re += Ext.isDate(d) ? '^' + Ext.String.escapeRegex(d.dateFormat(this.format)) + '$' : dd[i];
236                if (i !== len) {
237                    re += '|';
238                }
239            }, this);
240            this.disabledDatesRE = new RegExp(re + ')');
241        }
242    },
243
244    /**
245     * Replaces any existing disabled dates with new values and refreshes the Date picker.
246     * @param {String[]} disabledDates An array of date strings (see the {@link #disabledDates} config for details on
247     * supported values) used to disable a pattern of dates.
248     */
249    setDisabledDates : function(dd){
250        var me = this,
251            picker = me.picker;
252
253        me.disabledDates = dd;
254        me.initDisabledDays();
255        if (picker) {
256            picker.setDisabledDates(me.disabledDatesRE);
257        }
258    },
259
260    /**
261     * Replaces any existing disabled days (by index, 0-6) with new values and refreshes the Date picker.
262     * @param {Number[]} disabledDays An array of disabled day indexes. See the {@link #disabledDays} config for details on
263     * supported values.
264     */
265    setDisabledDays : function(dd){
266        var picker = this.picker;
267
268        this.disabledDays = dd;
269        if (picker) {
270            picker.setDisabledDays(dd);
271        }
272    },
273
274    /**
275     * Replaces any existing {@link #minValue} with the new value and refreshes the Date picker.
276     * @param {Date} value The minimum date that can be selected
277     */
278    setMinValue : function(dt){
279        var me = this,
280            picker = me.picker,
281            minValue = (Ext.isString(dt) ? me.parseDate(dt) : dt);
282
283        me.minValue = minValue;
284        if (picker) {
285            picker.minText = Ext.String.format(me.minText, me.formatDate(me.minValue));
286            picker.setMinDate(minValue);
287        }
288    },
289
290    /**
291     * Replaces any existing {@link #maxValue} with the new value and refreshes the Date picker.
292     * @param {Date} value The maximum date that can be selected
293     */
294    setMaxValue : function(dt){
295        var me = this,
296            picker = me.picker,
297            maxValue = (Ext.isString(dt) ? me.parseDate(dt) : dt);
298
299        me.maxValue = maxValue;
300        if (picker) {
301            picker.maxText = Ext.String.format(me.maxText, me.formatDate(me.maxValue));
302            picker.setMaxDate(maxValue);
303        }
304    },
305
306    /**
307     * Runs all of Date's validations and returns an array of any errors. Note that this first runs Text's validations,
308     * so the returned array is an amalgamation of all field errors. The additional validation checks are testing that
309     * the date format is valid, that the chosen date is within the min and max date constraints set, that the date
310     * chosen is not in the disabledDates regex and that the day chosed is not one of the disabledDays.
311     * @param {Object} [value] The value to get errors for (defaults to the current field value)
312     * @return {String[]} All validation errors for this field
313     */
314    getErrors: function(value) {
315        var me = this,
316            format = Ext.String.format,
317            clearTime = Ext.Date.clearTime,
318            errors = me.callParent(arguments),
319            disabledDays = me.disabledDays,
320            disabledDatesRE = me.disabledDatesRE,
321            minValue = me.minValue,
322            maxValue = me.maxValue,
323            len = disabledDays ? disabledDays.length : 0,
324            i = 0,
325            svalue,
326            fvalue,
327            day,
328            time;
329
330        value = me.formatDate(value || me.processRawValue(me.getRawValue()));
331
332        if (value === null || value.length < 1) { // if it's blank and textfield didn't flag it then it's valid
333             return errors;
334        }
335
336        svalue = value;
337        value = me.parseDate(value);
338        if (!value) {
339            errors.push(format(me.invalidText, svalue, me.format));
340            return errors;
341        }
342
343        time = value.getTime();
344        if (minValue && time < clearTime(minValue).getTime()) {
345            errors.push(format(me.minText, me.formatDate(minValue)));
346        }
347
348        if (maxValue && time > clearTime(maxValue).getTime()) {
349            errors.push(format(me.maxText, me.formatDate(maxValue)));
350        }
351
352        if (disabledDays) {
353            day = value.getDay();
354
355            for(; i < len; i++) {
356                if (day === disabledDays[i]) {
357                    errors.push(me.disabledDaysText);
358                    break;
359                }
360            }
361        }
362
363        fvalue = me.formatDate(value);
364        if (disabledDatesRE && disabledDatesRE.test(fvalue)) {
365            errors.push(format(me.disabledDatesText, fvalue));
366        }
367
368        return errors;
369    },
370
371    rawToValue: function(rawValue) {
372        return this.parseDate(rawValue) || rawValue || null;
373    },
374
375    valueToRaw: function(value) {
376        return this.formatDate(this.parseDate(value));
377    },
378
379    /**
380     * @method setValue
381     * Sets the value of the date field. You can pass a date object or any string that can be parsed into a valid date,
382     * using {@link #format} as the date format, according to the same rules as {@link Ext.Date#parse} (the default
383     * format used is "m/d/Y").
384     *
385     * Usage:
386     *
387     *     //All of these calls set the same date value (May 4, 2006)
388     *
389     *     //Pass a date object:
390     *     var dt = new Date('5/4/2006');
391     *     dateField.setValue(dt);
392     *
393     *     //Pass a date string (default format):
394     *     dateField.setValue('05/04/2006');
395     *
396     *     //Pass a date string (custom format):
397     *     dateField.format = 'Y-m-d';
398     *     dateField.setValue('2006-05-04');
399     *
400     * @param {String/Date} date The date or valid date string
401     * @return {Ext.form.field.Date} this
402     */
403
404    /**
405     * Attempts to parse a given string value using a given {@link Ext.Date#parse date format}.
406     * @param {String} value The value to attempt to parse
407     * @param {String} format A valid date format (see {@link Ext.Date#parse})
408     * @return {Date} The parsed Date object, or null if the value could not be successfully parsed.
409     */
410    safeParse : function(value, format) {
411        var me = this,
412            utilDate = Ext.Date,
413            parsedDate,
414            result = null;
415
416        if (utilDate.formatContainsHourInfo(format)) {
417            // if parse format contains hour information, no DST adjustment is necessary
418            result = utilDate.parse(value, format);
419        } else {
420            // set time to 12 noon, then clear the time
421            parsedDate = utilDate.parse(value + ' ' + me.initTime, format + ' ' + me.initTimeFormat);
422            if (parsedDate) {
423                result = utilDate.clearTime(parsedDate);
424            }
425        }
426        return result;
427    },
428
429    // @private
430    getSubmitValue: function() {
431        var format = this.submitFormat || this.format,
432            value = this.getValue();
433
434        return value ? Ext.Date.format(value, format) : '';
435    },
436
437    /**
438     * @private
439     */
440    parseDate : function(value) {
441        if(!value || Ext.isDate(value)){
442            return value;
443        }
444
445        var me = this,
446            val = me.safeParse(value, me.format),
447            altFormats = me.altFormats,
448            altFormatsArray = me.altFormatsArray,
449            i = 0,
450            len;
451
452        if (!val && altFormats) {
453            altFormatsArray = altFormatsArray || altFormats.split('|');
454            len = altFormatsArray.length;
455            for (; i < len && !val; ++i) {
456                val = me.safeParse(value, altFormatsArray[i]);
457            }
458        }
459        return val;
460    },
461
462    // private
463    formatDate : function(date){
464        return Ext.isDate(date) ? Ext.Date.dateFormat(date, this.format) : date;
465    },
466
467    createPicker: function() {
468        var me = this,
469            format = Ext.String.format;
470
471        return Ext.create('Ext.picker.Date', {
472            pickerField: me,
473            ownerCt: me.ownerCt,
474            renderTo: document.body,
475            floating: true,
476            hidden: true,
477            focusOnShow: true,
478            minDate: me.minValue,
479            maxDate: me.maxValue,
480            disabledDatesRE: me.disabledDatesRE,
481            disabledDatesText: me.disabledDatesText,
482            disabledDays: me.disabledDays,
483            disabledDaysText: me.disabledDaysText,
484            format: me.format,
485            showToday: me.showToday,
486            startDay: me.startDay,
487            minText: format(me.minText, me.formatDate(me.minValue)),
488            maxText: format(me.maxText, me.formatDate(me.maxValue)),
489            listeners: {
490                scope: me,
491                select: me.onSelect
492            },
493            keyNavConfig: {
494                esc: function() {
495                    me.collapse();
496                }
497            }
498        });
499    },
500
501    onSelect: function(m, d) {
502        var me = this;
503
504        me.setValue(d);
505        me.fireEvent('select', me, d);
506        me.collapse();
507    },
508
509    /**
510     * @private
511     * Sets the Date picker's value to match the current field value when expanding.
512     */
513    onExpand: function() {
514        var value = this.getValue();
515        this.picker.setValue(Ext.isDate(value) ? value : new Date());
516    },
517
518    /**
519     * @private
520     * Focuses the field when collapsing the Date picker.
521     */
522    onCollapse: function() {
523        this.focus(false, 60);
524    },
525
526    // private
527    beforeBlur : function(){
528        var me = this,
529            v = me.parseDate(me.getRawValue()),
530            focusTask = me.focusTask;
531
532        if (focusTask) {
533            focusTask.cancel();
534        }
535
536        if (v) {
537            me.setValue(v);
538        }
539    }
540
541    /**
542     * @hide
543     * @cfg {Boolean} grow
544     */
545    /**
546     * @hide
547     * @cfg {Number} growMin
548     */
549    /**
550     * @hide
551     * @cfg {Number} growMax
552     */
553    /**
554     * @hide
555     * @method autoSize
556     */
557});
558