root/trunk/common/Calendar.js

Revision 259, 14.2 kB (checked in by ddavis, 22 months ago)

fix the calendar. If setting the month fails, its due to an invalid month and day, like Feb 31. I check if the month setting fails and use the last day of the month before re-setting it. BugzID: 67764

  • Property svn:keywords set to Id
Line 
1/*
2Calendar Utility
3$Id$
4
5Copyright (c) 2006, Six Apart, Ltd.
6All rights reserved.
7
8Redistribution and use in source and binary forms, with or without
9modification, are permitted provided that the following conditions are
10met:
11
12    * Redistributions of source code must retain the above copyright
13notice, this list of conditions and the following disclaimer.
14
15    * Redistributions in binary form must reproduce the above
16copyright notice, this list of conditions and the following disclaimer
17in the documentation and/or other materials provided with the
18distribution.
19
20    * Neither the name of "Six Apart" nor the names of its
21contributors may be used to endorse or promote products derived from
22this software without specific prior written permission.
23
24THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35*/
36
37
38Calendar = new Class( Transient, {
39
40    msInDay: ( 1000 * 60 * 60 * 24 ),
41
42
43    initObject: function( element, templateName ) {
44        arguments.callee.applySuper( this, arguments );
45        this.templateName = templateName;
46       
47        this.contentElement = DOM.getElement( this.element.id + '-content' );
48        if( !this.contentElement )
49            this.contentElement = this.element;
50
51        this.timeElement = DOM.getElement( this.element.id + '-time-input' );
52        if( !this.timeElement )
53            throw "Cannot find time element:" + this.element.id + "-time-input";
54
55        this.dateObject = new Date();
56    },
57   
58
59    destroyObject: function() {
60        this.element = null;
61        this.contentElement = null;
62        this.timeElement = null;
63        this.dateObject = null;
64        this.currentDateObject = null;
65        arguments.callee.applySuper( this, arguments );
66    },
67
68   
69    open: function() {
70        arguments.callee.applySuper( this, arguments );
71
72        this.dateObject = ( !this.data.date ) ? ( new Date() ) : Date.fromISOString( this.data.date );
73
74        /* we need a current date object so we can do comparisons later for disallowing future dates */
75        this.currentDateObject = ( !this.data.currentDate ) ? ( new Date() ) : Date.fromISOString( this.data.currentDate );
76
77        this.disallowFuture = this.data.disallowFuture;
78
79        this.timeElement.value = this.getISOTimeShortString();
80       
81        this.render();
82        this.reflow();
83    },
84   
85
86    close: function( ok ) {
87        var r = !ok ? ok : this.dateObject;
88        arguments.callee.callSuper( this, r );
89    },
90
91
92    render: function() {
93       this.contentElement.innerHTML = Template.process( this.templateName, { cal: this } ); 
94    },
95   
96
97    eventClick: function( event ) {
98        if( !this.isOpen ) // This keeps unopened calendars from hearing the event and breaking (Case 33303).
99            return; 
100        var command = this.getMouseEventCommand( event );
101        if ( !command )
102            return;
103
104        event.stop();
105               
106        var m;
107        var day;
108        if ( m = command.match( /setDay-(\d+)/ ) ) {
109            day = m[ 1 ];
110            command = 'setDay';
111        }
112       
113        switch( command ) {
114           
115            case "nextMonth":
116                this.nextMonth();
117                this.render();
118                break;
119           
120            case "prevMonth":
121                this.prevMonth();
122                this.render();
123                break;
124           
125            case "cancel":
126                this.close();
127                break;
128
129            case "save":
130                this.checkTimeInput();
131                this.close( true );
132                break;
133
134            case "setDay":
135                var resetTime = false;
136                if ( this.disallowFuture ) {
137                    var dt = this.dateObject.clone();
138                    dt.setDate( day );
139                    /* detect if they are selecting a future date,
140                     * and reset the time
141                     */
142                    if ( dt > this.currentDateObject ) {
143                        dt.setSeconds( 1 );
144                        dt.setHours( 0 );
145                        dt.setMinutes( 0 );
146                        if ( dt > this.currentDateObject )
147                            break;
148                        else
149                            resetTime = true;
150                    }
151                }
152                var d = this.date();
153               
154                /* no nothing when clicking the same day */
155                if ( d == day )
156                    break;
157                this.date( day );
158               
159                var es = DOM.getElementsByClassName( this.contentElement, "day-" + d );
160                if ( es && es[ 0 ] )
161                    DOM.removeClassName( es[ 0 ], "selected" );
162               
163                es = DOM.getElementsByClassName( this.contentElement, "day-" + day );
164                if ( es && es[ 0 ] )
165                    DOM.addClassName( es[ 0 ], "selected" );
166
167                if ( resetTime ) {
168                    var d = new Date();
169                    var hours = d.getHours().toString().pad( 2, "0" );
170                    var minutes = d.getMinutes().toString().pad( 2, "0" );
171                    var seconds = d.getSeconds().toString().pad( 2, "0" );
172                    this.timeElement.value = hours + ":" + minutes + ":" + seconds;
173                    this.time( this.timeElement.value );
174                }
175                   
176                break;
177        }
178    },
179
180
181    eventBlur: function( event ) {
182        if ( event.target !== this.timeElement )
183            return;
184       
185        this.checkTimeInput();
186       
187        /* time adjustment can change the date, so re-render */
188        try { this.render(); } catch ( e ) { };
189    },
190
191
192    checkTimeInput: function() {
193        var time = this.timeElement.value;
194       
195        var m = time.match( /^\d{2}:\d{2}:\d{2}/ );
196        if ( !m ) {
197            this.timeElement.value = this.time();
198            return;
199        }
200       
201        if ( this.disallowFuture ) {
202            var dt = this.dateObject.clone();
203            this.time( time, dt );
204            if ( dt > this.currentDateObject ) {
205                this.timeElement.value = this.time();
206                return;
207            }
208        }
209
210        this.timeElement.value = this.time( time );
211       
212    },
213   
214   
215    eventMouseOver: function( event ) {
216        DOM.addClassName( this.element, "hover-over" );
217    },
218
219
220    eventMouseOut: function( event ) {
221        DOM.removeClassName( this.element, "hover-over" );
222    },
223
224
225    nextMonthAllowed: function() {
226        if ( this.disallowFuture ) {
227            var dt = this.dateObject.clone();
228            var month = this.month();
229            var year = this.year();
230           
231            if ( month == 11 ) {
232                month = 1;
233                year++;
234            } else
235                month++;
236           
237            dt.setMonth( month );
238            dt.setYear( year );
239            dt.setDate( 1 );
240           
241            if ( dt > this.currentDateObject )
242                return false;
243        }
244       
245        return true;
246    },
247
248   
249    nextMonth: function() {
250        var m = this.month();
251        var y = this.year();
252       
253        if ( m == 11 ) {
254            m = 0;
255            y++;
256        } else
257            m++;
258       
259        if ( this.disallowFuture ) {
260            var dt = this.dateObject.clone();
261            dt.setMonth( m );
262            dt.setYear( y );
263            if ( dt > this.currentDateObject ) {
264                dt.setDate( 1 );
265                if ( dt > this.currentDateObject ) {
266                    return false;
267                } else {
268                    this.date( 1 );
269                }
270            }
271        }
272       
273        this.year( y );
274
275        /* if setting the month fails, reset the date to the last day of the month first */
276        if ( this.month( m ) != m ) {
277            this.date( this.getDaysInMonth( m ) );
278            this.month( m );
279        }
280
281        return true;
282    },
283
284
285    prevMonth: function() {
286        var m = this.month();
287        var y = this.year();
288       
289        if ( m == 0 ) {
290            m = 11;
291            y--;
292        } else
293            m--;
294       
295        this.year( y );
296       
297        /* if setting the month fails, reset the date to the last day of the month first */
298        if ( this.month( m ) != m ) {
299            this.date( this.getDaysInMonth( m ) );
300            this.month( m );
301        }
302
303        return true;
304    },
305
306
307    isToday: function( day ) {
308        var dt = this.dateObject.clone();
309        dt.setDate( day );
310        return ( this.getCurrentISODate() == dt.toISODateString() );
311    },
312   
313   
314    isFuture: function( day ) {
315        var dt = this.dateObject.clone();
316        dt.setDate( day );
317        dt.setSeconds( 1 );
318        dt.setHours( 0 );
319        dt.setMinutes( 0 );
320        return ( dt > this.currentDateObject );
321    },
322
323   
324    getCurrentISODate: function() {
325        return this.currentDateObject.toISODateString();
326    },
327
328
329    /* get the calendar date in ISO format */
330    getISODate: function() {
331        return this.dateObject.toISODateString();
332    },
333   
334 
335    /* get the calendar time in ISO format */
336    getISOTime: function() {
337        return this.dateObject.toISOTimeString();
338    },
339   
340   
341    /* used to get a short version for editing */
342    getISOTimeShortString: function() {
343        var hours = this.dateObject.getHours().toString().pad( 2, "0" );
344        var minutes = this.dateObject.getMinutes().toString().pad( 2, "0" );
345        var seconds = this.dateObject.getSeconds().toString().pad( 2, "0" );
346        return hours + ":" + minutes + ":" + seconds;
347    },
348
349
350    /* get the calendar time and date in ISO format */
351    getISOString: function() {
352        return this.dateObject.toISOString();
353    },
354
355
356    /* getters and setters */
357
358    /* get/set the month, 0 - 11 */
359    month: function( m ) {
360        if ( defined( m ) )
361            this.dateObject.setMonth( m );
362
363        return this.dateObject.getMonth();
364    },
365
366
367    /* get/set the year, 2006 */
368    year: function( year ) {
369        if ( year )
370            this.dateObject.setFullYear( year );
371
372        return this.dateObject.getFullYear();
373    },
374
375
376    /* get/set the day of month, 1 - 31 */
377    date: function( day ) {
378        if ( day )
379            this.dateObject.setDate( day );
380
381        return this.dateObject.getDate();
382    },
383
384
385    time: function( time, dateobj ) {
386        if ( !dateobj )
387            dateobj = this.dateObject;
388        if ( time ) {
389            var m = time.match( /^(\d{2}):(\d{2}):(\d{2})/ );
390            if ( !m && m.length > 2 )
391                return dateobj.toISOTimeString();
392
393            dateobj.setHours( finiteInt( m[ 1 ], 10 ) );
394            dateobj.setMinutes( finiteInt( m[ 2 ], 10 ) );
395            dateobj.setSeconds( finiteInt( m[ 3 ], 10 ) );
396        }
397
398        return dateobj.toISOTimeString();
399    },
400
401
402    weekStart: function() {
403        return Date.strings.localeWeekStart;
404    },
405
406   
407    /* get the day of the week, 0 sunday - 6 saturday */
408    dow: function( opt ) {
409        if ( opt )
410            log.error( 'Calendar.dow() is only a getter' );
411
412        return this.dateObject.getDay();
413    },
414   
415   
416    getDOWFromDay: function( day ) {
417        var dt = this.dateObject.clone();
418        dt.setDate( day );
419        return dt.getDay();
420    },
421
422   
423    /* get the number of days in the month, year is optional */ 
424    getDaysInMonth: function( month, year ) {
425        if ( !month )
426            month = this.month();
427
428        if ( !year )
429            year = this.year();
430       
431        return 32 - ( new Date( year, month, 32 ) ).getDate();
432    },
433
434
435    getLocaleDayString: function( dow ) {
436        return this.dateObject.getLocaleDayString( dow );
437    },
438
439
440    getLocaleDayShortString: function( dow ) {
441        return this.dateObject.getLocaleDayShortString( dow );
442    },
443
444
445    getLocaleMonthString: function( month ) {
446        return this.dateObject.getLocaleMonthString( month );
447    },
448
449   
450    getLocaleMonthShortString: function( month ) {
451        return this.dateObject.getLocaleMonthShortString( month );
452    }
453});
454
455/*
456 * Calendar JavaScript Template
457    <div class="calendar-month">
458        <a href="javascript: void 0;" class="left command-prev-month">&laquo;</a>
459        [# if ( cal.nextMonthAllowed() ) { #]
460        <a href="javascript: void 0;" class="right command-next-month">&raquo;</a>
461        [# } #]
462        <h1>[#|h cal.getLocaleMonthShortString() #] [#= cal.year() #]</h1>
463    </div>
464    <ul class="calendar-week">
465        [# for ( var i = cal.weekStart(); i <= ( 6 + cal.weekStart() ); i++ ) { #]
466            <li>[#|h cal.getLocaleDayShortString( ( cal.weekStart() && i == ( 6 + cal.weekStart() ) ? 0 : i ) ) #]</li>
467        [# } #]
468    </ul>
469    <ul class="calendar-days">
470        [#
471            var daysInMonth = cal.getDaysInMonth();
472            var monthStart = ( cal.getDOWFromDay( 1 ) - cal.weekStart() );
473            if ( monthStart == -1 )
474                monthStart = 6;
475        #]
476        [# for ( var i = 0; i < monthStart; i++ ) { #]
477            <li>&nbsp;</li>
478        [# } #]
479        [# for ( var day = 1; day <= daysInMonth; day++ ) { #]
480            [# var isFuture = cal.isFuture( day ) ? true : false; #]
481            <li class="day-[#= day #][# if ( cal.date() == day ) { #] selected[# } #][# if ( cal.isToday( day ) ) { #] today[# } #][#
482                if ( isFuture ) { #] future[# } #]"
483             >[# if ( !( isFuture && cal.disallowFuture ) ) { #]<a href="javascript: void 0;" class="command-set-day-[#= day #]">[#= day #]</a>[# } else { #][#= day #][# } #]</li>
484        [# } #]
485        [# for ( var i = ( cal.getDOWFromDay( daysInMonth ) - cal.weekStart() ); i < 6; i++ ) {
486            if ( i == -i )
487                break; #]
488            <li>&nbsp;</li>
489        [# } #]
490    </ul>
491*/
Note: See TracBrowser for help on using the browser.