Subversion Repositories eFlore/Applications.cel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
27 jpm 1
/*
2
 * Ext JS Library 2.0.2
3
 * Copyright(c) 2006-2008, Ext JS, LLC.
4
 * licensing@extjs.com
5
 *
6
 * http://extjs.com/license
7
 */
8
 
9
/**
10
 * @class Ext.DatePicker
11
 * @extends Ext.Component
12
 * Simple date picker class.
13
 * @constructor
14
 * Create a new DatePicker
15
 * @param {Object} config The config object
16
 */
17
Ext.DatePicker = Ext.extend(Ext.Component, {
18
    /**
19
     * @cfg {String} todayText
20
     * The text to display on the button that selects the current date (defaults to "Today")
21
     */
22
    todayText : "Today",
23
    /**
24
     * @cfg {String} okText
25
     * The text to display on the ok button
26
     */
27
    okText : " OK ", //   to give the user extra clicking room
28
    /**
29
     * @cfg {String} cancelText
30
     * The text to display on the cancel button
31
     */
32
    cancelText : "Cancel",
33
    /**
34
     * @cfg {String} todayTip
35
     * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
36
     */
37
    todayTip : "{0} (Spacebar)",
38
    /**
39
     * @cfg {Date} minDate
40
     * Minimum allowable date (JavaScript date object, defaults to null)
41
     */
42
    minDate : null,
43
    /**
44
     * @cfg {Date} maxDate
45
     * Maximum allowable date (JavaScript date object, defaults to null)
46
     */
47
    maxDate : null,
48
    /**
49
     * @cfg {String} minText
50
     * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
51
     */
52
    minText : "This date is before the minimum date",
53
    /**
54
     * @cfg {String} maxText
55
     * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
56
     */
57
    maxText : "This date is after the maximum date",
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/d/y",
64
    /**
65
     * @cfg {Array} disabledDays
66
     * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
67
     */
68
    disabledDays : null,
69
    /**
70
     * @cfg {String} disabledDaysText
71
     * The tooltip to display when the date falls on a disabled day (defaults to "")
72
     */
73
    disabledDaysText : "",
74
    /**
75
     * @cfg {RegExp} disabledDatesRE
76
     * JavaScript regular expression used to disable a pattern of dates (defaults to null)
77
     */
78
    disabledDatesRE : null,
79
    /**
80
     * @cfg {String} disabledDatesText
81
     * The tooltip text to display when the date falls on a disabled date (defaults to "")
82
     */
83
    disabledDatesText : "",
84
    /**
85
     * @cfg {Boolean} constrainToViewport
86
     * True to constrain the date picker to the viewport (defaults to true)
87
     */
88
    constrainToViewport : true,
89
    /**
90
     * @cfg {Array} monthNames
91
     * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
92
     */
93
    monthNames : Date.monthNames,
94
    /**
95
     * @cfg {Array} dayNames
96
     * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
97
     */
98
    dayNames : Date.dayNames,
99
    /**
100
     * @cfg {String} nextText
101
     * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
102
     */
103
    nextText: 'Next Month (Control+Right)',
104
    /**
105
     * @cfg {String} prevText
106
     * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
107
     */
108
    prevText: 'Previous Month (Control+Left)',
109
    /**
110
     * @cfg {String} monthYearText
111
     * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
112
     */
113
    monthYearText: 'Choose a month (Control+Up/Down to move years)',
114
    /**
115
     * @cfg {Number} startDay
116
     * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
117
     */
118
    startDay : 0,
119
 
120
    initComponent : function(){
121
        Ext.DatePicker.superclass.initComponent.call(this);
122
 
123
        this.value = this.value ?
124
                 this.value.clearTime() : new Date().clearTime();
125
 
126
        this.addEvents(
127
            /**
128
             * @event select
129
             * Fires when a date is selected
130
             * @param {DatePicker} this
131
             * @param {Date} date The selected date
132
             */
133
            'select'
134
        );
135
 
136
        if(this.handler){
137
            this.on("select", this.handler,  this.scope || this);
138
        }
139
 
140
        this.initDisabledDays();
141
    },
142
 
143
    // private
144
    initDisabledDays : function(){
145
        if(!this.disabledDatesRE && this.disabledDates){
146
            var dd = this.disabledDates;
147
            var re = "(?:";
148
            for(var i = 0; i < dd.length; i++){
149
                re += dd[i];
150
                if(i != dd.length-1) re += "|";
151
            }
152
            this.disabledDatesRE = new RegExp(re + ")");
153
        }
154
    },
155
 
156
    /**
157
     * Sets the value of the date field
158
     * @param {Date} value The date to set
159
     */
160
    setValue : function(value){
161
        var old = this.value;
162
        this.value = value.clearTime(true);
163
        if(this.el){
164
            this.update(this.value);
165
        }
166
    },
167
 
168
    /**
169
     * Gets the current selected value of the date field
170
     * @return {Date} The selected date
171
     */
172
    getValue : function(){
173
        return this.value;
174
    },
175
 
176
    // private
177
    focus : function(){
178
        if(this.el){
179
            this.update(this.activeDate);
180
        }
181
    },
182
 
183
    // private
184
    onRender : function(container, position){
185
        var m = [
186
             '<table cellspacing="0">',
187
                '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
188
                '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
189
        var dn = this.dayNames;
190
        for(var i = 0; i < 7; i++){
191
            var d = this.startDay+i;
192
            if(d > 6){
193
                d = d-7;
194
            }
195
            m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
196
        }
197
        m[m.length] = "</tr></thead><tbody><tr>";
198
        for(var i = 0; i < 42; i++) {
199
            if(i % 7 == 0 && i != 0){
200
                m[m.length] = "</tr><tr>";
201
            }
202
            m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
203
        }
204
        m[m.length] = '</tr></tbody></table></td></tr><tr><td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
205
 
206
        var el = document.createElement("div");
207
        el.className = "x-date-picker";
208
        el.innerHTML = m.join("");
209
 
210
        container.dom.insertBefore(el, position);
211
 
212
        this.el = Ext.get(el);
213
        this.eventEl = Ext.get(el.firstChild);
214
 
215
        new Ext.util.ClickRepeater(this.el.child("td.x-date-left a"), {
216
            handler: this.showPrevMonth,
217
            scope: this,
218
            preventDefault:true,
219
            stopDefault:true
220
        });
221
 
222
        new Ext.util.ClickRepeater(this.el.child("td.x-date-right a"), {
223
            handler: this.showNextMonth,
224
            scope: this,
225
            preventDefault:true,
226
            stopDefault:true
227
        });
228
 
229
        this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
230
 
231
        this.monthPicker = this.el.down('div.x-date-mp');
232
        this.monthPicker.enableDisplayMode('block');
233
 
234
        var kn = new Ext.KeyNav(this.eventEl, {
235
            "left" : function(e){
236
                e.ctrlKey ?
237
                    this.showPrevMonth() :
238
                    this.update(this.activeDate.add("d", -1));
239
            },
240
 
241
            "right" : function(e){
242
                e.ctrlKey ?
243
                    this.showNextMonth() :
244
                    this.update(this.activeDate.add("d", 1));
245
            },
246
 
247
            "up" : function(e){
248
                e.ctrlKey ?
249
                    this.showNextYear() :
250
                    this.update(this.activeDate.add("d", -7));
251
            },
252
 
253
            "down" : function(e){
254
                e.ctrlKey ?
255
                    this.showPrevYear() :
256
                    this.update(this.activeDate.add("d", 7));
257
            },
258
 
259
            "pageUp" : function(e){
260
                this.showNextMonth();
261
            },
262
 
263
            "pageDown" : function(e){
264
                this.showPrevMonth();
265
            },
266
 
267
            "enter" : function(e){
268
                e.stopPropagation();
269
                return true;
270
            },
271
 
272
            scope : this
273
        });
274
 
275
        this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
276
 
277
        this.eventEl.addKeyListener(Ext.EventObject.SPACE, this.selectToday,  this);
278
 
279
        this.el.unselectable();
280
 
281
        this.cells = this.el.select("table.x-date-inner tbody td");
282
        this.textNodes = this.el.query("table.x-date-inner tbody span");
283
 
284
        this.mbtn = new Ext.Button({
285
            text: "&#160;",
286
            tooltip: this.monthYearText,
287
            renderTo: this.el.child("td.x-date-middle", true)
288
        });
289
 
290
        this.mbtn.on('click', this.showMonthPicker, this);
291
        this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
292
 
293
 
294
        var today = (new Date()).dateFormat(this.format);
295
        this.todayBtn = new Ext.Button({
296
            renderTo: this.el.child("td.x-date-bottom", true),
297
            text: String.format(this.todayText, today),
298
            tooltip: String.format(this.todayTip, today),
299
            handler: this.selectToday,
300
            scope: this
301
        });
302
 
303
        if(Ext.isIE){
304
            this.el.repaint();
305
        }
306
        this.update(this.value);
307
    },
308
 
309
    createMonthPicker : function(){
310
        if(!this.monthPicker.dom.firstChild){
311
            var buf = ['<table border="0" cellspacing="0">'];
312
            for(var i = 0; i < 6; i++){
313
                buf.push(
314
                    '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
315
                    '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
316
                    i == 0 ?
317
                    '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
318
                    '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
319
                );
320
            }
321
            buf.push(
322
                '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
323
                    this.okText,
324
                    '</button><button type="button" class="x-date-mp-cancel">',
325
                    this.cancelText,
326
                    '</button></td></tr>',
327
                '</table>'
328
            );
329
            this.monthPicker.update(buf.join(''));
330
            this.monthPicker.on('click', this.onMonthClick, this);
331
            this.monthPicker.on('dblclick', this.onMonthDblClick, this);
332
 
333
            this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
334
            this.mpYears = this.monthPicker.select('td.x-date-mp-year');
335
 
336
            this.mpMonths.each(function(m, a, i){
337
                i += 1;
338
                if((i%2) == 0){
339
                    m.dom.xmonth = 5 + Math.round(i * .5);
340
                }else{
341
                    m.dom.xmonth = Math.round((i-1) * .5);
342
                }
343
            });
344
        }
345
    },
346
 
347
    showMonthPicker : function(){
348
        this.createMonthPicker();
349
        var size = this.el.getSize();
350
        this.monthPicker.setSize(size);
351
        this.monthPicker.child('table').setSize(size);
352
 
353
        this.mpSelMonth = (this.activeDate || this.value).getMonth();
354
        this.updateMPMonth(this.mpSelMonth);
355
        this.mpSelYear = (this.activeDate || this.value).getFullYear();
356
        this.updateMPYear(this.mpSelYear);
357
 
358
        this.monthPicker.slideIn('t', {duration:.2});
359
    },
360
 
361
    updateMPYear : function(y){
362
        this.mpyear = y;
363
        var ys = this.mpYears.elements;
364
        for(var i = 1; i <= 10; i++){
365
            var td = ys[i-1], y2;
366
            if((i%2) == 0){
367
                y2 = y + Math.round(i * .5);
368
                td.firstChild.innerHTML = y2;
369
                td.xyear = y2;
370
            }else{
371
                y2 = y - (5-Math.round(i * .5));
372
                td.firstChild.innerHTML = y2;
373
                td.xyear = y2;
374
            }
375
            this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
376
        }
377
    },
378
 
379
    updateMPMonth : function(sm){
380
        this.mpMonths.each(function(m, a, i){
381
            m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
382
        });
383
    },
384
 
385
    selectMPMonth: function(m){
386
 
387
    },
388
 
389
    onMonthClick : function(e, t){
390
        e.stopEvent();
391
        var el = new Ext.Element(t), pn;
392
        if(el.is('button.x-date-mp-cancel')){
393
            this.hideMonthPicker();
394
        }
395
        else if(el.is('button.x-date-mp-ok')){
396
            this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
397
            this.hideMonthPicker();
398
        }
399
        else if(pn = el.up('td.x-date-mp-month', 2)){
400
            this.mpMonths.removeClass('x-date-mp-sel');
401
            pn.addClass('x-date-mp-sel');
402
            this.mpSelMonth = pn.dom.xmonth;
403
        }
404
        else if(pn = el.up('td.x-date-mp-year', 2)){
405
            this.mpYears.removeClass('x-date-mp-sel');
406
            pn.addClass('x-date-mp-sel');
407
            this.mpSelYear = pn.dom.xyear;
408
        }
409
        else if(el.is('a.x-date-mp-prev')){
410
            this.updateMPYear(this.mpyear-10);
411
        }
412
        else if(el.is('a.x-date-mp-next')){
413
            this.updateMPYear(this.mpyear+10);
414
        }
415
    },
416
 
417
    onMonthDblClick : function(e, t){
418
        e.stopEvent();
419
        var el = new Ext.Element(t), pn;
420
        if(pn = el.up('td.x-date-mp-month', 2)){
421
            this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
422
            this.hideMonthPicker();
423
        }
424
        else if(pn = el.up('td.x-date-mp-year', 2)){
425
            this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
426
            this.hideMonthPicker();
427
        }
428
    },
429
 
430
    hideMonthPicker : function(disableAnim){
431
        if(this.monthPicker){
432
            if(disableAnim === true){
433
                this.monthPicker.hide();
434
            }else{
435
                this.monthPicker.slideOut('t', {duration:.2});
436
            }
437
        }
438
    },
439
 
440
    // private
441
    showPrevMonth : function(e){
442
        this.update(this.activeDate.add("mo", -1));
443
    },
444
 
445
    // private
446
    showNextMonth : function(e){
447
        this.update(this.activeDate.add("mo", 1));
448
    },
449
 
450
    // private
451
    showPrevYear : function(){
452
        this.update(this.activeDate.add("y", -1));
453
    },
454
 
455
    // private
456
    showNextYear : function(){
457
        this.update(this.activeDate.add("y", 1));
458
    },
459
 
460
    // private
461
    handleMouseWheel : function(e){
462
        var delta = e.getWheelDelta();
463
        if(delta > 0){
464
            this.showPrevMonth();
465
            e.stopEvent();
466
        } else if(delta < 0){
467
            this.showNextMonth();
468
            e.stopEvent();
469
        }
470
    },
471
 
472
    // private
473
    handleDateClick : function(e, t){
474
        e.stopEvent();
475
        if(t.dateValue && !Ext.fly(t.parentNode).hasClass("x-date-disabled")){
476
            this.setValue(new Date(t.dateValue));
477
            this.fireEvent("select", this, this.value);
478
        }
479
    },
480
 
481
    // private
482
    selectToday : function(){
483
        this.setValue(new Date().clearTime());
484
        this.fireEvent("select", this, this.value);
485
    },
486
 
487
    // private
488
    update : function(date){
489
        var vd = this.activeDate;
490
        this.activeDate = date;
491
        if(vd && this.el){
492
            var t = date.getTime();
493
            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
494
                this.cells.removeClass("x-date-selected");
495
                this.cells.each(function(c){
496
                   if(c.dom.firstChild.dateValue == t){
497
                       c.addClass("x-date-selected");
498
                       setTimeout(function(){
499
                            try{c.dom.firstChild.focus();}catch(e){}
500
                       }, 50);
501
                       return false;
502
                   }
503
                });
504
                return;
505
            }
506
        }
507
        var days = date.getDaysInMonth();
508
        var firstOfMonth = date.getFirstDateOfMonth();
509
        var startingPos = firstOfMonth.getDay()-this.startDay;
510
 
511
        if(startingPos <= this.startDay){
512
            startingPos += 7;
513
        }
514
 
515
        var pm = date.add("mo", -1);
516
        var prevStart = pm.getDaysInMonth()-startingPos;
517
 
518
        var cells = this.cells.elements;
519
        var textEls = this.textNodes;
520
        days += startingPos;
521
 
522
        // convert everything to numbers so it's fast
523
        var day = 86400000;
524
        var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
525
        var today = new Date().clearTime().getTime();
526
        var sel = date.clearTime().getTime();
527
        var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
528
        var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
529
        var ddMatch = this.disabledDatesRE;
530
        var ddText = this.disabledDatesText;
531
        var ddays = this.disabledDays ? this.disabledDays.join("") : false;
532
        var ddaysText = this.disabledDaysText;
533
        var format = this.format;
534
 
535
        var setCellClass = function(cal, cell){
536
            cell.title = "";
537
            var t = d.getTime();
538
            cell.firstChild.dateValue = t;
539
            if(t == today){
540
                cell.className += " x-date-today";
541
                cell.title = cal.todayText;
542
            }
543
            if(t == sel){
544
                cell.className += " x-date-selected";
545
                setTimeout(function(){
546
                    try{cell.firstChild.focus();}catch(e){}
547
                }, 50);
548
            }
549
            // disabling
550
            if(t < min) {
551
                cell.className = " x-date-disabled";
552
                cell.title = cal.minText;
553
                return;
554
            }
555
            if(t > max) {
556
                cell.className = " x-date-disabled";
557
                cell.title = cal.maxText;
558
                return;
559
            }
560
            if(ddays){
561
                if(ddays.indexOf(d.getDay()) != -1){
562
                    cell.title = ddaysText;
563
                    cell.className = " x-date-disabled";
564
                }
565
            }
566
            if(ddMatch && format){
567
                var fvalue = d.dateFormat(format);
568
                if(ddMatch.test(fvalue)){
569
                    cell.title = ddText.replace("%0", fvalue);
570
                    cell.className = " x-date-disabled";
571
                }
572
            }
573
        };
574
 
575
        var i = 0;
576
        for(; i < startingPos; i++) {
577
            textEls[i].innerHTML = (++prevStart);
578
            d.setDate(d.getDate()+1);
579
            cells[i].className = "x-date-prevday";
580
            setCellClass(this, cells[i]);
581
        }
582
        for(; i < days; i++){
583
            intDay = i - startingPos + 1;
584
            textEls[i].innerHTML = (intDay);
585
            d.setDate(d.getDate()+1);
586
            cells[i].className = "x-date-active";
587
            setCellClass(this, cells[i]);
588
        }
589
        var extraDays = 0;
590
        for(; i < 42; i++) {
591
             textEls[i].innerHTML = (++extraDays);
592
             d.setDate(d.getDate()+1);
593
             cells[i].className = "x-date-nextday";
594
             setCellClass(this, cells[i]);
595
        }
596
 
597
        this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
598
 
599
        if(!this.internalRender){
600
            var main = this.el.dom.firstChild;
601
            var w = main.offsetWidth;
602
            this.el.setWidth(w + this.el.getBorderWidth("lr"));
603
            Ext.fly(main).setWidth(w);
604
            this.internalRender = true;
605
            // opera does not respect the auto grow header center column
606
            // then, after it gets a width opera refuses to recalculate
607
            // without a second pass
608
            if(Ext.isOpera && !this.secondPass){
609
                main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
610
                this.secondPass = true;
611
                this.update.defer(10, this, [date]);
612
            }
613
        }
614
    },
615
 
616
    // private
617
    beforeDestroy : function() {
618
        this.mbtn.destroy();
619
        this.todayBtn.destroy();
620
    }
621
 
622
    /**
623
     * @cfg {String} autoEl @hide
624
     */
625
});
626
Ext.reg('datepicker', Ext.DatePicker);