stuff_to_do_plugin 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. data/COPYRIGHT.txt +18 -0
  2. data/CREDITS.txt +6 -0
  3. data/GPL.txt +339 -0
  4. data/README.rdoc +61 -0
  5. data/Rakefile +38 -0
  6. data/VERSION +1 -0
  7. data/app/controllers/stuff_to_do_controller.rb +161 -0
  8. data/app/helpers/stuff_to_do_helper.rb +88 -0
  9. data/app/models/stuff_to_do.rb +208 -0
  10. data/app/models/stuff_to_do_filter.rb +32 -0
  11. data/app/models/stuff_to_do_mailer.rb +16 -0
  12. data/app/views/settings/_stuff_to_do_settings.html.erb +27 -0
  13. data/app/views/stuff_to_do/_issue.html.erb +16 -0
  14. data/app/views/stuff_to_do/_item.html.erb +5 -0
  15. data/app/views/stuff_to_do/_left_panes.html.erb +51 -0
  16. data/app/views/stuff_to_do/_panes.html.erb +11 -0
  17. data/app/views/stuff_to_do/_project.html.erb +6 -0
  18. data/app/views/stuff_to_do/_right_panes.html.erb +25 -0
  19. data/app/views/stuff_to_do/_time_grid.html.erb +113 -0
  20. data/app/views/stuff_to_do/_time_grid_form.html.erb +32 -0
  21. data/app/views/stuff_to_do/index.html.erb +44 -0
  22. data/app/views/stuff_to_do_mailer/recommended_below_threshold.erb +3 -0
  23. data/app/views/stuff_to_do_mailer/recommended_below_threshold.text.html.rhtml +1 -0
  24. data/assets/images/b.png +0 -0
  25. data/assets/images/bl.png +0 -0
  26. data/assets/images/br.png +0 -0
  27. data/assets/images/closelabel.gif +0 -0
  28. data/assets/images/loading.gif +0 -0
  29. data/assets/images/tl.png +0 -0
  30. data/assets/images/tr.png +0 -0
  31. data/assets/javascripts/facebox.js +319 -0
  32. data/assets/javascripts/jquery-1.2.6.min.js +32 -0
  33. data/assets/javascripts/jquery-ui.js +2839 -0
  34. data/assets/javascripts/jquery.contextMenu.js +212 -0
  35. data/assets/javascripts/semantic.cache +15 -0
  36. data/assets/javascripts/stuff-to-do.js +270 -0
  37. data/assets/javascripts/ui/build.xml +24 -0
  38. data/assets/javascripts/ui/effects.blind.js +49 -0
  39. data/assets/javascripts/ui/effects.bounce.js +78 -0
  40. data/assets/javascripts/ui/effects.clip.js +54 -0
  41. data/assets/javascripts/ui/effects.core.js +510 -0
  42. data/assets/javascripts/ui/effects.drop.js +50 -0
  43. data/assets/javascripts/ui/effects.explode.js +79 -0
  44. data/assets/javascripts/ui/effects.fold.js +55 -0
  45. data/assets/javascripts/ui/effects.highlight.js +48 -0
  46. data/assets/javascripts/ui/effects.pulsate.js +55 -0
  47. data/assets/javascripts/ui/effects.scale.js +180 -0
  48. data/assets/javascripts/ui/effects.shake.js +57 -0
  49. data/assets/javascripts/ui/effects.slide.js +50 -0
  50. data/assets/javascripts/ui/effects.transfer.js +59 -0
  51. data/assets/javascripts/ui/i18n/ui.datepicker-ar.js +26 -0
  52. data/assets/javascripts/ui/i18n/ui.datepicker-bg.js +25 -0
  53. data/assets/javascripts/ui/i18n/ui.datepicker-ca.js +25 -0
  54. data/assets/javascripts/ui/i18n/ui.datepicker-cs.js +25 -0
  55. data/assets/javascripts/ui/i18n/ui.datepicker-da.js +25 -0
  56. data/assets/javascripts/ui/i18n/ui.datepicker-de.js +25 -0
  57. data/assets/javascripts/ui/i18n/ui.datepicker-eo.js +25 -0
  58. data/assets/javascripts/ui/i18n/ui.datepicker-es.js +25 -0
  59. data/assets/javascripts/ui/i18n/ui.datepicker-fa.js +25 -0
  60. data/assets/javascripts/ui/i18n/ui.datepicker-fi.js +25 -0
  61. data/assets/javascripts/ui/i18n/ui.datepicker-fr.js +25 -0
  62. data/assets/javascripts/ui/i18n/ui.datepicker-he.js +25 -0
  63. data/assets/javascripts/ui/i18n/ui.datepicker-hr.js +25 -0
  64. data/assets/javascripts/ui/i18n/ui.datepicker-hu.js +25 -0
  65. data/assets/javascripts/ui/i18n/ui.datepicker-hy.js +25 -0
  66. data/assets/javascripts/ui/i18n/ui.datepicker-id.js +25 -0
  67. data/assets/javascripts/ui/i18n/ui.datepicker-is.js +25 -0
  68. data/assets/javascripts/ui/i18n/ui.datepicker-it.js +25 -0
  69. data/assets/javascripts/ui/i18n/ui.datepicker-ja.js +26 -0
  70. data/assets/javascripts/ui/i18n/ui.datepicker-ko.js +25 -0
  71. data/assets/javascripts/ui/i18n/ui.datepicker-lt.js +25 -0
  72. data/assets/javascripts/ui/i18n/ui.datepicker-lv.js +25 -0
  73. data/assets/javascripts/ui/i18n/ui.datepicker-nl.js +25 -0
  74. data/assets/javascripts/ui/i18n/ui.datepicker-no.js +25 -0
  75. data/assets/javascripts/ui/i18n/ui.datepicker-pl.js +25 -0
  76. data/assets/javascripts/ui/i18n/ui.datepicker-pt-BR.js +25 -0
  77. data/assets/javascripts/ui/i18n/ui.datepicker-ro.js +25 -0
  78. data/assets/javascripts/ui/i18n/ui.datepicker-ru.js +25 -0
  79. data/assets/javascripts/ui/i18n/ui.datepicker-sk.js +25 -0
  80. data/assets/javascripts/ui/i18n/ui.datepicker-sl.js +26 -0
  81. data/assets/javascripts/ui/i18n/ui.datepicker-sq.js +25 -0
  82. data/assets/javascripts/ui/i18n/ui.datepicker-sv.js +25 -0
  83. data/assets/javascripts/ui/i18n/ui.datepicker-th.js +25 -0
  84. data/assets/javascripts/ui/i18n/ui.datepicker-tr.js +25 -0
  85. data/assets/javascripts/ui/i18n/ui.datepicker-uk.js +25 -0
  86. data/assets/javascripts/ui/i18n/ui.datepicker-zh-CN.js +25 -0
  87. data/assets/javascripts/ui/i18n/ui.datepicker-zh-TW.js +25 -0
  88. data/assets/javascripts/ui/svn.log +11 -0
  89. data/assets/javascripts/ui/ui.accordion.js +400 -0
  90. data/assets/javascripts/ui/ui.core.js +533 -0
  91. data/assets/javascripts/ui/ui.datepicker.js +1754 -0
  92. data/assets/javascripts/ui/ui.dialog.js +630 -0
  93. data/assets/javascripts/ui/ui.draggable.js +696 -0
  94. data/assets/javascripts/ui/ui.droppable.js +314 -0
  95. data/assets/javascripts/ui/ui.progressbar.js +114 -0
  96. data/assets/javascripts/ui/ui.resizable.js +805 -0
  97. data/assets/javascripts/ui/ui.selectable.js +266 -0
  98. data/assets/javascripts/ui/ui.slider.js +552 -0
  99. data/assets/javascripts/ui/ui.sortable.js +1012 -0
  100. data/assets/javascripts/ui/ui.tabs.js +572 -0
  101. data/assets/stylesheets/stuff_to_do.css +216 -0
  102. data/config/locales/bg.yml +18 -0
  103. data/config/locales/ca-fr.yml +18 -0
  104. data/config/locales/cs.yml +16 -0
  105. data/config/locales/da.yml +16 -0
  106. data/config/locales/de.yml +18 -0
  107. data/config/locales/en.yml +24 -0
  108. data/config/locales/es.yml +19 -0
  109. data/config/locales/fr.yml +17 -0
  110. data/config/locales/hu.yml +16 -0
  111. data/config/locales/it.yml +16 -0
  112. data/config/locales/ja.yml +18 -0
  113. data/config/locales/ko.yml +18 -0
  114. data/config/locales/lt.yml +18 -0
  115. data/config/locales/nl.yml +20 -0
  116. data/config/locales/pt-BR.yml +18 -0
  117. data/config/locales/ru.yml +19 -0
  118. data/config/locales/sv.yml +19 -0
  119. data/config/locales/tr.yml +18 -0
  120. data/config/routes.rb +3 -0
  121. data/init.rb +54 -0
  122. data/lang/bg.yml +17 -0
  123. data/lang/ca-fr.yml +17 -0
  124. data/lang/cs.yml +15 -0
  125. data/lang/da.yml +15 -0
  126. data/lang/de.yml +17 -0
  127. data/lang/en.yml +21 -0
  128. data/lang/es.yml +18 -0
  129. data/lang/fr.yml +16 -0
  130. data/lang/hu.yml +15 -0
  131. data/lang/it.yml +15 -0
  132. data/lang/ja.yml +17 -0
  133. data/lang/ko.yml +17 -0
  134. data/lang/lt.yml +17 -0
  135. data/lang/pt-br.yml +17 -0
  136. data/lang/ru.yml +15 -0
  137. data/lang/sv.yml +18 -0
  138. data/lang/tr.yml +17 -0
  139. data/lib/redmine_stuff_to_do/stuff_to_do_compatibility.rb +15 -0
  140. data/lib/stuff_to_do_array_patch.rb +8 -0
  141. data/lib/stuff_to_do_issue_patch.rb +57 -0
  142. data/lib/stuff_to_do_project_patch.rb +31 -0
  143. data/lib/stuff_to_do_user_patch.rb +10 -0
  144. data/rails/init.rb +1 -0
  145. data/spec/controllers/stuff_to_do_controller_add_to_time_grid_spec.rb +58 -0
  146. data/spec/controllers/stuff_to_do_controller_index_spec.rb +155 -0
  147. data/spec/controllers/stuff_to_do_controller_remove_from_time_grid_spec.rb +56 -0
  148. data/spec/controllers/stuff_to_do_controller_reorder_spec.rb +179 -0
  149. data/spec/controllers/stuff_to_do_controller_save_time_entries_spec.rb +56 -0
  150. data/spec/controllers/stuff_to_do_private_methods_spec.rb +82 -0
  151. data/spec/lib/stuff_to_do_issue_patch_spec.rb +60 -0
  152. data/spec/lib/stuff_to_do_project_patch_spec.rb +50 -0
  153. data/spec/lib/stuff_to_do_user_patch_spec.rb +8 -0
  154. data/spec/models/stuff_to_do_filter_spec.rb +3 -0
  155. data/spec/models/stuff_to_do_mailer_spec.rb +42 -0
  156. data/spec/models/stuff_to_do_spec.rb +426 -0
  157. data/spec/sanity_spec.rb +7 -0
  158. data/spec/spec_helper.rb +130 -0
  159. metadata +211 -0
