source: trunk/spip/esqueleto-redcta/plugins/widget_calendar/img_pack/calendar.js @ 30

Last change on this file since 30 was 30, checked in by sebas, 17 years ago

nueva importacion del codigo del esqueleto de redcta con los plugins

File size: 91.0 KB
Line 
1/*
2Copyright (c) 2006 Spip! Inc. All rights reserved.
3version 0.9.0
4*/
5
6SPIP.namespace("SPIP.widget");
7
8/**
9* @class
10* <p>SPIP.widget.DateMath is used for simple date manipulation. The class is a static utility
11* used for adding, subtracting, and comparing dates.</p>
12*/
13SPIP.widget.DateMath = new function() {
14
15        /**
16        * Constant field representing Day
17        * @type String
18        */
19        this.DAY = "D";
20
21        /**
22        * Constant field representing Week
23        * @type String
24        */
25        this.WEEK = "W";
26
27        /**
28        * Constant field representing Year
29        * @type String
30        */
31        this.YEAR = "Y";
32
33        /**
34        * Constant field representing Month
35        * @type String
36        */
37        this.MONTH = "M";
38
39        /**
40        * Constant field representing one day, in milliseconds
41        * @type Integer
42        */
43        this.ONE_DAY_MS = 1000*60*60*24;
44
45        /**
46        * Adds the specified amount of time to the this instance.
47        * @param {Date} date    The JavaScript Date object to perform addition on
48        * @param {string} field The this field constant to be used for performing addition.
49        * @param {Integer} amount       The number of units (measured in the field constant) to add to the date.
50        */
51        this.add = function(date, field, amount) {
52                var d = new Date(date.getTime());
53                switch (field)
54                {
55                        case this.MONTH:
56                                var newMonth = date.getMonth() + amount;
57                                var years = 0;
58
59
60                                if (newMonth < 0) {
61                                        while (newMonth < 0)
62                                        {
63                                                newMonth += 12;
64                                                years -= 1;
65                                        }
66                                } else if (newMonth > 11) {
67                                        while (newMonth > 11)
68                                        {
69                                                newMonth -= 12;
70                                                years += 1;
71                                        }
72                                }
73
74                                d.setMonth(newMonth);
75                                d.setFullYear(date.getFullYear() + years);
76                                break;
77                        case this.DAY:
78                                d.setDate(date.getDate() + amount);
79                                break;
80                        case this.YEAR:
81                                d.setFullYear(date.getFullYear() + amount);
82                                break;
83                        case this.WEEK:
84                                d.setDate(date.getDate() + 7);
85                                break;
86                }
87                return d;
88        };
89
90        /**
91        * Subtracts the specified amount of time from the this instance.
92        * @param {Date} date    The JavaScript Date object to perform subtraction on
93        * @param {Integer} field        The this field constant to be used for performing subtraction.
94        * @param {Integer} amount       The number of units (measured in the field constant) to subtract from the date.
95        */
96        this.subtract = function(date, field, amount) {
97                return this.add(date, field, (amount*-1));
98        };
99
100        /**
101        * Determines whether a given date is before another date on the calendar.
102        * @param {Date} date            The Date object to compare with the compare argument
103        * @param {Date} compareTo       The Date object to use for the comparison
104        * @return {Boolean} true if the date occurs before the compared date; false if not.
105        */
106        this.before = function(date, compareTo) {
107                var ms = compareTo.getTime();
108                if (date.getTime() < ms) {
109                        return true;
110                } else {
111                        return false;
112                }
113        };
114
115        /**
116        * Determines whether a given date is after another date on the calendar.
117        * @param {Date} date            The Date object to compare with the compare argument
118        * @param {Date} compareTo       The Date object to use for the comparison
119        * @return {Boolean} true if the date occurs after the compared date; false if not.
120        */
121        this.after = function(date, compareTo) {
122                var ms = compareTo.getTime();
123                if (date.getTime() > ms) {
124                        return true;
125                } else {
126                        return false;
127                }
128        };
129
130        /**
131        * Retrieves a JavaScript Date object representing January 1 of any given year.
132        * @param {Integer} calendarYear         The calendar year for which to retrieve January 1
133        * @return {Date}        January 1 of the calendar year specified.
134        */
135        this.getJan1 = function(calendarYear) {
136                return new Date(calendarYear,0,1);
137        };
138
139        /**
140        * Calculates the number of days the specified date is from January 1 of the specified calendar year.
141        * Passing January 1 to this function would return an offset value of zero.
142        * @param {Date} date    The JavaScript date for which to find the offset
143        * @param {Integer} calendarYear The calendar year to use for determining the offset
144        * @return {Integer}     The number of days since January 1 of the given year
145        */
146        this.getDayOffset = function(date, calendarYear) {
147                var beginYear = this.getJan1(calendarYear); // Find the start of the year. This will be in week 1.
148
149                // Find the number of days the passed in date is away from the calendar year start
150                var dayOffset = Math.ceil((date.getTime()-beginYear.getTime()) / this.ONE_DAY_MS);
151                return dayOffset;
152        };
153
154        /**
155        * Calculates the week number for the given date. This function assumes that week 1 is the
156        * week in which January 1 appears, regardless of whether the week consists of a full 7 days.
157        * The calendar year can be specified to help find what a the week number would be for a given
158        * date if the date overlaps years. For instance, a week may be considered week 1 of 2005, or
159        * week 53 of 2004. Specifying the optional calendarYear allows one to make this distinction
160        * easily.
161        * @param {Date} date    The JavaScript date for which to find the week number
162        * @param {Integer} calendarYear OPTIONAL - The calendar year to use for determining the week number. Default is
163        *                                                                                       the calendar year of parameter "date".
164        * @param {Integer} weekStartsOn OPTIONAL - The integer (0-6) representing which day a week begins on. Default is 0 (for Sunday).
165        * @return {Integer}     The week number of the given date.
166        */
167        this.getWeekNumber = function(date, calendarYear, weekStartsOn) {
168                if (! weekStartsOn) {
169                        weekStartsOn = 0;
170                }
171                if (! calendarYear) {
172                        calendarYear = date.getFullYear();
173                }
174                var weekNum = -1;
175
176                var jan1 = this.getJan1(calendarYear);
177                var jan1DayOfWeek = jan1.getDay();
178
179                var month = date.getMonth();
180                var day = date.getDate();
181                var year = date.getFullYear();
182
183                var dayOffset = this.getDayOffset(date, calendarYear); // Days since Jan 1, Calendar Year
184
185                if (dayOffset < 0 && dayOffset >= (-1 * jan1DayOfWeek)) {
186                        weekNum = 1;
187                } else {
188                        weekNum = 1;
189                        var testDate = this.getJan1(calendarYear);
190
191                        while (testDate.getTime() < date.getTime() && testDate.getFullYear() == calendarYear) {
192                                weekNum += 1;
193                                testDate = this.add(testDate, this.WEEK, 1);
194                        }
195                }
196
197                return weekNum;
198        };
199
200        /**
201        * Determines if a given week overlaps two different years.
202        * @param {Date} weekBeginDate   The JavaScript Date representing the first day of the week.
203        * @return {Boolean}     true if the date overlaps two different years.
204        */
205        this.isYearOverlapWeek = function(weekBeginDate) {
206                var overlaps = false;
207                var nextWeek = this.add(weekBeginDate, this.DAY, 6);
208                if (nextWeek.getFullYear() != weekBeginDate.getFullYear()) {
209                        overlaps = true;
210                }
211                return overlaps;
212        };
213
214        /**
215        * Determines if a given week overlaps two different months.
216        * @param {Date} weekBeginDate   The JavaScript Date representing the first day of the week.
217        * @return {Boolean}     true if the date overlaps two different months.
218        */
219        this.isMonthOverlapWeek = function(weekBeginDate) {
220                var overlaps = false;
221                var nextWeek = this.add(weekBeginDate, this.DAY, 6);
222                if (nextWeek.getMonth() != weekBeginDate.getMonth()) {
223                        overlaps = true;
224                }
225                return overlaps;
226        };
227
228        /**
229        * Gets the first day of a month containing a given date.
230        * @param {Date} date    The JavaScript Date used to calculate the month start
231        * @return {Date}                The JavaScript Date representing the first day of the month
232        */
233        this.findMonthStart = function(date) {
234                var start = new Date(date.getFullYear(), date.getMonth(), 1);
235                return start;
236        };
237
238        /**
239        * Gets the last day of a month containing a given date.
240        * @param {Date} date    The JavaScript Date used to calculate the month end
241        * @return {Date}                The JavaScript Date representing the last day of the month
242        */
243        this.findMonthEnd = function(date) {
244                var start = this.findMonthStart(date);
245                var nextMonth = this.add(start, this.MONTH, 1);
246                var end = this.subtract(nextMonth, this.DAY, 1);
247                return end;
248        };
249
250        /**
251        * Clears the time fields from a given date, effectively setting the time to midnight.
252        * @param {Date} date    The JavaScript Date for which the time fields will be cleared
253        * @return {Date}                The JavaScript Date cleared of all time fields
254        */
255        this.clearTime = function(date) {
256                date.setHours(0,0,0,0);
257                return date;
258        };
259}
260
261SPIP.namespace("SPIP.widget");
262
263/**
264* @class
265* <p>Calendar_Core is the base class for the Calendar widget. In its most basic
266* implementation, it has the ability to render a calendar widget on the page
267* that can be manipulated to select a single date, move back and forth between
268* months and years.</p>
269* <p>To construct the placeholder for the calendar widget, the code is as
270* follows:
271*       <xmp>
272*               <div id="cal1Container"></div>
273*       </xmp>
274* Note that the table can be replaced with any kind of element.
275* </p>
276* @constructor
277* @param {String}       id                      The id of the table element that will represent the calendar widget
278* @param {String}       containerId     The id of the container element that will contain the calendar table
279* @param {String}       monthyear       The month/year string used to set the current calendar page
280* @param {String}       selected        A string of date values formatted using the date parser. The built-in
281                                                                default date format is MM/DD/YYYY. Ranges are defined using
282                                                                MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
283                                                                Any combination of these can be combined by delimiting the string with
284                                                                commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
285*/
286SPIP.widget.Calendar_Core = function(id, containerId, monthyear, selected) {
287        if (arguments.length > 0)
288        {
289                this.init(id, containerId, monthyear, selected);
290        }
291}
292
293/**
294* Type constant used for renderers to represent an individual date (M/D/Y)
295* @final
296* @type String
297*/
298SPIP.widget.Calendar_Core.DATE = "D";
299
300/**
301* Type constant used for renderers to represent an individual date across any year (M/D)
302* @final
303* @type String
304*/
305SPIP.widget.Calendar_Core.MONTH_DAY = "MD";
306
307/**
308* Type constant used for renderers to represent a weekday
309* @final
310* @type String
311*/
312SPIP.widget.Calendar_Core.WEEKDAY = "WD";
313
314/**
315* Type constant used for renderers to represent a range of individual dates (M/D/Y-M/D/Y)
316* @final
317* @type String
318*/
319SPIP.widget.Calendar_Core.RANGE = "R";
320
321/**
322* Type constant used for renderers to represent a month across any year
323* @final
324* @type String
325*/
326SPIP.widget.Calendar_Core.MONTH = "M";
327
328/**
329* Constant that represents the total number of date cells that are displayed in a given month
330* including
331* @final
332* @type Integer
333*/
334SPIP.widget.Calendar_Core.DISPLAY_DAYS = 42;
335
336/**
337* Constant used for halting the execution of the remainder of the render stack
338* @final
339* @type String
340*/
341SPIP.widget.Calendar_Core.STOP_RENDER = "S";
342
343SPIP.widget.Calendar_Core.prototype = {
344
345        /**
346        * The configuration object used to set up the calendars various locale and style options.
347        * @type Object
348        */
349        Config : null,
350
351        /**
352        * The parent CalendarGroup, only to be set explicitly by the parent group
353        * @type CalendarGroup
354        */
355        parent : null,
356
357        /**
358        * The index of this item in the parent group
359        * @type Integer
360        */
361        index : -1,
362
363        /**
364        * The collection of calendar table cells
365        * @type HTMLTableCellElement[]
366        */
367        cells : null,
368
369        /**
370        * The collection of calendar week header cells
371        * @type HTMLTableCellElement[]
372        */
373        weekHeaderCells : null,
374
375        /**
376        * The collection of calendar week footer cells
377        * @type HTMLTableCellElement[]
378        */
379        weekFooterCells : null,
380
381        /**
382        * The collection of calendar cell dates that is parallel to the cells collection. The array contains dates field arrays in the format of [YYYY, M, D].
383        * @type Array[](Integer[])
384        */
385        cellDates : null,
386
387        /**
388        * The id that uniquely identifies this calendar. This id should match the id of the placeholder element on the page.
389        * @type String
390        */
391        id : null,
392
393        /**
394        * The DOM element reference that points to this calendar's container element. The calendar will be inserted into this element when the shell is rendered.
395        * @type HTMLElement
396        */
397        oDomContainer : null,
398
399        /**
400        * A Date object representing today's date.
401        * @type Date
402        */
403        today : null,
404
405        /**
406        * The list of render functions, along with required parameters, used to render cells.
407        * @type Array[]
408        */
409        renderStack : null,
410
411        /**
412        * A copy of the initial render functions created before rendering.
413        * @type Array
414        * @private
415        */
416        _renderStack : null,
417
418        /**
419        * A Date object representing the month/year that the calendar is currently set to
420        * @type Date
421        */
422        pageDate : null,
423
424        /**
425        * A Date object representing the month/year that the calendar is initially set to
426        * @type Date
427        * @private
428        */
429        _pageDate : null,
430
431        /**
432        * A Date object representing the minimum selectable date
433        * @type Date
434        */
435        minDate : null,
436
437        /**
438        * A Date object representing the maximum selectable date
439        * @type Date
440        */
441        maxDate : null,
442
443        /**
444        * The list of currently selected dates. The data format for this local collection is
445        * an array of date field arrays, e.g:
446        * [
447        *       [2004,5,25],
448        *       [2004,5,26]
449        * ]
450        * @type Array[](Integer[])
451        */
452        selectedDates : null,
453
454        /**
455        * The private list of initially selected dates.
456        * @type Array
457        * @private
458        */
459        _selectedDates : null,
460
461        /**
462        * A boolean indicating whether the shell of the calendar has already been rendered to the page
463        * @type Boolean
464        */
465        shellRendered : false,
466
467        /**
468        * The HTML table element that represents this calendar
469        * @type HTMLTableElement
470        */
471        table : null,
472
473        /**
474        * The HTML cell element that represents the main header cell TH used in the calendar table
475        * @type HTMLTableCellElement
476        */
477        headerCell : null
478
479
480};
481
482
483
484/**
485* Initializes the calendar widget. This method must be called by all subclass constructors.
486* @param {String}       id                      The id of the table element that will represent the calendar widget
487* @param {String}       containerId     The id of the container element that will contain the calendar table
488* @param {String}       monthyear       The month/year string used to set the current calendar page
489* @param {String}       selected        A string of date values formatted using the date parser. The built-in
490                                                                default date format is MM/DD/YYYY. Ranges are defined using
491                                                                MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
492                                                                Any combination of these can be combined by delimiting the string with
493                                                                commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
494*/
495SPIP.widget.Calendar_Core.prototype.init = function(id, containerId, monthyear, selected) {
496        this.setupConfig();
497
498        this.id = id;
499
500        this.cellDates = new Array();
501
502        this.cells = new Array();
503
504        this.renderStack = new Array();
505        this._renderStack = new Array();
506
507        this.oDomContainer = document.getElementById(containerId);
508
509        this.today = new Date();
510        SPIP.widget.DateMath.clearTime(this.today);
511
512        var month;
513        var year;
514
515        if (monthyear)
516        {
517                var aMonthYear = monthyear.split(this.Locale.DATE_FIELD_DELIMITER);
518                month = parseInt(aMonthYear[this.Locale.MY_MONTH_POSITION-1]);
519                year = parseInt(aMonthYear[this.Locale.MY_YEAR_POSITION-1]);
520        } else {
521                month = this.today.getMonth()+1;
522                year = this.today.getFullYear();
523        }
524
525        this.pageDate = new Date(year, month-1, 1);
526        this._pageDate = new Date(this.pageDate.getTime());
527
528        if (selected)
529        {
530                this.selectedDates = this._parseDates(selected);
531                this._selectedDates = this.selectedDates.concat();
532        } else {
533                this.selectedDates = new Array();
534                this._selectedDates = new Array();
535        }
536
537        this.wireDefaultEvents();
538        this.wireCustomEvents();
539};
540
541
542/**
543* Wires the local DOM events for the Calendar, including cell selection, hover, and
544* default navigation that is used for moving back and forth between calendar pages.
545*/
546SPIP.widget.Calendar_Core.prototype.wireDefaultEvents = function() {
547
548        /**
549        * The default event function that is attached to a date link within a calendar cell
550        * when the calendar is rendered.
551        * @param        e               The event
552        * @param        cal             A reference to the calendar passed by the Event utility
553        */
554        this.doSelectCell = function(e, cal) {
555                var cell = this;
556                var index = cell.index;
557
558                if (cal.Options.MULTI_SELECT) {
559                        var link = cell.getElementsByTagName("A")[0];
560                        link.blur();
561
562                        var cellDate = cal.cellDates[index];
563                        var cellDateIndex = cal._indexOfSelectedFieldArray(cellDate);
564
565                        if (cellDateIndex > -1)
566                        {
567                                cal.deselectCell(index);
568                        } else {
569                                cal.selectCell(index);
570                        }
571
572                } else {
573                        var link = cell.getElementsByTagName("A")[0];
574                        link.blur()
575
576                        cal.selectCell(index);
577                }
578        }
579
580        /**
581        * The event that is executed when the user hovers over a cell
582        * @param        e               The event
583        * @param        cal             A reference to the calendar passed by the Event utility
584        * @private
585        */
586        this.doCellMouseOver = function(e, cal) {
587                SPIP.widget.Calendar_Core.prependCssClass(this, cal.Style.CSS_CELL_HOVER);
588        }
589
590        /**
591        * The event that is executed when the user moves the mouse out of a cell
592        * @param        e               The event
593        * @param        cal             A reference to the calendar passed by the Event utility
594        * @private
595        */
596        this.doCellMouseOut = function(e, cal) {
597                SPIP.widget.Calendar_Core.removeCssClass(this, cal.Style.CSS_CELL_HOVER);
598        }
599
600        /**
601        * A wrapper event that executes the nextMonth method through a DOM event
602        * @param        e               The event
603        * @param        cal             A reference to the calendar passed by the Event utility
604        * @private
605        */
606        this.doNextMonth = function(e, cal) {
607                cal.nextMonth();
608        }
609
610        /**
611        * A wrapper event that executes the previousMonth method through a DOM event
612        * @param        e               The event
613        * @param        cal             A reference to the calendar passed by the Event utility
614        * @private
615        */
616        this.doPreviousMonth = function(e, cal) {
617                cal.previousMonth();
618        }
619}
620
621/**
622* This function can be extended by subclasses to attach additional DOM events to
623* the calendar. By default, this method is unimplemented.
624*/
625SPIP.widget.Calendar_Core.prototype.wireCustomEvents = function() { }
626
627/**
628This method is called to initialize the widget configuration variables, including
629style, localization, and other display and behavioral options.
630<p>Config: Container for the CSS style configuration variables.</p>
631<p><strong>Config.Style</strong> - Defines the CSS classes used for different calendar elements</p>
632<blockquote>
633        <div><em>CSS_CALENDAR</em> : Container table</div>
634        <div><em>CSS_HEADER</em> : </div>
635        <div><em>CSS_HEADER_TEXT</em> : Calendar header</div>
636        <div><em>CSS_FOOTER</em> : Calendar footer</div>
637        <div><em>CSS_CELL</em> : Calendar day cell</div>
638        <div><em>CSS_CELL_OOM</em> : Calendar OOM (out of month) cell</div>
639        <div><em>CSS_CELL_SELECTED</em> : Calendar selected cell</div>
640        <div><em>CSS_CELL_RESTRICTED</em> : Calendar restricted cell</div>
641        <div><em>CSS_CELL_TODAY</em> : Calendar cell for today's date</div>
642        <div><em>CSS_ROW_HEADER</em> : The cell preceding a row (used for week number by default)</div>
643        <div><em>CSS_ROW_FOOTER</em> : The cell following a row (not implemented by default)</div>
644        <div><em>CSS_WEEKDAY_CELL</em> : The cells used for labeling weekdays</div>
645        <div><em>CSS_WEEKDAY_ROW</em> : The row containing the weekday label cells</div>
646        <div><em>CSS_BORDER</em> : The border style used for the default UED rendering</div>
647        <div><em>CSS_CONTAINER</em> : Special container class used to properly adjust the sizing and float</div>
648        <div><em>CSS_NAV_LEFT</em> : Left navigation arrow</div>
649        <div><em>CSS_NAV_RIGHT</em> : Right navigation arrow</div>
650        <div><em>CSS_CELL_TOP</em> : Outlying cell along the top row</div>
651        <div><em>CSS_CELL_LEFT</em> : Outlying cell along the left row</div>
652        <div><em>CSS_CELL_RIGHT</em> : Outlying cell along the right row</div>
653        <div><em>CSS_CELL_BOTTOM</em> : Outlying cell along the bottom row</div>
654        <div><em>CSS_CELL_HOVER</em> : Cell hover style</div>
655        <div><em>CSS_CELL_HIGHLIGHT1</em> : Highlight color 1 for styling cells</div>
656        <div><em>CSS_CELL_HIGHLIGHT2</em> : Highlight color 2 for styling cells</div>
657        <div><em>CSS_CELL_HIGHLIGHT3</em> : Highlight color 3 for styling cells</div>
658        <div><em>CSS_CELL_HIGHLIGHT4</em> : Highlight color 4 for styling cells</div>
659
660</blockquote>
661<p><strong>Config.Locale</strong> - Defines the locale string arrays used for localization</p>
662<blockquote>
663        <div><em>MONTHS_SHORT</em> : Array of 12 months in short format ("Jan", "Feb", etc.)</div>
664        <div><em>MONTHS_LONG</em> : Array of 12 months in short format ("Jan", "Feb", etc.)</div>
665        <div><em>WEEKDAYS_1CHAR</em> : Array of 7 days in 1-character format ("S", "M", etc.)</div>
666        <div><em>WEEKDAYS_SHORT</em> : Array of 7 days in short format ("Su", "Mo", etc.)</div>
667        <div><em>WEEKDAYS_MEDIUM</em> : Array of 7 days in medium format ("Sun", "Mon", etc.)</div>
668        <div><em>WEEKDAYS_LONG</em> : Array of 7 days in long format ("Sunday", "Monday", etc.)</div>
669        <div><em>DATE_DELIMITER</em> : The value used to delimit series of multiple dates (Default: ",")</div>
670        <div><em>DATE_FIELD_DELIMITER</em> : The value used to delimit date fields (Default: "/")</div>
671        <div><em>DATE_RANGE_DELIMITER</em> : The value used to delimit date fields (Default: "-")</div>
672        <div><em>MY_MONTH_POSITION</em> : The value used to determine the position of the month in a month/year combo (e.g. 12/2005) (Default: 1)</div>
673        <div><em>MY_YEAR_POSITION</em> : The value used to determine the position of the year in a month/year combo (e.g. 12/2005) (Default: 2)</div>
674        <div><em>MD_MONTH_POSITION</em> : The value used to determine the position of the month in a month/day combo (e.g. 12/25) (Default: 1)</div>
675        <div><em>MD_DAY_POSITION</em> : The value used to determine the position of the day in a month/day combo (e.g. 12/25) (Default: 2)</div>
676        <div><em>MDY_MONTH_POSITION</em> : The value used to determine the position of the month in a month/day/year combo (e.g. 12/25/2005) (Default: 1)</div>
677        <div><em>MDY_DAY_POSITION</em> : The value used to determine the position of the day in a month/day/year combo (e.g. 12/25/2005) (Default: 2)</div>
678        <div><em>MDY_YEAR_POSITION</em> : The value used to determine the position of the year in a month/day/year combo (e.g. 12/25/2005) (Default: 3)</div>
679</blockquote>
680<p><strong>Config.Options</strong> - Defines other configurable calendar widget options</p>
681<blockquote>
682        <div><em>SHOW_WEEKDAYS</em> : Boolean, determines whether to display the weekday headers (defaults to true)</div>
683        <div><em>LOCALE_MONTHS</em> : Array, points to the desired Config.Locale array (defaults to Config.Locale.MONTHS_LONG)</div>
684        <div><em>LOCALE_WEEKDAYS</em> : Array, points to the desired Config.Locale array (defaults to Config.Locale.WEEKDAYS_SHORT)</div>
685        <div><em>START_WEEKDAY</em> : Integer, 0-6, representing the day that a week begins on</div>
686        <div><em>SHOW_WEEK_HEADER</em> : Boolean, determines whether to display row headers</div>
687        <div><em>SHOW_WEEK_FOOTER</em> : Boolean, determines whether to display row footers</div>
688        <div><em>HIDE_BLANK_WEEKS</em> : Boolean, determines whether to hide extra weeks that are completely OOM</div>
689        <div><em>NAV_ARROW_LEFT</em> : String, the image path used for the left navigation arrow</div>
690        <div><em>NAV_ARROW_RIGHT</em> : String, the image path used for the right navigation arrow</div>
691</blockquote>
692*/
693SPIP.widget.Calendar_Core.prototype.setupConfig = function() {
694        /**
695        * Container for the CSS style configuration variables.
696        */
697        this.Config = new Object();
698
699        this.Config.Style = {
700                // Style variables
701                CSS_ROW_HEADER: "calrowhead",
702                CSS_ROW_FOOTER: "calrowfoot",
703                CSS_CELL : "calcell",
704                CSS_CELL_SELECTED : "selected",
705                CSS_CELL_RESTRICTED : "restricted",
706                CSS_CELL_TODAY : "today",
707                CSS_CELL_OOM : "oom",
708                CSS_HEADER : "calheader",
709                CSS_HEADER_TEXT : "calhead",
710                CSS_WEEKDAY_CELL : "calweekdaycell",
711                CSS_WEEKDAY_ROW : "calweekdayrow",
712                CSS_FOOTER : "calfoot",
713                CSS_CALENDAR : "calendar",
714                CSS_BORDER : "calbordered",
715                CSS_CONTAINER : "calcontainer",
716                CSS_NAV_LEFT : "calnavleft",
717                CSS_NAV_RIGHT : "calnavright",
718                CSS_CELL_TOP : "calcelltop",
719                CSS_CELL_LEFT : "calcellleft",
720                CSS_CELL_RIGHT : "calcellright",
721                CSS_CELL_BOTTOM : "calcellbottom",
722                CSS_CELL_HOVER : "calcellhover",
723                CSS_CELL_HIGHLIGHT1 : "highlight1",
724                CSS_CELL_HIGHLIGHT2 : "highlight2",
725                CSS_CELL_HIGHLIGHT3 : "highlight3",
726                CSS_CELL_HIGHLIGHT4 : "highlight4"
727        };
728
729        this.Style = this.Config.Style;
730
731        this.Config.Locale = {
732                // Locale definition
733                MONTHS_SHORT : ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
734                MONTHS_LONG : ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
735                WEEKDAYS_1CHAR : ["S", "M", "T", "W", "T", "F", "S"],
736                WEEKDAYS_SHORT : ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
737                WEEKDAYS_MEDIUM : ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
738                WEEKDAYS_LONG : ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
739                DATE_DELIMITER : ",",
740                DATE_FIELD_DELIMITER : "/",
741                DATE_RANGE_DELIMITER : "-",
742                MY_MONTH_POSITION : 1,
743                MY_YEAR_POSITION : 2,
744                MD_MONTH_POSITION : 1,
745                MD_DAY_POSITION : 2,
746                MDY_MONTH_POSITION : 1,
747                MDY_DAY_POSITION : 2,
748                MDY_YEAR_POSITION : 3
749        };
750
751        this.Locale = this.Config.Locale;
752
753        this.Config.Options = {
754                // Configuration variables
755                MULTI_SELECT : false,
756                SHOW_WEEKDAYS : true,
757                START_WEEKDAY : 0,
758                SHOW_WEEK_HEADER : false,
759                SHOW_WEEK_FOOTER : false,
760                HIDE_BLANK_WEEKS : false,
761                NAV_ARROW_LEFT : "img/callt.gif",
762                NAV_ARROW_RIGHT : "img/calrt.gif",
763                IMG_CLOSE : "img/calx.gif",
764                IMG_CLOSE_CLASS : "close-icon"
765        };
766
767        this.Options = this.Config.Options;
768
769        this.customConfig();
770
771        if (! this.Options.LOCALE_MONTHS) {
772                this.Options.LOCALE_MONTHS=this.Locale.MONTHS_LONG;
773        }
774        if (! this.Options.LOCALE_WEEKDAYS) {
775                this.Options.LOCALE_WEEKDAYS=this.Locale.WEEKDAYS_SHORT;
776        }
777
778        // If true, reconfigure weekday arrays to place Mondays first
779        if (this.Options.START_WEEKDAY > 0)
780        {
781                for (var w=0;w<this.Options.START_WEEKDAY;++w) {
782                        this.Locale.WEEKDAYS_SHORT.push(this.Locale.WEEKDAYS_SHORT.shift());
783                        this.Locale.WEEKDAYS_MEDIUM.push(this.Locale.WEEKDAYS_MEDIUM.shift());
784                        this.Locale.WEEKDAYS_LONG.push(this.Locale.WEEKDAYS_LONG.shift());
785                }
786        }
787};
788
789/**
790* This method is called when subclasses need to override configuration variables
791* or create new ones. Values can be explicitly set as follows:
792* <blockquote><code>
793*       this.Config.Style.CSS_CELL = "newcalcell";
794*       this.Config.Locale.MONTHS_SHORT = ["Jan", "Fév", "Mars", "Avr", "Mai", "Juin", "Juil", "Août", "Sept", "Oct", "Nov", "Déc"];
795* </code></blockquote>
796*/
797SPIP.widget.Calendar_Core.prototype.customConfig = function() { };
798
799/**
800* Builds the date label that will be displayed in the calendar header or
801* footer, depending on configuration.
802* @return       The formatted calendar month label
803* @type String
804*/
805SPIP.widget.Calendar_Core.prototype.buildMonthLabel = function() {
806        var text = this.Options.LOCALE_MONTHS[this.pageDate.getMonth()] + " " + this.pageDate.getFullYear();
807        return text;
808};
809
810/**
811* Builds the date digit that will be displayed in calendar cells
812* @return       The formatted day label
813* @type String
814*/
815SPIP.widget.Calendar_Core.prototype.buildDayLabel = function(workingDate) {
816        var day = workingDate.getDate();
817        return day;
818};
819
820
821
822/**
823* Builds the calendar table shell that will be filled in with dates and formatting.
824* This method calls buildShellHeader, buildShellBody, and buildShellFooter (in that order)
825* to construct the pieces of the calendar table. The construction of the shell should
826* only happen one time when the calendar is initialized.
827*/
828SPIP.widget.Calendar_Core.prototype.buildShell = function() {
829
830        this.table = document.createElement("TABLE");
831        this.table.cellSpacing = 0;
832        SPIP.widget.Calendar_Core.setCssClasses(this.table, [this.Style.CSS_CALENDAR]);
833
834        this.table.id = this.id;
835
836        this.buildShellHeader();
837        this.buildShellBody();
838        this.buildShellFooter();
839
840        SPIP.util.Event.addListener(window, "unload", this._unload, this);
841};
842
843/**
844* Builds the calendar shell header by inserting a THEAD into the local calendar table.
845*/
846SPIP.widget.Calendar_Core.prototype.buildShellHeader = function() {
847        var head = document.createElement("THEAD");
848        var headRow = document.createElement("TR");
849
850        var headerCell = document.createElement("TH");
851
852        var colSpan = 7;
853        if (this.Config.Options.SHOW_WEEK_HEADER) {
854                this.weekHeaderCells = new Array();
855                colSpan += 1;
856        }
857        if (this.Config.Options.SHOW_WEEK_FOOTER) {
858                this.weekFooterCells = new Array();
859                colSpan += 1;
860        }
861
862        headerCell.colSpan = colSpan;
863
864        SPIP.widget.Calendar_Core.setCssClasses(headerCell,[this.Style.CSS_HEADER_TEXT]);
865
866        this.headerCell = headerCell;
867
868        headRow.appendChild(headerCell);
869        head.appendChild(headRow);
870
871        // Append day labels, if needed
872        if (this.Options.SHOW_WEEKDAYS)
873        {
874                var row = document.createElement("TR");
875                var fillerCell;
876
877                SPIP.widget.Calendar_Core.setCssClasses(row,[this.Style.CSS_WEEKDAY_ROW]);
878
879                if (this.Config.Options.SHOW_WEEK_HEADER) {
880                        fillerCell = document.createElement("TH");
881                        SPIP.widget.Calendar_Core.setCssClasses(fillerCell,[this.Style.CSS_WEEKDAY_CELL]);
882                        row.appendChild(fillerCell);
883                }
884
885                for(var i=0;i<this.Options.LOCALE_WEEKDAYS.length;++i)
886                {
887                        var cell = document.createElement("TH");
888                        SPIP.widget.Calendar_Core.setCssClasses(cell,[this.Style.CSS_WEEKDAY_CELL]);
889                        cell.innerHTML=this.Options.LOCALE_WEEKDAYS[i];
890                        row.appendChild(cell);
891                }
892
893                if (this.Config.Options.SHOW_WEEK_FOOTER) {
894                        fillerCell = document.createElement("TH");
895                        SPIP.widget.Calendar_Core.setCssClasses(fillerCell,[this.Style.CSS_WEEKDAY_CELL]);
896                        row.appendChild(fillerCell);
897                }
898
899                head.appendChild(row);
900        }
901
902        this.table.appendChild(head);
903};
904
905/**
906* Builds the calendar shell body (6 weeks by 7 days)
907*/
908SPIP.widget.Calendar_Core.prototype.buildShellBody = function() {
909        // This should only get executed once
910        this.tbody = document.createElement("TBODY");
911
912        for (var r=0;r<6;++r)
913        {
914                var row = document.createElement("TR");
915
916                for (var c=0;c<this.headerCell.colSpan;++c)
917                {
918                        var cell;
919                        if (this.Config.Options.SHOW_WEEK_HEADER && c===0) { // Row header
920                                cell = document.createElement("TH");
921                                this.weekHeaderCells[this.weekHeaderCells.length] = cell;
922                        } else if (this.Config.Options.SHOW_WEEK_FOOTER && c==(this.headerCell.colSpan-1)){ // Row footer
923                                cell = document.createElement("TH");
924                                this.weekFooterCells[this.weekFooterCells.length] = cell;
925                        } else {
926                                cell = document.createElement("TD");
927                                this.cells[this.cells.length] = cell;
928                                SPIP.widget.Calendar_Core.setCssClasses(cell, [this.Style.CSS_CELL]);
929                        }
930
931                        row.appendChild(cell);
932                }
933                this.tbody.appendChild(row);
934        }
935
936        this.table.appendChild(this.tbody);
937};
938
939/**
940* Builds the calendar shell footer. In the default implementation, there is
941* no footer.
942*/
943SPIP.widget.Calendar_Core.prototype.buildShellFooter = function() { };
944
945/**
946* Outputs the calendar shell to the DOM, inserting it into the placeholder element.
947*/
948SPIP.widget.Calendar_Core.prototype.renderShell = function() {
949        this.oDomContainer.appendChild(this.table);
950        this.shellRendered = true;
951};
952
953/**
954* Renders the calendar after it has been configured. The render() method has a specific call chain that will execute
955* when the method is called: renderHeader, renderBody, renderFooter.
956* Refer to the documentation for those methods for information on
957* individual render tasks.
958*/
959SPIP.widget.Calendar_Core.prototype.render = function() {
960        if (! this.shellRendered)
961        {
962                this.buildShell();
963                this.renderShell();
964        }
965
966        this.resetRenderers();
967
968        this.cellDates.length = 0;
969
970        // Find starting day of the current month
971        var workingDate = SPIP.widget.DateMath.findMonthStart(this.pageDate);
972
973        this.renderHeader();
974        this.renderBody(workingDate);
975        this.renderFooter();
976
977        this.onRender();
978};
979
980
981
982/**
983* Appends the header contents into the widget header.
984*/
985SPIP.widget.Calendar_Core.prototype.renderHeader = function() {
986        this.headerCell.innerHTML = "";
987
988        var headerContainer = document.createElement("DIV");
989        headerContainer.className = this.Style.CSS_HEADER;
990
991        var labelMonth = document.createElement("span");
992        labelMonth.innerHTML = this.buildMonthLabel();
993        /*headerContainer.appendChild(document.createTextNode(this.buildMonthLabel()));*/
994        headerContainer.appendChild(labelMonth);
995        this.headerCell.appendChild(headerContainer);
996};
997
998/**
999* Appends the calendar body. The default implementation calculates the number of
1000* OOM (out of month) cells that need to be rendered at the start of the month, renders those,
1001* and renders all the day cells using the built-in cell rendering methods.
1002*
1003* While iterating through all of the cells, the calendar checks for renderers in the
1004* local render stack that match the date of the current cell, and then applies styles
1005* as necessary.
1006*
1007* @param {Date} workingDate     The current working Date object being used to generate the calendar
1008*/
1009SPIP.widget.Calendar_Core.prototype.renderBody = function(workingDate) {
1010
1011        this.preMonthDays = workingDate.getDay();
1012        if (this.Options.START_WEEKDAY > 0) {
1013                this.preMonthDays -= this.Options.START_WEEKDAY;
1014        }
1015        if (this.preMonthDays < 0) {
1016                this.preMonthDays += 7;
1017        }
1018
1019        this.monthDays = SPIP.widget.DateMath.findMonthEnd(workingDate).getDate();
1020        this.postMonthDays = SPIP.widget.Calendar_Core.DISPLAY_DAYS-this.preMonthDays-this.monthDays;
1021
1022        workingDate = SPIP.widget.DateMath.subtract(workingDate, SPIP.widget.DateMath.DAY, this.preMonthDays);
1023
1024        this.table.style.visibility = "hidden"; // Hide while we render
1025
1026        var weekRowIndex = 0;
1027
1028        for (var c=0;c<this.cells.length;++c)
1029        {
1030                var cellRenderers = new Array();
1031
1032                var cell = this.cells[c];
1033
1034                this.clearElement(cell);
1035
1036                SPIP.util.Event.removeListener(cell, "click", this.doSelectCell);
1037                if (SPIP.widget.Calendar_Core._getBrowser() == "ie") {
1038                        SPIP.util.Event.removeListener(cell, "mouseover", this.doCellMouseOver);
1039                        SPIP.util.Event.removeListener(cell, "mouseout", this.doCellMouseOut);
1040                }
1041
1042                cell.index = c;
1043                cell.id = this.id + "_cell" + c;
1044
1045                this.cellDates[this.cellDates.length]=[workingDate.getFullYear(),workingDate.getMonth()+1,workingDate.getDate()]; // Add this date to cellDates
1046
1047                if (workingDate.getDay() == this.Options.START_WEEKDAY) {
1048                        var rowHeaderCell = null;
1049                        var rowFooterCell = null;
1050
1051                        if (this.Options.SHOW_WEEK_HEADER) {
1052                                rowHeaderCell = this.weekHeaderCells[weekRowIndex];
1053                                this.clearElement(rowHeaderCell);
1054                        }
1055
1056                        if (this.Options.SHOW_WEEK_FOOTER) {
1057                                rowFooterCell = this.weekFooterCells[weekRowIndex];
1058                                this.clearElement(rowFooterCell);
1059                        }
1060
1061                        if (this.Options.HIDE_BLANK_WEEKS && this.isDateOOM(workingDate) && ! SPIP.widget.DateMath.isMonthOverlapWeek(workingDate)) {
1062                                // The first day of the week is not in this month, and it's not an overlap week
1063                                continue;
1064                        } else {
1065                                if (rowHeaderCell) {
1066                                        this.renderRowHeader(workingDate, rowHeaderCell);
1067                                }
1068                                if (rowFooterCell) {
1069                                        this.renderRowFooter(workingDate, rowFooterCell);
1070                                }
1071                        }
1072                }
1073
1074
1075
1076                var renderer = null;
1077
1078                if (workingDate.getFullYear()   == this.today.getFullYear() &&
1079                        workingDate.getMonth()          == this.today.getMonth() &&
1080                        workingDate.getDate()           == this.today.getDate())
1081                {
1082                        cellRenderers[cellRenderers.length]=this.renderCellStyleToday;
1083                }
1084
1085                if (this.isDateOOM(workingDate))
1086                {
1087                        cellRenderers[cellRenderers.length]=this.renderCellNotThisMonth;
1088                } else {
1089                        for (var r=0;r<this.renderStack.length;++r)
1090                        {
1091                                var rArray = this.renderStack[r];
1092                                var type = rArray[0];
1093
1094                                var month;
1095                                var day;
1096                                var year;
1097
1098                                switch (type) {
1099                                        case SPIP.widget.Calendar_Core.DATE:
1100                                                month = rArray[1][1];
1101                                                day = rArray[1][2];
1102                                                year = rArray[1][0];
1103
1104                                                if (workingDate.getMonth()+1 == month && workingDate.getDate() == day && workingDate.getFullYear() == year)
1105                                                {
1106                                                        renderer = rArray[2];
1107                                                        this.renderStack.splice(r,1);
1108                                                }
1109                                                break;
1110                                        case SPIP.widget.Calendar_Core.MONTH_DAY:
1111                                                month = rArray[1][0];
1112                                                day = rArray[1][1];
1113
1114                                                if (workingDate.getMonth()+1 == month && workingDate.getDate() == day)
1115                                                {
1116                                                        renderer = rArray[2];
1117                                                        this.renderStack.splice(r,1);
1118                                                }
1119                                                break;
1120                                        case SPIP.widget.Calendar_Core.RANGE:
1121                                                var date1 = rArray[1][0];
1122                                                var date2 = rArray[1][1];
1123
1124                                                var d1month = date1[1];
1125                                                var d1day = date1[2];
1126                                                var d1year = date1[0];
1127
1128                                                var d1 = new Date(d1year, d1month-1, d1day);
1129
1130                                                var d2month = date2[1];
1131                                                var d2day = date2[2];
1132                                                var d2year = date2[0];
1133
1134                                                var d2 = new Date(d2year, d2month-1, d2day);
1135
1136                                                if (workingDate.getTime() >= d1.getTime() && workingDate.getTime() <= d2.getTime())
1137                                                {
1138                                                        renderer = rArray[2];
1139
1140                                                        if (workingDate.getTime()==d2.getTime()) {
1141                                                                this.renderStack.splice(r,1);
1142                                                        }
1143                                                }
1144                                                break;
1145                                        case SPIP.widget.Calendar_Core.WEEKDAY:
1146
1147                                                var weekday = rArray[1][0];
1148                                                if (workingDate.getDay()+1 == weekday)
1149                                                {
1150                                                        renderer = rArray[2];
1151                                                }
1152                                                break;
1153                                        case SPIP.widget.Calendar_Core.MONTH:
1154
1155                                                month = rArray[1][0];
1156                                                if (workingDate.getMonth()+1 == month)
1157                                                {
1158                                                        renderer = rArray[2];
1159                                                }
1160                                                break;
1161                                }
1162
1163                                if (renderer) {
1164                                        cellRenderers[cellRenderers.length]=renderer;
1165                                }
1166                        }
1167
1168                }
1169
1170                if (this._indexOfSelectedFieldArray([workingDate.getFullYear(),workingDate.getMonth()+1,workingDate.getDate()]) > -1)
1171                {
1172                        cellRenderers[cellRenderers.length]=this.renderCellStyleSelected;
1173                }
1174
1175                if (this.minDate)
1176                {
1177                        this.minDate = SPIP.widget.DateMath.clearTime(this.minDate);
1178                }
1179                if (this.maxDate)
1180                {
1181                        this.maxDate = SPIP.widget.DateMath.clearTime(this.maxDate);
1182                }
1183
1184                if (
1185                        (this.minDate && (workingDate.getTime() < this.minDate.getTime())) ||
1186                        (this.maxDate && (workingDate.getTime() > this.maxDate.getTime()))
1187                ) {
1188                        cellRenderers[cellRenderers.length]=this.renderOutOfBoundsDate;
1189                } else {
1190                        cellRenderers[cellRenderers.length]=this.renderCellDefault;
1191                }
1192
1193                for (var x=0;x<cellRenderers.length;++x)
1194                {
1195                        var ren = cellRenderers[x];
1196                        if (ren.call(this,workingDate,cell) == SPIP.widget.Calendar_Core.STOP_RENDER) {
1197                                break;
1198                        }
1199                }
1200
1201                workingDate = SPIP.widget.DateMath.add(workingDate, SPIP.widget.DateMath.DAY, 1); // Go to the next day
1202                if (workingDate.getDay() == this.Options.START_WEEKDAY) {
1203                        weekRowIndex += 1;
1204                }
1205
1206                SPIP.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL);
1207                if (c >= 0 && c <= 6) {
1208                        SPIP.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_TOP);
1209                }
1210                if ((c % 7) == 0) {
1211                        SPIP.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_LEFT);
1212                }
1213                if (((c+1) % 7) == 0) {
1214                        SPIP.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_RIGHT);
1215                }
1216
1217                var postDays = this.postMonthDays;
1218                if (postDays >= 7 && this.Options.HIDE_BLANK_WEEKS) {
1219                        var blankWeeks = Math.floor(postDays/7);
1220                        for (var p=0;p<blankWeeks;++p) {
1221                                postDays -= 7;
1222                        }
1223                }
1224
1225                if (c >= ((this.preMonthDays+postDays+this.monthDays)-7)) {
1226                        SPIP.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_BOTTOM);
1227                }
1228        }
1229
1230        this.table.style.visibility = "visible"; // Show table, now that it's rendered
1231
1232};
1233
1234/**
1235* Appends the contents of the calendar widget footer into the shell. By default,
1236* the calendar does not contain a footer, and this method must be implemented by
1237* subclassing the widget.
1238*/
1239SPIP.widget.Calendar_Core.prototype.renderFooter = function() { };
1240
1241/**
1242* @private
1243*/
1244SPIP.widget.Calendar_Core.prototype._unload = function(e, cal) {
1245        for (var c in cal.cells) {
1246                c = null;
1247        }
1248
1249        cal.cells = null;
1250
1251        cal.tbody = null;
1252        cal.oDomContainer = null;
1253        cal.table = null;
1254        cal.headerCell = null;
1255
1256        cal = null;
1257};
1258
1259
1260/****************** BEGIN BUILT-IN TABLE CELL RENDERERS ************************************/
1261
1262SPIP.widget.Calendar_Core.prototype.renderOutOfBoundsDate = function(workingDate, cell) {
1263        SPIP.widget.Calendar_Core.addCssClass(cell, "previous");
1264        cell.innerHTML = workingDate.getDate();
1265        return SPIP.widget.Calendar_Core.STOP_RENDER;
1266}
1267
1268/**
1269* Renders the row header for a week. The date passed in should be
1270* the first date of the given week.
1271* @param {Date}                                 workingDate             The current working Date object (beginning of the week) being used to generate the calendar
1272* @param {HTMLTableCellElement} cell                    The current working cell in the calendar
1273*/
1274SPIP.widget.Calendar_Core.prototype.renderRowHeader = function(workingDate, cell) {
1275        SPIP.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_ROW_HEADER);
1276
1277        var useYear = this.pageDate.getFullYear();
1278
1279        if (! SPIP.widget.DateMath.isYearOverlapWeek(workingDate)) {
1280                useYear = workingDate.getFullYear();
1281        }
1282
1283        var weekNum = SPIP.widget.DateMath.getWeekNumber(workingDate, useYear, this.Options.START_WEEKDAY);
1284        cell.innerHTML = weekNum;
1285
1286        if (this.isDateOOM(workingDate) && ! SPIP.widget.DateMath.isMonthOverlapWeek(workingDate)) {
1287                SPIP.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_OOM);
1288        }
1289};
1290
1291/**
1292* Renders the row footer for a week. The date passed in should be
1293* the first date of the given week.
1294* @param {Date}                                 workingDate             The current working Date object (beginning of the week) being used to generate the calendar
1295* @param {HTMLTableCellElement} cell                    The current working cell in the calendar
1296*/
1297SPIP.widget.Calendar_Core.prototype.renderRowFooter = function(workingDate, cell) {
1298        SPIP.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_ROW_FOOTER);
1299
1300        if (this.isDateOOM(workingDate) && ! SPIP.widget.DateMath.isMonthOverlapWeek(workingDate)) {
1301                SPIP.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_OOM);
1302        }
1303};
1304
1305/**
1306* Renders a single standard calendar cell in the calendar widget table.
1307* All logic for determining how a standard default cell will be rendered is
1308* encapsulated in this method, and must be accounted for when extending the
1309* widget class.
1310* @param {Date}                                 workingDate             The current working Date object being used to generate the calendar
1311* @param {HTMLTableCellElement} cell                    The current working cell in the calendar
1312* @return SPIP.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
1313*                       should not be terminated
1314* @type String
1315*/
1316SPIP.widget.Calendar_Core.prototype.renderCellDefault = function(workingDate, cell) {
1317        cell.innerHTML = "";
1318        var link = document.createElement("a");
1319
1320        link.href="javascript:void(null);";
1321        link.name=this.id+"__"+workingDate.getFullYear()+"_"+(workingDate.getMonth()+1)+"_"+workingDate.getDate();
1322
1323        //link.onclick = this._selectEventLink;
1324        //cell.onclick = this.doSelectCell;
1325
1326        SPIP.util.Event.addListener(cell, "click", this.doSelectCell, this);
1327        if (SPIP.widget.Calendar_Core._getBrowser() == "ie") {
1328                SPIP.util.Event.addListener(cell, "mouseover", this.doCellMouseOver, this);
1329                SPIP.util.Event.addListener(cell, "mouseout", this.doCellMouseOut, this);
1330        }
1331        link.appendChild(document.createTextNode(this.buildDayLabel(workingDate)));
1332        cell.appendChild(link);
1333
1334        //cell.onmouseover = this.doCellMouseOver;
1335        //cell.onmouseout = this.doCellMouseOut;
1336};
1337
1338SPIP.widget.Calendar_Core.prototype.renderCellStyleHighlight1 = function(workingDate, cell) {
1339        SPIP.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_HIGHLIGHT1);
1340};
1341SPIP.widget.Calendar_Core.prototype.renderCellStyleHighlight2 = function(workingDate, cell) {
1342        SPIP.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_HIGHLIGHT2);
1343};
1344SPIP.widget.Calendar_Core.prototype.renderCellStyleHighlight3 = function(workingDate, cell) {
1345        SPIP.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_HIGHLIGHT3);
1346};
1347SPIP.widget.Calendar_Core.prototype.renderCellStyleHighlight4 = function(workingDate, cell) {
1348        SPIP.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_HIGHLIGHT4);
1349};
1350
1351/**
1352* Applies the default style used for rendering today's date to the current calendar cell
1353* @param {Date}                                 workingDate             The current working Date object being used to generate the calendar
1354* @param {HTMLTableCellElement} cell                    The current working cell in the calendar
1355* @return       SPIP.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
1356*                       should not be terminated
1357* @type String
1358*/
1359SPIP.widget.Calendar_Core.prototype.renderCellStyleToday = function(workingDate, cell) {
1360        SPIP.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_TODAY);
1361};
1362
1363/**
1364* Applies the default style used for rendering selected dates to the current calendar cell
1365* @param {Date}                                 workingDate             The current working Date object being used to generate the calendar
1366* @param {HTMLTableCellElement} cell                    The current working cell in the calendar
1367* @return       SPIP.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
1368*                       should not be terminated
1369* @type String
1370*/
1371SPIP.widget.Calendar_Core.prototype.renderCellStyleSelected = function(workingDate, cell) {
1372        SPIP.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_SELECTED);
1373};
1374
1375/**
1376* Applies the default style used for rendering dates that are not a part of the current
1377* month (preceding or trailing the cells for the current month)
1378* @param {Date}                                 workingDate             The current working Date object being used to generate the calendar
1379* @param {HTMLTableCellElement} cell                    The current working cell in the calendar
1380* @return       SPIP.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
1381*                       should not be terminated
1382* @type String
1383*/
1384SPIP.widget.Calendar_Core.prototype.renderCellNotThisMonth = function(workingDate, cell) {
1385        SPIP.widget.Calendar_Core.addCssClass(cell, this.Style.CSS_CELL_OOM);
1386        cell.innerHTML=workingDate.getDate();
1387        return SPIP.widget.Calendar_Core.STOP_RENDER;
1388};
1389
1390/**
1391* Renders the current calendar cell as a non-selectable "black-out" date using the default
1392* restricted style.
1393* @param {Date}                                 workingDate             The current working Date object being used to generate the calendar
1394* @param {HTMLTableCellElement} cell                    The current working cell in the calendar
1395* @return       SPIP.widget.Calendar_Core.STOP_RENDER if rendering should stop with this style, null or nothing if rendering
1396*                       should not be terminated
1397* @type String
1398*/
1399SPIP.widget.Calendar_Core.prototype.renderBodyCellRestricted = function(workingDate, cell) {
1400        SPIP.widget.Calendar_Core.setCssClasses(cell, [this.Style.CSS_CELL,this.Style.CSS_CELL_RESTRICTED]);
1401        cell.innerHTML=workingDate.getDate();
1402        return SPIP.widget.Calendar_Core.STOP_RENDER;
1403};
1404/******************** END BUILT-IN TABLE CELL RENDERERS ************************************/
1405
1406/******************** BEGIN MONTH NAVIGATION METHODS ************************************/
1407/**
1408* Adds the designated number of months to the current calendar month, and sets the current
1409* calendar page date to the new month.
1410* @param {Integer}      count   The number of months to add to the current calendar
1411*/
1412SPIP.widget.Calendar_Core.prototype.addMonths = function(count) {
1413        this.pageDate = SPIP.widget.DateMath.add(this.pageDate, SPIP.widget.DateMath.MONTH, count);
1414        this.resetRenderers();
1415        this.onChangePage();
1416};
1417
1418/**
1419* Subtracts the designated number of months from the current calendar month, and sets the current
1420* calendar page date to the new month.
1421* @param {Integer}      count   The number of months to subtract from the current calendar
1422*/
1423SPIP.widget.Calendar_Core.prototype.subtractMonths = function(count) {
1424        this.pageDate = SPIP.widget.DateMath.subtract(this.pageDate, SPIP.widget.DateMath.MONTH, count);
1425        this.resetRenderers();
1426        this.onChangePage();
1427};
1428
1429/**
1430* Adds the designated number of years to the current calendar, and sets the current
1431* calendar page date to the new month.
1432* @param {Integer}      count   The number of years to add to the current calendar
1433*/
1434SPIP.widget.Calendar_Core.prototype.addYears = function(count) {
1435        this.pageDate = SPIP.widget.DateMath.add(this.pageDate, SPIP.widget.DateMath.YEAR, count);
1436        this.resetRenderers();
1437        this.onChangePage();
1438};
1439
1440/**
1441* Subtcats the designated number of years from the current calendar, and sets the current
1442* calendar page date to the new month.
1443* @param {Integer}      count   The number of years to subtract from the current calendar
1444*/
1445SPIP.widget.Calendar_Core.prototype.subtractYears = function(count) {
1446        this.pageDate = SPIP.widget.DateMath.subtract(this.pageDate, SPIP.widget.DateMath.YEAR, count);
1447        this.resetRenderers();
1448        this.onChangePage();
1449};
1450
1451/**
1452* Navigates to the next month page in the calendar widget.
1453*/
1454SPIP.widget.Calendar_Core.prototype.nextMonth = function() {
1455        this.addMonths(1);
1456};
1457
1458/**
1459* Navigates to the previous month page in the calendar widget.
1460*/
1461SPIP.widget.Calendar_Core.prototype.previousMonth = function() {
1462        this.subtractMonths(1);
1463};
1464
1465/**
1466* Navigates to the next year in the currently selected month in the calendar widget.
1467*/
1468SPIP.widget.Calendar_Core.prototype.nextYear = function() {
1469        this.addYears(1);
1470};
1471
1472/**
1473* Navigates to the previous year in the currently selected month in the calendar widget.
1474*/
1475SPIP.widget.Calendar_Core.prototype.previousYear = function() {
1476        this.subtractYears(1);
1477};
1478
1479/****************** END MONTH NAVIGATION METHODS ************************************/
1480
1481/************* BEGIN SELECTION METHODS *************************************************************/
1482
1483/**
1484* Resets the calendar widget to the originally selected month and year, and
1485* sets the calendar to the initial selection(s).
1486*/
1487SPIP.widget.Calendar_Core.prototype.reset = function() {
1488        this.selectedDates.length = 0;
1489        this.selectedDates = this._selectedDates.concat();
1490        this.deselectAll();
1491        /*this.pageDate = new Date(this._pageDate.getTime());*/
1492        this.onReset();
1493};
1494
1495/**
1496* Clears the selected dates in the current calendar widget and sets the calendar
1497* to the current month and year.
1498*/
1499SPIP.widget.Calendar_Core.prototype.clear = function() {
1500        this.selectedDates.length = 0;
1501        /*this.pageDate = new Date(this.today.getTime());*/
1502        this.onClear();
1503};
1504
1505/**
1506* Selects a date or a collection of dates on the current calendar. This method, by default,
1507* does not call the render method explicitly. Once selection has completed, render must be
1508* called for the changes to be reflected visually.
1509* @param        {String/Date/Date[]}    date    The date string of dates to select in the current calendar. Valid formats are
1510*                                                               individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
1511*                                                               Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
1512*                                                               This method can also take a JavaScript Date object or an array of Date objects.
1513* @return                                               Array of JavaScript Date objects representing all individual dates that are currently selected.
1514* @type Date[]
1515*/
1516SPIP.widget.Calendar_Core.prototype.select = function(date) {
1517        this.onBeforeSelect();
1518
1519        var aToBeSelected = this._toFieldArray(date);
1520
1521        for (var a=0;a<aToBeSelected.length;++a)
1522        {
1523                var toSelect = aToBeSelected[a]; // For each date item in the list of dates we're trying to select
1524                if (this._indexOfSelectedFieldArray(toSelect) == -1) // not already selected?
1525                {
1526                        this.selectedDates[this.selectedDates.length]=toSelect;
1527                }
1528        }
1529
1530        if (this.parent) {
1531                this.parent.sync(this);
1532        }
1533
1534        this.onSelect();
1535
1536        return this.getSelectedDates();
1537};
1538
1539/**
1540* Selects a date on the current calendar by referencing the index of the cell that should be selected.
1541* This method is used to easily select a single cell (usually with a mouse click) without having to do
1542* a full render. The selected style is applied to the cell directly.
1543* @param        {Integer}       cellIndex       The index of the cell to select in the current calendar.
1544* @return                                                       Array of JavaScript Date objects representing all individual dates that are currently selected.
1545* @type Date[]
1546*/
1547SPIP.widget.Calendar_Core.prototype.selectCell = function(cellIndex) {
1548        this.onBeforeSelect();
1549
1550        this.cells = this.tbody.getElementsByTagName("TD");
1551
1552        var cell = this.cells[cellIndex];
1553        var cellDate = this.cellDates[cellIndex];
1554
1555        var dCellDate = this._toDate(cellDate);
1556
1557        var selectDate = cellDate.concat();
1558
1559        this.selectedDates.push(selectDate);
1560
1561        if (this.parent) {
1562                this.parent.sync(this);
1563        }
1564
1565        this.renderCellStyleSelected(dCellDate,cell);
1566
1567        this.onSelect();
1568        this.doCellMouseOut.call(cell, null, this);
1569
1570        return this.getSelectedDates();
1571};
1572
1573/**
1574* Deselects a date or a collection of dates on the current calendar. This method, by default,
1575* does not call the render method explicitly. Once deselection has completed, render must be
1576* called for the changes to be reflected visually.
1577* @param        {String/Date/Date[]}    date    The date string of dates to deselect in the current calendar. Valid formats are
1578*                                                               individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
1579*                                                               Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
1580*                                                               This method can also take a JavaScript Date object or an array of Date objects.
1581* @return                                               Array of JavaScript Date objects representing all individual dates that are currently selected.
1582* @type Date[]
1583*/
1584SPIP.widget.Calendar_Core.prototype.deselect = function(date) {
1585        this.onBeforeDeselect();
1586
1587        var aToBeSelected = this._toFieldArray(date);
1588
1589        for (var a=0;a<aToBeSelected.length;++a)
1590        {
1591                var toSelect = aToBeSelected[a]; // For each date item in the list of dates we're trying to select
1592                var index = this._indexOfSelectedFieldArray(toSelect);
1593
1594                if (index != -1)
1595                {
1596                        this.selectedDates.splice(index,1);
1597                }
1598        }
1599
1600        if (this.parent) {
1601                this.parent.sync(this);
1602        }
1603
1604        this.onDeselect();
1605        return this.getSelectedDates();
1606};
1607
1608/**
1609* Deselects a date on the current calendar by referencing the index of the cell that should be deselected.
1610* This method is used to easily deselect a single cell (usually with a mouse click) without having to do
1611* a full render. The selected style is removed from the cell directly.
1612* @param        {Integer}       cellIndex       The index of the cell to deselect in the current calendar.
1613* @return                                                       Array of JavaScript Date objects representing all individual dates that are currently selected.
1614* @type Date[]
1615*/
1616SPIP.widget.Calendar_Core.prototype.deselectCell = function(i) {
1617        this.onBeforeDeselect();
1618        this.cells = this.tbody.getElementsByTagName("TD");
1619
1620        var cell = this.cells[i];
1621        var cellDate = this.cellDates[i];
1622        var cellDateIndex = this._indexOfSelectedFieldArray(cellDate);
1623
1624        var dCellDate = this._toDate(cellDate);
1625
1626        var selectDate = cellDate.concat();
1627
1628        if (cellDateIndex > -1)
1629        {
1630                if (this.pageDate.getMonth() == dCellDate.getMonth() &&
1631                        this.pageDate.getFullYear() == dCellDate.getFullYear())
1632                {
1633                        SPIP.widget.Calendar_Core.removeCssClass(cell, this.Style.CSS_CELL_SELECTED);
1634                }
1635
1636                this.selectedDates.splice(cellDateIndex, 1);
1637        }
1638
1639
1640        if (this.parent) {
1641                this.parent.sync(this);
1642        }
1643
1644        this.onDeselect();
1645        return this.getSelectedDates();
1646};
1647
1648/**
1649* Deselects all dates on the current calendar.
1650* @return                               Array of JavaScript Date objects representing all individual dates that are currently selected.
1651*                                               Assuming that this function executes properly, the return value should be an empty array.
1652*                                               However, the empty array is returned for the sake of being able to check the selection status
1653*                                               of the calendar.
1654* @type Date[]
1655*/
1656SPIP.widget.Calendar_Core.prototype.deselectAll = function() {
1657        this.onBeforeDeselect();
1658        var count = this.selectedDates.length;
1659        this.selectedDates.length = 0;
1660
1661        if (this.parent) {
1662                this.parent.sync(this);
1663        }
1664
1665        if (count > 0) {
1666                this.onDeselect();
1667        }
1668
1669        return this.getSelectedDates();
1670};
1671/************* END SELECTION METHODS *************************************************************/
1672
1673
1674/************* BEGIN TYPE CONVERSION METHODS ****************************************************/
1675
1676/**
1677* Converts a date (either a JavaScript Date object, or a date string) to the internal data structure
1678* used to represent dates: [[yyyy,mm,dd],[yyyy,mm,dd]].
1679* @private
1680* @param        {String/Date/Date[]}    date    The date string of dates to deselect in the current calendar. Valid formats are
1681*                                                               individual date(s) (12/24/2005,12/26/2005) or date range(s) (12/24/2005-1/1/2006).
1682*                                                               Multiple comma-delimited dates can also be passed to this method (12/24/2005,12/11/2005-12/13/2005).
1683*                                                               This method can also take a JavaScript Date object or an array of Date objects.
1684* @return                                               Array of date field arrays
1685* @type Array[](Integer[])
1686*/
1687SPIP.widget.Calendar_Core.prototype._toFieldArray = function(date) {
1688        var returnDate = new Array();
1689
1690        if (date instanceof Date)
1691        {
1692                returnDate = [[date.getFullYear(), date.getMonth()+1, date.getDate()]];
1693        }
1694        else if (typeof date == 'string')
1695        {
1696                returnDate = this._parseDates(date);
1697        }
1698        else if (date instanceof Array)
1699        {
1700                for (var i=0;i<date.length;++i)
1701                {
1702                        var d = date[i];
1703                        returnDate[returnDate.length] = [d.getFullYear(),d.getMonth()+1,d.getDate()];
1704                }
1705        }
1706
1707        return returnDate;
1708};
1709
1710/**
1711* Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
1712* @private
1713* @param        {Integer[]}             dateFieldArray  The date field array to convert to a JavaScript Date.
1714* @return                                       JavaScript Date object representing the date field array
1715* @type Date
1716*/
1717SPIP.widget.Calendar_Core.prototype._toDate = function(dateFieldArray) {
1718        if (dateFieldArray instanceof Date)
1719        {
1720                return dateFieldArray;
1721        } else
1722        {
1723                return new Date(dateFieldArray[0],dateFieldArray[1]-1,dateFieldArray[2]);
1724        }
1725};
1726/************* END TYPE CONVERSION METHODS ******************************************************/
1727
1728
1729/************* BEGIN UTILITY METHODS ****************************************************/
1730/**
1731* Converts a date field array [yyyy,mm,dd] to a JavaScript Date object.
1732* @private
1733* @param        {Integer[]}     array1  The first date field array to compare
1734* @param        {Integer[]}     array2  The first date field array to compare
1735* @return                                               The boolean that represents the equality of the two arrays
1736* @type Boolean
1737*/
1738SPIP.widget.Calendar_Core.prototype._fieldArraysAreEqual = function(array1, array2) {
1739        var match = false;
1740
1741        if (array1[0]==array2[0]&&array1[1]==array2[1]&&array1[2]==array2[2])
1742        {
1743                match=true;
1744        }
1745
1746        return match;
1747};
1748
1749/**
1750* Gets the index of a date field array [yyyy,mm,dd] in the current list of selected dates.
1751* @private
1752* @param        {Integer[]}             find    The date field array to search for
1753* @return                                       The index of the date field array within the collection of selected dates.
1754*                                                               -1 will be returned if the date is not found.
1755* @type Integer
1756*/
1757SPIP.widget.Calendar_Core.prototype._indexOfSelectedFieldArray = function(find) {
1758        var selected = -1;
1759
1760        for (var s=0;s<this.selectedDates.length;++s)
1761        {
1762                var sArray = this.selectedDates[s];
1763                if (find[0]==sArray[0]&&find[1]==sArray[1]&&find[2]==sArray[2])
1764                {
1765                        selected = s;
1766                        break;
1767                }
1768        }
1769
1770        return selected;
1771};
1772
1773/**
1774* Determines whether a given date is OOM (out of month).
1775* @param        {Date}  date    The JavaScript Date object for which to check the OOM status
1776* @return       {Boolean}       true if the date is OOM
1777*/
1778SPIP.widget.Calendar_Core.prototype.isDateOOM = function(date) {
1779        var isOOM = false;
1780        if (date.getMonth() != this.pageDate.getMonth()) {
1781                isOOM = true;
1782        }
1783        return isOOM;
1784};
1785
1786/************* END UTILITY METHODS *******************************************************/
1787
1788/************* BEGIN EVENT HANDLERS ******************************************************/
1789
1790/**
1791* Event executed before a date is selected in the calendar widget.
1792*/
1793SPIP.widget.Calendar_Core.prototype.onBeforeSelect = function() {
1794        if (! this.Options.MULTI_SELECT) {
1795                this.clearAllBodyCellStyles(this.Style.CSS_CELL_SELECTED);
1796                this.deselectAll();
1797        }
1798};
1799
1800/**
1801* Event executed when a date is selected in the calendar widget.
1802*/
1803SPIP.widget.Calendar_Core.prototype.onSelect = function() { };
1804
1805/**
1806* Event executed before a date is deselected in the calendar widget.
1807*/
1808SPIP.widget.Calendar_Core.prototype.onBeforeDeselect = function() { };
1809
1810/**
1811* Event executed when a date is deselected in the calendar widget.
1812*/
1813SPIP.widget.Calendar_Core.prototype.onDeselect = function() { };
1814
1815/**
1816* Event executed when the user navigates to a different calendar page.
1817*/
1818SPIP.widget.Calendar_Core.prototype.onChangePage = function() { this.render(); };
1819
1820/**
1821* Event executed when the calendar widget is rendered.
1822*/
1823SPIP.widget.Calendar_Core.prototype.onRender = function() { };
1824
1825/**
1826* Event executed when the calendar widget is reset to its original state.
1827*/
1828SPIP.widget.Calendar_Core.prototype.onReset = function() { this.render(); };
1829
1830/**
1831* Event executed when the calendar widget is completely cleared to the current month with no selections.
1832*/
1833SPIP.widget.Calendar_Core.prototype.onClear = function() { this.render(); };
1834
1835/**
1836* Validates the calendar widget. This method has no default implementation
1837* and must be extended by subclassing the widget.
1838* @return       Should return true if the widget validates, and false if
1839* it doesn't.
1840* @type Boolean
1841*/
1842SPIP.widget.Calendar_Core.prototype.validate = function() { return true; };
1843
1844/************* END EVENT HANDLERS *********************************************************/
1845
1846
1847/************* BEGIN DATE PARSE METHODS ***************************************************/
1848
1849
1850/**
1851* Converts a date string to a date field array
1852* @private
1853* @param        {String}        sDate                   Date string. Valid formats are mm/dd and mm/dd/yyyy.
1854* @return                               A date field array representing the string passed to the method
1855* @type Array[](Integer[])
1856*/
1857SPIP.widget.Calendar_Core.prototype._parseDate = function(sDate) {
1858        var aDate = sDate.split(this.Locale.DATE_FIELD_DELIMITER);
1859        var rArray;
1860
1861        if (aDate.length == 2)
1862        {
1863                rArray = [aDate[this.Locale.MD_MONTH_POSITION-1],aDate[this.Locale.MD_DAY_POSITION-1]];
1864                rArray.type = SPIP.widget.Calendar_Core.MONTH_DAY;
1865        } else {
1866                rArray = [aDate[this.Locale.MDY_YEAR_POSITION-1],aDate[this.Locale.MDY_MONTH_POSITION-1],aDate[this.Locale.MDY_DAY_POSITION-1]];
1867                rArray.type = SPIP.widget.Calendar_Core.DATE;
1868        }
1869        return rArray;
1870};
1871
1872/**
1873* Converts a multi or single-date string to an array of date field arrays
1874* @private
1875* @param        {String}        sDates          Date string with one or more comma-delimited dates. Valid formats are mm/dd, mm/dd/yyyy, mm/dd/yyyy-mm/dd/yyyy
1876* @return                                                       An array of date field arrays
1877* @type Array[](Integer[])
1878*/
1879SPIP.widget.Calendar_Core.prototype._parseDates = function(sDates) {
1880        var aReturn = new Array();
1881
1882        var aDates = sDates.split(this.Locale.DATE_DELIMITER);
1883
1884        for (var d=0;d<aDates.length;++d)
1885        {
1886                var sDate = aDates[d];
1887
1888                if (sDate.indexOf(this.Locale.DATE_RANGE_DELIMITER) != -1) {
1889                        // This is a range
1890                        var aRange = sDate.split(this.Locale.DATE_RANGE_DELIMITER);
1891
1892                        var dateStart = this._parseDate(aRange[0]);
1893                        var dateEnd = this._parseDate(aRange[1]);
1894
1895                        var fullRange = this._parseRange(dateStart, dateEnd);
1896                        aReturn = aReturn.concat(fullRange);
1897                } else {
1898                        // This is not a range
1899                        var aDate = this._parseDate(sDate);
1900                        aReturn.push(aDate);
1901                }
1902        }
1903        return aReturn;
1904};
1905
1906/**
1907* Converts a date range to the full list of included dates
1908* @private
1909* @param        {Integer[]}     startDate       Date field array representing the first date in the range
1910* @param        {Integer[]}     endDate         Date field array representing the last date in the range
1911* @return                                                       An array of date field arrays
1912* @type Array[](Integer[])
1913*/
1914SPIP.widget.Calendar_Core.prototype._parseRange = function(startDate, endDate) {
1915        var dStart   = new Date(startDate[0],startDate[1]-1,startDate[2]);
1916        var dCurrent = SPIP.widget.DateMath.add(new Date(startDate[0],startDate[1]-1,startDate[2]),SPIP.widget.DateMath.DAY,1);
1917        var dEnd     = new Date(endDate[0],  endDate[1]-1,  endDate[2]);
1918
1919        var results = new Array();
1920        results.push(startDate);
1921        while (dCurrent.getTime() <= dEnd.getTime())
1922        {
1923                results.push([dCurrent.getFullYear(),dCurrent.getMonth()+1,dCurrent.getDate()]);
1924                dCurrent = SPIP.widget.DateMath.add(dCurrent,SPIP.widget.DateMath.DAY,1);
1925        }
1926        return results;
1927};
1928
1929/************* END DATE PARSE METHODS *****************************************************/
1930
1931/************* BEGIN RENDERER METHODS *****************************************************/
1932
1933/**
1934* Resets the render stack of the current calendar to its original pre-render value.
1935*/
1936SPIP.widget.Calendar_Core.prototype.resetRenderers = function() {
1937        this.renderStack = this._renderStack.concat();
1938};
1939
1940/**
1941* Clears the inner HTML, CSS class and style information from the specified cell.
1942* @param        {HTMLTableCellElement}  The cell to clear
1943*/
1944SPIP.widget.Calendar_Core.prototype.clearElement = function(cell) {
1945        cell.innerHTML = "&nbsp;";
1946        cell.className="";
1947};
1948
1949/**
1950* Adds a renderer to the render stack. The function reference passed to this method will be executed
1951* when a date cell matches the conditions specified in the date string for this renderer.
1952* @param        {String}        sDates          A date string to associate with the specified renderer. Valid formats
1953*                                                                       include date (12/24/2005), month/day (12/24), and range (12/1/2004-1/1/2005)
1954* @param        {Function}      fnRender        The function executed to render cells that match the render rules for this renderer.
1955*/
1956SPIP.widget.Calendar_Core.prototype.addRenderer = function(sDates, fnRender) {
1957        var aDates = this._parseDates(sDates);
1958        for (var i=0;i<aDates.length;++i)
1959        {
1960                var aDate = aDates[i];
1961
1962                if (aDate.length == 2) // this is either a range or a month/day combo
1963                {
1964                        if (aDate[0] instanceof Array) // this is a range
1965                        {
1966                                this._addRenderer(SPIP.widget.Calendar_Core.RANGE,aDate,fnRender);
1967                        } else { // this is a month/day combo
1968                                this._addRenderer(SPIP.widget.Calendar_Core.MONTH_DAY,aDate,fnRender);
1969                        }
1970                } else if (aDate.length == 3)
1971                {
1972                        this._addRenderer(SPIP.widget.Calendar_Core.DATE,aDate,fnRender);
1973                }
1974        }
1975};
1976
1977/**
1978* The private method used for adding cell renderers to the local render stack.
1979* This method is called by other methods that set the renderer type prior to the method call.
1980* @private
1981* @param        {String}        type            The type string that indicates the type of date renderer being added.
1982*                                                                       Values are SPIP.widget.Calendar_Core.DATE, SPIP.widget.Calendar_Core.MONTH_DAY, SPIP.widget.Calendar_Core.WEEKDAY,
1983*                                                                       SPIP.widget.Calendar_Core.RANGE, SPIP.widget.Calendar_Core.MONTH
1984* @param        {Array}         aDates          An array of dates used to construct the renderer. The format varies based
1985*                                                                       on the renderer type
1986* @param        {Function}      fnRender        The function executed to render cells that match the render rules for this renderer.
1987*/
1988SPIP.widget.Calendar_Core.prototype._addRenderer = function(type, aDates, fnRender) {
1989        var add = [type,aDates,fnRender];
1990        this.renderStack.unshift(add);
1991
1992        this._renderStack = this.renderStack.concat();
1993};
1994
1995/**
1996* Adds a month to the render stack. The function reference passed to this method will be executed
1997* when a date cell matches the month passed to this method.
1998* @param        {Integer}       month           The month (1-12) to associate with this renderer
1999* @param        {Function}      fnRender        The function executed to render cells that match the render rules for this renderer.
2000*/
2001SPIP.widget.Calendar_Core.prototype.addMonthRenderer = function(month, fnRender) {
2002        this._addRenderer(SPIP.widget.Calendar_Core.MONTH,[month],fnRender);
2003};
2004
2005/**
2006* Adds a weekday to the render stack. The function reference passed to this method will be executed
2007* when a date cell matches the weekday passed to this method.
2008* @param        {Integer}       weekay          The weekday (1-7) to associate with this renderer
2009* @param        {Function}      fnRender        The function executed to render cells that match the render rules for this renderer.
2010*/
2011SPIP.widget.Calendar_Core.prototype.addWeekdayRenderer = function(weekday, fnRender) {
2012        this._addRenderer(SPIP.widget.Calendar_Core.WEEKDAY,[weekday],fnRender);
2013};
2014/************* END RENDERER METHODS *******************************************************/
2015
2016/***************** BEGIN CSS METHODS *******************************************/
2017/**
2018* Adds the specified CSS class to the element passed to this method.
2019* @param        {HTMLElement}   element         The element to which the CSS class should be applied
2020* @param        {String}        style           The CSS class name to add to the referenced element
2021*/
2022SPIP.widget.Calendar_Core.addCssClass = function(element, style) {
2023        if (element.className.length === 0)
2024        {
2025                element.className += style;
2026        } else {
2027                element.className += " " + style;
2028        }
2029};
2030
2031SPIP.widget.Calendar_Core.prependCssClass = function(element, style) {
2032        element.className = style + " " + element.className;
2033}
2034
2035/**
2036* Removed the specified CSS class from the element passed to this method.
2037* @param        {HTMLElement}   element         The element from which the CSS class should be removed
2038* @param        {String}        style           The CSS class name to remove from the referenced element
2039*/
2040SPIP.widget.Calendar_Core.removeCssClass = function(element, style) {
2041        var aStyles = element.className.split(" ");
2042        for (var s=0;s<aStyles.length;++s)
2043        {
2044                if (aStyles[s] == style)
2045                {
2046                        aStyles.splice(s,1);
2047                        break;
2048                }
2049        }
2050        SPIP.widget.Calendar_Core.setCssClasses(element, aStyles);
2051};
2052
2053/**
2054* Sets the specified array of CSS classes into the referenced element
2055* @param        {HTMLElement}   element         The element to set the CSS classes into
2056* @param        {String[]}              aStyles         An array of CSS class names
2057*/
2058SPIP.widget.Calendar_Core.setCssClasses = function(element, aStyles) {
2059        element.className = "";
2060        var className = aStyles.join(" ");
2061        element.className = className;
2062};
2063
2064/**
2065* Removes all styles from all body cells in the current calendar table.
2066* @param        {style}         The CSS class name to remove from all calendar body cells
2067*/
2068SPIP.widget.Calendar_Core.prototype.clearAllBodyCellStyles = function(style) {
2069        for (var c=0;c<this.cells.length;++c)
2070        {
2071                SPIP.widget.Calendar_Core.removeCssClass(this.cells[c],style);
2072        }
2073};
2074
2075/***************** END CSS METHODS *********************************************/
2076
2077/***************** BEGIN GETTER/SETTER METHODS *********************************/
2078/**
2079* Sets the calendar's month explicitly.
2080* @param {Integer}      month           The numeric month, from 1 (January) to 12 (December)
2081*/
2082SPIP.widget.Calendar_Core.prototype.setMonth = function(month) {
2083        this.pageDate.setMonth(month);
2084};
2085
2086/**
2087* Sets the calendar's year explicitly.
2088* @param {Integer}      year            The numeric 4-digit year
2089*/
2090SPIP.widget.Calendar_Core.prototype.setYear = function(year) {
2091        this.pageDate.setFullYear(year);
2092};
2093
2094/**
2095* Gets the list of currently selected dates from the calendar.
2096* @return       An array of currently selected JavaScript Date objects.
2097* @type Date[]
2098*/
2099SPIP.widget.Calendar_Core.prototype.getSelectedDates = function() {
2100        var returnDates = new Array();
2101
2102        for (var d=0;d<this.selectedDates.length;++d)
2103        {
2104                var dateArray = this.selectedDates[d];
2105
2106                var date = new Date(dateArray[0],dateArray[1]-1,dateArray[2]);
2107                returnDates.push(date);
2108        }
2109
2110        returnDates.sort();
2111        return returnDates;
2112};
2113
2114/***************** END GETTER/SETTER METHODS *********************************/
2115
2116SPIP.widget.Calendar_Core._getBrowser = function()
2117{
2118  /**
2119   * UserAgent
2120   * @private
2121   * @type String
2122   */
2123  var ua = navigator.userAgent.toLowerCase();
2124
2125  if (ua.indexOf('opera')!=-1) // Opera (check first in case of spoof)
2126         return 'opera';
2127  else if (ua.indexOf('msie')!=-1) // IE
2128         return 'ie';
2129  else if (ua.indexOf('safari')!=-1) // Safari (check before Gecko because it includes "like Gecko")
2130         return 'safari';
2131  else if (ua.indexOf('gecko') != -1) // Gecko
2132         return 'gecko';
2133 else
2134  return false;
2135}
2136
2137
2138SPIP.widget.Cal_Core = SPIP.widget.Calendar_Core;
2139
2140SPIP.namespace("SPIP.widget");
2141
2142/**
2143* @class
2144* <p>SPIP.widget.CalendarGroup is a special container class for SPIP.widget.Calendar_Core. This class facilitates
2145* the ability to have multi-page calendar views that share a single dataset and are
2146* dependent on each other.</p>
2147*
2148* <p>The calendar group instance will refer to each of its elements using a 0-based index.
2149* For example, to construct the placeholder for a calendar group widget with id "cal1" and
2150* containerId of "cal1Container", the markup would be as follows:
2151*       <xmp>
2152*               <div id="cal1Container_0"></div>
2153*               <div id="cal1Container_1"></div>
2154*       </xmp>
2155* The tables for the calendars ("cal1_0" and "cal1_1") will be inserted into those containers.
2156* </p>
2157* @constructor
2158* @param {Integer}              pageCount       The number of pages that this calendar should display.
2159* @param {String}               id                      The id of the element that will be inserted into the DOM.
2160* @param {String}       containerId     The id of the container element that the calendar will be inserted into.
2161* @param {String}               monthyear       The month/year string used to set the current calendar page
2162* @param {String}               selected        A string of date values formatted using the date parser. The built-in
2163                                                                        default date format is MM/DD/YYYY. Ranges are defined using
2164                                                                        MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
2165                                                                        Any combination of these can be combined by delimiting the string with
2166                                                                        commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
2167*/
2168SPIP.widget.CalendarGroup = function(pageCount, id, containerId, monthyear, selected) {
2169        if (arguments.length > 0)
2170        {
2171                this.init(pageCount, id, containerId, monthyear, selected);
2172        }
2173}
2174
2175/**
2176* Initializes the calendar group. All subclasses must call this method in order for the
2177* group to be initialized properly.
2178* @param {Integer}              pageCount       The number of pages that this calendar should display.
2179* @param {String}               id                      The id of the element that will be inserted into the DOM.
2180* @param {String}               containerId     The id of the container element that the calendar will be inserted into.
2181* @param {String}               monthyear       The month/year string used to set the current calendar page
2182* @param {String}               selected        A string of date values formatted using the date parser. The built-in
2183                                                                        default date format is MM/DD/YYYY. Ranges are defined using
2184                                                                        MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
2185                                                                        Any combination of these can be combined by delimiting the string with
2186                                                                        commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
2187*/
2188SPIP.widget.CalendarGroup.prototype.init = function(pageCount, id, containerId, monthyear, selected) {
2189        //var self=this;
2190
2191        this.id = id;
2192        this.selectedDates = new Array();
2193        this.containerId = containerId;
2194
2195        this.pageCount = pageCount;
2196
2197        this.pages = new Array();
2198
2199        for (var p=0;p<pageCount;++p)
2200        {
2201                var cal = this.constructChild(id + "_" + p, this.containerId + "_" + p , monthyear, selected);
2202
2203                cal.parent = this;
2204
2205                cal.index = p;
2206
2207                cal.pageDate.setMonth(cal.pageDate.getMonth()+p);
2208                cal._pageDateOrig = new Date(cal.pageDate.getFullYear(),cal.pageDate.getMonth(),cal.pageDate.getDate());
2209                this.pages.push(cal);
2210        }
2211
2212        this.doNextMonth = function(e, calGroup) {
2213                calGroup.nextMonth();
2214        }
2215
2216        this.doPreviousMonth = function(e, calGroup) {
2217                calGroup.previousMonth();
2218        }
2219};
2220
2221SPIP.widget.CalendarGroup.prototype.setChildFunction = function(fnName, fn) {
2222        for (var p=0;p<this.pageCount;++p) {
2223                this.pages[p][fnName] = fn;
2224        }
2225}
2226
2227SPIP.widget.CalendarGroup.prototype.callChildFunction = function(fnName, args) {
2228        for (var p=0;p<this.pageCount;++p) {
2229                var page = this.pages[p];
2230                if (page[fnName]) {
2231                        var fn = page[fnName];
2232                        fn.call(page, args);
2233                }
2234        }
2235}
2236
2237/**
2238* Constructs a child calendar. This method can be overridden if a subclassed version of the default
2239* calendar is to be used.
2240* @param {String}               id                      The id of the element that will be inserted into the DOM.
2241* @param {String}               containerId     The id of the container element that the calendar will be inserted into.
2242* @param {String}               monthyear       The month/year string used to set the current calendar page
2243* @param {String}               selected        A string of date values formatted using the date parser. The built-in
2244                                                                        default date format is MM/DD/YYYY. Ranges are defined using
2245                                                                        MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
2246                                                                        Any combination of these can be combined by delimiting the string with
2247                                                                        commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
2248* @return                                                       The SPIP.widget.Calendar_Core instance that is constructed
2249* @type SPIP.widget.Calendar_Core
2250*/
2251SPIP.widget.CalendarGroup.prototype.constructChild = function(id,containerId,monthyear,selected) {
2252        return new SPIP.widget.Calendar_Core(id,containerId,monthyear,selected);
2253};
2254
2255
2256/**
2257* Sets the calendar group's month explicitly. This month will be set into the first
2258* page of the multi-page calendar, and all other months will be iterated appropriately.
2259* @param {Integer}      month           The numeric month, from 1 (January) to 12 (December)
2260*/
2261SPIP.widget.CalendarGroup.prototype.setMonth = function(month) {
2262        for (var p=0;p<this.pages.length;++p)
2263        {
2264                var cal = this.pages[p];
2265                cal.setMonth(month+p);
2266        }
2267};
2268
2269/**
2270* Sets the calendar group's year explicitly. This year will be set into the first
2271* page of the multi-page calendar, and all other months will be iterated appropriately.
2272* @param {Integer}      year            The numeric 4-digit year
2273*/
2274SPIP.widget.CalendarGroup.prototype.setYear = function(year) {
2275        for (var p=0;p<this.pages.length;++p)
2276        {
2277                var cal = this.pages[p];
2278                if ((cal.pageDate.getMonth()+1) == 1 && p>0)
2279                {
2280                        year+=1;
2281                }
2282                cal.setYear(year);
2283        }
2284};
2285
2286/**
2287* Calls the render function of all child calendars within the group.
2288*/
2289SPIP.widget.CalendarGroup.prototype.render = function() {
2290        for (var p=0;p<this.pages.length;++p)
2291        {
2292                var cal = this.pages[p];
2293                cal.render();
2294        }
2295};
2296
2297/**
2298* Calls the select function of all child calendars within the group.
2299*/
2300SPIP.widget.CalendarGroup.prototype.select = function(date) {
2301        var ret;
2302        for (var p=0;p<this.pages.length;++p)
2303        {
2304                var cal = this.pages[p];
2305                ret = cal.select(date);
2306        }
2307        return ret;
2308};
2309
2310/**
2311* Calls the selectCell function of all child calendars within the group.
2312*/
2313SPIP.widget.CalendarGroup.prototype.selectCell = function(cellIndex) {
2314        var ret;
2315        for (var p=0;p<this.pages.length;++p)
2316        {
2317                var cal = this.pages[p];
2318                ret = cal.selectCell(cellIndex);
2319        }
2320        return ret;
2321};
2322
2323/**
2324* Calls the deselect function of all child calendars within the group.
2325*/
2326SPIP.widget.CalendarGroup.prototype.deselect = function(date) {
2327        var ret;
2328        for (var p=0;p<this.pages.length;++p)
2329        {
2330                var cal = this.pages[p];
2331                ret = cal.deselect(date);
2332        }
2333        return ret;
2334};
2335
2336/**
2337* Calls the deselectAll function of all child calendars within the group.
2338*/
2339SPIP.widget.CalendarGroup.prototype.deselectAll = function() {
2340        var ret;
2341        for (var p=0;p<this.pages.length;++p)
2342        {
2343                var cal = this.pages[p];
2344                ret = cal.deselectAll();
2345        }
2346        return ret;
2347};
2348
2349/**
2350* Calls the deselectAll function of all child calendars within the group.
2351*/
2352SPIP.widget.CalendarGroup.prototype.deselectCell = function(cellIndex) {
2353        for (var p=0;p<this.pages.length;++p)
2354        {
2355                var cal = this.pages[p];
2356                cal.deselectCell(cellIndex);
2357        }
2358        return this.getSelectedDates();
2359};
2360
2361/**
2362* Calls the reset function of all child calendars within the group.
2363*/
2364SPIP.widget.CalendarGroup.prototype.reset = function() {
2365        for (var p=0;p<this.pages.length;++p)
2366        {
2367                var cal = this.pages[p];
2368                cal.reset();
2369        }
2370};
2371
2372/**
2373* Calls the clear function of all child calendars within the group.
2374*/
2375SPIP.widget.CalendarGroup.prototype.clear = function() {
2376        for (var p=0;p<this.pages.length;++p)
2377        {
2378                var cal = this.pages[p];
2379                cal.clear();
2380        }
2381};
2382
2383/**
2384* Calls the nextMonth function of all child calendars within the group.
2385*/
2386SPIP.widget.CalendarGroup.prototype.nextMonth = function() {
2387        for (var p=0;p<this.pages.length;++p)
2388        {
2389                var cal = this.pages[p];
2390                cal.nextMonth();
2391        }
2392};
2393
2394/**
2395* Calls the previousMonth function of all child calendars within the group.
2396*/
2397SPIP.widget.CalendarGroup.prototype.previousMonth = function() {
2398        for (var p=this.pages.length-1;p>=0;--p)
2399        {
2400                var cal = this.pages[p];
2401                cal.previousMonth();
2402        }
2403};
2404
2405/**
2406* Calls the nextYear function of all child calendars within the group.
2407*/
2408SPIP.widget.CalendarGroup.prototype.nextYear = function() {
2409        for (var p=0;p<this.pages.length;++p)
2410        {
2411                var cal = this.pages[p];
2412                cal.nextYear();
2413        }
2414};
2415
2416/**
2417* Calls the previousYear function of all child calendars within the group.
2418*/
2419SPIP.widget.CalendarGroup.prototype.previousYear = function() {
2420        for (var p=0;p<this.pages.length;++p)
2421        {
2422                var cal = this.pages[p];
2423                cal.previousYear();
2424        }
2425};
2426
2427/**
2428* Synchronizes the data values for all child calendars within the group. If the sync
2429* method is called passing in the caller object, the values of all children will be set
2430* to the values of the caller. If the argument is ommitted, the values from all children
2431* will be combined into one distinct list and set into each child.
2432* @param        {SPIP.widget.Calendar_Core}     caller          The SPIP.widget.Calendar_Core that is initiating the call to sync().
2433* @return                                                               Array of selected dates, in JavaScript Date object form.
2434* @type Date[]
2435*/
2436SPIP.widget.CalendarGroup.prototype.sync = function(caller) {
2437        var calendar;
2438
2439        if (caller)
2440        {
2441                this.selectedDates = caller.selectedDates.concat();
2442        } else {
2443                var hash = new Object();
2444                var combinedDates = new Array();
2445
2446                for (var p=0;p<this.pages.length;++p)
2447                {
2448                        calendar = this.pages[p];
2449
2450                        var values = calendar.selectedDates;
2451
2452                        for (var v=0;v<values.length;++v)
2453                        {
2454                                var valueArray = values[v];
2455                                hash[valueArray.toString()] = valueArray;
2456                        }
2457                }
2458
2459                for (var val in hash)
2460                {
2461                        combinedDates[combinedDates.length]=hash[val];
2462                }
2463
2464                this.selectedDates = combinedDates.concat();
2465        }
2466
2467        // Set all the values into the children
2468        for (p=0;p<this.pages.length;++p)
2469        {
2470                calendar = this.pages[p];
2471                if (! calendar.Options.MULTI_SELECT) {
2472                        calendar.clearAllBodyCellStyles(calendar.Config.Style.CSS_CELL_SELECTED);
2473                }
2474                calendar.selectedDates = this.selectedDates.concat();
2475
2476        }
2477
2478        return this.getSelectedDates();
2479};
2480
2481/**
2482* Gets the list of currently selected dates from the calendar.
2483* @return                       An array of currently selected JavaScript Date objects.
2484* @type Date[]
2485*/
2486SPIP.widget.CalendarGroup.prototype.getSelectedDates = function() {
2487        var returnDates = new Array();
2488
2489        for (var d=0;d<this.selectedDates.length;++d)
2490        {
2491                var dateArray = this.selectedDates[d];
2492
2493                var date = new Date(dateArray[0],dateArray[1]-1,dateArray[2]);
2494                returnDates.push(date);
2495        }
2496
2497        returnDates.sort();
2498        return returnDates;
2499};
2500
2501/**
2502* Calls the addRenderer function of all child calendars within the group.
2503*/
2504SPIP.widget.CalendarGroup.prototype.addRenderer = function(sDates, fnRender) {
2505        for (var p=0;p<this.pages.length;++p)
2506        {
2507                var cal = this.pages[p];
2508                cal.addRenderer(sDates, fnRender);
2509        }
2510};
2511
2512/**
2513* Calls the addMonthRenderer function of all child calendars within the group.
2514*/
2515SPIP.widget.CalendarGroup.prototype.addMonthRenderer = function(month, fnRender) {
2516        for (var p=0;p<this.pages.length;++p)
2517        {
2518                var cal = this.pages[p];
2519                cal.addMonthRenderer(month, fnRender);
2520        }
2521};
2522
2523/**
2524* Calls the addWeekdayRenderer function of all child calendars within the group.
2525*/
2526SPIP.widget.CalendarGroup.prototype.addWeekdayRenderer = function(weekday, fnRender) {
2527        for (var p=0;p<this.pages.length;++p)
2528        {
2529                var cal = this.pages[p];
2530                cal.addWeekdayRenderer(weekday, fnRender);
2531        }
2532};
2533
2534/**
2535* Sets an event handler universally across all child calendars within the group. For instance,
2536* to set the onSelect handler for all child calendars to a function called fnSelect, the call would be:
2537* <code>
2538* calGroup.wireEvent("onSelect", fnSelect);
2539* </code>
2540* @param        {String}        eventName       The name of the event to handler to set within all child calendars.
2541* @param        {Function}      fn                      The function to set into the specified event handler.
2542*/
2543SPIP.widget.CalendarGroup.prototype.wireEvent = function(eventName, fn) {
2544        for (var p=0;p<this.pages.length;++p)
2545        {
2546                var cal = this.pages[p];
2547                cal[eventName] = fn;
2548        }
2549};
2550
2551SPIP.widget.CalGrp = SPIP.widget.CalendarGroup;
2552
2553SPIP.namespace("SPIP.widget");
2554
2555/**
2556* @class
2557* Calendar is the default implementation of the SPIP.widget.Calendar_Core base class.
2558* This class is the UED-approved version of the calendar selector widget. For all documentation
2559* on the implemented methods listed here, see the documentation for SPIP.widget.Calendar_Core.
2560* @constructor
2561* @param {String}       id                      The id of the table element that will represent the calendar widget
2562* @param {String}       containerId     The id of the container element that will contain the calendar table
2563* @param {String}       monthyear       The month/year string used to set the current calendar page
2564* @param {String}       selected        A string of date values formatted using the date parser. The built-in
2565                                                                default date format is MM/DD/YYYY. Ranges are defined using
2566                                                                MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
2567                                                                Any combination of these can be combined by delimiting the string with
2568                                                                commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
2569*/
2570SPIP.widget.Calendar = function(id, containerId, monthyear, selected) {
2571        if (arguments.length > 0)
2572        {
2573                this.init(id, containerId, monthyear, selected);
2574        }
2575}
2576
2577SPIP.widget.Calendar.prototype = new SPIP.widget.Calendar_Core();
2578
2579SPIP.widget.Calendar.prototype.buildShell = function() {
2580        this.border = document.createElement("DIV");
2581        this.border.className =  this.Style.CSS_BORDER;
2582
2583        this.table = document.createElement("TABLE");
2584        this.table.cellSpacing = 0;
2585
2586        SPIP.widget.Calendar_Core.setCssClasses(this.table, [this.Style.CSS_CALENDAR]);
2587
2588        this.border.id = this.id;
2589
2590        this.buildShellHeader();
2591        this.buildShellBody();
2592        this.buildShellFooter();
2593};
2594
2595SPIP.widget.Calendar.prototype.renderShell = function() {
2596        this.border.appendChild(this.table);
2597        this.oDomContainer.appendChild(this.border);
2598        this.shellRendered = true;
2599};
2600
2601SPIP.widget.Calendar.prototype.renderHeader = function() {
2602        this.headerCell.innerHTML = "";
2603
2604        var headerContainer = document.createElement("DIV");
2605        headerContainer.className = this.Style.CSS_HEADER;
2606
2607        var linkLeft = document.createElement("A");
2608        linkLeft.href = "javascript:" + this.id + ".previousMonth()";
2609        var imgLeft = document.createElement("IMG");
2610        imgLeft.src = this.Options.NAV_ARROW_LEFT;
2611        imgLeft.className = this.Style.CSS_NAV_LEFT;
2612        linkLeft.appendChild(imgLeft);
2613
2614        var linkRight = document.createElement("A");
2615        linkRight.href = "javascript:" + this.id + ".nextMonth()";
2616        var imgRight = document.createElement("IMG");
2617        imgRight.src = this.Options.NAV_ARROW_RIGHT;
2618        imgRight.className = this.Style.CSS_NAV_RIGHT;
2619        linkRight.appendChild(imgRight);
2620
2621        headerContainer.appendChild(linkLeft);
2622        var labelMonth = document.createElement("span");
2623        labelMonth.innerHTML = this.buildMonthLabel();
2624        headerContainer.appendChild(labelMonth);
2625        headerContainer.appendChild(linkRight);
2626
2627        this.headerCell.appendChild(headerContainer);
2628};
2629
2630SPIP.widget.Cal = SPIP.widget.Calendar;
2631
2632SPIP.namespace("SPIP.widget");
2633
2634/**
2635* @class
2636* Calendar2up_Cal is the default implementation of the Calendar_Core base class, when used
2637* in a 2-up view. This class is the UED-approved version of the calendar selector widget. For all documentation
2638* on the implemented methods listed here, see the documentation for Calendar_Core. This class
2639* has some special attributes that only apply to calendars rendered within the calendar group implementation.
2640* There should be no reason to instantiate this class directly.
2641* @constructor
2642* @param {String}       id                      The id of the table element that will represent the calendar widget
2643* @param {String}       containerId     The id of the container element that will contain the calendar table
2644* @param {String}       monthyear       The month/year string used to set the current calendar page
2645* @param {String}       selected        A string of date values formatted using the date parser. The built-in
2646                                                                default date format is MM/DD/YYYY. Ranges are defined using
2647                                                                MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
2648                                                                Any combination of these can be combined by delimiting the string with
2649                                                                commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
2650*/
2651SPIP.widget.Calendar2up_Cal = function(id, containerId, monthyear, selected) {
2652        if (arguments.length > 0)
2653        {
2654                this.init(id, containerId, monthyear, selected);
2655        }
2656}
2657
2658SPIP.widget.Calendar2up_Cal.prototype = new SPIP.widget.Calendar_Core();
2659
2660/**
2661* Renders the header for each individual calendar in the 2-up view. More
2662* specifically, this method handles the placement of left and right arrows for
2663* navigating between calendar pages.
2664*/
2665SPIP.widget.Calendar2up_Cal.prototype.renderHeader = function() {
2666        this.headerCell.innerHTML = "";
2667
2668        var headerContainer = document.createElement("DIV");
2669        headerContainer.className = this.Style.CSS_HEADER;
2670
2671        if (this.index == 0) {
2672                var linkLeft = document.createElement("A");
2673                linkLeft.href = "javascript:void(null)";
2674                SPIP.util.Event.addListener(linkLeft, "click", this.parent.doPreviousMonth, this.parent);
2675                var imgLeft = document.createElement("IMG");
2676                imgLeft.src = this.Options.NAV_ARROW_LEFT;
2677                imgLeft.className = this.Style.CSS_NAV_LEFT;
2678                linkLeft.appendChild(imgLeft);
2679                headerContainer.appendChild(linkLeft);
2680        }
2681
2682        var labelMonth = document.createElement("span");
2683        labelMonth.innerHTML = this.buildMonthLabel();
2684        /*headerContainer.appendChild(document.createTextNode(this.buildMonthLabel()));*/
2685        headerContainer.appendChild(labelMonth);
2686
2687        if (this.index == 1) {
2688                var linkRight = document.createElement("A");
2689                linkRight.href = "javascript:void(null)";
2690                SPIP.util.Event.addListener(linkRight, "click", this.parent.doNextMonth, this.parent);
2691                var imgRight = document.createElement("IMG");
2692                imgRight.src = this.Options.NAV_ARROW_RIGHT;
2693                imgRight.className = this.Style.CSS_NAV_RIGHT;
2694                linkRight.appendChild(imgRight);
2695                headerContainer.appendChild(linkRight);
2696        }
2697
2698        this.headerCell.appendChild(headerContainer);
2699};
2700
2701
2702
2703
2704/**
2705* @class
2706* Calendar2up is the default implementation of the Calendar2up_Core base class, when used
2707* in a 2-up view. This class is the UED-approved version of the 2-up calendar selector widget. For all documentation
2708* on the implemented methods listed here, see the documentation for Calendar2up_Core.
2709* @constructor
2710* @param {String}       id                      The id of the table element that will represent the calendar widget
2711* @param {String}       containerId     The id of the container element that will contain the calendar table
2712* @param {String}       monthyear       The month/year string used to set the current calendar page
2713* @param {String}       selected        A string of date values formatted using the date parser. The built-in
2714                                                                default date format is MM/DD/YYYY. Ranges are defined using
2715                                                                MM/DD/YYYY-MM/DD/YYYY. Month/day combinations are defined using MM/DD.
2716                                                                Any combination of these can be combined by delimiting the string with
2717                                                                commas. Example: "12/24/2005,12/25,1/18/2006-1/21/2006"
2718*/
2719SPIP.widget.Calendar2up = function(id, containerId, monthyear, selected) {
2720        if (arguments.length > 0)
2721        {
2722                this.buildWrapper(containerId);
2723                this.init(2, id, containerId, monthyear, selected);
2724        }
2725}
2726
2727SPIP.widget.Calendar2up.prototype = new SPIP.widget.CalendarGroup();
2728
2729/**
2730* Implementation of CalendarGroup.constructChild that ensures that child calendars of
2731* Calendar2up will be of type Calendar2up_Cal.
2732*/
2733SPIP.widget.Calendar2up.prototype.constructChild = function(id,containerId,monthyear,selected) {
2734        var cal = new SPIP.widget.Calendar2up_Cal(id,containerId,monthyear,selected);
2735        return cal;
2736};
2737
2738/**
2739* Builds the wrapper container for the 2-up calendar.
2740* @param {String} containerId   The id of the outer container element.
2741*/
2742SPIP.widget.Calendar2up.prototype.buildWrapper = function(containerId) {
2743        var outerContainer = document.getElementById(containerId);
2744
2745        outerContainer.className = "calcontainer";
2746        /*outerContainer.innerHTML="";
2747        if (SPIP.widget.Calendar_Core._getBrowser() == "ie") {
2748                outerContainer.innerHTML="<iframe src='about:blank' scrolling='no' frameborder='0' style='position:absolute;left:0px;top:0px;z-index:0;' class='califrame'></iframe>";
2749        }*/
2750
2751        var innerContainer = document.createElement("DIV");
2752        innerContainer.className = "calbordered";
2753        innerContainer.id = containerId + "_inner";
2754
2755        var cal1Container = document.createElement("DIV");
2756        cal1Container.id = containerId + "_0";
2757        cal1Container.className = "cal2up";
2758        cal1Container.style.marginRight = "10px";
2759        cal1Container.style.zIndex="99";
2760        //cal1Container.style.cssFloat="left";
2761
2762        var cal2Container = document.createElement("DIV");
2763        cal2Container.id = containerId + "_1";
2764        cal2Container.className = "cal2up2";
2765        cal1Container.style.zIndex="99";
2766        //cal2Container.style.cssFloat="left";
2767
2768        outerContainer.appendChild(innerContainer);
2769        innerContainer.appendChild(cal1Container);
2770        innerContainer.appendChild(cal2Container);
2771
2772        this.innerContainer = innerContainer;
2773        this.outerContainer = outerContainer;
2774}
2775
2776/**
2777* Renders the 2-up calendar.
2778*/
2779SPIP.widget.Calendar2up.prototype.render = function() {
2780        this.renderHeader();
2781        SPIP.widget.CalendarGroup.prototype.render.call(this);
2782        this.renderFooter();
2783};
2784
2785/**
2786* Renders the header located at the top of the container for the 2-up calendar.
2787*/
2788SPIP.widget.Calendar2up.prototype.renderHeader = function() {
2789        if (! this.title) {
2790                this.title = "";
2791        }
2792        if (! this.titleDiv)
2793        {
2794                this.titleDiv = document.createElement("DIV");
2795                if (this.title == "")
2796                {
2797                        this.titleDiv.style.display="none";
2798                }
2799        }
2800
2801        this.titleDiv.className = "title";
2802        this.titleDiv.innerHTML = this.title;
2803
2804        if (this.outerContainer.style.position == "absolute")
2805        {
2806                var linkClose = document.createElement("A");
2807                linkClose.href = "javascript:void(null)";
2808                SPIP.util.Event.addListener(linkClose, "click", this.hide, this);
2809
2810                var imgClose = document.createElement("IMG");
2811                imgClose.src = this.pages[0].Options.IMG_CLOSE;
2812                imgClose.className = this.pages[0].Options.IMG_CLOSE_CLASS;
2813
2814                linkClose.appendChild(imgClose);
2815
2816                this.linkClose = linkClose;
2817
2818                this.titleDiv.appendChild(linkClose);
2819        }
2820
2821        this.innerContainer.insertBefore(this.titleDiv, this.innerContainer.firstChild);
2822}
2823
2824/**
2825* Hides the 2-up calendar's outer container from view.
2826*/
2827SPIP.widget.Calendar2up.prototype.hide = function(e, cal) {
2828        if (! cal)
2829        {
2830                cal = this;
2831        }
2832        cal.outerContainer.style.display = "none";
2833}
2834
2835/**
2836* Renders a footer for the 2-up calendar container. By default, this method is
2837* unimplemented.
2838*/
2839SPIP.widget.Calendar2up.prototype.renderFooter = function() {}
2840
2841SPIP.widget.Cal2up = SPIP.widget.Calendar2up;
Note: See TracBrowser for help on using the repository browser.