Bug 20489 Configure illegal file characters https://bugzilla.wikimedia.org/show_bug...
[mediawiki.git] / js2 / mwEmbed / jquery / plugins / jquery.datePicker.js
blobf576c379dc2fa549f2f5ce6fb5b73251955d2ef9
1 /**
2  * Copyright (c) 2007 Kelvin Luck (http://www.kelvinluck.com/)
3  * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
4  * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
5  *
6  * $Id: jquery.datePicker.js 3739 2007-10-25 13:55:30Z kelvin.luck $
7  **/
9 (function($){
11         $.fn.extend({
12 /**
13  * Render a calendar table into any matched elements.
14  *
15  * @param Object s (optional) Customize your calendars.
16  * @option Number month The month to render (NOTE that months are zero based). Default is today's month.
17  * @option Number year The year to render. Default is today's year.
18  * @option Function renderCallback A reference to a function that is called as each cell is rendered and which can add classes and event listeners to the created nodes. Default is no callback.
19  * @option Number showHeader Whether or not to show the header row, possible values are: $.dpConst.SHOW_HEADER_NONE (no header), $.dpConst.SHOW_HEADER_SHORT (first letter of each day) and $.dpConst.SHOW_HEADER_LONG (full name of each day). Default is $.dpConst.SHOW_HEADER_SHORT.
20  * @option String hoverClass The class to attach to each cell when you hover over it (to allow you to use hover effects in IE6 which doesn't support the :hover pseudo-class on elements other than links). Default is dp-hover. Pass false if you don't want a hover class.
21  * @type jQuery
22  * @name renderCalendar
23  * @cat plugins/datePicker
24  * @author Kelvin Luck (http://www.kelvinluck.com/)
25  *
26  * @example $('#calendar-me').renderCalendar({month:0, year:2007});
27  * @desc Renders a calendar displaying January 2007 into the element with an id of calendar-me.
28  *
29  * @example
30  * var testCallback = function($td, thisDate, month, year)
31  * {
32  * if ($td.is('.current-month') && thisDate.getDay() == 4) {
33  *              var d = thisDate.getDate();
34  *              $td.bind(
35  *                      'click',
36  *                      function()
37  *                      {
38  *                              alert('You clicked on ' + d + '/' + (Number(month)+1) + '/' + year);
39  *                      }
40  *              ).addClass('thursday');
41  *      } else if (thisDate.getDay() == 5) {
42  *              $td.html('Friday the ' + $td.html() + 'th');
43  *      }
44  * }
45  * $('#calendar-me').renderCalendar({month:0, year:2007, renderCallback:testCallback});
46  *
47  * @desc Renders a calendar displaying January 2007 into the element with an id of calendar-me. Every Thursday in the current month has a class of "thursday" applied to it, is clickable and shows an alert when clicked. Every Friday on the calendar has the number inside replaced with text.
48  **/
49                 renderCalendar  :   function(s)
50                 {
51                         var dc = function(a)
52                         {
53                                 return document.createElement(a);
54                         };
56                         s = $.extend(
57                                 {
58                                         month                   : null,
59                                         year                    : null,
60                                         renderCallback  : null,
61                                         showHeader              : $.dpConst.SHOW_HEADER_SHORT,
62                                         dpController    : null,
63                                         hoverClass              : 'dp-hover'
64                                 }
65                                 , s
66                         );
68                         if (s.showHeader != $.dpConst.SHOW_HEADER_NONE) {
69                                 var headRow = $(dc('tr'));
70                                 for (var i=Date.firstDayOfWeek; i<Date.firstDayOfWeek+7; i++) {
71                                         var weekday = i%7;
72                                         var day = Date.dayNames[weekday];
73                                         headRow.append(
74                                                 jQuery(dc('th')).attr({'scope':'col', 'abbr':day, 'title':day, 'class':(weekday == 0 || weekday == 6 ? 'weekend' : 'weekday')}).html(s.showHeader == $.dpConst.SHOW_HEADER_SHORT ? day.substr(0, 1) : day)
75                                         );
76                                 }
77                         };
79                         var calendarTable = $(dc('table'))
80                                                                         .attr(
81                                                                                 {
82                                                                                         'cellspacing':2,
83                                                                                         'className':'jCalendar'
84                                                                                 }
85                                                                         )
86                                                                         .append(
87                                                                                 (s.showHeader != $.dpConst.SHOW_HEADER_NONE ?
88                                                                                         $(dc('thead'))
89                                                                                                 .append(headRow)
90                                                                                         :
91                                                                                         dc('thead')
92                                                                                 )
93                                                                         );
94                         var tbody = $(dc('tbody'));
96                         var today = (new Date()).zeroTime();
98                         var month = s.month == undefined ? today.getMonth() : s.month;
99                         var year = s.year || today.getFullYear();
101                         var currentDate = new Date(year, month, 1);
104                         var firstDayOffset = Date.firstDayOfWeek - currentDate.getDay() + 1;
105                         if (firstDayOffset > 1) firstDayOffset -= 7;
106                         var weeksToDraw = Math.ceil(( (-1*firstDayOffset+1) + currentDate.getDaysInMonth() ) /7);
107                         currentDate.addDays(firstDayOffset-1);
109                         var doHover = function()
110                         {
111                                 if (s.hoverClass) {
112                                         $(this).addClass(s.hoverClass);
113                                 }
114                         };
115                         var unHover = function()
116                         {
117                                 if (s.hoverClass) {
118                                         $(this).removeClass(s.hoverClass);
119                                 }
120                         };
122                         var w = 0;
123                         while (w++<weeksToDraw) {
124                                 var r = jQuery(dc('tr'));
125                                 for (var i=0; i<7; i++) {
126                                         var thisMonth = currentDate.getMonth() == month;
127                                         var d = $(dc('td'))
128                                                                 .text(currentDate.getDate() + '')
129                                                                 .attr('className', (thisMonth ? 'current-month ' : 'other-month ') +
130                                                                                                         (currentDate.isWeekend() ? 'weekend ' : 'weekday ') +
131                                                                                                         (thisMonth && currentDate.getTime() == today.getTime() ? 'today ' : '')
132                                                                 )
133                                                                 .hover(doHover, unHover)
134                                                         ;
135                                         if (s.renderCallback) {
136                                                 s.renderCallback(d, currentDate, month, year);
137                                         }
138                                         r.append(d);
139                                         currentDate.addDays(1);
140                                 }
141                                 tbody.append(r);
142                         }
143                         calendarTable.append(tbody);
145                         return this.each(
146                                 function()
147                                 {
148                                         $(this).empty().append(calendarTable);
149                                 }
150                         );
151                 },
153  * Create a datePicker associated with each of the matched elements.
155  * The matched element will receive a few custom events with the following signatures:
157  * dateSelected(event, date, $td, status)
158  * Triggered when a date is selected. event is a reference to the event, date is the Date selected, $td is a jquery object wrapped around the TD that was clicked on and status is whether the date was selected (true) or deselected (false)
160  * dpClosed(event, selected)
161  * Triggered when the date picker is closed. event is a reference to the event and selected is an Array containing Date objects.
163  * dpMonthChanged(event, displayedMonth, displayedYear)
164  * Triggered when the month of the popped up calendar is changed. event is a reference to the event, displayedMonth is the number of the month now displayed (zero based) and displayedYear is the year of the month.
166  * dpDisplayed(event, $datePickerDiv)
167  * Triggered when the date picker is created. $datePickerDiv is the div containing the date picker. Use this event to add custom content/ listeners to the popped up date picker.
169  * @param Object s (optional) Customize your date pickers.
170  * @option Number month The month to render when the date picker is opened (NOTE that months are zero based). Default is today's month.
171  * @option Number year The year to render when the date picker is opened. Default is today's year.
172  * @option String startDate The first date date can be selected.
173  * @option String endDate The last date that can be selected.
174  * @option Boolean inline Whether to create the datePicker as inline (e.g. always on the page) or as a model popup. Default is false (== modal popup)
175  * @option Boolean createButton Whether to create a .dp-choose-date anchor directly after the matched element which when clicked will trigger the showing of the date picker. Default is true.
176  * @option Boolean showYearNavigation Whether to display buttons which allow the user to navigate through the months a year at a time. Default is true.
177  * @option Boolean closeOnSelect Whether to close the date picker when a date is selected. Default is true.
178  * @option Boolean displayClose Whether to create a "Close" button within the date picker popup. Default is false.
179  * @option Boolean selectMultiple Whether a user should be able to select multiple dates with this date picker. Default is false.
180  * @option Boolean clickInput If the matched element is an input type="text" and this option is true then clicking on the input will cause the date picker to appear.
181  * @option Number verticalPosition The vertical alignment of the popped up date picker to the matched element. One of $.dpConst.POS_TOP and $.dpConst.POS_BOTTOM. Default is $.dpConst.POS_TOP.
182  * @option Number horizontalPosition The horizontal alignment of the popped up date picker to the matched element. One of $.dpConst.POS_LEFT and $.dpConst.POS_RIGHT.
183  * @option Number verticalOffset The number of pixels offset from the defined verticalPosition of this date picker that it should pop up in. Default in 0.
184  * @option Number horizontalOffset The number of pixels offset from the defined horizontalPosition of this date picker that it should pop up in. Default in 0.
185  * @option (Function|Array) renderCallback A reference to a function (or an array of separate functions) that is called as each cell is rendered and which can add classes and event listeners to the created nodes. Each callback function will receive four arguments; a jquery object wrapping the created TD, a Date object containing the date this TD represents, a number giving the currently rendered month and a number giving the currently rendered year. Default is no callback.
186  * @option String hoverClass The class to attach to each cell when you hover over it (to allow you to use hover effects in IE6 which doesn't support the :hover pseudo-class on elements other than links). Default is dp-hover. Pass false if you don't want a hover class.
187  * @type jQuery
188  * @name datePicker
189  * @cat plugins/datePicker
190  * @author Kelvin Luck (http://www.kelvinluck.com/)
192  * @example $('input.date-picker').datePicker();
193  * @desc Creates a date picker button next to all matched input elements. When the button is clicked on the value of the selected date will be placed in the corresponding input (formatted according to Date.format).
195  * @example demo/index.html
196  * @desc See the projects homepage for many more complex examples...
197  **/
198                 datePicker : function(s)
199                 {
200                         if (!$.event._dpCache) $.event._dpCache = [];
202                         // initialise the date picker controller with the relevant settings...
203                         s = $.extend(
204                                 {
205                                         month                           : undefined,
206                                         year                            : undefined,
207                                         startDate                       : undefined,
208                                         endDate                         : undefined,
209                                         inline                          : false,
210                                         renderCallback          : [],
211                                         createButton            : true,
212                                         showYearNavigation      : true,
213                                         closeOnSelect           : true,
214                                         displayClose            : false,
215                                         selectMultiple          : false,
216                                         clickInput                      : false,
217                                         verticalPosition        : $.dpConst.POS_TOP,
218                                         horizontalPosition      : $.dpConst.POS_LEFT,
219                                         verticalOffset          : 0,
220                                         horizontalOffset        : 0,
221                                         hoverClass                      : 'dp-hover'
222                                 }
223                                 , s
224                         );
226                         return this.each(
227                                 function()
228                                 {
229                                         var $this = $(this);
230                                         var alreadyExists = true;
232                                         if (!this._dpId) {
233                                                 this._dpId = $.event.guid++;
234                                                 $.event._dpCache[this._dpId] = new DatePicker(this);
235                                                 alreadyExists = false;
236                                         }
238                                         if (s.inline) {
239                                                 s.createButton = false;
240                                                 s.displayClose = false;
241                                                 s.closeOnSelect = false;
242                                                 $this.empty();
243                                         }
245                                         var controller = $.event._dpCache[this._dpId];
247                                         controller.init(s);
249                                         if (!alreadyExists && s.createButton) {
250                                                 // create it!
251                                                 controller.button = $('<a href="#" class="dp-choose-date" title="' + $.dpText.TEXT_CHOOSE_DATE + '">' + $.dpText.TEXT_CHOOSE_DATE + '</a>')
252                                                                 .bind(
253                                                                         'click',
254                                                                         function()
255                                                                         {
256                                                                                 $this.dpDisplay(this);
257                                                                                 this.blur();
258                                                                                 return false;
259                                                                         }
260                                                                 );
261                                                 $this.after(controller.button);
262                                         }
264                                         if (!alreadyExists && $this.is(':text')) {
265                                                 $this
266                                                         .bind(
267                                                                 'dateSelected',
268                                                                 function(e, selectedDate, $td)
269                                                                 {
270                                                                         this.value = selectedDate.asString();
271                                                                 }
272                                                         ).bind(
273                                                                 'change',
274                                                                 function()
275                                                                 {
276                                                                         var d = Date.fromString(this.value);
277                                                                         if (d) {
278                                                                                 controller.setSelected(d, true, true);
279                                                                         }
280                                                                 }
281                                                         );
282                                                 if (s.clickInput) {
283                                                         $this.bind(
284                                                                 'click',
285                                                                 function()
286                                                                 {
287                                                                         $this.dpDisplay();
288                                                                 }
289                                                         );
290                                                 }
291                                                 var d = Date.fromString(this.value);
292                                                 if (this.value != '' && d) {
293                                                         controller.setSelected(d, true, true);
294                                                 }
295                                         }
297                                         $this.addClass('dp-applied');
299                                 }
300                         )
301                 },
303  * Disables or enables this date picker
305  * @param Boolean s Whether to disable (true) or enable (false) this datePicker
306  * @type jQuery
307  * @name dpSetDisabled
308  * @cat plugins/datePicker
309  * @author Kelvin Luck (http://www.kelvinluck.com/)
311  * @example $('.date-picker').datePicker();
312  * $('.date-picker').dpSetDisabled(true);
313  * @desc Prevents this date picker from displaying and adds a class of dp-disabled to it (and it's associated button if it has one) for styling purposes. If the matched element is an input field then it will also set the disabled attribute to stop people directly editing the field.
314  **/
315                 dpSetDisabled : function(s)
316                 {
317                         return _w.call(this, 'setDisabled', s);
318                 },
320  * Updates the first selectable date for any date pickers on any matched elements.
322  * @param String d A string representing the first selectable date (formatted according to Date.format).
323  * @type jQuery
324  * @name dpSetStartDate
325  * @cat plugins/datePicker
326  * @author Kelvin Luck (http://www.kelvinluck.com/)
328  * @example $('.date-picker').datePicker();
329  * $('.date-picker').dpSetStartDate('01/01/2000');
330  * @desc Creates a date picker associated with all elements with a class of "date-picker" then sets the first selectable date for each of these to the first day of the millenium.
331  **/
332                 dpSetStartDate : function(d)
333                 {
334                         return _w.call(this, 'setStartDate', d);
335                 },
337  * Updates the last selectable date for any date pickers on any matched elements.
339  * @param String d A string representing the last selectable date (formatted according to Date.format).
340  * @type jQuery
341  * @name dpSetEndDate
342  * @cat plugins/datePicker
343  * @author Kelvin Luck (http://www.kelvinluck.com/)
345  * @example $('.date-picker').datePicker();
346  * $('.date-picker').dpSetEndDate('01/01/2010');
347  * @desc Creates a date picker associated with all elements with a class of "date-picker" then sets the last selectable date for each of these to the first Janurary 2010.
348  **/
349                 dpSetEndDate : function(d)
350                 {
351                         return _w.call(this, 'setEndDate', d);
352                 },
354  * Gets a list of Dates currently selected by this datePicker. This will be an empty array if no dates are currently selected or NULL if there is no datePicker associated with the matched element.
356  * @type Array
357  * @name dpGetSelected
358  * @cat plugins/datePicker
359  * @author Kelvin Luck (http://www.kelvinluck.com/)
361  * @example $('.date-picker').datePicker();
362  * alert($('.date-picker').dpGetSelected());
363  * @desc Will alert an empty array (as nothing is selected yet)
364  **/
365                 dpGetSelected : function()
366                 {
367                         var c = _getController(this[0]);
368                         if (c) {
369                                 return c.getSelected();
370                         }
371                         return null;
372                 },
374  * Selects or deselects a date on any matched element's date pickers. Deselcting is only useful on date pickers where selectMultiple==true. Selecting will only work if the passed date is within the startDate and endDate boundries for a given date picker.
376  * @param String d A string representing the date you want to select (formatted according to Date.format).
377  * @param Boolean v Whether you want to select (true) or deselect (false) this date. Optional - default = true.
378  * @param Boolean m Whether you want the date picker to open up on the month of this date when it is next opened. Optional - default = true.
379  * @type jQuery
380  * @name dpSetSelected
381  * @cat plugins/datePicker
382  * @author Kelvin Luck (http://www.kelvinluck.com/)
384  * @example $('.date-picker').datePicker();
385  * $('.date-picker').dpSetSelected('01/01/2010');
386  * @desc Creates a date picker associated with all elements with a class of "date-picker" then sets the selected date on these date pickers to the first Janurary 2010. When the date picker is next opened it will display Janurary 2010.
387  **/
388                 dpSetSelected : function(d, v, m)
389                 {
390                         if (v == undefined) v=true;
391                         if (m == undefined) m=true;
392                         return _w.call(this, 'setSelected', Date.fromString(d), v, m);
393                 },
395  * Sets the month that will be displayed when the date picker is next opened. If the passed month is before startDate then the month containing startDate will be displayed instead. If the passed month is after endDate then the month containing the endDate will be displayed instead.
397  * @param Number m The month you want the date picker to display. Optional - defaults to the currently displayed month.
398  * @param Number y The year you want the date picker to display. Optional - defaults to the currently displayed year.
399  * @type jQuery
400  * @name dpSetDisplayedMonth
401  * @cat plugins/datePicker
402  * @author Kelvin Luck (http://www.kelvinluck.com/)
404  * @example $('.date-picker').datePicker();
405  * $('.date-picker').dpSetDisplayedMonth(10, 2008);
406  * @desc Creates a date picker associated with all elements with a class of "date-picker" then sets the selected date on these date pickers to the first Janurary 2010. When the date picker is next opened it will display Janurary 2010.
407  **/
408                 dpSetDisplayedMonth : function(m, y)
409                 {
410                         return _w.call(this, 'setDisplayedMonth', Number(m), Number(y));
411                 },
413  * Displays the date picker associated with the matched elements. Since only one date picker can be displayed at once then the date picker associated with the last matched element will be the one that is displayed.
415  * @param HTMLElement e An element that you want the date picker to pop up relative in position to. Optional - default behaviour is to pop up next to the element associated with this date picker.
416  * @type jQuery
417  * @name dpDisplay
418  * @cat plugins/datePicker
419  * @author Kelvin Luck (http://www.kelvinluck.com/)
421  * @example $('#date-picker').datePicker();
422  * $('#date-picker').dpDisplay();
423  * @desc Creates a date picker associated with the element with an id of date-picker and then causes it to pop up.
424  **/
425                 dpDisplay : function(e)
426                 {
427                         return _w.call(this, 'display', e);
428                 },
430  * Sets a function or array of functions that is called when each TD of the date picker popup is rendered to the page
432  * @param (Function|Array) a A function or an array of functions that are called when each td is rendered. Each function will receive four arguments; a jquery object wrapping the created TD, a Date object containing the date this TD represents, a number giving the currently rendered month and a number giving the currently rendered year.
433  * @type jQuery
434  * @name dpSetRenderCallback
435  * @cat plugins/datePicker
436  * @author Kelvin Luck (http://www.kelvinluck.com/)
438  * @example $('#date-picker').datePicker();
439  * $('#date-picker').dpSetRenderCallback(function($td, thisDate, month, year)
440  * {
441  *      // do stuff as each td is rendered dependant on the date in the td and the displayed month and year
442  * });
443  * @desc Creates a date picker associated with the element with an id of date-picker and then creates a function which is called as each td is rendered when this date picker is displayed.
444  **/
445                 dpSetRenderCallback : function(a)
446                 {
447                         return _w.call(this, 'setRenderCallback', a);
448                 },
450  * Sets the position that the datePicker will pop up (relative to it's associated element)
452  * @param Number v The vertical alignment of the created date picker to it's associated element. Possible values are $.dpConst.POS_TOP and $.dpConst.POS_BOTTOM
453  * @param Number h The horizontal alignment of the created date picker to it's associated element. Possible values are $.dpConst.POS_LEFT and $.dpConst.POS_RIGHT
454  * @type jQuery
455  * @name dpSetPosition
456  * @cat plugins/datePicker
457  * @author Kelvin Luck (http://www.kelvinluck.com/)
459  * @example $('#date-picker').datePicker();
460  * $('#date-picker').dpSetPosition($.dpConst.POS_BOTTOM, $.dpConst.POS_RIGHT);
461  * @desc Creates a date picker associated with the element with an id of date-picker and makes it so that when this date picker pops up it will be bottom and right aligned to the #date-picker element.
462  **/
463                 dpSetPosition : function(v, h)
464                 {
465                         return _w.call(this, 'setPosition', v, h);
466                 },
468  * Sets the offset that the popped up date picker will have from it's default position relative to it's associated element (as set by dpSetPosition)
470  * @param Number v The vertical offset of the created date picker.
471  * @param Number h The horizontal offset of the created date picker.
472  * @type jQuery
473  * @name dpSetOffset
474  * @cat plugins/datePicker
475  * @author Kelvin Luck (http://www.kelvinluck.com/)
477  * @example $('#date-picker').datePicker();
478  * $('#date-picker').dpSetOffset(-20, 200);
479  * @desc Creates a date picker associated with the element with an id of date-picker and makes it so that when this date picker pops up it will be 20 pixels above and 200 pixels to the right of it's default position.
480  **/
481                 dpSetOffset : function(v, h)
482                 {
483                         return _w.call(this, 'setOffset', v, h);
484                 },
486  * Closes the open date picker associated with this element.
488  * @type jQuery
489  * @name dpClose
490  * @cat plugins/datePicker
491  * @author Kelvin Luck (http://www.kelvinluck.com/)
493  * @example $('.date-pick')
494  *              .datePicker()
495  *              .bind(
496  *                      'focus',
497  *                      function()
498  *                      {
499  *                              $(this).dpDisplay();
500  *                      }
501  *              ).bind(
502  *                      'blur',
503  *                      function()
504  *                      {
505  *                              $(this).dpClose();
506  *                      }
507  *              );
508  * @desc Creates a date picker and makes it appear when the relevant element is focused and disappear when it is blurred.
509  **/
510                 dpClose : function()
511                 {
512                         return _w.call(this, '_closeCalendar', false, this[0]);
513                 },
514                 // private function called on unload to clean up any expandos etc and prevent memory links...
515                 _dpDestroy : function()
516                 {
517                         // TODO - implement this?
518                 }
519         });
521         // private internal function to cut down on the amount of code needed where we forward
522         // dp* methods on the jQuery object on to the relevant DatePicker controllers...
523         var _w = function(f, a1, a2, a3)
524         {
525                 return this.each(
526                         function()
527                         {
528                                 var c = _getController(this);
529                                 if (c) {
530                                         c[f](a1, a2, a3);
531                                 }
532                         }
533                 );
534         };
536         function DatePicker(ele)
537         {
538                 this.ele = ele;
540                 // initial values...
541                 this.displayedMonth             =       null;
542                 this.displayedYear              =       null;
543                 this.startDate                  =       null;
544                 this.endDate                    =       null;
545                 this.showYearNavigation =       null;
546                 this.closeOnSelect              =       null;
547                 this.displayClose               =       null;
548                 this.selectMultiple             =       null;
549                 this.verticalPosition   =       null;
550                 this.horizontalPosition =       null;
551                 this.verticalOffset             =       null;
552                 this.horizontalOffset   =       null;
553                 this.button                             =       null;
554                 this.renderCallback             =       [];
555                 this.selectedDates              =       {};
556                 this.inline                             =       null;
557                 this.context                    =       '#dp-popup';
558         };
559         $.extend(
560                 DatePicker.prototype,
561                 {
562                         init : function(s)
563                         {
564                                 this.setStartDate(s.startDate);
565                                 this.setEndDate(s.endDate);
566                                 this.setDisplayedMonth(Number(s.month), Number(s.year));
567                                 this.setRenderCallback(s.renderCallback);
568                                 this.showYearNavigation = s.showYearNavigation;
569                                 this.closeOnSelect = s.closeOnSelect;
570                                 this.displayClose = s.displayClose;
571                                 this.selectMultiple = s.selectMultiple;
572                                 this.verticalPosition = s.verticalPosition;
573                                 this.horizontalPosition = s.horizontalPosition;
574                                 this.hoverClass = s.hoverClass;
575                                 this.setOffset(s.verticalOffset, s.horizontalOffset);
576                                 this.inline = s.inline;
577                                 if (this.inline) {
578                                         this.context = this.ele;
579                                         this.display();
580                                 }
581                         },
582                         setStartDate : function(d)
583                         {
584                                 if (d) {
585                                         this.startDate = Date.fromString(d);
586                                 }
587                                 if (!this.startDate) {
588                                         this.startDate = (new Date()).zeroTime();
589                                 }
590                                 this.setDisplayedMonth(this.displayedMonth, this.displayedYear);
591                         },
592                         setEndDate : function(d)
593                         {
594                                 if (d) {
595                                         this.endDate = Date.fromString(d);
596                                 }
597                                 if (!this.endDate) {
598                                         this.endDate = (new Date('12/31/2999')); // using the JS Date.parse function which expects mm/dd/yyyy
599                                 }
600                                 if (this.endDate.getTime() < this.startDate.getTime()) {
601                                         this.endDate = this.startDate;
602                                 }
603                                 this.setDisplayedMonth(this.displayedMonth, this.displayedYear);
604                         },
605                         setPosition : function(v, h)
606                         {
607                                 this.verticalPosition = v;
608                                 this.horizontalPosition = h;
609                         },
610                         setOffset : function(v, h)
611                         {
612                                 this.verticalOffset = parseInt(v) || 0;
613                                 this.horizontalOffset = parseInt(h) || 0;
614                         },
615                         setDisabled : function(s)
616                         {
617                                 $e = $(this.ele);
618                                 $e[s ? 'addClass' : 'removeClass']('dp-disabled');
619                                 if (this.button) {
620                                         $but = $(this.button);
621                                         $but[s ? 'addClass' : 'removeClass']('dp-disabled');
622                                         $but.attr('title', s ? '' : $.dpText.TEXT_CHOOSE_DATE);
623                                 }
624                                 if ($e.is(':text')) {
625                                         $e.attr('disabled', s ? 'disabled' : '');
626                                 }
627                         },
628                         setDisplayedMonth : function(m, y)
629                         {
630                                 if (this.startDate == undefined || this.endDate == undefined) {
631                                         return;
632                                 }
633                                 var s = new Date(this.startDate.getTime());
634                                 s.setDate(1);
635                                 var e = new Date(this.endDate.getTime());
636                                 e.setDate(1);
638                                 var t;
639                                 if ((!m && !y) || (isNaN(m) && isNaN(y))) {
640                                         // no month or year passed - default to current month
641                                         t = new Date().zeroTime();
642                                         t.setDate(1);
643                                 } else if (isNaN(m)) {
644                                         // just year passed in - presume we want the displayedMonth
645                                         t = new Date(y, this.displayedMonth, 1);
646                                 } else if (isNaN(y)) {
647                                         // just month passed in - presume we want the displayedYear
648                                         t = new Date(this.displayedYear, m, 1);
649                                 } else {
650                                         // year and month passed in - that's the date we want!
651                                         t = new Date(y, m, 1)
652                                 }
654                                 // check if the desired date is within the range of our defined startDate and endDate
655                                 if (t.getTime() < s.getTime()) {
656                                         t = s;
657                                 } else if (t.getTime() > e.getTime()) {
658                                         t = e;
659                                 }
660                                 this.displayedMonth = t.getMonth();
661                                 this.displayedYear = t.getFullYear();
662                         },
663                         setSelected : function(d, v, moveToMonth)
664                         {
665                                 if (this.selectMultiple == false) {
666                                         this.selectedDates = {};
667                                         $('td.selected', this.context).removeClass('selected');
668                                 }
669                                 if (moveToMonth) {
670                                         this.setDisplayedMonth(d.getMonth(), d.getFullYear());
671                                 }
672                                 this.selectedDates[d.toString()] = v;
673                         },
674                         isSelected : function(d)
675                         {
676                                 return this.selectedDates[d.toString()];
677                         },
678                         getSelected : function()
679                         {
680                                 var r = [];
681                                 for(s in this.selectedDates) {
682                                         if (this.selectedDates[s] == true) {
683                                                 r.push(Date.parse(s));
684                                         }
685                                 }
686                                 return r;
687                         },
688                         display : function(eleAlignTo)
689                         {
690                                 if ($(this.ele).is('.dp-disabled')) return;
692                                 eleAlignTo = eleAlignTo || this.ele;
693                                 var c = this;
694                                 var $ele = $(eleAlignTo);
695                                 var eleOffset = $ele.offset();
697                                 var $createIn;
698                                 var attrs;
699                                 var attrsCalendarHolder;
700                                 var cssRules;
702                                 if (c.inline) {
703                                         $createIn = $(this.ele);
704                                         attrs = {
705                                                 'id'            :       'calendar-' + this.ele._dpId,
706                                                 'className'     :       'dp-popup dp-popup-inline'
707                                         };
708                                         cssRules = {
709                                         };
710                                 } else {
711                                         $createIn = $('body');
712                                         attrs = {
713                                                 'id'            :       'dp-popup',
714                                                 'className'     :       'dp-popup'
715                                         };
716                                         cssRules = {
717                                                 'top'   :       eleOffset.top + c.verticalOffset,
718                                                 'left'  :       eleOffset.left + c.horizontalOffset
719                                         };
721                                         var _checkMouse = function(e)
722                                         {
723                                                 var el = e.target;
724                                                 var cal = $('#dp-popup')[0];
726                                                 while (true){
727                                                         if (el == cal) {
728                                                                 return true;
729                                                         } else if (el == document) {
730                                                                 c._closeCalendar();
731                                                                 return false;
732                                                         } else {
733                                                                 el = $(el).parent()[0];
734                                                         }
735                                                 }
736                                         };
737                                         this._checkMouse = _checkMouse;
739                                         this._closeCalendar(true);
740                                 }
743                                 $createIn
744                                         .append(
745                                                 $('<div></div>')
746                                                         .attr(attrs)
747                                                         .css(cssRules)
748                                                         .append(
749                                                                 $('<h2></h2>'),
750                                                                 $('<div class="dp-nav-prev"></div>')
751                                                                         .append(
752                                                                                 $('<a class="dp-nav-prev-year" href="#" title="' + $.dpText.TEXT_PREV_YEAR + '">&lt;&lt;</a>')
753                                                                                         .bind(
754                                                                                                 'click',
755                                                                                                 function()
756                                                                                                 {
757                                                                                                         return c._displayNewMonth.call(c, this, 0, -1);
758                                                                                                 }
759                                                                                         ),
760                                                                                 $('<a class="dp-nav-prev-month" href="#" title="' + $.dpText.TEXT_PREV_MONTH + '">&lt;</a>')
761                                                                                         .bind(
762                                                                                                 'click',
763                                                                                                 function()
764                                                                                                 {
765                                                                                                         return c._displayNewMonth.call(c, this, -1, 0);
766                                                                                                 }
767                                                                                         )
768                                                                         ),
769                                                                 $('<div class="dp-nav-next"></div>')
770                                                                         .append(
771                                                                                 $('<a class="dp-nav-next-year" href="#" title="' + $.dpText.TEXT_NEXT_YEAR + '">&gt;&gt;</a>')
772                                                                                         .bind(
773                                                                                                 'click',
774                                                                                                 function()
775                                                                                                 {
776                                                                                                         return c._displayNewMonth.call(c, this, 0, 1);
777                                                                                                 }
778                                                                                         ),
779                                                                                 $('<a class="dp-nav-next-month" href="#" title="' + $.dpText.TEXT_NEXT_MONTH + '">&gt;</a>')
780                                                                                         .bind(
781                                                                                                 'click',
782                                                                                                 function()
783                                                                                                 {
784                                                                                                         return c._displayNewMonth.call(c, this, 1, 0);
785                                                                                                 }
786                                                                                         )
787                                                                         ),
788                                                                 $('<div></div>')
789                                                                         .attr('className', 'dp-calendar')
790                                                         )
791                                                         .bgIframe()
792                                                 );
794                                 var $pop = this.inline ? $('.dp-popup', this.context) : $('#dp-popup');
796                                 if (this.showYearNavigation == false) {
797                                         $('.dp-nav-prev-year, .dp-nav-next-year', c.context).css('display', 'none');
798                                 }
799                                 if (this.displayClose) {
800                                         $pop.append(
801                                                 $('<a href="#" id="dp-close">' + $.dpText.TEXT_CLOSE + '</a>')
802                                                         .bind(
803                                                                 'click',
804                                                                 function()
805                                                                 {
806                                                                         c._closeCalendar();
807                                                                         return false;
808                                                                 }
809                                                         )
810                                         );
811                                 }
812                                 c._renderCalendar();
814                                 $(this.ele).trigger('dpDisplayed', $pop);
816                                 if (!c.inline) {
817                                         if (this.verticalPosition == $.dpConst.POS_BOTTOM) {
818                                                 $pop.css('top', eleOffset.top + $ele.height() - $pop.height() + c.verticalOffset);
819                                         }
820                                         if (this.horizontalPosition == $.dpConst.POS_RIGHT) {
821                                                 $pop.css('left', eleOffset.left + $ele.width() - $pop.width() + c.horizontalOffset);
822                                         }
823                                         $(document).bind('mousedown', this._checkMouse);
824                                 }
825                         },
826                         setRenderCallback : function(a)
827                         {
828                                 if (a && typeof(a) == 'function') {
829                                         a = [a];
830                                 }
831                                 this.renderCallback = this.renderCallback.concat(a);
832                         },
833                         cellRender : function ($td, thisDate, month, year) {
834                                 var c = this.dpController;
835                                 var d = new Date(thisDate.getTime());
837                                 // add our click handlers to deal with it when the days are clicked...
839                                 $td.bind(
840                                         'click',
841                                         function()
842                                         {
843                                                 var $this = $(this);
844                                                 if (!$this.is('.disabled')) {
845                                                         c.setSelected(d, !$this.is('.selected') || !c.selectMultiple);
846                                                         var s = c.isSelected(d);
847                                                         $(c.ele).trigger('dateSelected', [d, $td, s]);
848                                                         $(c.ele).trigger('change');
849                                                         if (c.closeOnSelect) {
850                                                                 c._closeCalendar();
851                                                         } else {
852                                                                 $this[s ? 'addClass' : 'removeClass']('selected');
853                                                         }
854                                                 }
855                                         }
856                                 );
858                                 if (c.isSelected(d)) {
859                                         $td.addClass('selected');
860                                 }
862                                 // call any extra renderCallbacks that were passed in
863                                 for (var i=0; i<c.renderCallback.length; i++) {
864                                         c.renderCallback[i].apply(this, arguments);
865                                 }
868                         },
869                         // ele is the clicked button - only proceed if it doesn't have the class disabled...
870                         // m and y are -1, 0 or 1 depending which direction we want to go in...
871                         _displayNewMonth : function(ele, m, y)
872                         {
873                                 if (!$(ele).is('.disabled')) {
874                                         this.setDisplayedMonth(this.displayedMonth + m, this.displayedYear + y);
875                                         this._clearCalendar();
876                                         this._renderCalendar();
877                                         $(this.ele).trigger('dpMonthChanged', [this.displayedMonth, this.displayedYear]);
878                                 }
879                                 ele.blur();
880                                 return false;
881                         },
882                         _renderCalendar : function()
883                         {
884                                 // set the title...
885                                 $('h2', this.context).html(Date.monthNames[this.displayedMonth] + ' ' + this.displayedYear);
887                                 // render the calendar...
888                                 $('.dp-calendar', this.context).renderCalendar(
889                                         {
890                                                 month                   : this.displayedMonth,
891                                                 year                    : this.displayedYear,
892                                                 renderCallback  : this.cellRender,
893                                                 dpController    : this,
894                                                 hoverClass              : this.hoverClass
895                                         }
896                                 );
898                                 // update the status of the control buttons and disable dates before startDate or after endDate...
899                                 // TODO: When should the year buttons be disabled? When you can't go forward a whole year from where you are or is that annoying?
900                                 if (this.displayedYear == this.startDate.getFullYear() && this.displayedMonth == this.startDate.getMonth()) {
901                                         $('.dp-nav-prev-year', this.context).addClass('disabled');
902                                         $('.dp-nav-prev-month', this.context).addClass('disabled');
903                                         $('.dp-calendar td.other-month', this.context).each(
904                                                 function()
905                                                 {
906                                                         var $this = $(this);
907                                                         if (Number($this.text()) > 20) {
908                                                                 $this.addClass('disabled');
909                                                         }
910                                                 }
911                                         );
912                                         var d = this.startDate.getDate();
913                                         $('.dp-calendar td.current-month', this.context).each(
914                                                 function()
915                                                 {
916                                                         var $this = $(this);
917                                                         if (Number($this.text()) < d) {
918                                                                 $this.addClass('disabled');
919                                                         }
920                                                 }
921                                         );
922                                 } else {
923                                         $('.dp-nav-prev-year', this.context).removeClass('disabled');
924                                         $('.dp-nav-prev-month', this.context).removeClass('disabled');
925                                         var d = this.startDate.getDate();
926                                         if (d > 20) {
927                                                 // check if the startDate is last month as we might need to add some disabled classes...
928                                                 var sd = new Date(this.startDate.getTime());
929                                                 sd.addMonths(1);
930                                                 if (this.displayedYear == sd.getFullYear() && this.displayedMonth == sd.getMonth()) {
931                                                         $('dp-calendar td.other-month', this.context).each(
932                                                                 function()
933                                                                 {
934                                                                         var $this = $(this);
935                                                                         if (Number($this.text()) < d) {
936                                                                                 $this.addClass('disabled');
937                                                                         }
938                                                                 }
939                                                         );
940                                                 }
941                                         }
942                                 }
943                                 if (this.displayedYear == this.endDate.getFullYear() && this.displayedMonth == this.endDate.getMonth()) {
944                                         $('.dp-nav-next-year', this.context).addClass('disabled');
945                                         $('.dp-nav-next-month', this.context).addClass('disabled');
946                                         $('.dp-calendar td.other-month', this.context).each(
947                                                 function()
948                                                 {
949                                                         var $this = $(this);
950                                                         if (Number($this.text()) < 14) {
951                                                                 $this.addClass('disabled');
952                                                         }
953                                                 }
954                                         );
955                                         var d = this.endDate.getDate();
956                                         $('.dp-calendar td.current-month', this.context).each(
957                                                 function()
958                                                 {
959                                                         var $this = $(this);
960                                                         if (Number($this.text()) > d) {
961                                                                 $this.addClass('disabled');
962                                                         }
963                                                 }
964                                         );
965                                 } else {
966                                         $('.dp-nav-next-year', this.context).removeClass('disabled');
967                                         $('.dp-nav-next-month', this.context).removeClass('disabled');
968                                         var d = this.endDate.getDate();
969                                         if (d < 13) {
970                                                 // check if the endDate is next month as we might need to add some disabled classes...
971                                                 var ed = new Date(this.endDate.getTime());
972                                                 ed.addMonths(-1);
973                                                 if (this.displayedYear == ed.getFullYear() && this.displayedMonth == ed.getMonth()) {
974                                                         $('.dp-calendar td.other-month', this.context).each(
975                                                                 function()
976                                                                 {
977                                                                         var $this = $(this);
978                                                                         if (Number($this.text()) > d) {
979                                                                                 $this.addClass('disabled');
980                                                                         }
981                                                                 }
982                                                         );
983                                                 }
984                                         }
985                                 }
986                         },
987                         _closeCalendar : function(programatic, ele)
988                         {
989                                 if (!ele || ele == this.ele)
990                                 {
991                                         $(document).unbind('mousedown', this._checkMouse);
992                                         this._clearCalendar();
993                                         $('#dp-popup a').unbind();
994                                         $('#dp-popup').empty().remove();
995                                         if (!programatic) {
996                                                 $(this.ele).trigger('dpClosed', [this.getSelected()]);
997                                         }
998                                 }
999                         },
1000                         // empties the current dp-calendar div and makes sure that all events are unbound
1001                         // and expandos removed to avoid memory leaks...
1002                         _clearCalendar : function()
1003                         {
1004                                 // TODO.
1005                                 $('.dp-calendar td', this.context).unbind();
1006                                 $('.dp-calendar', this.context).empty();
1007                         }
1008                 }
1009         );
1011         // static constants
1012         $.dpConst = {
1013                 SHOW_HEADER_NONE        :       0,
1014                 SHOW_HEADER_SHORT       :       1,
1015                 SHOW_HEADER_LONG        :       2,
1016                 POS_TOP                         :       0,
1017                 POS_BOTTOM                      :       1,
1018                 POS_LEFT                        :       0,
1019                 POS_RIGHT                       :       1
1020         };
1021         // localisable text
1022         $.dpText = {
1023                 TEXT_PREV_YEAR          :       'Previous year',
1024                 TEXT_PREV_MONTH         :       'Previous month',
1025                 TEXT_NEXT_YEAR          :       'Next year',
1026                 TEXT_NEXT_MONTH         :       'Next month',
1027                 TEXT_CLOSE                      :       'Close',
1028                 TEXT_CHOOSE_DATE        :       'Choose date'
1029         };
1030         // version
1031         $.dpVersion = '$Id: jquery.datePicker.js 3739 2007-10-25 13:55:30Z kelvin.luck $';
1033         function _getController(ele)
1034         {
1035                 if (ele._dpId) return $.event._dpCache[ele._dpId];
1036                 return false;
1037         };
1039         // make it so that no error is thrown if bgIframe plugin isn't included (allows you to use conditional
1040         // comments to only include bgIframe where it is needed in IE without breaking this plugin).
1041         if ($.fn.bgIframe == undefined) {
1042                 $.fn.bgIframe = function() {return this; };
1043         };
1046         // clean-up
1047         $(window)
1048                 .bind('unload', function() {
1049                         var els = $.event._dpCache || [];
1050                         for (var i in els) {
1051                                 $(els[i].ele)._dpDestroy();
1052                         }
1053                 });
1056 })(jQuery);
1060  * Date prototype extensions. Doesn't depend on any
1061  * other code. Doens't overwrite existing methods.
1063  * Adds dayNames, abbrDayNames, monthNames and abbrMonthNames static properties and isLeapYear,
1064  * isWeekend, isWeekDay, getDaysInMonth, getDayName, getMonthName, getDayOfYear, getWeekOfYear,
1065  * setDayOfYear, addYears, addMonths, addDays, addHours, addMinutes, addSeconds methods
1067  * Copyright (c) 2006 Jörn Zaefferer and Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net)
1069  * Additional methods and properties added by Kelvin Luck: firstDayOfWeek, dateFormat, zeroTime, asString, fromString -
1070  * I've added my name to these methods so you know who to blame if they are broken!
1072  * Dual licensed under the MIT and GPL licenses:
1073  *   http://www.opensource.org/licenses/mit-license.php
1074  *   http://www.gnu.org/licenses/gpl.html
1076  */
1079  * An Array of day names starting with Sunday.
1081  * @example dayNames[0]
1082  * @result 'Sunday'
1084  * @name dayNames
1085  * @type Array
1086  * @cat Plugins/Methods/Date
1087  */
1088 Date.dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
1091  * An Array of abbreviated day names starting with Sun.
1093  * @example abbrDayNames[0]
1094  * @result 'Sun'
1096  * @name abbrDayNames
1097  * @type Array
1098  * @cat Plugins/Methods/Date
1099  */
1100 Date.abbrDayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
1103  * An Array of month names starting with Janurary.
1105  * @example monthNames[0]
1106  * @result 'January'
1108  * @name monthNames
1109  * @type Array
1110  * @cat Plugins/Methods/Date
1111  */
1112 Date.monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
1115  * An Array of abbreviated month names starting with Jan.
1117  * @example abbrMonthNames[0]
1118  * @result 'Jan'
1120  * @name monthNames
1121  * @type Array
1122  * @cat Plugins/Methods/Date
1123  */
1124 Date.abbrMonthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
1127  * The first day of the week for this locale.
1129  * @name firstDayOfWeek
1130  * @type Number
1131  * @cat Plugins/Methods/Date
1132  * @author Kelvin Luck
1133  */
1134 Date.firstDayOfWeek = 1;
1137  * The format that string dates should be represented as (e.g. 'dd/mm/yyyy' for UK, 'mm/dd/yyyy' for US, 'yyyy-mm-dd' for Unicode etc).
1139  * @name format
1140  * @type String
1141  * @cat Plugins/Methods/Date
1142  * @author Kelvin Luck
1143  */
1144 Date.format = 'dd/mm/yyyy';
1145 //Date.format = 'mm/dd/yyyy';
1146 //Date.format = 'yyyy-mm-dd';
1147 //Date.format = 'dd mmm yy';
1150  * The first two numbers in the century to be used when decoding a two digit year. Since a two digit year is ambiguous (and date.setYear
1151  * only works with numbers < 99 and so doesn't allow you to set years after 2000) we need to use this to disambiguate the two digit year codes.
1153  * @name format
1154  * @type String
1155  * @cat Plugins/Methods/Date
1156  * @author Kelvin Luck
1157  */
1158 Date.fullYearStart = '20';
1160 (function() {
1162         /**
1163          * Adds a given method under the given name
1164          * to the Date prototype if it doesn't
1165          * currently exist.
1166          *
1167          * @private
1168          */
1169         function add(name, method) {
1170                 if( !Date.prototype[name] ) {
1171                         Date.prototype[name] = method;
1172                 }
1173         };
1175         /**
1176          * Checks if the year is a leap year.
1177          *
1178          * @example var dtm = new Date("01/12/2008");
1179          * dtm.isLeapYear();
1180          * @result true
1181          *
1182          * @name isLeapYear
1183          * @type Boolean
1184          * @cat Plugins/Methods/Date
1185          */
1186         add("isLeapYear", function() {
1187                 var y = this.getFullYear();
1188                 return (y%4==0 && y%100!=0) || y%400==0;
1189         });
1191         /**
1192          * Checks if the day is a weekend day (Sat or Sun).
1193          *
1194          * @example var dtm = new Date("01/12/2008");
1195          * dtm.isWeekend();
1196          * @result false
1197          *
1198          * @name isWeekend
1199          * @type Boolean
1200          * @cat Plugins/Methods/Date
1201          */
1202         add("isWeekend", function() {
1203                 return this.getDay()==0 || this.getDay()==6;
1204         });
1206         /**
1207          * Check if the day is a day of the week (Mon-Fri)
1208          *
1209          * @example var dtm = new Date("01/12/2008");
1210          * dtm.isWeekDay();
1211          * @result false
1212          *
1213          * @name isWeekDay
1214          * @type Boolean
1215          * @cat Plugins/Methods/Date
1216          */
1217         add("isWeekDay", function() {
1218                 return !this.isWeekend();
1219         });
1221         /**
1222          * Gets the number of days in the month.
1223          *
1224          * @example var dtm = new Date("01/12/2008");
1225          * dtm.getDaysInMonth();
1226          * @result 31
1227          *
1228          * @name getDaysInMonth
1229          * @type Number
1230          * @cat Plugins/Methods/Date
1231          */
1232         add("getDaysInMonth", function() {
1233                 return [31,(this.isLeapYear() ? 29:28),31,30,31,30,31,31,30,31,30,31][this.getMonth()];
1234         });
1236         /**
1237          * Gets the name of the day.
1238          *
1239          * @example var dtm = new Date("01/12/2008");
1240          * dtm.getDayName();
1241          * @result 'Saturday'
1242          *
1243          * @example var dtm = new Date("01/12/2008");
1244          * dtm.getDayName(true);
1245          * @result 'Sat'
1246          *
1247          * @param abbreviated Boolean When set to true the name will be abbreviated.
1248          * @name getDayName
1249          * @type String
1250          * @cat Plugins/Methods/Date
1251          */
1252         add("getDayName", function(abbreviated) {
1253                 return abbreviated ? Date.abbrDayNames[this.getDay()] : Date.dayNames[this.getDay()];
1254         });
1256         /**
1257          * Gets the name of the month.
1258          *
1259          * @example var dtm = new Date("01/12/2008");
1260          * dtm.getMonthName();
1261          * @result 'Janurary'
1262          *
1263          * @example var dtm = new Date("01/12/2008");
1264          * dtm.getMonthName(true);
1265          * @result 'Jan'
1266          *
1267          * @param abbreviated Boolean When set to true the name will be abbreviated.
1268          * @name getDayName
1269          * @type String
1270          * @cat Plugins/Methods/Date
1271          */
1272         add("getMonthName", function(abbreviated) {
1273                 return abbreviated ? Date.abbrMonthNames[this.getMonth()] : Date.monthNames[this.getMonth()];
1274         });
1276         /**
1277          * Get the number of the day of the year.
1278          *
1279          * @example var dtm = new Date("01/12/2008");
1280          * dtm.getDayOfYear();
1281          * @result 11
1282          *
1283          * @name getDayOfYear
1284          * @type Number
1285          * @cat Plugins/Methods/Date
1286          */
1287         add("getDayOfYear", function() {
1288                 var tmpdtm = new Date("1/1/" + this.getFullYear());
1289                 return Math.floor((this.getTime() - tmpdtm.getTime()) / 86400000);
1290         });
1292         /**
1293          * Get the number of the week of the year.
1294          *
1295          * @example var dtm = new Date("01/12/2008");
1296          * dtm.getWeekOfYear();
1297          * @result 2
1298          *
1299          * @name getWeekOfYear
1300          * @type Number
1301          * @cat Plugins/Methods/Date
1302          */
1303         add("getWeekOfYear", function() {
1304                 return Math.ceil(this.getDayOfYear() / 7);
1305         });
1307         /**
1308          * Set the day of the year.
1309          *
1310          * @example var dtm = new Date("01/12/2008");
1311          * dtm.setDayOfYear(1);
1312          * dtm.toString();
1313          * @result 'Tue Jan 01 2008 00:00:00'
1314          *
1315          * @name setDayOfYear
1316          * @type Date
1317          * @cat Plugins/Methods/Date
1318          */
1319         add("setDayOfYear", function(day) {
1320                 this.setMonth(0);
1321                 this.setDate(day);
1322                 return this;
1323         });
1325         /**
1326          * Add a number of years to the date object.
1327          *
1328          * @example var dtm = new Date("01/12/2008");
1329          * dtm.addYears(1);
1330          * dtm.toString();
1331          * @result 'Mon Jan 12 2009 00:00:00'
1332          *
1333          * @name addYears
1334          * @type Date
1335          * @cat Plugins/Methods/Date
1336          */
1337         add("addYears", function(num) {
1338                 this.setFullYear(this.getFullYear() + num);
1339                 return this;
1340         });
1342         /**
1343          * Add a number of months to the date object.
1344          *
1345          * @example var dtm = new Date("01/12/2008");
1346          * dtm.addMonths(1);
1347          * dtm.toString();
1348          * @result 'Tue Feb 12 2008 00:00:00'
1349          *
1350          * @name addMonths
1351          * @type Date
1352          * @cat Plugins/Methods/Date
1353          */
1354         add("addMonths", function(num) {
1355                 var tmpdtm = this.getDate();
1357                 this.setMonth(this.getMonth() + num);
1359                 if (tmpdtm > this.getDate())
1360                         this.addDays(-this.getDate());
1362                 return this;
1363         });
1365         /**
1366          * Add a number of days to the date object.
1367          *
1368          * @example var dtm = new Date("01/12/2008");
1369          * dtm.addDays(1);
1370          * dtm.toString();
1371          * @result 'Sun Jan 13 2008 00:00:00'
1372          *
1373          * @name addDays
1374          * @type Date
1375          * @cat Plugins/Methods/Date
1376          */
1377         add("addDays", function(num) {
1378                 this.setDate(this.getDate() + num);
1379                 return this;
1380         });
1382         /**
1383          * Add a number of hours to the date object.
1384          *
1385          * @example var dtm = new Date("01/12/2008");
1386          * dtm.addHours(24);
1387          * dtm.toString();
1388          * @result 'Sun Jan 13 2008 00:00:00'
1389          *
1390          * @name addHours
1391          * @type Date
1392          * @cat Plugins/Methods/Date
1393          */
1394         add("addHours", function(num) {
1395                 this.setHours(this.getHours() + num);
1396                 return this;
1397         });
1399         /**
1400          * Add a number of minutes to the date object.
1401          *
1402          * @example var dtm = new Date("01/12/2008");
1403          * dtm.addMinutes(60);
1404          * dtm.toString();
1405          * @result 'Sat Jan 12 2008 01:00:00'
1406          *
1407          * @name addMinutes
1408          * @type Date
1409          * @cat Plugins/Methods/Date
1410          */
1411         add("addMinutes", function(num) {
1412                 this.setMinutes(this.getMinutes() + num);
1413                 return this;
1414         });
1416         /**
1417          * Add a number of seconds to the date object.
1418          *
1419          * @example var dtm = new Date("01/12/2008");
1420          * dtm.addSeconds(60);
1421          * dtm.toString();
1422          * @result 'Sat Jan 12 2008 00:01:00'
1423          *
1424          * @name addSeconds
1425          * @type Date
1426          * @cat Plugins/Methods/Date
1427          */
1428         add("addSeconds", function(num) {
1429                 this.setSeconds(this.getSeconds() + num);
1430                 return this;
1431         });
1433         /**
1434          * Sets the time component of this Date to zero for cleaner, easier comparison of dates where time is not relevant.
1435          *
1436          * @example var dtm = new Date();
1437          * dtm.zeroTime();
1438          * dtm.toString();
1439          * @result 'Sat Jan 12 2008 00:01:00'
1440          *
1441          * @name zeroTime
1442          * @type Date
1443          * @cat Plugins/Methods/Date
1444          * @author Kelvin Luck
1445          */
1446         add("zeroTime", function() {
1447                 this.setMilliseconds(0);
1448                 this.setSeconds(0);
1449                 this.setMinutes(0);
1450                 this.setHours(0);
1451                 return this;
1452         });
1454         /**
1455          * Returns a string representation of the date object according to Date.format.
1456          * (Date.toString may be used in other places so I purposefully didn't overwrite it)
1457          *
1458          * @example var dtm = new Date("01/12/2008");
1459          * dtm.asString();
1460          * @result '12/01/2008' // (where Date.format == 'dd/mm/yyyy'
1461          *
1462          * @name asString
1463          * @type Date
1464          * @cat Plugins/Methods/Date
1465          * @author Kelvin Luck
1466          */
1467         add("asString", function() {
1468                 var r = Date.format;
1469                 return r
1470                         .split('yyyy').join(this.getFullYear())
1471                         .split('yy').join((this.getFullYear() + '').substring(2))
1472                         .split('mmm').join(this.getMonthName(true))
1473                         .split('mm').join(_zeroPad(this.getMonth()+1))
1474                         .split('dd').join(_zeroPad(this.getDate()));
1475         });
1477         /**
1478          * Returns a new date object created from the passed String according to Date.format or false if the attempt to do this results in an invalid date object
1479          * (We can't simple use Date.parse as it's not aware of locale and I chose not to overwrite it incase it's functionality is being relied on elsewhere)
1480          *
1481          * @example var dtm = Date.fromString("12/01/2008");
1482          * dtm.toString();
1483          * @result 'Sat Jan 12 2008 00:00:00' // (where Date.format == 'dd/mm/yyyy'
1484          *
1485          * @name fromString
1486          * @type Date
1487          * @cat Plugins/Methods/Date
1488          * @author Kelvin Luck
1489          */
1490         Date.fromString = function(s)
1491         {
1492                 var f = Date.format;
1493                 var d = new Date('01/01/1977');
1494                 var iY = f.indexOf('yyyy');
1495                 if (iY > -1) {
1496                         d.setFullYear(Number(s.substr(iY, 4)));
1497                 } else {
1498                         // TODO - this doesn't work very well - are there any rules for what is meant by a two digit year?
1499                         d.setFullYear(Number(Date.fullYearStart + s.substr(f.indexOf('yy'), 2)));
1500                 }
1501                 var iM = f.indexOf('mmm');
1502                 if (iM > -1) {
1503                         var mStr = s.substr(iM, 3);
1504                         for (var i=0; i<Date.abbrMonthNames.length; i++) {
1505                                 if (Date.abbrMonthNames[i] == mStr) break;
1506                         }
1507                         d.setMonth(i);
1508                 } else {
1509                         d.setMonth(Number(s.substr(f.indexOf('mm'), 2)) - 1);
1510                 }
1511                 d.setDate(Number(s.substr(f.indexOf('dd'), 2)));
1512                 if (isNaN(d.getTime())) {
1513                         return false;
1514                 }
1515                 return d;
1516         };
1518         // utility method
1519         var _zeroPad = function(num) {
1520                 var s = '0'+num;
1521                 return s.substring(s.length-2)
1522                 //return ('0'+num).substring(-2); // doesn't work on IE :(
1523         };
1525 })();