@@ -0,0 +1,1754 @@
1
+ /*
2
+ * jQuery UI Datepicker @VERSION
3
+ *
4
+ * Copyright (c) 2008 AUTHORS.txt (http://ui.jquery.com/about)
5
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
6
+ * and GPL (GPL-LICENSE.txt) licenses.
7
+ *
8
+ * http://docs.jquery.com/UI/Datepicker
9
+ *
10
+ * Depends:
11
+ * ui.core.js
12
+ */
13
+
14
+ (function($) { // hide the namespace
15
+
16
+ $.extend($.ui, { datepicker: { version: "@VERSION" } });
17
+
18
+ var PROP_NAME = 'datepicker';
19
+
20
+ /* Date picker manager.
21
+ Use the singleton instance of this class, $.datepicker, to interact with the date picker.
22
+ Settings for (groups of) date pickers are maintained in an instance object,
23
+ allowing multiple different settings on the same page. */
24
+
25
+ function Datepicker() {
26
+ this.debug = false; // Change this to true to start debugging
27
+ this._curInst = null; // The current instance in use
28
+ this._keyEvent = false; // If the last event was a key event
29
+ this._disabledInputs = []; // List of date picker inputs that have been disabled
30
+ this._datepickerShowing = false; // True if the popup picker is showing , false if not
31
+ this._inDialog = false; // True if showing within a "dialog", false if not
32
+ this._mainDivId = 'ui-datepicker-div'; // The ID of the main datepicker division
33
+ this._inlineClass = 'ui-datepicker-inline'; // The name of the inline marker class
34
+ this._appendClass = 'ui-datepicker-append'; // The name of the append marker class
35
+ this._triggerClass = 'ui-datepicker-trigger'; // The name of the trigger marker class
36
+ this._dialogClass = 'ui-datepicker-dialog'; // The name of the dialog marker class
37
+ this._promptClass = 'ui-datepicker-prompt'; // The name of the dialog prompt marker class
38
+ this._disableClass = 'ui-datepicker-disabled'; // The name of the disabled covering marker class
39
+ this._unselectableClass = 'ui-datepicker-unselectable'; // The name of the unselectable cell marker class
40
+ this._currentClass = 'ui-datepicker-current-day'; // The name of the current day marker class
41
+ this._dayOverClass = 'ui-datepicker-days-cell-over'; // The name of the day hover marker class
42
+ this._weekOverClass = 'ui-datepicker-week-over'; // The name of the week hover marker class
43
+ this.regional = []; // Available regional settings, indexed by language code
44
+ this.regional[''] = { // Default regional settings
45
+ clearText: 'Clear', // Display text for clear link
46
+ clearStatus: 'Erase the current date', // Status text for clear link
47
+ closeText: 'Close', // Display text for close link
48
+ closeStatus: 'Close without change', // Status text for close link
49
+ prevText: '<Prev', // Display text for previous month link
50
+ prevStatus: 'Show the previous month', // Status text for previous month link
51
+ prevBigText: '<<', // Display text for previous year link
52
+ prevBigStatus: 'Show the previous year', // Status text for previous year link
53
+ nextText: 'Next>', // Display text for next month link
54
+ nextStatus: 'Show the next month', // Status text for next month link
55
+ nextBigText: '>>', // Display text for next year link
56
+ nextBigStatus: 'Show the next year', // Status text for next year link
57
+ currentText: 'Today', // Display text for current month link
58
+ currentStatus: 'Show the current month', // Status text for current month link
59
+ monthNames: ['January','February','March','April','May','June',
60
+ 'July','August','September','October','November','December'], // Names of months for drop-down and formatting
61
+ monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting
62
+ monthStatus: 'Show a different month', // Status text for selecting a month
63
+ yearStatus: 'Show a different year', // Status text for selecting a year
64
+ weekHeader: 'Wk', // Header for the week of the year column
65
+ weekStatus: 'Week of the year', // Status text for the week of the year column
66
+ dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting
67
+ dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting
68
+ dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday
69
+ dayStatus: 'Set DD as first week day', // Status text for the day of the week selection
70
+ dateStatus: 'Select DD, M d', // Status text for the date selection
71
+ dateFormat: 'mm/dd/yy', // See format options on parseDate
72
+ firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
73
+ initStatus: 'Select a date', // Initial Status text on opening
74
+ isRTL: false // True if right-to-left language, false if left-to-right
75
+ };
76
+ this._defaults = { // Global defaults for all the date picker instances
77
+ showOn: 'focus', // 'focus' for popup on focus,
78
+ // 'button' for trigger button, or 'both' for either
79
+ showAnim: 'show', // Name of jQuery animation for popup
80
+ showOptions: {}, // Options for enhanced animations
81
+ defaultDate: null, // Used when field is blank: actual date,
82
+ // +/-number for offset from today, null for today
83
+ appendText: '', // Display text following the input box, e.g. showing the format
84
+ buttonText: '...', // Text for trigger button
85
+ buttonImage: '', // URL for trigger button image
86
+ buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
87
+ closeAtTop: true, // True to have the clear/close at the top,
88
+ // false to have them at the bottom
89
+ mandatory: false, // True to hide the Clear link, false to include it
90
+ hideIfNoPrevNext: false, // True to hide next/previous month links
91
+ // if not applicable, false to just disable them
92
+ navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
93
+ showBigPrevNext: false, // True to show big prev/next links
94
+ gotoCurrent: false, // True if today link goes back to current selection instead
95
+ changeMonth: true, // True if month can be selected directly, false if only prev/next
96
+ changeYear: true, // True if year can be selected directly, false if only prev/next
97
+ showMonthAfterYear: false, // True if the year select precedes month, false for month then year
98
+ yearRange: '-10:+10', // Range of years to display in drop-down,
99
+ // either relative to current year (-nn:+nn) or absolute (nnnn:nnnn)
100
+ changeFirstDay: true, // True to click on day name to change, false to remain as set
101
+ highlightWeek: false, // True to highlight the selected week
102
+ showOtherMonths: false, // True to show dates in other months, false to leave blank
103
+ showWeeks: false, // True to show week of the year, false to omit
104
+ calculateWeek: this.iso8601Week, // How to calculate the week of the year,
105
+ // takes a Date and returns the number of the week for it
106
+ shortYearCutoff: '+10', // Short year values < this are in the current century,
107
+ // > this are in the previous century,
108
+ // string value starting with '+' for current year + value
109
+ showStatus: false, // True to show status bar at bottom, false to not show it
110
+ statusForDate: this.dateStatus, // Function to provide status text for a date -
111
+ // takes date and instance as parameters, returns display text
112
+ minDate: null, // The earliest selectable date, or null for no limit
113
+ maxDate: null, // The latest selectable date, or null for no limit
114
+ duration: 'normal', // Duration of display/closure
115
+ beforeShowDay: null, // Function that takes a date and returns an array with
116
+ // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or '',
117
+ // [2] = cell title (optional), e.g. $.datepicker.noWeekends
118
+ beforeShow: null, // Function that takes an input field and
119
+ // returns a set of custom settings for the date picker
120
+ onSelect: null, // Define a callback function when a date is selected
121
+ onChangeMonthYear: null, // Define a callback function when the month or year is changed
122
+ onClose: null, // Define a callback function when the datepicker is closed
123
+ numberOfMonths: 1, // Number of months to show at a time
124
+ showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
125
+ stepMonths: 1, // Number of months to step back/forward
126
+ stepBigMonths: 12, // Number of months to step back/forward for the big links
127
+ rangeSelect: false, // Allows for selecting a date range on one date picker
128
+ rangeSeparator: ' - ', // Text between two dates in a range
129
+ altField: '', // Selector for an alternate field to store selected dates into
130
+ altFormat: '', // The date format to use for the alternate field
131
+ constrainInput: true // The input is constrained by the current date format
132
+ };
133
+ $.extend(this._defaults, this.regional['']);
134
+ this.dpDiv = $('<div id="' + this._mainDivId + '" style="display: none;"></div>');
135
+ }
136
+
137
+ $.extend(Datepicker.prototype, {
138
+ /* Class name added to elements to indicate already configured with a date picker. */
139
+ markerClassName: 'hasDatepicker',
140
+
141
+ /* Debug logging (if enabled). */
142
+ log: function () {
143
+ if (this.debug)
144
+ console.log.apply('', arguments);
145
+ },
146
+
147
+ /* Override the default settings for all instances of the date picker.
148
+ @param settings object - the new settings to use as defaults (anonymous object)
149
+ @return the manager object */
150
+ setDefaults: function(settings) {
151
+ extendRemove(this._defaults, settings || {});
152
+ return this;
153
+ },
154
+
155
+ /* Attach the date picker to a jQuery selection.
156
+ @param target element - the target input field or division or span
157
+ @param settings object - the new settings to use for this date picker instance (anonymous) */
158
+ _attachDatepicker: function(target, settings) {
159
+ // check for settings on the control itself - in namespace 'date:'
160
+ var inlineSettings = null;
161
+ for (var attrName in this._defaults) {
162
+ var attrValue = target.getAttribute('date:' + attrName);
163
+ if (attrValue) {
164
+ inlineSettings = inlineSettings || {};
165
+ try {
166
+ inlineSettings[attrName] = eval(attrValue);
167
+ } catch (err) {
168
+ inlineSettings[attrName] = attrValue;
169
+ }
170
+ }
171
+ }
172
+ var nodeName = target.nodeName.toLowerCase();
173
+ var inline = (nodeName == 'div' || nodeName == 'span');
174
+ if (!target.id)
175
+ target.id = 'dp' + (++this.uuid);
176
+ var inst = this._newInst($(target), inline);
177
+ inst.settings = $.extend({}, settings || {}, inlineSettings || {});
178
+ if (nodeName == 'input') {
179
+ this._connectDatepicker(target, inst);
180
+ } else if (inline) {
181
+ this._inlineDatepicker(target, inst);
182
+ }
183
+ },
184
+
185
+ /* Create a new instance object. */
186
+ _newInst: function(target, inline) {
187
+ var id = target[0].id.replace(/([:\[\]\.])/g, '\\\\$1'); // escape jQuery meta chars
188
+ return {id: id, input: target, // associated target
189
+ selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
190
+ drawMonth: 0, drawYear: 0, // month being drawn
191
+ inline: inline, // is datepicker inline or not
192
+ dpDiv: (!inline ? this.dpDiv : // presentation div
193
+ $('<div class="' + this._inlineClass + '"></div>'))};
194
+ },
195
+
196
+ /* Attach the date picker to an input field. */
197
+ _connectDatepicker: function(target, inst) {
198
+ var input = $(target);
199
+ if (input.hasClass(this.markerClassName))
200
+ return;
201
+ var appendText = this._get(inst, 'appendText');
202
+ var isRTL = this._get(inst, 'isRTL');
203
+ if (appendText)
204
+ input[isRTL ? 'before' : 'after']('<span class="' + this._appendClass + '">' + appendText + '</span>');
205
+ var showOn = this._get(inst, 'showOn');
206
+ if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field
207
+ input.focus(this._showDatepicker);
208
+ if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked
209
+ var buttonText = this._get(inst, 'buttonText');
210
+ var buttonImage = this._get(inst, 'buttonImage');
211
+ var trigger = $(this._get(inst, 'buttonImageOnly') ?
212
+ $('<img/>').addClass(this._triggerClass).
213
+ attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
214
+ $('<button type="button"></button>').addClass(this._triggerClass).
215
+ html(buttonImage == '' ? buttonText : $('<img/>').attr(
216
+ { src:buttonImage, alt:buttonText, title:buttonText })));
217
+ input[isRTL ? 'before' : 'after'](trigger);
218
+ trigger.click(function() {
219
+ if ($.datepicker._datepickerShowing && $.datepicker._lastInput == target)
220
+ $.datepicker._hideDatepicker();
221
+ else
222
+ $.datepicker._showDatepicker(target);
223
+ return false;
224
+ });
225
+ }
226
+ input.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).
227
+ bind("setData.datepicker", function(event, key, value) {
228
+ inst.settings[key] = value;
229
+ }).bind("getData.datepicker", function(event, key) {
230
+ return this._get(inst, key);
231
+ });
232
+ $.data(target, PROP_NAME, inst);
233
+ },
234
+
235
+ /* Attach an inline date picker to a div. */
236
+ _inlineDatepicker: function(target, inst) {
237
+ var divSpan = $(target);
238
+ if (divSpan.hasClass(this.markerClassName))
239
+ return;
240
+ divSpan.addClass(this.markerClassName).append(inst.dpDiv).
241
+ bind("setData.datepicker", function(event, key, value){
242
+ inst.settings[key] = value;
243
+ }).bind("getData.datepicker", function(event, key){
244
+ return this._get(inst, key);
245
+ });
246
+ $.data(target, PROP_NAME, inst);
247
+ this._setDate(inst, this._getDefaultDate(inst));
248
+ this._updateDatepicker(inst);
249
+ this._updateAlternate(inst);
250
+ },
251
+
252
+ /* Pop-up the date picker in a "dialog" box.
253
+ @param input element - ignored
254
+ @param dateText string - the initial date to display (in the current format)
255
+ @param onSelect function - the function(dateText) to call when a date is selected
256
+ @param settings object - update the dialog date picker instance's settings (anonymous object)
257
+ @param pos int[2] - coordinates for the dialog's position within the screen or
258
+ event - with x/y coordinates or
259
+ leave empty for default (screen centre)
260
+ @return the manager object */
261
+ _dialogDatepicker: function(input, dateText, onSelect, settings, pos) {
262
+ var inst = this._dialogInst; // internal instance
263
+ if (!inst) {
264
+ var id = 'dp' + (++this.uuid);
265
+ this._dialogInput = $('<input type="text" id="' + id +
266
+ '" size="1" style="position: absolute; top: -100px;"/>');
267
+ this._dialogInput.keydown(this._doKeyDown);
268
+ $('body').append(this._dialogInput);
269
+ inst = this._dialogInst = this._newInst(this._dialogInput, false);
270
+ inst.settings = {};
271
+ $.data(this._dialogInput[0], PROP_NAME, inst);
272
+ }
273
+ extendRemove(inst.settings, settings || {});
274
+ this._dialogInput.val(dateText);
275
+
276
+ this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
277
+ if (!this._pos) {
278
+ var browserWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
279
+ var browserHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
280
+ var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
281
+ var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
282
+ this._pos = // should use actual width/height below
283
+ [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
284
+ }
285
+
286
+ // move input on screen for focus, but hidden behind dialog
287
+ this._dialogInput.css('left', this._pos[0] + 'px').css('top', this._pos[1] + 'px');
288
+ inst.settings.onSelect = onSelect;
289
+ this._inDialog = true;
290
+ this.dpDiv.addClass(this._dialogClass);
291
+ this._showDatepicker(this._dialogInput[0]);
292
+ if ($.blockUI)
293
+ $.blockUI(this.dpDiv);
294
+ $.data(this._dialogInput[0], PROP_NAME, inst);
295
+ return this;
296
+ },
297
+
298
+ /* Detach a datepicker from its control.
299
+ @param target element - the target input field or division or span */
300
+ _destroyDatepicker: function(target) {
301
+ var $target = $(target);
302
+ if (!$target.hasClass(this.markerClassName)) {
303
+ return;
304
+ }
305
+ var nodeName = target.nodeName.toLowerCase();
306
+ $.removeData(target, PROP_NAME);
307
+ if (nodeName == 'input') {
308
+ $target.siblings('.' + this._appendClass).remove().end().
309
+ siblings('.' + this._triggerClass).remove().end().
310
+ removeClass(this.markerClassName).
311
+ unbind('focus', this._showDatepicker).
312
+ unbind('keydown', this._doKeyDown).
313
+ unbind('keypress', this._doKeyPress);
314
+ } else if (nodeName == 'div' || nodeName == 'span')
315
+ $target.removeClass(this.markerClassName).empty();
316
+ },
317
+
318
+ /* Enable the date picker to a jQuery selection.
319
+ @param target element - the target input field or division or span */
320
+ _enableDatepicker: function(target) {
321
+ var $target = $(target);
322
+ if (!$target.hasClass(this.markerClassName)) {
323
+ return;
324
+ }
325
+ var nodeName = target.nodeName.toLowerCase();
326
+ if (nodeName == 'input') {
327
+ target.disabled = false;
328
+ $target.siblings('button.' + this._triggerClass).
329
+ each(function() { this.disabled = false; }).end().
330
+ siblings('img.' + this._triggerClass).
331
+ css({opacity: '1.0', cursor: ''});
332
+ }
333
+ else if (nodeName == 'div' || nodeName == 'span') {
334
+ $target.children('.' + this._disableClass).remove();
335
+ }
336
+ this._disabledInputs = $.map(this._disabledInputs,
337
+ function(value) { return (value == target ? null : value); }); // delete entry
338
+ },
339
+
340
+ /* Disable the date picker to a jQuery selection.
341
+ @param target element - the target input field or division or span */
342
+ _disableDatepicker: function(target) {
343
+ var $target = $(target);
344
+ if (!$target.hasClass(this.markerClassName)) {
345
+ return;
346
+ }
347
+ var nodeName = target.nodeName.toLowerCase();
348
+ if (nodeName == 'input') {
349
+ target.disabled = true;
350
+ $target.siblings('button.' + this._triggerClass).
351
+ each(function() { this.disabled = true; }).end().
352
+ siblings('img.' + this._triggerClass).
353
+ css({opacity: '0.5', cursor: 'default'});
354
+ }
355
+ else if (nodeName == 'div' || nodeName == 'span') {
356
+ var inline = $target.children('.' + this._inlineClass);
357
+ var offset = inline.offset();
358
+ var relOffset = {left: 0, top: 0};
359
+ inline.parents().each(function() {
360
+ if ($(this).css('position') == 'relative') {
361
+ relOffset = $(this).offset();
362
+ return false;
363
+ }
364
+ });
365
+ $target.prepend('<div class="' + this._disableClass + '" style="' +
366
+ ($.browser.msie ? 'background-color: transparent; ' : '') +
367
+ 'width: ' + inline.width() + 'px; height: ' + inline.height() +
368
+ 'px; left: ' + (offset.left - relOffset.left) +
369
+ 'px; top: ' + (offset.top - relOffset.top) + 'px;"></div>');
370
+ }
371
+ this._disabledInputs = $.map(this._disabledInputs,
372
+ function(value) { return (value == target ? null : value); }); // delete entry
373
+ this._disabledInputs[this._disabledInputs.length] = target;
374
+ },
375
+
376
+ /* Is the first field in a jQuery collection disabled as a datepicker?
377
+ @param target element - the target input field or division or span
378
+ @return boolean - true if disabled, false if enabled */
379
+ _isDisabledDatepicker: function(target) {
380
+ if (!target)
381
+ return false;
382
+ for (var i = 0; i < this._disabledInputs.length; i++) {
383
+ if (this._disabledInputs[i] == target)
384
+ return true;
385
+ }
386
+ return false;
387
+ },
388
+
389
+ /* Retrieve the instance data for the target control.
390
+ @param target element - the target input field or division or span
391
+ @return object - the associated instance data
392
+ @throws error if a jQuery problem getting data */
393
+ _getInst: function(target) {
394
+ try {
395
+ return $.data(target, PROP_NAME);
396
+ }
397
+ catch (err) {
398
+ throw 'Missing instance data for this datepicker';
399
+ }
400
+ },
401
+
402
+ /* Update the settings for a date picker attached to an input field or division.
403
+ @param target element - the target input field or division or span
404
+ @param name object - the new settings to update or
405
+ string - the name of the setting to change or
406
+ @param value any - the new value for the setting (omit if above is an object) */
407
+ _optionDatepicker: function(target, name, value) {
408
+ var settings = name || {};
409
+ if (typeof name == 'string') {
410
+ settings = {};
411
+ settings[name] = value;
412
+ }
413
+ var inst = this._getInst(target);
414
+ if (inst) {
415
+ if (this._curInst == inst) {
416
+ this._hideDatepicker(null);
417
+ }
418
+ extendRemove(inst.settings, settings);
419
+ var date = new Date();
420
+ extendRemove(inst, {rangeStart: null, // start of range
421
+ endDay: null, endMonth: null, endYear: null, // end of range
422
+ selectedDay: date.getDate(), selectedMonth: date.getMonth(),
423
+ selectedYear: date.getFullYear(), // starting point
424
+ currentDay: date.getDate(), currentMonth: date.getMonth(),
425
+ currentYear: date.getFullYear(), // current selection
426
+ drawMonth: date.getMonth(), drawYear: date.getFullYear()}); // month being drawn
427
+ this._updateDatepicker(inst);
428
+ }
429
+ },
430
+
431
+ // change method deprecated
432
+ _changeDatepicker: function(target, name, value) {
433
+ this._optionDatepicker(target, name, value);
434
+ },
435
+
436
+ /* Redraw the date picker attached to an input field or division.
437
+ @param target element - the target input field or division or span */
438
+ _refreshDatepicker: function(target) {
439
+ var inst = this._getInst(target);
440
+ if (inst) {
441
+ this._updateDatepicker(inst);
442
+ }
443
+ },
444
+
445
+ /* Set the dates for a jQuery selection.
446
+ @param target element - the target input field or division or span
447
+ @param date Date - the new date
448
+ @param endDate Date - the new end date for a range (optional) */
449
+ _setDateDatepicker: function(target, date, endDate) {
450
+ var inst = this._getInst(target);
451
+ if (inst) {
452
+ this._setDate(inst, date, endDate);
453
+ this._updateDatepicker(inst);
454
+ this._updateAlternate(inst);
455
+ }
456
+ },
457
+
458
+ /* Get the date(s) for the first entry in a jQuery selection.
459
+ @param target element - the target input field or division or span
460
+ @return Date - the current date or
461
+ Date[2] - the current dates for a range */
462
+ _getDateDatepicker: function(target) {
463
+ var inst = this._getInst(target);
464
+ if (inst && !inst.inline)
465
+ this._setDateFromField(inst);
466
+ return (inst ? this._getDate(inst) : null);
467
+ },
468
+
469
+ /* Handle keystrokes. */
470
+ _doKeyDown: function(event) {
471
+ var inst = $.datepicker._getInst(event.target);
472
+ var handled = true;
473
+ inst._keyEvent = true;
474
+ if ($.datepicker._datepickerShowing)
475
+ switch (event.keyCode) {
476
+ case 9: $.datepicker._hideDatepicker(null, '');
477
+ break; // hide on tab out
478
+ case 13: var sel = $('td.' + $.datepicker._dayOverClass +
479
+ ', td.' + $.datepicker._currentClass, inst.dpDiv);
480
+ if (sel[0])
481
+ $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
482
+ else
483
+ $.datepicker._hideDatepicker(null, $.datepicker._get(inst, 'duration'));
484
+ return false; // don't submit the form
485
+ break; // select the value on enter
486
+ case 27: $.datepicker._hideDatepicker(null, $.datepicker._get(inst, 'duration'));
487
+ break; // hide on escape
488
+ case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
489
+ -$.datepicker._get(inst, 'stepBigMonths') :
490
+ -$.datepicker._get(inst, 'stepMonths')), 'M');
491
+ break; // previous month/year on page up/+ ctrl
492
+ case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
493
+ +$.datepicker._get(inst, 'stepBigMonths') :
494
+ +$.datepicker._get(inst, 'stepMonths')), 'M');
495
+ break; // next month/year on page down/+ ctrl
496
+ case 35: if (event.ctrlKey || event.metaKey) $.datepicker._clearDate(event.target);
497
+ handled = event.ctrlKey || event.metaKey;
498
+ break; // clear on ctrl or command +end
499
+ case 36: if (event.ctrlKey || event.metaKey) $.datepicker._gotoToday(event.target);
500
+ handled = event.ctrlKey || event.metaKey;
501
+ break; // current on ctrl or command +home
502
+ case 37: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, -1, 'D');
503
+ handled = event.ctrlKey || event.metaKey;
504
+ // -1 day on ctrl or command +left
505
+ if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
506
+ -$.datepicker._get(inst, 'stepBigMonths') :
507
+ -$.datepicker._get(inst, 'stepMonths')), 'M');
508
+ // next month/year on alt +left on Mac
509
+ break;
510
+ case 38: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, -7, 'D');
511
+ handled = event.ctrlKey || event.metaKey;
512
+ break; // -1 week on ctrl or command +up
513
+ case 39: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, +1, 'D');
514
+ handled = event.ctrlKey || event.metaKey;
515
+ // +1 day on ctrl or command +right
516
+ if (event.originalEvent.altKey) $.datepicker._adjustDate(event.target, (event.ctrlKey ?
517
+ +$.datepicker._get(inst, 'stepBigMonths') :
518
+ +$.datepicker._get(inst, 'stepMonths')), 'M');
519
+ // next month/year on alt +right
520
+ break;
521
+ case 40: if (event.ctrlKey || event.metaKey) $.datepicker._adjustDate(event.target, +7, 'D');
522
+ handled = event.ctrlKey || event.metaKey;
523
+ break; // +1 week on ctrl or command +down
524
+ default: handled = false;
525
+ }
526
+ else if (event.keyCode == 36 && event.ctrlKey) // display the date picker on ctrl+home
527
+ $.datepicker._showDatepicker(this);
528
+ else {
529
+ handled = false;
530
+ }
531
+ if (handled) {
532
+ event.preventDefault();
533
+ event.stopPropagation();
534
+ }
535
+ },
536
+
537
+ /* Filter entered characters - based on date format. */
538
+ _doKeyPress: function(event) {
539
+ var inst = $.datepicker._getInst(event.target);
540
+ if ($.datepicker._get(inst, 'constrainInput')) {
541
+ var chars = $.datepicker._possibleChars($.datepicker._get(inst, 'dateFormat'));
542
+ var chr = String.fromCharCode(event.charCode == undefined ? event.keyCode : event.charCode);
543
+ return event.ctrlKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1);
544
+ }
545
+ },
546
+
547
+ /* Pop-up the date picker for a given input field.
548
+ @param input element - the input field attached to the date picker or
549
+ event - if triggered by focus */
550
+ _showDatepicker: function(input) {
551
+ input = input.target || input;
552
+ if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger
553
+ input = $('input', input.parentNode)[0];
554
+ if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput == input) // already here
555
+ return;
556
+ var inst = $.datepicker._getInst(input);
557
+ var beforeShow = $.datepicker._get(inst, 'beforeShow');
558
+ extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
559
+ $.datepicker._hideDatepicker(null, '');
560
+ $.datepicker._lastInput = input;
561
+ $.datepicker._setDateFromField(inst);
562
+ if ($.datepicker._inDialog) // hide cursor
563
+ input.value = '';
564
+ if (!$.datepicker._pos) { // position below input
565
+ $.datepicker._pos = $.datepicker._findPos(input);
566
+ $.datepicker._pos[1] += input.offsetHeight; // add the height
567
+ }
568
+ var isFixed = false;
569
+ $(input).parents().each(function() {
570
+ isFixed |= $(this).css('position') == 'fixed';
571
+ return !isFixed;
572
+ });
573
+ if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled
574
+ $.datepicker._pos[0] -= document.documentElement.scrollLeft;
575
+ $.datepicker._pos[1] -= document.documentElement.scrollTop;
576
+ }
577
+ var offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
578
+ $.datepicker._pos = null;
579
+ inst.rangeStart = null;
580
+ // determine sizing offscreen
581
+ inst.dpDiv.css({position: 'absolute', display: 'block', top: '-1000px'});
582
+ $.datepicker._updateDatepicker(inst);
583
+ // fix width for dynamic number of date pickers
584
+ inst.dpDiv.width($.datepicker._getNumberOfMonths(inst)[1] *
585
+ $('.ui-datepicker', inst.dpDiv[0])[0].offsetWidth);
586
+ // and adjust position before showing
587
+ offset = $.datepicker._checkOffset(inst, offset, isFixed);
588
+ inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
589
+ 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
590
+ left: offset.left + 'px', top: offset.top + 'px'});
591
+ if (!inst.inline) {
592
+ var showAnim = $.datepicker._get(inst, 'showAnim') || 'show';
593
+ var duration = $.datepicker._get(inst, 'duration');
594
+ var postProcess = function() {
595
+ $.datepicker._datepickerShowing = true;
596
+ if ($.browser.msie && parseInt($.browser.version,10) < 7) // fix IE < 7 select problems
597
+ $('iframe.ui-datepicker-cover').css({width: inst.dpDiv.width() + 4,
598
+ height: inst.dpDiv.height() + 4});
599
+ };
600
+ if ($.effects && $.effects[showAnim])
601
+ inst.dpDiv.show(showAnim, $.datepicker._get(inst, 'showOptions'), duration, postProcess);
602
+ else
603
+ inst.dpDiv[showAnim](duration, postProcess);
604
+ if (duration == '')
605
+ postProcess();
606
+ if (inst.input[0].type != 'hidden')
607
+ inst.input[0].focus();
608
+ $.datepicker._curInst = inst;
609
+ }
610
+ },
611
+
612
+ /* Generate the date picker content. */
613
+ _updateDatepicker: function(inst) {
614
+ var dims = {width: inst.dpDiv.width() + 4,
615
+ height: inst.dpDiv.height() + 4};
616
+ inst.dpDiv.empty().append(this._generateHTML(inst)).
617
+ find('iframe.ui-datepicker-cover').
618
+ css({width: dims.width, height: dims.height});
619
+ var numMonths = this._getNumberOfMonths(inst);
620
+ inst.dpDiv[(numMonths[0] != 1 || numMonths[1] != 1 ? 'add' : 'remove') +
621
+ 'Class']('ui-datepicker-multi');
622
+ inst.dpDiv[(this._get(inst, 'isRTL') ? 'add' : 'remove') +
623
+ 'Class']('ui-datepicker-rtl');
624
+ if (inst.input && inst.input[0].type != 'hidden' && inst == $.datepicker._curInst)
625
+ $(inst.input[0]).focus();
626
+ },
627
+
628
+ /* Check positioning to remain on screen. */
629
+ _checkOffset: function(inst, offset, isFixed) {
630
+ var pos = inst.input ? this._findPos(inst.input[0]) : null;
631
+ var browserWidth = window.innerWidth || (document.documentElement ?
632
+ document.documentElement.clientWidth : document.body.clientWidth);
633
+ var browserHeight = window.innerHeight || (document.documentElement ?
634
+ document.documentElement.clientHeight : document.body.clientHeight);
635
+ var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
636
+ var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
637
+ // reposition date picker horizontally if outside the browser window
638
+ if (this._get(inst, 'isRTL') || (offset.left + inst.dpDiv.width() - scrollX) > browserWidth)
639
+ offset.left = Math.max((isFixed ? 0 : scrollX),
640
+ pos[0] + (inst.input ? inst.input.width() : 0) - (isFixed ? scrollX : 0) - inst.dpDiv.width() -
641
+ (isFixed && $.browser.opera ? document.documentElement.scrollLeft : 0));
642
+ else
643
+ offset.left -= (isFixed ? scrollX : 0);
644
+ // reposition date picker vertically if outside the browser window
645
+ if ((offset.top + inst.dpDiv.height() - scrollY) > browserHeight)
646
+ offset.top = Math.max((isFixed ? 0 : scrollY),
647
+ pos[1] - (isFixed ? scrollY : 0) - (this._inDialog ? 0 : inst.dpDiv.height()) -
648
+ (isFixed && $.browser.opera ? document.documentElement.scrollTop : 0));
649
+ else
650
+ offset.top -= (isFixed ? scrollY : 0);
651
+ return offset;
652
+ },
653
+
654
+ /* Find an object's position on the screen. */
655
+ _findPos: function(obj) {
656
+ while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
657
+ obj = obj.nextSibling;
658
+ }
659
+ var position = $(obj).offset();
660
+ return [position.left, position.top];
661
+ },
662
+
663
+ /* Hide the date picker from view.
664
+ @param input element - the input field attached to the date picker
665
+ @param duration string - the duration over which to close the date picker */
666
+ _hideDatepicker: function(input, duration) {
667
+ var inst = this._curInst;
668
+ if (!inst || (input && inst != $.data(input, PROP_NAME)))
669
+ return;
670
+ var rangeSelect = this._get(inst, 'rangeSelect');
671
+ if (rangeSelect && inst.stayOpen)
672
+ this._selectDate('#' + inst.id, this._formatDate(inst,
673
+ inst.currentDay, inst.currentMonth, inst.currentYear));
674
+ inst.stayOpen = false;
675
+ if (this._datepickerShowing) {
676
+ duration = (duration != null ? duration : this._get(inst, 'duration'));
677
+ var showAnim = this._get(inst, 'showAnim');
678
+ var postProcess = function() {
679
+ $.datepicker._tidyDialog(inst);
680
+ };
681
+ if (duration != '' && $.effects && $.effects[showAnim])
682
+ inst.dpDiv.hide(showAnim, $.datepicker._get(inst, 'showOptions'),
683
+ duration, postProcess);
684
+ else
685
+ inst.dpDiv[(duration == '' ? 'hide' : (showAnim == 'slideDown' ? 'slideUp' :
686
+ (showAnim == 'fadeIn' ? 'fadeOut' : 'hide')))](duration, postProcess);
687
+ if (duration == '')
688
+ this._tidyDialog(inst);
689
+ var onClose = this._get(inst, 'onClose');
690
+ if (onClose)
691
+ onClose.apply((inst.input ? inst.input[0] : null),
692
+ [(inst.input ? inst.input.val() : ''), inst]); // trigger custom callback
693
+ this._datepickerShowing = false;
694
+ this._lastInput = null;
695
+ inst.settings.prompt = null;
696
+ if (this._inDialog) {
697
+ this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
698
+ if ($.blockUI) {
699
+ $.unblockUI();
700
+ $('body').append(this.dpDiv);
701
+ }
702
+ }
703
+ this._inDialog = false;
704
+ }
705
+ this._curInst = null;
706
+ },
707
+
708
+ /* Tidy up after a dialog display. */
709
+ _tidyDialog: function(inst) {
710
+ inst.dpDiv.removeClass(this._dialogClass).unbind('.ui-datepicker');
711
+ $('.' + this._promptClass, inst.dpDiv).remove();
712
+ },
713
+
714
+ /* Close date picker if clicked elsewhere. */
715
+ _checkExternalClick: function(event) {
716
+ if (!$.datepicker._curInst)
717
+ return;
718
+ var $target = $(event.target);
719
+ if (($target.parents('#' + $.datepicker._mainDivId).length == 0) &&
720
+ !$target.hasClass($.datepicker.markerClassName) &&
721
+ !$target.hasClass($.datepicker._triggerClass) &&
722
+ $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI))
723
+ $.datepicker._hideDatepicker(null, '');
724
+ },
725
+
726
+ /* Adjust one of the date sub-fields. */
727
+ _adjustDate: function(id, offset, period) {
728
+ var target = $(id);
729
+ var inst = this._getInst(target[0]);
730
+ this._adjustInstDate(inst, offset, period);
731
+ this._updateDatepicker(inst);
732
+ },
733
+
734
+ /* Action for current link. */
735
+ _gotoToday: function(id) {
736
+ var target = $(id);
737
+ var inst = this._getInst(target[0]);
738
+ if (this._get(inst, 'gotoCurrent') && inst.currentDay) {
739
+ inst.selectedDay = inst.currentDay;
740
+ inst.drawMonth = inst.selectedMonth = inst.currentMonth;
741
+ inst.drawYear = inst.selectedYear = inst.currentYear;
742
+ }
743
+ else {
744
+ var date = new Date();
745
+ inst.selectedDay = date.getDate();
746
+ inst.drawMonth = inst.selectedMonth = date.getMonth();
747
+ inst.drawYear = inst.selectedYear = date.getFullYear();
748
+ }
749
+ this._notifyChange(inst);
750
+ this._adjustDate(target);
751
+ },
752
+
753
+ /* Action for selecting a new month/year. */
754
+ _selectMonthYear: function(id, select, period) {
755
+ var target = $(id);
756
+ var inst = this._getInst(target[0]);
757
+ inst._selectingMonthYear = false;
758
+ inst['selected' + (period == 'M' ? 'Month' : 'Year')] =
759
+ inst['draw' + (period == 'M' ? 'Month' : 'Year')] =
760
+ parseInt(select.options[select.selectedIndex].value,10);
761
+ this._notifyChange(inst);
762
+ this._adjustDate(target);
763
+ },
764
+
765
+ /* Restore input focus after not changing month/year. */
766
+ _clickMonthYear: function(id) {
767
+ var target = $(id);
768
+ var inst = this._getInst(target[0]);
769
+ if (inst.input && inst._selectingMonthYear && !$.browser.msie)
770
+ inst.input[0].focus();
771
+ inst._selectingMonthYear = !inst._selectingMonthYear;
772
+ },
773
+
774
+ /* Action for changing the first week day. */
775
+ _changeFirstDay: function(id, day) {
776
+ var target = $(id);
777
+ var inst = this._getInst(target[0]);
778
+ inst.settings.firstDay = day;
779
+ this._updateDatepicker(inst);
780
+ },
781
+
782
+ /* Action for selecting a day. */
783
+ _selectDay: function(id, month, year, td) {
784
+ if ($(td).hasClass(this._unselectableClass))
785
+ return;
786
+ var target = $(id);
787
+ var inst = this._getInst(target[0]);
788
+ var rangeSelect = this._get(inst, 'rangeSelect');
789
+ if (rangeSelect) {
790
+ inst.stayOpen = !inst.stayOpen;
791
+ if (inst.stayOpen) {
792
+ $('.ui-datepicker td', inst.dpDiv).removeClass(this._currentClass);
793
+ $(td).addClass(this._currentClass);
794
+ }
795
+ }
796
+ inst.selectedDay = inst.currentDay = $('a', td).html();
797
+ inst.selectedMonth = inst.currentMonth = month;
798
+ inst.selectedYear = inst.currentYear = year;
799
+ if (inst.stayOpen) {
800
+ inst.endDay = inst.endMonth = inst.endYear = null;
801
+ }
802
+ else if (rangeSelect) {
803
+ inst.endDay = inst.currentDay;
804
+ inst.endMonth = inst.currentMonth;
805
+ inst.endYear = inst.currentYear;
806
+ }
807
+ this._selectDate(id, this._formatDate(inst,
808
+ inst.currentDay, inst.currentMonth, inst.currentYear));
809
+ if (inst.stayOpen) {
810
+ inst.rangeStart = this._daylightSavingAdjust(
811
+ new Date(inst.currentYear, inst.currentMonth, inst.currentDay));
812
+ this._updateDatepicker(inst);
813
+ }
814
+ else if (rangeSelect) {
815
+ inst.selectedDay = inst.currentDay = inst.rangeStart.getDate();
816
+ inst.selectedMonth = inst.currentMonth = inst.rangeStart.getMonth();
817
+ inst.selectedYear = inst.currentYear = inst.rangeStart.getFullYear();
818
+ inst.rangeStart = null;
819
+ if (inst.inline)
820
+ this._updateDatepicker(inst);
821
+ }
822
+ },
823
+
824
+ /* Erase the input field and hide the date picker. */
825
+ _clearDate: function(id) {
826
+ var target = $(id);
827
+ var inst = this._getInst(target[0]);
828
+ if (this._get(inst, 'mandatory'))
829
+ return;
830
+ inst.stayOpen = false;
831
+ inst.endDay = inst.endMonth = inst.endYear = inst.rangeStart = null;
832
+ this._selectDate(target, '');
833
+ },
834
+
835
+ /* Update the input field with the selected date. */
836
+ _selectDate: function(id, dateStr) {
837
+ var target = $(id);
838
+ var inst = this._getInst(target[0]);
839
+ dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
840
+ if (this._get(inst, 'rangeSelect') && dateStr)
841
+ dateStr = (inst.rangeStart ? this._formatDate(inst, inst.rangeStart) :
842
+ dateStr) + this._get(inst, 'rangeSeparator') + dateStr;
843
+ if (inst.input)
844
+ inst.input.val(dateStr);
845
+ this._updateAlternate(inst);
846
+ var onSelect = this._get(inst, 'onSelect');
847
+ if (onSelect)
848
+ onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
849
+ else if (inst.input)
850
+ inst.input.trigger('change'); // fire the change event
851
+ if (inst.inline)
852
+ this._updateDatepicker(inst);
853
+ else if (!inst.stayOpen) {
854
+ this._hideDatepicker(null, this._get(inst, 'duration'));
855
+ this._lastInput = inst.input[0];
856
+ if (typeof(inst.input[0]) != 'object')
857
+ inst.input[0].focus(); // restore focus
858
+ this._lastInput = null;
859
+ }
860
+ },
861
+
862
+ /* Update any alternate field to synchronise with the main field. */
863
+ _updateAlternate: function(inst) {
864
+ var altField = this._get(inst, 'altField');
865
+ if (altField) { // update alternate field too
866
+ var altFormat = this._get(inst, 'altFormat') || this._get(inst, 'dateFormat');
867
+ var date = this._getDate(inst);
868
+ dateStr = (isArray(date) ? (!date[0] && !date[1] ? '' :
869
+ this.formatDate(altFormat, date[0], this._getFormatConfig(inst)) +
870
+ this._get(inst, 'rangeSeparator') + this.formatDate(
871
+ altFormat, date[1] || date[0], this._getFormatConfig(inst))) :
872
+ this.formatDate(altFormat, date, this._getFormatConfig(inst)));
873
+ $(altField).each(function() { $(this).val(dateStr); });
874
+ }
875
+ },
876
+
877
+ /* Set as beforeShowDay function to prevent selection of weekends.
878
+ @param date Date - the date to customise
879
+ @return [boolean, string] - is this date selectable?, what is its CSS class? */
880
+ noWeekends: function(date) {
881
+ var day = date.getDay();
882
+ return [(day > 0 && day < 6), ''];
883
+ },
884
+
885
+ /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
886
+ @param date Date - the date to get the week for
887
+ @return number - the number of the week within the year that contains this date */
888
+ iso8601Week: function(date) {
889
+ var checkDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
890
+ var firstMon = new Date(checkDate.getFullYear(), 1 - 1, 4); // First week always contains 4 Jan
891
+ var firstDay = firstMon.getDay() || 7; // Day of week: Mon = 1, ..., Sun = 7
892
+ firstMon.setDate(firstMon.getDate() + 1 - firstDay); // Preceding Monday
893
+ if (firstDay < 4 && checkDate < firstMon) { // Adjust first three days in year if necessary
894
+ checkDate.setDate(checkDate.getDate() - 3); // Generate for previous year
895
+ return $.datepicker.iso8601Week(checkDate);
896
+ } else if (checkDate > new Date(checkDate.getFullYear(), 12 - 1, 28)) { // Check last three days in year
897
+ firstDay = new Date(checkDate.getFullYear() + 1, 1 - 1, 4).getDay() || 7;
898
+ if (firstDay > 4 && (checkDate.getDay() || 7) < firstDay - 3) { // Adjust if necessary
899
+ return 1;
900
+ }
901
+ }
902
+ return Math.floor(((checkDate - firstMon) / 86400000) / 7) + 1; // Weeks to given date
903
+ },
904
+
905
+ /* Provide status text for a particular date.
906
+ @param date the date to get the status for
907
+ @param inst the current datepicker instance
908
+ @return the status display text for this date */
909
+ dateStatus: function(date, inst) {
910
+ return $.datepicker.formatDate($.datepicker._get(inst, 'dateStatus'),
911
+ date, $.datepicker._getFormatConfig(inst));
912
+ },
913
+
914
+ /* Parse a string value into a date object.
915
+ See formatDate below for the possible formats.
916
+
917
+ @param format string - the expected format of the date
918
+ @param value string - the date in the above format
919
+ @param settings Object - attributes include:
920
+ shortYearCutoff number - the cutoff year for determining the century (optional)
921
+ dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
922
+ dayNames string[7] - names of the days from Sunday (optional)
923
+ monthNamesShort string[12] - abbreviated names of the months (optional)
924
+ monthNames string[12] - names of the months (optional)
925
+ @return Date - the extracted date value or null if value is blank */
926
+ parseDate: function (format, value, settings) {
927
+ if (format == null || value == null)
928
+ throw 'Invalid arguments';
929
+ value = (typeof value == 'object' ? value.toString() : value + '');
930
+ if (value == '')
931
+ return null;
932
+ var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff;
933
+ var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
934
+ var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
935
+ var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
936
+ var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
937
+ var year = -1;
938
+ var month = -1;
939
+ var day = -1;
940
+ var doy = -1;
941
+ var literal = false;
942
+ // Check whether a format character is doubled
943
+ var lookAhead = function(match) {
944
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
945
+ if (matches)
946
+ iFormat++;
947
+ return matches;
948
+ };
949
+ // Extract a number from the string value
950
+ var getNumber = function(match) {
951
+ lookAhead(match);
952
+ var origSize = (match == '@' ? 14 : (match == 'y' ? 4 : (match == 'o' ? 3 : 2)));
953
+ var size = origSize;
954
+ var num = 0;
955
+ while (size > 0 && iValue < value.length &&
956
+ value.charAt(iValue) >= '0' && value.charAt(iValue) <= '9') {
957
+ num = num * 10 + parseInt(value.charAt(iValue++),10);
958
+ size--;
959
+ }
960
+ if (size == origSize)
961
+ throw 'Missing number at position ' + iValue;
962
+ return num;
963
+ };
964
+ // Extract a name from the string value and convert to an index
965
+ var getName = function(match, shortNames, longNames) {
966
+ var names = (lookAhead(match) ? longNames : shortNames);
967
+ var size = 0;
968
+ for (var j = 0; j < names.length; j++)
969
+ size = Math.max(size, names[j].length);
970
+ var name = '';
971
+ var iInit = iValue;
972
+ while (size > 0 && iValue < value.length) {
973
+ name += value.charAt(iValue++);
974
+ for (var i = 0; i < names.length; i++)
975
+ if (name == names[i])
976
+ return i + 1;
977
+ size--;
978
+ }
979
+ throw 'Unknown name at position ' + iInit;
980
+ };
981
+ // Confirm that a literal character matches the string value
982
+ var checkLiteral = function() {
983
+ if (value.charAt(iValue) != format.charAt(iFormat))
984
+ throw 'Unexpected literal at position ' + iValue;
985
+ iValue++;
986
+ };
987
+ var iValue = 0;
988
+ for (var iFormat = 0; iFormat < format.length; iFormat++) {
989
+ if (literal)
990
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
991
+ literal = false;
992
+ else
993
+ checkLiteral();
994
+ else
995
+ switch (format.charAt(iFormat)) {
996
+ case 'd':
997
+ day = getNumber('d');
998
+ break;
999
+ case 'D':
1000
+ getName('D', dayNamesShort, dayNames);
1001
+ break;
1002
+ case 'o':
1003
+ doy = getNumber('o');
1004
+ break;
1005
+ case 'm':
1006
+ month = getNumber('m');
1007
+ break;
1008
+ case 'M':
1009
+ month = getName('M', monthNamesShort, monthNames);
1010
+ break;
1011
+ case 'y':
1012
+ year = getNumber('y');
1013
+ break;
1014
+ case '@':
1015
+ var date = new Date(getNumber('@'));
1016
+ year = date.getFullYear();
1017
+ month = date.getMonth() + 1;
1018
+ day = date.getDate();
1019
+ break;
1020
+ case "'":
1021
+ if (lookAhead("'"))
1022
+ checkLiteral();
1023
+ else
1024
+ literal = true;
1025
+ break;
1026
+ default:
1027
+ checkLiteral();
1028
+ }
1029
+ }
1030
+ if (year == -1)
1031
+ year = new Date().getFullYear();
1032
+ else if (year < 100)
1033
+ year += new Date().getFullYear() - new Date().getFullYear() % 100 +
1034
+ (year <= shortYearCutoff ? 0 : -100);
1035
+ if (doy > -1) {
1036
+ month = 1;
1037
+ day = doy;
1038
+ do {
1039
+ var dim = this._getDaysInMonth(year, month - 1);
1040
+ if (day <= dim)
1041
+ break;
1042
+ month++;
1043
+ day -= dim;
1044
+ } while (true);
1045
+ }
1046
+ var date = this._daylightSavingAdjust(new Date(year, month - 1, day));
1047
+ if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day)
1048
+ throw 'Invalid date'; // E.g. 31/02/*
1049
+ return date;
1050
+ },
1051
+
1052
+ /* Standard date formats. */
1053
+ ATOM: 'yy-mm-dd', // RFC 3339 (ISO 8601)
1054
+ COOKIE: 'D, dd M yy',
1055
+ ISO_8601: 'yy-mm-dd',
1056
+ RFC_822: 'D, d M y',
1057
+ RFC_850: 'DD, dd-M-y',
1058
+ RFC_1036: 'D, d M y',
1059
+ RFC_1123: 'D, d M yy',
1060
+ RFC_2822: 'D, d M yy',
1061
+ RSS: 'D, d M y', // RFC 822
1062
+ TIMESTAMP: '@',
1063
+ W3C: 'yy-mm-dd', // ISO 8601
1064
+
1065
+ /* Format a date object into a string value.
1066
+ The format can be combinations of the following:
1067
+ d - day of month (no leading zero)
1068
+ dd - day of month (two digit)
1069
+ o - day of year (no leading zeros)
1070
+ oo - day of year (three digit)
1071
+ D - day name short
1072
+ DD - day name long
1073
+ m - month of year (no leading zero)
1074
+ mm - month of year (two digit)
1075
+ M - month name short
1076
+ MM - month name long
1077
+ y - year (two digit)
1078
+ yy - year (four digit)
1079
+ @ - Unix timestamp (ms since 01/01/1970)
1080
+ '...' - literal text
1081
+ '' - single quote
1082
+
1083
+ @param format string - the desired format of the date
1084
+ @param date Date - the date value to format
1085
+ @param settings Object - attributes include:
1086
+ dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
1087
+ dayNames string[7] - names of the days from Sunday (optional)
1088
+ monthNamesShort string[12] - abbreviated names of the months (optional)
1089
+ monthNames string[12] - names of the months (optional)
1090
+ @return string - the date in the above format */
1091
+ formatDate: function (format, date, settings) {
1092
+ if (!date)
1093
+ return '';
1094
+ var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort;
1095
+ var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames;
1096
+ var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort;
1097
+ var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames;
1098
+ // Check whether a format character is doubled
1099
+ var lookAhead = function(match) {
1100
+ var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match);
1101
+ if (matches)
1102
+ iFormat++;
1103
+ return matches;
1104
+ };
1105
+ // Format a number, with leading zero if necessary
1106
+ var formatNumber = function(match, value, len) {
1107
+ var num = '' + value;
1108
+ if (lookAhead(match))
1109
+ while (num.length < len)
1110
+ num = '0' + num;
1111
+ return num;
1112
+ };
1113
+ // Format a name, short or long as requested
1114
+ var formatName = function(match, value, shortNames, longNames) {
1115
+ return (lookAhead(match) ? longNames[value] : shortNames[value]);
1116
+ };
1117
+ var output = '';
1118
+ var literal = false;
1119
+ if (date)
1120
+ for (var iFormat = 0; iFormat < format.length; iFormat++) {
1121
+ if (literal)
1122
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
1123
+ literal = false;
1124
+ else
1125
+ output += format.charAt(iFormat);
1126
+ else
1127
+ switch (format.charAt(iFormat)) {
1128
+ case 'd':
1129
+ output += formatNumber('d', date.getDate(), 2);
1130
+ break;
1131
+ case 'D':
1132
+ output += formatName('D', date.getDay(), dayNamesShort, dayNames);
1133
+ break;
1134
+ case 'o':
1135
+ var doy = date.getDate();
1136
+ for (var m = date.getMonth() - 1; m >= 0; m--)
1137
+ doy += this._getDaysInMonth(date.getFullYear(), m);
1138
+ output += formatNumber('o', doy, 3);
1139
+ break;
1140
+ case 'm':
1141
+ output += formatNumber('m', date.getMonth() + 1, 2);
1142
+ break;
1143
+ case 'M':
1144
+ output += formatName('M', date.getMonth(), monthNamesShort, monthNames);
1145
+ break;
1146
+ case 'y':
1147
+ output += (lookAhead('y') ? date.getFullYear() :
1148
+ (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100);
1149
+ break;
1150
+ case '@':
1151
+ output += date.getTime();
1152
+ break;
1153
+ case "'":
1154
+ if (lookAhead("'"))
1155
+ output += "'";
1156
+ else
1157
+ literal = true;
1158
+ break;
1159
+ default:
1160
+ output += format.charAt(iFormat);
1161
+ }
1162
+ }
1163
+ return output;
1164
+ },
1165
+
1166
+ /* Extract all possible characters from the date format. */
1167
+ _possibleChars: function (format) {
1168
+ var chars = '';
1169
+ var literal = false;
1170
+ for (var iFormat = 0; iFormat < format.length; iFormat++)
1171
+ if (literal)
1172
+ if (format.charAt(iFormat) == "'" && !lookAhead("'"))
1173
+ literal = false;
1174
+ else
1175
+ chars += format.charAt(iFormat);
1176
+ else
1177
+ switch (format.charAt(iFormat)) {
1178
+ case 'd': case 'm': case 'y': case '@':
1179
+ chars += '0123456789';
1180
+ break;
1181
+ case 'D': case 'M':
1182
+ return null; // Accept anything
1183
+ case "'":
1184
+ if (lookAhead("'"))
1185
+ chars += "'";
1186
+ else
1187
+ literal = true;
1188
+ break;
1189
+ default:
1190
+ chars += format.charAt(iFormat);
1191
+ }
1192
+ return chars;
1193
+ },
1194
+
1195
+ /* Get a setting value, defaulting if necessary. */
1196
+ _get: function(inst, name) {
1197
+ return inst.settings[name] !== undefined ?
1198
+ inst.settings[name] : this._defaults[name];
1199
+ },
1200
+
1201
+ /* Parse existing date and initialise date picker. */
1202
+ _setDateFromField: function(inst) {
1203
+ var dateFormat = this._get(inst, 'dateFormat');
1204
+ var dates = inst.input ? inst.input.val().split(this._get(inst, 'rangeSeparator')) : null;
1205
+ inst.endDay = inst.endMonth = inst.endYear = null;
1206
+ var date = defaultDate = this._getDefaultDate(inst);
1207
+ if (dates.length > 0) {
1208
+ var settings = this._getFormatConfig(inst);
1209
+ if (dates.length > 1) {
1210
+ date = this.parseDate(dateFormat, dates[1], settings) || defaultDate;
1211
+ inst.endDay = date.getDate();
1212
+ inst.endMonth = date.getMonth();
1213
+ inst.endYear = date.getFullYear();
1214
+ }
1215
+ try {
1216
+ date = this.parseDate(dateFormat, dates[0], settings) || defaultDate;
1217
+ } catch (event) {
1218
+ this.log(event);
1219
+ date = defaultDate;
1220
+ }
1221
+ }
1222
+ inst.selectedDay = date.getDate();
1223
+ inst.drawMonth = inst.selectedMonth = date.getMonth();
1224
+ inst.drawYear = inst.selectedYear = date.getFullYear();
1225
+ inst.currentDay = (dates[0] ? date.getDate() : 0);
1226
+ inst.currentMonth = (dates[0] ? date.getMonth() : 0);
1227
+ inst.currentYear = (dates[0] ? date.getFullYear() : 0);
1228
+ this._adjustInstDate(inst);
1229
+ },
1230
+
1231
+ /* Retrieve the default date shown on opening. */
1232
+ _getDefaultDate: function(inst) {
1233
+ var date = this._determineDate(this._get(inst, 'defaultDate'), new Date());
1234
+ var minDate = this._getMinMaxDate(inst, 'min', true);
1235
+ var maxDate = this._getMinMaxDate(inst, 'max');
1236
+ date = (minDate && date < minDate ? minDate : date);
1237
+ date = (maxDate && date > maxDate ? maxDate : date);
1238
+ return date;
1239
+ },
1240
+
1241
+ /* A date may be specified as an exact value or a relative one. */
1242
+ _determineDate: function(date, defaultDate) {
1243
+ var offsetNumeric = function(offset) {
1244
+ var date = new Date();
1245
+ date.setDate(date.getDate() + offset);
1246
+ return date;
1247
+ };
1248
+ var offsetString = function(offset, getDaysInMonth) {
1249
+ var date = new Date();
1250
+ var year = date.getFullYear();
1251
+ var month = date.getMonth();
1252
+ var day = date.getDate();
1253
+ var pattern = /([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g;
1254
+ var matches = pattern.exec(offset);
1255
+ while (matches) {
1256
+ switch (matches[2] || 'd') {
1257
+ case 'd' : case 'D' :
1258
+ day += parseInt(matches[1],10); break;
1259
+ case 'w' : case 'W' :
1260
+ day += parseInt(matches[1],10) * 7; break;
1261
+ case 'm' : case 'M' :
1262
+ month += parseInt(matches[1],10);
1263
+ day = Math.min(day, getDaysInMonth(year, month));
1264
+ break;
1265
+ case 'y': case 'Y' :
1266
+ year += parseInt(matches[1],10);
1267
+ day = Math.min(day, getDaysInMonth(year, month));
1268
+ break;
1269
+ }
1270
+ matches = pattern.exec(offset);
1271
+ }
1272
+ return new Date(year, month, day);
1273
+ };
1274
+ date = (date == null ? defaultDate :
1275
+ (typeof date == 'string' ? offsetString(date, this._getDaysInMonth) :
1276
+ (typeof date == 'number' ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : date)));
1277
+ date = (date && date.toString() == 'Invalid Date' ? defaultDate : date);
1278
+ if (date) {
1279
+ date.setHours(0);
1280
+ date.setMinutes(0);
1281
+ date.setSeconds(0);
1282
+ date.setMilliseconds(0);
1283
+ }
1284
+ return this._daylightSavingAdjust(date);
1285
+ },
1286
+
1287
+ /* Handle switch to/from daylight saving.
1288
+ Hours may be non-zero on daylight saving cut-over:
1289
+ > 12 when midnight changeover, but then cannot generate
1290
+ midnight datetime, so jump to 1AM, otherwise reset.
1291
+ @param date (Date) the date to check
1292
+ @return (Date) the corrected date */
1293
+ _daylightSavingAdjust: function(date) {
1294
+ if (!date) return null;
1295
+ date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
1296
+ return date;
1297
+ },
1298
+
1299
+ /* Set the date(s) directly. */
1300
+ _setDate: function(inst, date, endDate) {
1301
+ var clear = !(date);
1302
+ var origMonth = inst.selectedMonth;
1303
+ var origYear = inst.selectedYear;
1304
+ date = this._determineDate(date, new Date());
1305
+ inst.selectedDay = inst.currentDay = date.getDate();
1306
+ inst.drawMonth = inst.selectedMonth = inst.currentMonth = date.getMonth();
1307
+ inst.drawYear = inst.selectedYear = inst.currentYear = date.getFullYear();
1308
+ if (this._get(inst, 'rangeSelect')) {
1309
+ if (endDate) {
1310
+ endDate = this._determineDate(endDate, null);
1311
+ inst.endDay = endDate.getDate();
1312
+ inst.endMonth = endDate.getMonth();
1313
+ inst.endYear = endDate.getFullYear();
1314
+ } else {
1315
+ inst.endDay = inst.currentDay;
1316
+ inst.endMonth = inst.currentMonth;
1317
+ inst.endYear = inst.currentYear;
1318
+ }
1319
+ }
1320
+ if (origMonth != inst.selectedMonth || origYear != inst.selectedYear)
1321
+ this._notifyChange(inst);
1322
+ this._adjustInstDate(inst);
1323
+ if (inst.input)
1324
+ inst.input.val(clear ? '' : this._formatDate(inst) +
1325
+ (!this._get(inst, 'rangeSelect') ? '' : this._get(inst, 'rangeSeparator') +
1326
+ this._formatDate(inst, inst.endDay, inst.endMonth, inst.endYear)));
1327
+ },
1328
+
1329
+ /* Retrieve the date(s) directly. */
1330
+ _getDate: function(inst) {
1331
+ var startDate = (!inst.currentYear || (inst.input && inst.input.val() == '') ? null :
1332
+ this._daylightSavingAdjust(new Date(
1333
+ inst.currentYear, inst.currentMonth, inst.currentDay)));
1334
+ if (this._get(inst, 'rangeSelect')) {
1335
+ return [inst.rangeStart || startDate,
1336
+ (!inst.endYear ? inst.rangeStart || startDate :
1337
+ this._daylightSavingAdjust(new Date(inst.endYear, inst.endMonth, inst.endDay)))];
1338
+ } else
1339
+ return startDate;
1340
+ },
1341
+
1342
+ /* Generate the HTML for the current state of the date picker. */
1343
+ _generateHTML: function(inst) {
1344
+ var today = new Date();
1345
+ today = this._daylightSavingAdjust(
1346
+ new Date(today.getFullYear(), today.getMonth(), today.getDate())); // clear time
1347
+ var showStatus = this._get(inst, 'showStatus');
1348
+ var initStatus = this._get(inst, 'initStatus') || '&#xa0;';
1349
+ var isRTL = this._get(inst, 'isRTL');
1350
+ // build the date picker HTML
1351
+ var clear = (this._get(inst, 'mandatory') ? '' :
1352
+ '<div class="ui-datepicker-clear"><a onclick="jQuery.datepicker._clearDate(\'#' + inst.id + '\');"' +
1353
+ this._addStatus(showStatus, inst.id, this._get(inst, 'clearStatus'), initStatus) + '>' +
1354
+ this._get(inst, 'clearText') + '</a></div>');
1355
+ var controls = '<div class="ui-datepicker-control">' + (isRTL ? '' : clear) +
1356
+ '<div class="ui-datepicker-close"><a onclick="jQuery.datepicker._hideDatepicker();"' +
1357
+ this._addStatus(showStatus, inst.id, this._get(inst, 'closeStatus'), initStatus) + '>' +
1358
+ this._get(inst, 'closeText') + '</a></div>' + (isRTL ? clear : '') + '</div>';
1359
+ var prompt = this._get(inst, 'prompt');
1360
+ var closeAtTop = this._get(inst, 'closeAtTop');
1361
+ var hideIfNoPrevNext = this._get(inst, 'hideIfNoPrevNext');
1362
+ var navigationAsDateFormat = this._get(inst, 'navigationAsDateFormat');
1363
+ var showBigPrevNext = this._get(inst, 'showBigPrevNext');
1364
+ var numMonths = this._getNumberOfMonths(inst);
1365
+ var showCurrentAtPos = this._get(inst, 'showCurrentAtPos');
1366
+ var stepMonths = this._get(inst, 'stepMonths');
1367
+ var stepBigMonths = this._get(inst, 'stepBigMonths');
1368
+ var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1);
1369
+ var currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
1370
+ new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
1371
+ var minDate = this._getMinMaxDate(inst, 'min', true);
1372
+ var maxDate = this._getMinMaxDate(inst, 'max');
1373
+ var drawMonth = inst.drawMonth - showCurrentAtPos;
1374
+ var drawYear = inst.drawYear;
1375
+ if (drawMonth < 0) {
1376
+ drawMonth += 12;
1377
+ drawYear--;
1378
+ }
1379
+ if (maxDate) {
1380
+ var maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
1381
+ maxDate.getMonth() - numMonths[1] + 1, maxDate.getDate()));
1382
+ maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
1383
+ while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
1384
+ drawMonth--;
1385
+ if (drawMonth < 0) {
1386
+ drawMonth = 11;
1387
+ drawYear--;
1388
+ }
1389
+ }
1390
+ }
1391
+ // controls and links
1392
+ var prevText = this._get(inst, 'prevText');
1393
+ prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
1394
+ this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
1395
+ this._getFormatConfig(inst)));
1396
+ var prevBigText = (showBigPrevNext ? this._get(inst, 'prevBigText') : '');
1397
+ prevBigText = (!navigationAsDateFormat ? prevBigText : this.formatDate(prevBigText,
1398
+ this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepBigMonths, 1)),
1399
+ this._getFormatConfig(inst)));
1400
+ var prev = '<div class="ui-datepicker-prev">' + (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
1401
+ (showBigPrevNext ? '<a onclick="jQuery.datepicker._adjustDate(\'#' + inst.id + '\', -' + stepBigMonths + ', \'M\');"' +
1402
+ this._addStatus(showStatus, inst.id, this._get(inst, 'prevBigStatus'), initStatus) + '>' + prevBigText + '</a>' : '') +
1403
+ '<a onclick="jQuery.datepicker._adjustDate(\'#' + inst.id + '\', -' + stepMonths + ', \'M\');"' +
1404
+ this._addStatus(showStatus, inst.id, this._get(inst, 'prevStatus'), initStatus) + '>' + prevText + '</a>' :
1405
+ (hideIfNoPrevNext ? '' : (showBigPrevNext ? '<label>' + prevBigText + '</label>' : '') +
1406
+ '<label>' + prevText + '</label>')) + '</div>';
1407
+ var nextText = this._get(inst, 'nextText');
1408
+ nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
1409
+ this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
1410
+ this._getFormatConfig(inst)));
1411
+ var nextBigText = (showBigPrevNext ? this._get(inst, 'nextBigText') : '');
1412
+ nextBigText = (!navigationAsDateFormat ? nextBigText : this.formatDate(nextBigText,
1413
+ this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepBigMonths, 1)),
1414
+ this._getFormatConfig(inst)));
1415
+ var next = '<div class="ui-datepicker-next">' + (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
1416
+ '<a onclick="jQuery.datepicker._adjustDate(\'#' + inst.id + '\', +' + stepMonths + ', \'M\');"' +
1417
+ this._addStatus(showStatus, inst.id, this._get(inst, 'nextStatus'), initStatus) + '>' + nextText + '</a>' +
1418
+ (showBigPrevNext ? '<a onclick="jQuery.datepicker._adjustDate(\'#' + inst.id + '\', +' + stepBigMonths + ', \'M\');"' +
1419
+ this._addStatus(showStatus, inst.id, this._get(inst, 'nextBigStatus'), initStatus) + '>' + nextBigText + '</a>' : '') :
1420
+ (hideIfNoPrevNext ? '' : '<label>' + nextText + '</label>' +
1421
+ (showBigPrevNext ? '<label>' + nextBigText + '</label>' : ''))) + '</div>';
1422
+ var currentText = this._get(inst, 'currentText');
1423
+ var gotoDate = (this._get(inst, 'gotoCurrent') && inst.currentDay ? currentDate : today);
1424
+ currentText = (!navigationAsDateFormat ? currentText :
1425
+ this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
1426
+ var html = (closeAtTop && !inst.inline ? controls : '') +
1427
+ '<div class="ui-datepicker-links">' + (isRTL ? next : prev) +
1428
+ (this._isInRange(inst, gotoDate) ? '<div class="ui-datepicker-current">' +
1429
+ '<a onclick="jQuery.datepicker._gotoToday(\'#' + inst.id + '\');"' +
1430
+ this._addStatus(showStatus, inst.id, this._get(inst, 'currentStatus'), initStatus) + '>' +
1431
+ currentText + '</a></div>' : '') + (isRTL ? prev : next) + '</div>' +
1432
+ (prompt ? '<div class="' + this._promptClass + '"><span>' + prompt + '</span></div>' : '');
1433
+ var firstDay = parseInt(this._get(inst, 'firstDay'));
1434
+ firstDay = (isNaN(firstDay) ? 0 : firstDay);
1435
+ var changeFirstDay = this._get(inst, 'changeFirstDay');
1436
+ var dayNames = this._get(inst, 'dayNames');
1437
+ var dayNamesShort = this._get(inst, 'dayNamesShort');
1438
+ var dayNamesMin = this._get(inst, 'dayNamesMin');
1439
+ var monthNames = this._get(inst, 'monthNames');
1440
+ var beforeShowDay = this._get(inst, 'beforeShowDay');
1441
+ var highlightWeek = this._get(inst, 'highlightWeek');
1442
+ var showOtherMonths = this._get(inst, 'showOtherMonths');
1443
+ var showWeeks = this._get(inst, 'showWeeks');
1444
+ var calculateWeek = this._get(inst, 'calculateWeek') || this.iso8601Week;
1445
+ var weekStatus = this._get(inst, 'weekStatus');
1446
+ var status = (showStatus ? this._get(inst, 'dayStatus') || initStatus : '');
1447
+ var dateStatus = this._get(inst, 'statusForDate') || this.dateStatus;
1448
+ var endDate = inst.endDay ? this._daylightSavingAdjust(
1449
+ new Date(inst.endYear, inst.endMonth, inst.endDay)) : currentDate;
1450
+ var defaultDate = this._getDefaultDate(inst);
1451
+ for (var row = 0; row < numMonths[0]; row++)
1452
+ for (var col = 0; col < numMonths[1]; col++) {
1453
+ var selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
1454
+ html += '<div class="ui-datepicker-one-month' + (col == 0 ? ' ui-datepicker-new-row' : '') + '">' +
1455
+ this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
1456
+ selectedDate, row > 0 || col > 0, showStatus, initStatus, monthNames) + // draw month headers
1457
+ '<table class="ui-datepicker" cellpadding="0" cellspacing="0"><thead>' +
1458
+ '<tr class="ui-datepicker-title-row">' +
1459
+ (showWeeks ? '<td' + this._addStatus(showStatus, inst.id, weekStatus, initStatus) + '>' +
1460
+ this._get(inst, 'weekHeader') + '</td>' : '');
1461
+ for (var dow = 0; dow < 7; dow++) { // days of the week
1462
+ var day = (dow + firstDay) % 7;
1463
+ var dayStatus = (status.indexOf('DD') > -1 ? status.replace(/DD/, dayNames[day]) :
1464
+ status.replace(/D/, dayNamesShort[day]));
1465
+ html += '<td' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="ui-datepicker-week-end-cell"' : '') + '>' +
1466
+ (!changeFirstDay ? '<span' :
1467
+ '<a onclick="jQuery.datepicker._changeFirstDay(\'#' + inst.id + '\', ' + day + ');"') +
1468
+ this._addStatus(showStatus, inst.id, dayStatus, initStatus) + ' title="' + dayNames[day] + '">' +
1469
+ dayNamesMin[day] + (changeFirstDay ? '</a>' : '</span>') + '</td>';
1470
+ }
1471
+ html += '</tr></thead><tbody>';
1472
+ var daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
1473
+ if (drawYear == inst.selectedYear && drawMonth == inst.selectedMonth)
1474
+ inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
1475
+ var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
1476
+ var numRows = (isMultiMonth ? 6 : Math.ceil((leadDays + daysInMonth) / 7)); // calculate the number of rows to generate
1477
+ var printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
1478
+ for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows
1479
+ html += '<tr class="ui-datepicker-days-row">' +
1480
+ (showWeeks ? '<td class="ui-datepicker-week-col"' +
1481
+ this._addStatus(showStatus, inst.id, weekStatus, initStatus) + '>' +
1482
+ calculateWeek(printDate) + '</td>' : '');
1483
+ for (var dow = 0; dow < 7; dow++) { // create date picker days
1484
+ var daySettings = (beforeShowDay ?
1485
+ beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, '']);
1486
+ var otherMonth = (printDate.getMonth() != drawMonth);
1487
+ var unselectable = otherMonth || !daySettings[0] ||
1488
+ (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
1489
+ html += '<td class="ui-datepicker-days-cell' +
1490
+ ((dow + firstDay + 6) % 7 >= 5 ? ' ui-datepicker-week-end-cell' : '') + // highlight weekends
1491
+ (otherMonth ? ' ui-datepicker-other-month' : '') + // highlight days from other months
1492
+ ((printDate.getTime() == selectedDate.getTime() && drawMonth == inst.selectedMonth && inst._keyEvent) || // user pressed key
1493
+ (defaultDate.getTime() == printDate.getTime() && defaultDate.getTime() == selectedDate.getTime()) ?
1494
+ // or defaultDate is current printedDate and defaultDate is selectedDate
1495
+ ' ' + $.datepicker._dayOverClass : '') + // highlight selected day
1496
+ (unselectable ? ' ' + this._unselectableClass : '') + // highlight unselectable days
1497
+ (otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates
1498
+ (printDate.getTime() >= currentDate.getTime() && printDate.getTime() <= endDate.getTime() ? // in current range
1499
+ ' ' + this._currentClass : '') + // highlight selected day
1500
+ (printDate.getTime() == today.getTime() ? ' ui-datepicker-today' : '')) + '"' + // highlight today (if different)
1501
+ ((!otherMonth || showOtherMonths) && daySettings[2] ? ' title="' + daySettings[2] + '"' : '') + // cell title
1502
+ (unselectable ? (highlightWeek ? ' onmouseover="jQuery(this).parent().addClass(\'' + this._weekOverClass + '\');"' + // highlight selection week
1503
+ ' onmouseout="jQuery(this).parent().removeClass(\'' + this._weekOverClass + '\');"' : '') : // unhighlight selection week
1504
+ ' onmouseover="jQuery(this).addClass(\'' + this._dayOverClass + '\')' + // highlight selection
1505
+ (highlightWeek ? '.parent().addClass(\'' + this._weekOverClass + '\')' : '') + ';' + // highlight selection week
1506
+ (!showStatus || (otherMonth && !showOtherMonths) ? '' : 'jQuery(\'#ui-datepicker-status-' +
1507
+ inst.id + '\').html(\'' + (dateStatus.apply((inst.input ? inst.input[0] : null),
1508
+ [printDate, inst]) || initStatus) +'\');') + '"' +
1509
+ ' onmouseout="jQuery(this).removeClass(\'' + this._dayOverClass + '\')' + // unhighlight selection
1510
+ (highlightWeek ? '.parent().removeClass(\'' + this._weekOverClass + '\')' : '') + ';' + // unhighlight selection week
1511
+ (!showStatus || (otherMonth && !showOtherMonths) ? '' : 'jQuery(\'#ui-datepicker-status-' +
1512
+ inst.id + '\').html(\'' + initStatus + '\');') + '" onclick="jQuery.datepicker._selectDay(\'#' +
1513
+ inst.id + '\',' + drawMonth + ',' + drawYear + ', this);"') + '>' + // actions
1514
+ (otherMonth ? (showOtherMonths ? printDate.getDate() : '&#xa0;') : // display for other months
1515
+ (unselectable ? printDate.getDate() : '<a>' + printDate.getDate() + '</a>')) + '</td>'; // display for this month
1516
+ printDate.setDate(printDate.getDate() + 1);
1517
+ printDate = this._daylightSavingAdjust(printDate);
1518
+ }
1519
+ html += '</tr>';
1520
+ }
1521
+ drawMonth++;
1522
+ if (drawMonth > 11) {
1523
+ drawMonth = 0;
1524
+ drawYear++;
1525
+ }
1526
+ html += '</tbody></table></div>';
1527
+ }
1528
+ html += (showStatus ? '<div style="clear: both;"></div><div id="ui-datepicker-status-' + inst.id +
1529
+ '" class="ui-datepicker-status">' + initStatus + '</div>' : '') +
1530
+ (!closeAtTop && !inst.inline ? controls : '') +
1531
+ '<div style="clear: both;"></div>' +
1532
+ ($.browser.msie && parseInt($.browser.version,10) < 7 && !inst.inline ?
1533
+ '<iframe src="javascript:false;" class="ui-datepicker-cover"></iframe>' : '');
1534
+ inst._keyEvent = false;
1535
+ return html;
1536
+ },
1537
+
1538
+ /* Generate the month and year header. */
1539
+ _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
1540
+ selectedDate, secondary, showStatus, initStatus, monthNames) {
1541
+ minDate = (inst.rangeStart && minDate && selectedDate < minDate ? selectedDate : minDate);
1542
+ var changeMonth = this._get(inst, 'changeMonth');
1543
+ var changeYear = this._get(inst, 'changeYear');
1544
+ var showMonthAfterYear = this._get(inst, 'showMonthAfterYear');
1545
+ var html = '<div class="ui-datepicker-header">';
1546
+ var monthHtml = '';
1547
+ // month selection
1548
+ if (secondary || !changeMonth)
1549
+ monthHtml += monthNames[drawMonth];
1550
+ else {
1551
+ var inMinYear = (minDate && minDate.getFullYear() == drawYear);
1552
+ var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear);
1553
+ monthHtml += '<select class="ui-datepicker-new-month" ' +
1554
+ 'onchange="jQuery.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'M\');" ' +
1555
+ 'onclick="jQuery.datepicker._clickMonthYear(\'#' + inst.id + '\');"' +
1556
+ this._addStatus(showStatus, inst.id, this._get(inst, 'monthStatus'), initStatus) + '>';
1557
+ for (var month = 0; month < 12; month++) {
1558
+ if ((!inMinYear || month >= minDate.getMonth()) &&
1559
+ (!inMaxYear || month <= maxDate.getMonth()))
1560
+ monthHtml += '<option value="' + month + '"' +
1561
+ (month == drawMonth ? ' selected="selected"' : '') +
1562
+ '>' + monthNames[month] + '</option>';
1563
+ }
1564
+ monthHtml += '</select>';
1565
+ }
1566
+ if (!showMonthAfterYear)
1567
+ html += monthHtml + (secondary || changeMonth || changeYear ? '&#xa0;' : '');
1568
+ // year selection
1569
+ if (secondary || !changeYear)
1570
+ html += drawYear;
1571
+ else {
1572
+ // determine range of years to display
1573
+ var years = this._get(inst, 'yearRange').split(':');
1574
+ var year = 0;
1575
+ var endYear = 0;
1576
+ if (years.length != 2) {
1577
+ year = drawYear - 10;
1578
+ endYear = drawYear + 10;
1579
+ } else if (years[0].charAt(0) == '+' || years[0].charAt(0) == '-') {
1580
+ year = endYear = new Date().getFullYear();
1581
+ year += parseInt(years[0], 10);
1582
+ endYear += parseInt(years[1], 10);
1583
+ } else {
1584
+ year = parseInt(years[0], 10);
1585
+ endYear = parseInt(years[1], 10);
1586
+ }
1587
+ year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
1588
+ endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
1589
+ html += '<select class="ui-datepicker-new-year" ' +
1590
+ 'onchange="jQuery.datepicker._selectMonthYear(\'#' + inst.id + '\', this, \'Y\');" ' +
1591
+ 'onclick="jQuery.datepicker._clickMonthYear(\'#' + inst.id + '\');"' +
1592
+ this._addStatus(showStatus, inst.id, this._get(inst, 'yearStatus'), initStatus) + '>';
1593
+ for (; year <= endYear; year++) {
1594
+ html += '<option value="' + year + '"' +
1595
+ (year == drawYear ? ' selected="selected"' : '') +
1596
+ '>' + year + '</option>';
1597
+ }
1598
+ html += '</select>';
1599
+ }
1600
+ if (showMonthAfterYear)
1601
+ html += (secondary || changeMonth || changeYear ? '&#xa0;' : '') + monthHtml;
1602
+ html += '</div>'; // Close datepicker_header
1603
+ return html;
1604
+ },
1605
+
1606
+ /* Provide code to set and clear the status panel. */
1607
+ _addStatus: function(showStatus, id, text, initStatus) {
1608
+ return (showStatus ? ' onmouseover="jQuery(\'#ui-datepicker-status-' + id +
1609
+ '\').html(\'' + (text || initStatus) + '\');" ' +
1610
+ 'onmouseout="jQuery(\'#ui-datepicker-status-' + id +
1611
+ '\').html(\'' + initStatus + '\');"' : '');
1612
+ },
1613
+
1614
+ /* Adjust one of the date sub-fields. */
1615
+ _adjustInstDate: function(inst, offset, period) {
1616
+ var year = inst.drawYear + (period == 'Y' ? offset : 0);
1617
+ var month = inst.drawMonth + (period == 'M' ? offset : 0);
1618
+ var day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) +
1619
+ (period == 'D' ? offset : 0);
1620
+ var date = this._daylightSavingAdjust(new Date(year, month, day));
1621
+ // ensure it is within the bounds set
1622
+ var minDate = this._getMinMaxDate(inst, 'min', true);
1623
+ var maxDate = this._getMinMaxDate(inst, 'max');
1624
+ date = (minDate && date < minDate ? minDate : date);
1625
+ date = (maxDate && date > maxDate ? maxDate : date);
1626
+ inst.selectedDay = date.getDate();
1627
+ inst.drawMonth = inst.selectedMonth = date.getMonth();
1628
+ inst.drawYear = inst.selectedYear = date.getFullYear();
1629
+ if (period == 'M' || period == 'Y')
1630
+ this._notifyChange(inst);
1631
+ },
1632
+
1633
+ /* Notify change of month/year. */
1634
+ _notifyChange: function(inst) {
1635
+ var onChange = this._get(inst, 'onChangeMonthYear');
1636
+ if (onChange)
1637
+ onChange.apply((inst.input ? inst.input[0] : null),
1638
+ [inst.selectedYear, inst.selectedMonth + 1, inst]);
1639
+ },
1640
+
1641
+ /* Determine the number of months to show. */
1642
+ _getNumberOfMonths: function(inst) {
1643
+ var numMonths = this._get(inst, 'numberOfMonths');
1644
+ return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths));
1645
+ },
1646
+
1647
+ /* Determine the current maximum date - ensure no time components are set - may be overridden for a range. */
1648
+ _getMinMaxDate: function(inst, minMax, checkRange) {
1649
+ var date = this._determineDate(this._get(inst, minMax + 'Date'), null);
1650
+ return (!checkRange || !inst.rangeStart ? date :
1651
+ (!date || inst.rangeStart > date ? inst.rangeStart : date));
1652
+ },
1653
+
1654
+ /* Find the number of days in a given month. */
1655
+ _getDaysInMonth: function(year, month) {
1656
+ return 32 - new Date(year, month, 32).getDate();
1657
+ },
1658
+
1659
+ /* Find the day of the week of the first of a month. */
1660
+ _getFirstDayOfMonth: function(year, month) {
1661
+ return new Date(year, month, 1).getDay();
1662
+ },
1663
+
1664
+ /* Determines if we should allow a "next/prev" month display change. */
1665
+ _canAdjustMonth: function(inst, offset, curYear, curMonth) {
1666
+ var numMonths = this._getNumberOfMonths(inst);
1667
+ var date = this._daylightSavingAdjust(new Date(
1668
+ curYear, curMonth + (offset < 0 ? offset : numMonths[1]), 1));
1669
+ if (offset < 0)
1670
+ date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
1671
+ return this._isInRange(inst, date);
1672
+ },
1673
+
1674
+ /* Is the given date in the accepted range? */
1675
+ _isInRange: function(inst, date) {
1676
+ // during range selection, use minimum of selected date and range start
1677
+ var newMinDate = (!inst.rangeStart ? null : this._daylightSavingAdjust(
1678
+ new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay)));
1679
+ newMinDate = (newMinDate && inst.rangeStart < newMinDate ? inst.rangeStart : newMinDate);
1680
+ var minDate = newMinDate || this._getMinMaxDate(inst, 'min');
1681
+ var maxDate = this._getMinMaxDate(inst, 'max');
1682
+ return ((!minDate || date >= minDate) && (!maxDate || date <= maxDate));
1683
+ },
1684
+
1685
+ /* Provide the configuration settings for formatting/parsing. */
1686
+ _getFormatConfig: function(inst) {
1687
+ var shortYearCutoff = this._get(inst, 'shortYearCutoff');
1688
+ shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff :
1689
+ new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
1690
+ return {shortYearCutoff: shortYearCutoff,
1691
+ dayNamesShort: this._get(inst, 'dayNamesShort'), dayNames: this._get(inst, 'dayNames'),
1692
+ monthNamesShort: this._get(inst, 'monthNamesShort'), monthNames: this._get(inst, 'monthNames')};
1693
+ },
1694
+
1695
+ /* Format the given date for display. */
1696
+ _formatDate: function(inst, day, month, year) {
1697
+ if (!day) {
1698
+ inst.currentDay = inst.selectedDay;
1699
+ inst.currentMonth = inst.selectedMonth;
1700
+ inst.currentYear = inst.selectedYear;
1701
+ }
1702
+ var date = (day ? (typeof day == 'object' ? day :
1703
+ this._daylightSavingAdjust(new Date(year, month, day))) :
1704
+ this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
1705
+ return this.formatDate(this._get(inst, 'dateFormat'), date, this._getFormatConfig(inst));
1706
+ }
1707
+ });
1708
+
1709
+ /* jQuery extend now ignores nulls! */
1710
+ function extendRemove(target, props) {
1711
+ $.extend(target, props);
1712
+ for (var name in props)
1713
+ if (props[name] == null || props[name] == undefined)
1714
+ target[name] = props[name];
1715
+ return target;
1716
+ };
1717
+
1718
+ /* Determine whether an object is an array. */
1719
+ function isArray(a) {
1720
+ return (a && (($.browser.safari && typeof a == 'object' && a.length) ||
1721
+ (a.constructor && a.constructor.toString().match(/\Array\(\)/))));
1722
+ };
1723
+
1724
+ /* Invoke the datepicker functionality.
1725
+ @param options string - a command, optionally followed by additional parameters or
1726
+ Object - settings for attaching new datepicker functionality
1727
+ @return jQuery object */
1728
+ $.fn.datepicker = function(options){
1729
+
1730
+ /* Initialise the date picker. */
1731
+ if (!$.datepicker.initialized) {
1732
+ $(document.body).append($.datepicker.dpDiv).
1733
+ mousedown($.datepicker._checkExternalClick);
1734
+ $.datepicker.initialized = true;
1735
+ }
1736
+
1737
+ var otherArgs = Array.prototype.slice.call(arguments, 1);
1738
+ if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate'))
1739
+ return $.datepicker['_' + options + 'Datepicker'].
1740
+ apply($.datepicker, [this[0]].concat(otherArgs));
1741
+ return this.each(function() {
1742
+ typeof options == 'string' ?
1743
+ $.datepicker['_' + options + 'Datepicker'].
1744
+ apply($.datepicker, [this].concat(otherArgs)) :
1745
+ $.datepicker._attachDatepicker(this, options);
1746
+ });
1747
+ };
1748
+
1749
+ $.datepicker = new Datepicker(); // singleton instance
1750
+ $.datepicker.initialized = false;
1751
+ $.datepicker.uuid = new Date().getTime();
1752
+ $.datepicker.version = "@VERSION";
1753
+
1754
+ })(jQuery);