|
1 /* jQuery UI Date Picker v3.4.3 (previously jQuery Calendar) |
|
2 Written by Marc Grabanski (m@marcgrabanski.com) and Keith Wood (kbwood@virginbroadband.com.au). |
|
3 |
|
4 Copyright (c) 2007 Marc Grabanski (http://marcgrabanski.com/code/ui-datetimepicker) |
|
5 Dual licensed under the MIT (MIT-LICENSE.txt) |
|
6 and GPL (GPL-LICENSE.txt) licenses. |
|
7 Date: 09-03-2007 */ |
|
8 /* |
|
9 * Time functionality added by Stanislav Dobry (stanislav.dobry@datesoft.cz) |
|
10 * Date: 2008-06-04 |
|
11 */ |
|
12 |
|
13 ;(function($) { // hide the namespace |
|
14 |
|
15 /* Date picker manager. |
|
16 Use the singleton instance of this class, $.datetimepicker, to interact with the date picker. |
|
17 Settings for (groups of) date pickers are maintained in an instance object |
|
18 (DatepickerInstance), allowing multiple different settings on the same page. */ |
|
19 |
|
20 function DateTimepicker() { |
|
21 this.debug = false; // Change this to true to start debugging |
|
22 this._nextId = 0; // Next ID for a date picker instance |
|
23 this._inst = []; // List of instances indexed by ID |
|
24 this._curInst = null; // The current instance in use |
|
25 this._disabledInputs = []; // List of date picker inputs that have been disabled |
|
26 this._datetimepickerShowing = false; // True if the popup picker is showing , false if not |
|
27 this._inDialog = false; // True if showing within a "dialog", false if not |
|
28 this.regional = []; // Available regional settings, indexed by language code |
|
29 this.regional[''] = { // Default regional settings |
|
30 clearText: 'Clear', // Display text for clear link |
|
31 clearStatus: 'Erase the current date', // Status text for clear link |
|
32 closeText: 'Close', // Display text for close link |
|
33 closeStatus: 'Close without change', // Status text for close link |
|
34 prevText: '<Prev', // Display text for previous month link |
|
35 prevStatus: 'Show the previous month', // Status text for previous month link |
|
36 nextText: 'Next>', // Display text for next month link |
|
37 nextStatus: 'Show the next month', // Status text for next month link |
|
38 currentText: 'Today', // Display text for current month link |
|
39 currentStatus: 'Show the current month', // Status text for current month link |
|
40 monthNames: ['January','February','March','April','May','June', |
|
41 'July','August','September','October','November','December'], // Names of months for drop-down and formatting |
|
42 monthNamesShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], // For formatting |
|
43 monthStatus: 'Show a different month', // Status text for selecting a month |
|
44 yearStatus: 'Show a different year', // Status text for selecting a year |
|
45 weekHeader: 'Wk', // Header for the week of the year column |
|
46 weekStatus: 'Week of the year', // Status text for the week of the year column |
|
47 dayNames: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], // For formatting |
|
48 dayNamesShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], // For formatting |
|
49 dayNamesMin: ['Su','Mo','Tu','We','Th','Fr','Sa'], // Column headings for days starting at Sunday |
|
50 dayStatus: 'Set DD as first week day', // Status text for the day of the week selection |
|
51 dateStatus: 'Select DD, M d', // Status text for the date selection |
|
52 dateFormat: 'mm/dd/yy', // See format options on parseDate |
|
53 timeFormat: 'hh:ii', |
|
54 firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ... |
|
55 initStatus: 'Select a date', // Initial Status text on opening |
|
56 isRTL: false // True if right-to-left language, false if left-to-right |
|
57 }; |
|
58 this._defaults = { // Global defaults for all the date picker instances |
|
59 showOn: 'focus', // 'focus' for popup on focus, |
|
60 // 'button' for trigger button, or 'both' for either |
|
61 showAnim: 'show', // Name of jQuery animation for popup |
|
62 defaultDate: null, // Used when field is blank: actual date, |
|
63 // +/-number for offset from today, null for today |
|
64 appendText: '', // Display text following the input box, e.g. showing the format |
|
65 buttonText: '...', // Text for trigger button |
|
66 buttonImage: '', // URL for trigger button image |
|
67 buttonImageOnly: false, // True if the image appears alone, false if it appears on a button |
|
68 closeAtTop: true, // True to have the clear/close at the top, |
|
69 // false to have them at the bottom |
|
70 mandatory: false, // True to hide the Clear link, false to include it |
|
71 hideIfNoPrevNext: false, // True to hide next/previous month links |
|
72 // if not applicable, false to just disable them |
|
73 changeMonth: true, // True if month can be selected directly, false if only prev/next |
|
74 changeYear: true, // True if year can be selected directly, false if only prev/next |
|
75 yearRange: '-10:+10', // Range of years to display in drop-down, |
|
76 // either relative to current year (-nn:+nn) or absolute (nnnn:nnnn) |
|
77 changeFirstDay: true, // True to click on day name to change, false to remain as set |
|
78 showOtherMonths: false, // True to show dates in other months, false to leave blank |
|
79 showWeeks: false, // True to show week of the year, false to omit |
|
80 calculateWeek: this.iso8601Week, // How to calculate the week of the year, |
|
81 // takes a Date and returns the number of the week for it |
|
82 shortYearCutoff: '+10', // Short year values < this are in the current century, |
|
83 // > this are in the previous century, |
|
84 // string value starting with '+' for current year + value |
|
85 showStatus: false, // True to show status bar at bottom, false to not show it |
|
86 statusForDate: this.dateStatus, // Function to provide status text for a date - |
|
87 // takes date and instance as parameters, returns display text |
|
88 minDate: null, // The earliest selectable date, or null for no limit |
|
89 maxDate: null, // The latest selectable date, or null for no limit |
|
90 speed: 'normal', // Speed of display/closure |
|
91 beforeShowDay: null, // Function that takes a date and returns an array with |
|
92 // [0] = true if selectable, false if not, |
|
93 // [1] = custom CSS class name(s) or '', e.g. $.datetimepicker.noWeekends |
|
94 beforeShow: null, // Function that takes an input field and |
|
95 // returns a set of custom settings for the date picker |
|
96 onSelect: null, // Define a callback function when a date is selected |
|
97 onClose: null, // Define a callback function when the datetimepicker is closed |
|
98 numberOfMonths: 1, // Number of months to show at a time |
|
99 stepMonths: 1, // Number of months to step back/forward |
|
100 rangeSelect: false, // Allows for selecting a date range on one date picker |
|
101 rangeSeparator: ' - ' // Text between two dates in a range |
|
102 }; |
|
103 $.extend(this._defaults, this.regional['']); |
|
104 this._datetimepickerDiv = $('<div id="datetimepicker_div"></div>'); |
|
105 } |
|
106 |
|
107 $.extend(DateTimepicker.prototype, { |
|
108 /* Class name added to elements to indicate already configured with a date picker. */ |
|
109 markerClassName: 'hasDatepicker', |
|
110 |
|
111 /* Debug logging (if enabled). */ |
|
112 log: function () { |
|
113 if (this.debug) |
|
114 console.log.apply('', arguments); |
|
115 }, |
|
116 |
|
117 /* Register a new date picker instance - with custom settings. */ |
|
118 _register: function(inst) { |
|
119 var id = this._nextId++; |
|
120 this._inst[id] = inst; |
|
121 return id; |
|
122 }, |
|
123 |
|
124 /* Retrieve a particular date picker instance based on its ID. */ |
|
125 _getInst: function(id) { |
|
126 return this._inst[id] || id; |
|
127 }, |
|
128 |
|
129 /* Override the default settings for all instances of the date picker. |
|
130 @param settings object - the new settings to use as defaults (anonymous object) |
|
131 @return the manager object */ |
|
132 setDefaults: function(settings) { |
|
133 extendRemove(this._defaults, settings || {}); |
|
134 return this; |
|
135 }, |
|
136 |
|
137 /* Attach the date picker to a jQuery selection. |
|
138 @param target element - the target input field or division or span |
|
139 @param settings object - the new settings to use for this date picker instance (anonymous) */ |
|
140 _attachDatepicker: function(target, settings) { |
|
141 // check for settings on the control itself - in namespace 'date:' |
|
142 var inlineSettings = null; |
|
143 for (attrName in this._defaults) { |
|
144 var attrValue = target.getAttribute('date:' + attrName); |
|
145 if (attrValue) { |
|
146 inlineSettings = inlineSettings || {}; |
|
147 try { |
|
148 inlineSettings[attrName] = eval(attrValue); |
|
149 } catch (err) { |
|
150 inlineSettings[attrName] = attrValue; |
|
151 } |
|
152 } |
|
153 } |
|
154 var nodeName = target.nodeName.toLowerCase(); |
|
155 var instSettings = (inlineSettings ? |
|
156 $.extend(settings || {}, inlineSettings || {}) : settings); |
|
157 if (nodeName == 'input') { |
|
158 var inst = (inst && !inlineSettings ? inst : |
|
159 new DateTimepickerInstance(instSettings, false)); |
|
160 this._connectDatepicker(target, inst); |
|
161 } else if (nodeName == 'div' || nodeName == 'span') { |
|
162 var inst = new DateTimepickerInstance(instSettings, true); |
|
163 this._inlineDatepicker(target, inst); |
|
164 } |
|
165 }, |
|
166 |
|
167 /* Detach a datetimepicker from its control. |
|
168 @param target element - the target input field or division or span */ |
|
169 _destroyDatepicker: function(target) { |
|
170 var nodeName = target.nodeName.toLowerCase(); |
|
171 var calId = target._calId; |
|
172 target._calId = null; |
|
173 var $target = $(target); |
|
174 if (nodeName == 'input') { |
|
175 $target.siblings('.datetimepicker_append').replaceWith('').end() |
|
176 .siblings('.datetimepicker_trigger').replaceWith('').end() |
|
177 .removeClass(this.markerClassName) |
|
178 .unbind('focus', this._showDatepicker) |
|
179 .unbind('keydown', this._doKeyDown) |
|
180 .unbind('keypress', this._doKeyPress); |
|
181 var wrapper = $target.parents('.datetimepicker_wrap'); |
|
182 if (wrapper) |
|
183 wrapper.replaceWith(wrapper.html()); |
|
184 } else if (nodeName == 'div' || nodeName == 'span') |
|
185 $target.removeClass(this.markerClassName).empty(); |
|
186 if ($('input[_calId=' + calId + ']').length == 0) |
|
187 // clean up if last for this ID |
|
188 this._inst[calId] = null; |
|
189 }, |
|
190 |
|
191 /* Enable the date picker to a jQuery selection. |
|
192 @param target element - the target input field or division or span */ |
|
193 _enableDatepicker: function(target) { |
|
194 target.disabled = false; |
|
195 $(target).siblings('button.datetimepicker_trigger').each(function() { this.disabled = false; }).end() |
|
196 .siblings('img.datetimepicker_trigger').css({opacity: '1.0', cursor: ''}); |
|
197 this._disabledInputs = $.map(this._disabledInputs, |
|
198 function(value) { return (value == target ? null : value); }); // delete entry |
|
199 }, |
|
200 |
|
201 /* Disable the date picker to a jQuery selection. |
|
202 @param target element - the target input field or division or span */ |
|
203 _disableDatepicker: function(target) { |
|
204 target.disabled = true; |
|
205 $(target).siblings('button.datetimepicker_trigger').each(function() { this.disabled = true; }).end() |
|
206 .siblings('img.datetimepicker_trigger').css({opacity: '0.5', cursor: 'default'}); |
|
207 this._disabledInputs = $.map($.datetimepicker._disabledInputs, |
|
208 function(value) { return (value == target ? null : value); }); // delete entry |
|
209 this._disabledInputs[$.datetimepicker._disabledInputs.length] = target; |
|
210 }, |
|
211 |
|
212 /* Is the first field in a jQuery collection disabled as a datetimepicker? |
|
213 @param target element - the target input field or division or span |
|
214 @return boolean - true if disabled, false if enabled */ |
|
215 _isDisabledDatepicker: function(target) { |
|
216 if (!target) |
|
217 return false; |
|
218 for (var i = 0; i < this._disabledInputs.length; i++) { |
|
219 if (this._disabledInputs[i] == target) |
|
220 return true; |
|
221 } |
|
222 return false; |
|
223 }, |
|
224 |
|
225 /* Update the settings for a date picker attached to an input field or division. |
|
226 @param target element - the target input field or division or span |
|
227 @param name string - the name of the setting to change or |
|
228 object - the new settings to update |
|
229 @param value any - the new value for the setting (omit if above is an object) */ |
|
230 _changeDatepicker: function(target, name, value) { |
|
231 var settings = name || {}; |
|
232 if (typeof name == 'string') { |
|
233 settings = {}; |
|
234 settings[name] = value; |
|
235 } |
|
236 if (inst = this._getInst(target._calId)) { |
|
237 extendRemove(inst._settings, settings); |
|
238 this._updateDatepicker(inst); |
|
239 } |
|
240 }, |
|
241 |
|
242 /* Set the dates for a jQuery selection. |
|
243 @param target element - the target input field or division or span |
|
244 @param date Date - the new date |
|
245 @param endDate Date - the new end date for a range (optional) */ |
|
246 _setDateDatepicker: function(target, date, endDate) { |
|
247 if (inst = this._getInst(target._calId)) { |
|
248 inst._setDate(date, endDate); |
|
249 this._updateDatepicker(inst); |
|
250 } |
|
251 }, |
|
252 |
|
253 /* Get the date(s) for the first entry in a jQuery selection. |
|
254 @param target element - the target input field or division or span |
|
255 @return Date - the current date or |
|
256 Date[2] - the current dates for a range */ |
|
257 _getDateDatepicker: function(target) { |
|
258 var inst = this._getInst(target._calId); |
|
259 return (inst ? inst._getDate() : null); |
|
260 }, |
|
261 |
|
262 /* Handle keystrokes. */ |
|
263 _doKeyDown: function(e) { |
|
264 var inst = $.datetimepicker._getInst(this._calId); |
|
265 if ($.datetimepicker._datetimepickerShowing) |
|
266 switch (e.keyCode) { |
|
267 case 9: $.datetimepicker._hideDatepicker(null, ''); |
|
268 break; // hide on tab out |
|
269 case 13: $.datetimepicker._selectDay(inst, inst._selectedMonth, inst._selectedYear, |
|
270 $('td.datetimepicker_daysCellOver', inst._datetimepickerDiv)[0]); |
|
271 return false; // don't submit the form |
|
272 break; // select the value on enter |
|
273 case 27: $.datetimepicker._hideDatepicker(null, inst._get('speed')); |
|
274 break; // hide on escape |
|
275 case 33: $.datetimepicker._adjustDate(inst, |
|
276 (e.ctrlKey ? -1 : -inst._get('stepMonths')), (e.ctrlKey ? 'Y' : 'M')); |
|
277 break; // previous month/year on page up/+ ctrl |
|
278 case 34: $.datetimepicker._adjustDate(inst, |
|
279 (e.ctrlKey ? +1 : +inst._get('stepMonths')), (e.ctrlKey ? 'Y' : 'M')); |
|
280 break; // next month/year on page down/+ ctrl |
|
281 case 35: if (e.ctrlKey) $.datetimepicker._clearDate(inst); |
|
282 break; // clear on ctrl+end |
|
283 case 36: if (e.ctrlKey) $.datetimepicker._gotoToday(inst); |
|
284 break; // current on ctrl+home |
|
285 case 37: if (e.ctrlKey) $.datetimepicker._adjustDate(inst, -1, 'D'); |
|
286 break; // -1 day on ctrl+left |
|
287 case 38: if (e.ctrlKey) $.datetimepicker._adjustDate(inst, -7, 'D'); |
|
288 break; // -1 week on ctrl+up |
|
289 case 39: if (e.ctrlKey) $.datetimepicker._adjustDate(inst, +1, 'D'); |
|
290 break; // +1 day on ctrl+right |
|
291 case 40: if (e.ctrlKey) $.datetimepicker._adjustDate(inst, +7, 'D'); |
|
292 break; // +1 week on ctrl+down |
|
293 } |
|
294 else if (e.keyCode == 36 && e.ctrlKey) // display the date picker on ctrl+home |
|
295 $.datetimepicker._showDatepicker(this); |
|
296 }, |
|
297 |
|
298 /* Filter entered characters - based on date format. */ |
|
299 _doKeyPress: function(e) { |
|
300 var inst = $.datetimepicker._getInst(this._calId); |
|
301 var chars = $.datetimepicker._possibleChars(inst._get('dateFormat')+' '+inst._get('timeFormat')); |
|
302 var chr = String.fromCharCode(e.charCode == undefined ? e.keyCode : e.charCode); |
|
303 return e.ctrlKey || (chr < ' ' || !chars || chars.indexOf(chr) > -1); |
|
304 }, |
|
305 |
|
306 /* Attach the date picker to an input field. */ |
|
307 _connectDatepicker: function(target, inst) { |
|
308 var input = $(target); |
|
309 if (input.is('.' + this.markerClassName)) |
|
310 return; |
|
311 var appendText = inst._get('appendText'); |
|
312 var isRTL = inst._get('isRTL'); |
|
313 if (appendText) { |
|
314 if (isRTL) |
|
315 input.before('<span class="datetimepicker_append">' + appendText); |
|
316 else |
|
317 input.after('<span class="datetimepicker_append">' + appendText); |
|
318 } |
|
319 var showOn = inst._get('showOn'); |
|
320 if (showOn == 'focus' || showOn == 'both') // pop-up date picker when in the marked field |
|
321 input.focus(this._showDatepicker); |
|
322 if (showOn == 'button' || showOn == 'both') { // pop-up date picker when button clicked |
|
323 input.wrap('<span class="datetimepicker_wrap">'); |
|
324 var buttonText = inst._get('buttonText'); |
|
325 var buttonImage = inst._get('buttonImage'); |
|
326 var trigger = $(inst._get('buttonImageOnly') ? |
|
327 $('<img>').addClass('datetimepicker_trigger').attr({ src: buttonImage, alt: buttonText, title: buttonText }) : |
|
328 $('<button>').addClass('datetimepicker_trigger').attr({ type: 'button' }).html(buttonImage != '' ? |
|
329 $('<img>').attr({ src:buttonImage, alt:buttonText, title:buttonText }) : buttonText)); |
|
330 if (isRTL) |
|
331 input.before(trigger); |
|
332 else |
|
333 input.after(trigger); |
|
334 trigger.click(function() { |
|
335 if ($.datetimepicker._datetimepickerShowing && $.datetimepicker._lastInput == target) |
|
336 $.datetimepicker._hideDatepicker(); |
|
337 else |
|
338 $.datetimepicker._showDatepicker(target); |
|
339 }); |
|
340 } |
|
341 input.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress) |
|
342 .bind("setData.datetimepicker", function(event, key, value) { |
|
343 inst._settings[key] = value; |
|
344 }).bind("getData.datetimepicker", function(event, key) { |
|
345 return inst._get(key); |
|
346 }); |
|
347 input[0]._calId = inst._id; |
|
348 }, |
|
349 |
|
350 /* Attach an inline date picker to a div. */ |
|
351 _inlineDatepicker: function(target, inst) { |
|
352 var input = $(target); |
|
353 if (input.is('.' + this.markerClassName)) |
|
354 return; |
|
355 input.addClass(this.markerClassName).append(inst._datetimepickerDiv) |
|
356 .bind("setData.datetimepicker", function(event, key, value){ |
|
357 inst._settings[key] = value; |
|
358 }).bind("getData.datetimepicker", function(event, key){ |
|
359 return inst._get(key); |
|
360 }); |
|
361 input[0]._calId = inst._id; |
|
362 this._updateDatepicker(inst); |
|
363 }, |
|
364 |
|
365 /* Tidy up after displaying the date picker. */ |
|
366 _inlineShow: function(inst) { |
|
367 var numMonths = inst._getNumberOfMonths(); // fix width for dynamic number of date pickers |
|
368 inst._datetimepickerDiv.width(numMonths[1] * $('.datetimepicker', inst._datetimepickerDiv[0]).width()); |
|
369 }, |
|
370 |
|
371 /* Pop-up the date picker in a "dialog" box. |
|
372 @param input element - ignored |
|
373 @param dateText string - the initial date to display (in the current format) |
|
374 @param onSelect function - the function(dateText) to call when a date is selected |
|
375 @param settings object - update the dialog date picker instance's settings (anonymous object) |
|
376 @param pos int[2] - coordinates for the dialog's position within the screen or |
|
377 event - with x/y coordinates or |
|
378 leave empty for default (screen centre) |
|
379 @return the manager object */ |
|
380 _dialogDatepicker: function(input, dateText, onSelect, settings, pos) { |
|
381 var inst = this._dialogInst; // internal instance |
|
382 if (!inst) { |
|
383 inst = this._dialogInst = new DateTimepickerInstance({}, false); |
|
384 this._dialogInput = $('<input type="text" size="1" style="position: absolute; top: -100px;"/>'); |
|
385 this._dialogInput.keydown(this._doKeyDown); |
|
386 $('body').append(this._dialogInput); |
|
387 this._dialogInput[0]._calId = inst._id; |
|
388 } |
|
389 extendRemove(inst._settings, settings || {}); |
|
390 this._dialogInput.val(dateText); |
|
391 |
|
392 this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null); |
|
393 if (!this._pos) { |
|
394 var browserWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; |
|
395 var browserHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; |
|
396 var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft; |
|
397 var scrollY = document.documentElement.scrollTop || document.body.scrollTop; |
|
398 this._pos = // should use actual width/height below |
|
399 [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY]; |
|
400 } |
|
401 |
|
402 // move input on screen for focus, but hidden behind dialog |
|
403 this._dialogInput.css('left', this._pos[0] + 'px').css('top', this._pos[1] + 'px'); |
|
404 inst._settings.onSelect = onSelect; |
|
405 this._inDialog = true; |
|
406 this._datetimepickerDiv.addClass('datetimepicker_dialog'); |
|
407 this._showDatepicker(this._dialogInput[0]); |
|
408 if ($.blockUI) |
|
409 $.blockUI(this._datetimepickerDiv); |
|
410 return this; |
|
411 }, |
|
412 |
|
413 /* Pop-up the date picker for a given input field. |
|
414 @param input element - the input field attached to the date picker or |
|
415 event - if triggered by focus */ |
|
416 _showDatepicker: function(input) { |
|
417 input = input.target || input; |
|
418 if (input.nodeName.toLowerCase() != 'input') // find from button/image trigger |
|
419 input = $('input', input.parentNode)[0]; |
|
420 if ($.datetimepicker._isDisabledDatepicker(input) || $.datetimepicker._lastInput == input) // already here |
|
421 return; |
|
422 var inst = $.datetimepicker._getInst(input._calId); |
|
423 var beforeShow = inst._get('beforeShow'); |
|
424 extendRemove(inst._settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {})); |
|
425 $.datetimepicker._hideDatepicker(null, ''); |
|
426 $.datetimepicker._lastInput = input; |
|
427 inst._setDateFromField(input); |
|
428 if ($.datetimepicker._inDialog) // hide cursor |
|
429 input.value = ''; |
|
430 if (!$.datetimepicker._pos) { // position below input |
|
431 $.datetimepicker._pos = $.datetimepicker._findPos(input); |
|
432 $.datetimepicker._pos[1] += input.offsetHeight; // add the height |
|
433 } |
|
434 var isFixed = false; |
|
435 $(input).parents().each(function() { |
|
436 isFixed |= $(this).css('position') == 'fixed'; |
|
437 }); |
|
438 if (isFixed && $.browser.opera) { // correction for Opera when fixed and scrolled |
|
439 $.datetimepicker._pos[0] -= document.documentElement.scrollLeft; |
|
440 $.datetimepicker._pos[1] -= document.documentElement.scrollTop; |
|
441 } |
|
442 inst._datetimepickerDiv.css('position', ($.datetimepicker._inDialog && $.blockUI ? |
|
443 'static' : (isFixed ? 'fixed' : 'absolute'))) |
|
444 .css({ left: $.datetimepicker._pos[0] + 'px', top: $.datetimepicker._pos[1] + 'px' }); |
|
445 $.datetimepicker._pos = null; |
|
446 inst._rangeStart = null; |
|
447 $.datetimepicker._updateDatepicker(inst); |
|
448 if (!inst._inline) { |
|
449 var speed = inst._get('speed'); |
|
450 var postProcess = function() { |
|
451 $.datetimepicker._datetimepickerShowing = true; |
|
452 $.datetimepicker._afterShow(inst); |
|
453 }; |
|
454 var showAnim = inst._get('showAnim') || 'show'; |
|
455 inst._datetimepickerDiv[showAnim](speed, postProcess); |
|
456 if (speed == '') |
|
457 postProcess(); |
|
458 if (inst._input[0].type != 'hidden') |
|
459 inst._input[0].focus(); |
|
460 $.datetimepicker._curInst = inst; |
|
461 } |
|
462 }, |
|
463 |
|
464 /* Generate the date picker content. */ |
|
465 _updateDatepicker: function(inst) { |
|
466 inst._datetimepickerDiv.empty().append(inst._generateDatepicker()); |
|
467 var numMonths = inst._getNumberOfMonths(); |
|
468 if (numMonths[0] != 1 || numMonths[1] != 1) |
|
469 inst._datetimepickerDiv.addClass('datetimepicker_multi'); |
|
470 else |
|
471 inst._datetimepickerDiv.removeClass('datetimepicker_multi'); |
|
472 |
|
473 if (inst._get('isRTL')) |
|
474 inst._datetimepickerDiv.addClass('datetimepicker_rtl'); |
|
475 else |
|
476 inst._datetimepickerDiv.removeClass('datetimepicker_rtl'); |
|
477 |
|
478 if (inst._input && inst._input[0].type != 'hidden') |
|
479 $(inst._input[0]).focus(); |
|
480 }, |
|
481 |
|
482 /* Tidy up after displaying the date picker. */ |
|
483 _afterShow: function(inst) { |
|
484 var numMonths = inst._getNumberOfMonths(); // fix width for dynamic number of date pickers |
|
485 inst._datetimepickerDiv.width(numMonths[1] * $('.datetimepicker', inst._datetimepickerDiv[0])[0].offsetWidth); |
|
486 if ($.browser.msie && parseInt($.browser.version) < 7) { // fix IE < 7 select problems |
|
487 $('iframe.datetimepicker_cover').css({width: inst._datetimepickerDiv.width() + 4, |
|
488 height: inst._datetimepickerDiv.height() + 4}); |
|
489 } |
|
490 // re-position on screen if necessary |
|
491 var isFixed = inst._datetimepickerDiv.css('position') == 'fixed'; |
|
492 var pos = inst._input ? $.datetimepicker._findPos(inst._input[0]) : null; |
|
493 var browserWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; |
|
494 var browserHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; |
|
495 var scrollX = (isFixed ? 0 : document.documentElement.scrollLeft || document.body.scrollLeft); |
|
496 var scrollY = (isFixed ? 0 : document.documentElement.scrollTop || document.body.scrollTop); |
|
497 // reposition date picker horizontally if outside the browser window |
|
498 if ((inst._datetimepickerDiv.offset().left + inst._datetimepickerDiv.width() - |
|
499 (isFixed && $.browser.msie ? document.documentElement.scrollLeft : 0)) > |
|
500 (browserWidth + scrollX)) { |
|
501 inst._datetimepickerDiv.css('left', Math.max(scrollX, |
|
502 pos[0] + (inst._input ? $(inst._input[0]).width() : null) - inst._datetimepickerDiv.width() - |
|
503 (isFixed && $.browser.opera ? document.documentElement.scrollLeft : 0)) + 'px'); |
|
504 } |
|
505 // reposition date picker vertically if outside the browser window |
|
506 if ((inst._datetimepickerDiv.offset().top + inst._datetimepickerDiv.height() - |
|
507 (isFixed && $.browser.msie ? document.documentElement.scrollTop : 0)) > |
|
508 (browserHeight + scrollY) ) { |
|
509 inst._datetimepickerDiv.css('top', Math.max(scrollY, |
|
510 pos[1] - (this._inDialog ? 0 : inst._datetimepickerDiv.height()) - |
|
511 (isFixed && $.browser.opera ? document.documentElement.scrollTop : 0)) + 'px'); |
|
512 } |
|
513 }, |
|
514 |
|
515 /* Find an object's position on the screen. */ |
|
516 _findPos: function(obj) { |
|
517 while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) { |
|
518 obj = obj.nextSibling; |
|
519 } |
|
520 var position = $(obj).offset(); |
|
521 return [position.left, position.top]; |
|
522 }, |
|
523 |
|
524 /* Hide the date picker from view. |
|
525 @param input element - the input field attached to the date picker |
|
526 @param speed string - the speed at which to close the date picker */ |
|
527 _hideDatepicker: function(input, speed) { |
|
528 var inst = this._curInst; |
|
529 if (!inst) |
|
530 return; |
|
531 var rangeSelect = inst._get('rangeSelect'); |
|
532 if (rangeSelect && this._stayOpen) { |
|
533 this._selectDate(inst, inst._formatDateTime( |
|
534 inst._currentDay, inst._currentMonth, inst._currentYear, inst._currentHour, inst.currentMinute)); |
|
535 } |
|
536 this._stayOpen = false; |
|
537 if (this._datetimepickerShowing) { |
|
538 speed = (speed != null ? speed : inst._get('speed')); |
|
539 var showAnim = inst._get('showAnim'); |
|
540 inst._datetimepickerDiv[(showAnim == 'slideDown' ? 'slideUp' : |
|
541 (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))](speed, function() { |
|
542 $.datetimepicker._tidyDialog(inst); |
|
543 }); |
|
544 if (speed == '') |
|
545 this._tidyDialog(inst); |
|
546 var onClose = inst._get('onClose'); |
|
547 if (onClose) { |
|
548 onClose.apply((inst._input ? inst._input[0] : null), |
|
549 [inst._getDate(), inst]); // trigger custom callback |
|
550 } |
|
551 this._datetimepickerShowing = false; |
|
552 this._lastInput = null; |
|
553 inst._settings.prompt = null; |
|
554 if (this._inDialog) { |
|
555 this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' }); |
|
556 if ($.blockUI) { |
|
557 $.unblockUI(); |
|
558 $('body').append(this._datetimepickerDiv); |
|
559 } |
|
560 } |
|
561 this._inDialog = false; |
|
562 } |
|
563 this._curInst = null; |
|
564 }, |
|
565 |
|
566 /* Tidy up after a dialog display. */ |
|
567 _tidyDialog: function(inst) { |
|
568 inst._datetimepickerDiv.removeClass('datetimepicker_dialog').unbind('.datetimepicker'); |
|
569 $('.datetimepicker_prompt', inst._datetimepickerDiv).remove(); |
|
570 }, |
|
571 |
|
572 /* Close date picker if clicked elsewhere. */ |
|
573 _checkExternalClick: function(event) { |
|
574 if (!$.datetimepicker._curInst) |
|
575 return; |
|
576 var $target = $(event.target); |
|
577 if (($target.parents("#datetimepicker_div").length == 0) && |
|
578 ($target.attr('class') != 'datetimepicker_trigger') && |
|
579 $.datetimepicker._datetimepickerShowing && !($.datetimepicker._inDialog && $.blockUI)) { |
|
580 $.datetimepicker._hideDatepicker(null, ''); |
|
581 } |
|
582 }, |
|
583 |
|
584 /* Adjust one of the date sub-fields. */ |
|
585 _adjustDate: function(id, offset, period) { |
|
586 var inst = this._getInst(id); |
|
587 inst._adjustDate(offset, period); |
|
588 this._updateDatepicker(inst); |
|
589 }, |
|
590 |
|
591 /* Action for current link. */ |
|
592 _gotoToday: function(id) { |
|
593 var date = new Date(); |
|
594 var inst = this._getInst(id); |
|
595 inst._selectedDay = date.getDate(); |
|
596 inst._drawMonth = inst._selectedMonth = date.getMonth(); |
|
597 inst._drawYear = inst._selectedYear = date.getFullYear(); |
|
598 inst._drawHour = inst._selectedHour = date.getHours(); |
|
599 inst._drawMinute = inst._selectedMinute = date.getMinutes(); |
|
600 this._adjustDate(inst); |
|
601 }, |
|
602 |
|
603 /* Action for selecting a new month/year. */ |
|
604 _selectMonthYear: function(id, select, period) { |
|
605 var inst = this._getInst(id); |
|
606 inst._selectingMonthYear = false; |
|
607 inst[period == 'M' ? '_drawMonth' : '_drawYear'] = |
|
608 select.options[select.selectedIndex].value - 0; |
|
609 this._adjustDate(inst); |
|
610 }, |
|
611 _selectTime: function(id, select, period) { |
|
612 var inst = this._getInst(id); |
|
613 inst._selectingMonthYear = false; |
|
614 inst[period == 'M' ? '_drawMinute' : '_drawHour'] = |
|
615 select.options[select.selectedIndex].value - 0; |
|
616 this._adjustDate(inst); |
|
617 |
|
618 this._doNotHide = true; |
|
619 $('td.datetimepicker_currentDay').each(function(){ |
|
620 $.datetimepicker._selectDay(inst, inst._selectedMonth, inst._selectedYear,$(this)); |
|
621 }); |
|
622 this._doNotHide = false; |
|
623 }, |
|
624 |
|
625 /* Restore input focus after not changing month/year. */ |
|
626 _clickMonthYear: function(id) { |
|
627 var inst = this._getInst(id); |
|
628 if (inst._input && inst._selectingMonthYear && !$.browser.msie) |
|
629 inst._input[0].focus(); |
|
630 inst._selectingMonthYear = !inst._selectingMonthYear; |
|
631 }, |
|
632 |
|
633 _clickTime: function(id) { |
|
634 var inst = this._getInst(id); |
|
635 if (inst._input && inst._selectingTime && !$.browser.msie) |
|
636 inst._input[0].focus(); |
|
637 inst._selectingTime = !inst._selectingTime; |
|
638 }, |
|
639 |
|
640 /* Action for changing the first week day. */ |
|
641 _changeFirstDay: function(id, day) { |
|
642 var inst = this._getInst(id); |
|
643 inst._settings.firstDay = day; |
|
644 this._updateDatepicker(inst); |
|
645 }, |
|
646 |
|
647 /* Action for selecting a day. */ |
|
648 _selectDay: function(id, month, year, td) { |
|
649 if ($(td).is('.datetimepicker_unselectable')) |
|
650 return; |
|
651 var inst = this._getInst(id); |
|
652 var rangeSelect = inst._get('rangeSelect'); |
|
653 if (rangeSelect) { |
|
654 if (!this._stayOpen) { |
|
655 $('.datetimepicker td').removeClass('datetimepicker_currentDay'); |
|
656 $(td).addClass('datetimepicker_currentDay'); |
|
657 } |
|
658 this._stayOpen = !this._stayOpen; |
|
659 } |
|
660 inst._selectedDay = inst._currentDay = $('a', td).html(); |
|
661 inst._selectedMonth = inst._currentMonth = month; |
|
662 inst._selectedYear = inst._currentYear = year; |
|
663 inst._selectedHour = inst._currentHour = $('select.datetimepicker_newHour option:selected').val(); |
|
664 inst._selectedMinute = inst._currentMinute = $('select.datetimepicker_newMinute option:selected').val(); |
|
665 this._selectDate(id, inst._formatDateTime( |
|
666 inst._currentDay, inst._currentMonth, inst._currentYear, inst._currentHour, inst._currentMinute)); |
|
667 if (this._stayOpen) { |
|
668 inst._endDay = inst._endMonth = inst._endYear = null; |
|
669 inst._rangeStart = new Date(inst._currentYear, inst._currentMonth, inst._currentDay); |
|
670 this._updateDatepicker(inst); |
|
671 } |
|
672 else if (rangeSelect) { |
|
673 inst._endDay = inst._currentDay; |
|
674 inst._endMonth = inst._currentMonth; |
|
675 inst._endYear = inst._currentYear; |
|
676 inst._selectedDay = inst._currentDay = inst._rangeStart.getDate(); |
|
677 inst._selectedMonth = inst._currentMonth = inst._rangeStart.getMonth(); |
|
678 inst._selectedYear = inst._currentYear = inst._rangeStart.getFullYear(); |
|
679 inst._rangeStart = null; |
|
680 if (inst._inline) |
|
681 this._updateDatepicker(inst); |
|
682 } |
|
683 }, |
|
684 |
|
685 /* Erase the input field and hide the date picker. */ |
|
686 _clearDate: function(id) { |
|
687 var inst = this._getInst(id); |
|
688 if (inst._get('mandatory')) |
|
689 return; |
|
690 this._stayOpen = false; |
|
691 inst._endDay = inst._endMonth = inst._endYear = inst._rangeStart = null; |
|
692 this._selectDate(inst, ''); |
|
693 }, |
|
694 |
|
695 /* Update the input field with the selected date. */ |
|
696 _selectDate: function(id, dateStr) { |
|
697 var inst = this._getInst(id); |
|
698 dateStr = (dateStr != null ? dateStr : inst._formatDateTime()); |
|
699 if (inst._rangeStart) |
|
700 dateStr = inst._formatDateTime(inst._rangeStart) + inst._get('rangeSeparator') + dateStr; |
|
701 if (inst._input) |
|
702 inst._input.val(dateStr); |
|
703 var onSelect = inst._get('onSelect'); |
|
704 if (onSelect) |
|
705 onSelect.apply((inst._input ? inst._input[0] : null), [dateStr, inst]); // trigger custom callback |
|
706 else if (inst._input) |
|
707 inst._input.trigger('change'); // fire the change event |
|
708 if (inst._inline) |
|
709 this._updateDatepicker(inst); |
|
710 else if (!this._stayOpen) { |
|
711 if (! this._doNotHide) { |
|
712 this._hideDatepicker(null, inst._get('speed')); |
|
713 this._lastInput = inst._input[0]; |
|
714 if (typeof(inst._input[0]) != 'object') |
|
715 inst._input[0].focus(); // restore focus |
|
716 this._lastInput = null; |
|
717 } |
|
718 } |
|
719 }, |
|
720 |
|
721 /* Set as beforeShowDay function to prevent selection of weekends. |
|
722 @param date Date - the date to customise |
|
723 @return [boolean, string] - is this date selectable?, what is its CSS class? */ |
|
724 noWeekends: function(date) { |
|
725 var day = date.getDay(); |
|
726 return [(day > 0 && day < 6), '']; |
|
727 }, |
|
728 |
|
729 /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition. |
|
730 @param date Date - the date to get the week for |
|
731 @return number - the number of the week within the year that contains this date */ |
|
732 iso8601Week: function(date) { |
|
733 var checkDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), (date.getTimezoneOffset() / -60)); |
|
734 var firstMon = new Date(checkDate.getFullYear(), 1 - 1, 4); // First week always contains 4 Jan |
|
735 var firstDay = firstMon.getDay() || 7; // Day of week: Mon = 1, ..., Sun = 7 |
|
736 firstMon.setDate(firstMon.getDate() + 1 - firstDay); // Preceding Monday |
|
737 if (firstDay < 4 && checkDate < firstMon) { // Adjust first three days in year if necessary |
|
738 checkDate.setDate(checkDate.getDate() - 3); // Generate for previous year |
|
739 return $.datetimepicker.iso8601Week(checkDate); |
|
740 } else if (checkDate > new Date(checkDate.getFullYear(), 12 - 1, 28)) { // Check last three days in year |
|
741 firstDay = new Date(checkDate.getFullYear() + 1, 1 - 1, 4).getDay() || 7; |
|
742 if (firstDay > 4 && (checkDate.getDay() || 7) < firstDay - 3) { // Adjust if necessary |
|
743 checkDate.setDate(checkDate.getDate() + 3); // Generate for next year |
|
744 return $.datetimepicker.iso8601Week(checkDate); |
|
745 } |
|
746 } |
|
747 return Math.floor(((checkDate - firstMon) / 86400000) / 7) + 1; // Weeks to given date |
|
748 }, |
|
749 |
|
750 /* Provide status text for a particular date. |
|
751 @param date the date to get the status for |
|
752 @param inst the current datetimepicker instance |
|
753 @return the status display text for this date */ |
|
754 dateStatus: function(date, inst) { |
|
755 return $.datetimepicker.formatDate(inst._get('dateStatus'), date, inst._getFormatConfig()); |
|
756 }, |
|
757 |
|
758 /* Parse a string value into a date object. |
|
759 The format can be combinations of the following: |
|
760 d - day of month (no leading zero) |
|
761 dd - day of month (two digit) |
|
762 D - day name short |
|
763 DD - day name long |
|
764 m - month of year (no leading zero) |
|
765 mm - month of year (two digit) |
|
766 M - month name short |
|
767 MM - month name long |
|
768 y - year (two digit) |
|
769 yy - year (four digit) |
|
770 '...' - literal text |
|
771 '' - single quote |
|
772 |
|
773 @param format String - the expected format of the date |
|
774 @param value String - the date in the above format |
|
775 @param settings Object - attributes include: |
|
776 shortYearCutoff Number - the cutoff year for determining the century (optional) |
|
777 dayNamesShort String[7] - abbreviated names of the days from Sunday (optional) |
|
778 dayNames String[7] - names of the days from Sunday (optional) |
|
779 monthNamesShort String[12] - abbreviated names of the months (optional) |
|
780 monthNames String[12] - names of the months (optional) |
|
781 @return Date - the extracted date value or null if value is blank */ |
|
782 parseDate: function (format, value, settings) { |
|
783 if (format == null || value == null) |
|
784 throw 'Invalid arguments'; |
|
785 value = (typeof value == 'object' ? value.toString() : value + ''); |
|
786 if (value == '') |
|
787 return null; |
|
788 var shortYearCutoff = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff; |
|
789 var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort; |
|
790 var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames; |
|
791 var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort; |
|
792 var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames; |
|
793 var year = -1; |
|
794 var month = -1; |
|
795 var day = -1; |
|
796 var hour = -1; |
|
797 var minute = -1; |
|
798 var literal = false; |
|
799 // Check whether a format character is doubled |
|
800 var lookAhead = function(match) { |
|
801 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match); |
|
802 if (matches) |
|
803 iFormat++; |
|
804 return matches; |
|
805 }; |
|
806 // Extract a number from the string value |
|
807 var getNumber = function(match) { |
|
808 lookAhead(match); |
|
809 var size = (match == 'y' ? 4 : 2); |
|
810 var num = 0; |
|
811 while (size > 0 && iValue < value.length && |
|
812 value.charAt(iValue) >= '0' && value.charAt(iValue) <= '9') { |
|
813 num = num * 10 + (value.charAt(iValue++) - 0); |
|
814 size--; |
|
815 } |
|
816 if (size == (match == 'y' ? 4 : 2)) |
|
817 throw 'Missing number at position ' + iValue; |
|
818 return num; |
|
819 }; |
|
820 // Extract a name from the string value and convert to an index |
|
821 var getName = function(match, shortNames, longNames) { |
|
822 var names = (lookAhead(match) ? longNames : shortNames); |
|
823 var size = 0; |
|
824 for (var j = 0; j < names.length; j++) |
|
825 size = Math.max(size, names[j].length); |
|
826 var name = ''; |
|
827 var iInit = iValue; |
|
828 while (size > 0 && iValue < value.length) { |
|
829 name += value.charAt(iValue++); |
|
830 for (var i = 0; i < names.length; i++) |
|
831 if (name == names[i]) |
|
832 return i + 1; |
|
833 size--; |
|
834 } |
|
835 throw 'Unknown name at position ' + iInit; |
|
836 }; |
|
837 // Confirm that a literal character matches the string value |
|
838 var checkLiteral = function() { |
|
839 if (value.charAt(iValue) != format.charAt(iFormat)) |
|
840 throw 'Unexpected literal at position ' + iValue; |
|
841 iValue++; |
|
842 }; |
|
843 var iValue = 0; |
|
844 for (var iFormat = 0; iFormat < format.length; iFormat++) { |
|
845 if (literal) |
|
846 if (format.charAt(iFormat) == "'" && !lookAhead("'")) |
|
847 literal = false; |
|
848 else |
|
849 checkLiteral(); |
|
850 else |
|
851 switch (format.charAt(iFormat)) { |
|
852 case 'h': |
|
853 hour = getNumber('h'); |
|
854 break; |
|
855 case 'i': |
|
856 minute = getNumber('i'); |
|
857 break; |
|
858 case 'd': |
|
859 day = getNumber('d'); |
|
860 break; |
|
861 case 'D': |
|
862 getName('D', dayNamesShort, dayNames); |
|
863 break; |
|
864 case 'm': |
|
865 month = getNumber('m'); |
|
866 break; |
|
867 case 'M': |
|
868 month = getName('M', monthNamesShort, monthNames); |
|
869 break; |
|
870 case 'y': |
|
871 year = getNumber('y'); |
|
872 break; |
|
873 case "'": |
|
874 if (lookAhead("'")) |
|
875 checkLiteral(); |
|
876 else |
|
877 literal = true; |
|
878 break; |
|
879 default: |
|
880 checkLiteral(); |
|
881 } |
|
882 } |
|
883 if (year < 100) { |
|
884 year += new Date().getFullYear() - new Date().getFullYear() % 100 + |
|
885 (year <= shortYearCutoff ? 0 : -100); |
|
886 } |
|
887 var date = new Date(year, month - 1, day,hour,minute); |
|
888 if (date.getFullYear() != year || date.getMonth() + 1 != month || date.getDate() != day) { |
|
889 throw 'Invalid date'; // E.g. 31/02/* |
|
890 } |
|
891 return date; |
|
892 }, |
|
893 |
|
894 /* Format a date object into a string value. |
|
895 The format can be combinations of the following: |
|
896 d - day of month (no leading zero) |
|
897 dd - day of month (two digit) |
|
898 D - day name short |
|
899 DD - day name long |
|
900 m - month of year (no leading zero) |
|
901 mm - month of year (two digit) |
|
902 M - month name short |
|
903 MM - month name long |
|
904 y - year (two digit) |
|
905 yy - year (four digit) |
|
906 '...' - literal text |
|
907 '' - single quote |
|
908 |
|
909 @param format String - the desired format of the date |
|
910 @param date Date - the date value to format |
|
911 @param settings Object - attributes include: |
|
912 dayNamesShort String[7] - abbreviated names of the days from Sunday (optional) |
|
913 dayNames String[7] - names of the days from Sunday (optional) |
|
914 monthNamesShort String[12] - abbreviated names of the months (optional) |
|
915 monthNames String[12] - names of the months (optional) |
|
916 @return String - the date in the above format */ |
|
917 formatDate: function (format, date, settings) { |
|
918 if (!date) |
|
919 return ''; |
|
920 var dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort; |
|
921 var dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames; |
|
922 var monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort; |
|
923 var monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames; |
|
924 // Check whether a format character is doubled |
|
925 var lookAhead = function(match) { |
|
926 var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) == match); |
|
927 if (matches) |
|
928 iFormat++; |
|
929 return matches; |
|
930 }; |
|
931 // Format a number, with leading zero if necessary |
|
932 var formatNumber = function(match, value) { |
|
933 return (lookAhead(match) && value < 10 ? '0' : '') + value; |
|
934 }; |
|
935 // Format a name, short or long as requested |
|
936 var formatName = function(match, value, shortNames, longNames) { |
|
937 return (lookAhead(match) ? longNames[value] : shortNames[value]); |
|
938 }; |
|
939 var output = ''; |
|
940 var literal = false; |
|
941 if (date) { |
|
942 for (var iFormat = 0; iFormat < format.length; iFormat++) { |
|
943 if (literal) |
|
944 if (format.charAt(iFormat) == "'" && !lookAhead("'")) |
|
945 literal = false; |
|
946 else |
|
947 output += format.charAt(iFormat); |
|
948 else |
|
949 switch (format.charAt(iFormat)) { |
|
950 case 'h': |
|
951 output += formatNumber('h', date.getHours()); |
|
952 break; |
|
953 case 'i': |
|
954 output += formatNumber('i', date.getMinutes()); |
|
955 break; |
|
956 case 'd': |
|
957 output += formatNumber('d', date.getDate()); |
|
958 break; |
|
959 case 'D': |
|
960 output += formatName('D', date.getDay(), dayNamesShort, dayNames); |
|
961 break; |
|
962 case 'm': |
|
963 output += formatNumber('m', date.getMonth() + 1); |
|
964 break; |
|
965 case 'M': |
|
966 output += formatName('M', date.getMonth(), monthNamesShort, monthNames); |
|
967 break; |
|
968 case 'y': |
|
969 output += (lookAhead('y') ? date.getFullYear() : |
|
970 (date.getYear() % 100 < 10 ? '0' : '') + date.getYear() % 100); |
|
971 break; |
|
972 case "'": |
|
973 if (lookAhead("'")) |
|
974 output += "'"; |
|
975 else |
|
976 literal = true; |
|
977 break; |
|
978 default: |
|
979 output += format.charAt(iFormat); |
|
980 } |
|
981 } |
|
982 } |
|
983 return output; |
|
984 }, |
|
985 |
|
986 /* Extract all possible characters from the date format. */ |
|
987 _possibleChars: function (format) { |
|
988 var chars = ''; |
|
989 var literal = false; |
|
990 for (var iFormat = 0; iFormat < format.length; iFormat++) |
|
991 if (literal) |
|
992 if (format.charAt(iFormat) == "'" && !lookAhead("'")) |
|
993 literal = false; |
|
994 else |
|
995 chars += format.charAt(iFormat); |
|
996 else |
|
997 switch (format.charAt(iFormat)) { |
|
998 case 'd' || 'm' || 'y': |
|
999 chars += '0123456789'; |
|
1000 break; |
|
1001 case 'D' || 'M': |
|
1002 return null; // Accept anything |
|
1003 case "'": |
|
1004 if (lookAhead("'")) |
|
1005 chars += "'"; |
|
1006 else |
|
1007 literal = true; |
|
1008 break; |
|
1009 default: |
|
1010 chars += format.charAt(iFormat); |
|
1011 } |
|
1012 return chars; |
|
1013 } |
|
1014 }); |
|
1015 |
|
1016 /* Individualised settings for date picker functionality applied to one or more related inputs. |
|
1017 Instances are managed and manipulated through the Datepicker manager. */ |
|
1018 function DateTimepickerInstance(settings, inline) { |
|
1019 this._id = $.datetimepicker._register(this); |
|
1020 this._selectedDay = 0; // Current date for selection |
|
1021 this._selectedMonth = 0; // 0-11 |
|
1022 this._selectedYear = 0; // 4-digit year |
|
1023 this._drawMonth = 0; // Current month at start of datetimepicker |
|
1024 this._drawYear = 0; |
|
1025 this._drawHour = 0; |
|
1026 this._drawMinute = 0; |
|
1027 this._input = null; // The attached input field |
|
1028 this._inline = inline; // True if showing inline, false if used in a popup |
|
1029 this._datetimepickerDiv = (!inline ? $.datetimepicker._datetimepickerDiv : |
|
1030 $('<div id="datetimepicker_div_' + this._id + '" class="datetimepicker_inline">')); |
|
1031 // customise the date picker object - uses manager defaults if not overridden |
|
1032 this._settings = extendRemove(settings || {}); // clone |
|
1033 if (inline) |
|
1034 this._setDate(this._getDefaultDate()); |
|
1035 } |
|
1036 |
|
1037 $.extend(DateTimepickerInstance.prototype, { |
|
1038 /* Get a setting value, defaulting if necessary. */ |
|
1039 _get: function(name) { |
|
1040 return this._settings[name] !== undefined ? this._settings[name] : $.datetimepicker._defaults[name]; |
|
1041 }, |
|
1042 |
|
1043 /* Parse existing date and initialise date picker. */ |
|
1044 _setDateFromField: function(input) { |
|
1045 this._input = $(input); |
|
1046 var dateFormat = this._get('dateFormat')+' '+this._get('timeFormat'); |
|
1047 var dates = this._input ? this._input.val().split(this._get('rangeSeparator')) : null; |
|
1048 this._endDay = this._endMonth = this._endYear = null; |
|
1049 var date = defaultDate = this._getDefaultDate(); |
|
1050 if (dates.length > 0) { |
|
1051 var settings = this._getFormatConfig(); |
|
1052 if (dates.length > 1) { |
|
1053 date = $.datetimepicker.parseDate(dateFormat, dates[1], settings) || defaultDate; |
|
1054 this._endDay = date.getDate(); |
|
1055 this._endMonth = date.getMonth(); |
|
1056 this._endYear = date.getFullYear(); |
|
1057 } |
|
1058 try { |
|
1059 date = $.datetimepicker.parseDate(dateFormat, dates[0], settings) || defaultDate; |
|
1060 } catch (e) { |
|
1061 $.datetimepicker.log(e); |
|
1062 date = defaultDate; |
|
1063 } |
|
1064 } |
|
1065 this._selectedDay = date.getDate(); |
|
1066 this._drawMonth = this._selectedMonth = date.getMonth(); |
|
1067 this._drawYear = this._selectedYear = date.getFullYear(); |
|
1068 this._drawHour = this._selectedHour = date.getHours(); |
|
1069 this._drawMinute = this._selectedMinute = date.getMinutes(); |
|
1070 this._currentDay = (dates[0] ? date.getDate() : 0); |
|
1071 this._currentMonth = (dates[0] ? date.getMonth() : 0); |
|
1072 this._currentYear = (dates[0] ? date.getFullYear() : 0); |
|
1073 this._adjustDate(); |
|
1074 }, |
|
1075 |
|
1076 /* Retrieve the default date shown on opening. */ |
|
1077 _getDefaultDate: function() { |
|
1078 var date = this._determineDate('defaultDate', new Date()); |
|
1079 var minDate = this._getMinMaxDate('min', true); |
|
1080 var maxDate = this._getMinMaxDate('max'); |
|
1081 date = (minDate && date < minDate ? minDate : date); |
|
1082 date = (maxDate && date > maxDate ? maxDate : date); |
|
1083 return date; |
|
1084 }, |
|
1085 |
|
1086 /* A date may be specified as an exact value or a relative one. */ |
|
1087 _determineDate: function(name, defaultDate) { |
|
1088 var offsetNumeric = function(offset) { |
|
1089 var date = new Date(); |
|
1090 date.setDate(date.getDate() + offset); |
|
1091 return date; |
|
1092 }; |
|
1093 var offsetString = function(offset, getDaysInMonth) { |
|
1094 var date = new Date(); |
|
1095 var matches = /^([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?$/.exec(offset); |
|
1096 if (matches) { |
|
1097 var year = date.getFullYear(); |
|
1098 var month = date.getMonth(); |
|
1099 var day = date.getDate(); |
|
1100 switch (matches[2] || 'd') { |
|
1101 case 'd' : case 'D' : |
|
1102 day += (matches[1] - 0); break; |
|
1103 case 'w' : case 'W' : |
|
1104 day += (matches[1] * 7); break; |
|
1105 case 'm' : case 'M' : |
|
1106 month += (matches[1] - 0); |
|
1107 day = Math.min(day, getDaysInMonth(year, month)); |
|
1108 break; |
|
1109 case 'y': case 'Y' : |
|
1110 year += (matches[1] - 0); |
|
1111 day = Math.min(day, getDaysInMonth(year, month)); |
|
1112 break; |
|
1113 } |
|
1114 date = new Date(year, month, day); |
|
1115 } |
|
1116 return date; |
|
1117 }; |
|
1118 var date = this._get(name); |
|
1119 return (date == null ? defaultDate : |
|
1120 (typeof date == 'string' ? offsetString(date, this._getDaysInMonth) : |
|
1121 (typeof date == 'number' ? offsetNumeric(date) : date))); |
|
1122 }, |
|
1123 |
|
1124 /* Set the date(s) directly. */ |
|
1125 _setDate: function(date, endDate) { |
|
1126 this._selectedDay = this._currentDay = date.getDate(); |
|
1127 this._drawMonth = this._selectedMonth = this._currentMonth = date.getMonth(); |
|
1128 this._drawYear = this._selectedYear = this._currentYear = date.getFullYear(); |
|
1129 this._drawHour = this._selectedHour = this._currentHour = date.getHours(); |
|
1130 this._drawMinute = this._selectedMinute = this._currentMinute = date.getMinutes(); |
|
1131 if (this._get('rangeSelect')) { |
|
1132 if (endDate) { |
|
1133 this._endDay = endDate.getDate(); |
|
1134 this._endMonth = endDate.getMonth(); |
|
1135 this._endYear = endDate.getFullYear(); |
|
1136 } else { |
|
1137 this._endDay = this._currentDay; |
|
1138 this._endMonth = this._currentMonth; |
|
1139 this._endYear = this._currentYear; |
|
1140 } |
|
1141 } |
|
1142 this._adjustDate(); |
|
1143 }, |
|
1144 |
|
1145 /* Retrieve the date(s) directly. */ |
|
1146 _getDate: function() { |
|
1147 var startDate = (!this._currentYear || (this._input && this._input.val() == '') ? null : |
|
1148 new Date(this._currentYear, this._currentMonth, this._currentDay)); |
|
1149 if (this._get('rangeSelect')) { |
|
1150 return [startDate, (!this._endYear ? null : |
|
1151 new Date(this._endYear, this._endMonth, this._endDay))]; |
|
1152 } else |
|
1153 return startDate; |
|
1154 }, |
|
1155 |
|
1156 /* Generate the HTML for the current state of the date picker. */ |
|
1157 _generateDatepicker: function() { |
|
1158 var today = new Date(); |
|
1159 today = new Date(today.getFullYear(), today.getMonth(), today.getDate()); // clear time |
|
1160 var showStatus = this._get('showStatus'); |
|
1161 var isRTL = this._get('isRTL'); |
|
1162 // build the date picker HTML |
|
1163 var clear = (this._get('mandatory') ? '' : |
|
1164 '<div class="datetimepicker_clear"><a onclick="jQuery.datetimepicker._clearDate(' + this._id + ');"' + |
|
1165 (showStatus ? this._addStatus(this._get('clearStatus') || ' ') : '') + '>' + |
|
1166 this._get('clearText') + '</a></div>'); |
|
1167 var controls = '<div class="datetimepicker_control">' + (isRTL ? '' : clear) + |
|
1168 '<div class="datetimepicker_close"><a onclick="jQuery.datetimepicker._hideDatepicker();"' + |
|
1169 (showStatus ? this._addStatus(this._get('closeStatus') || ' ') : '') + '>' + |
|
1170 this._get('closeText') + '</a></div>' + (isRTL ? clear : '') + '</div>'; |
|
1171 var prompt = this._get('prompt'); |
|
1172 var closeAtTop = this._get('closeAtTop'); |
|
1173 var hideIfNoPrevNext = this._get('hideIfNoPrevNext'); |
|
1174 var numMonths = this._getNumberOfMonths(); |
|
1175 var stepMonths = this._get('stepMonths'); |
|
1176 var isMultiMonth = (numMonths[0] != 1 || numMonths[1] != 1); |
|
1177 var minDate = this._getMinMaxDate('min', true); |
|
1178 var maxDate = this._getMinMaxDate('max'); |
|
1179 var drawMonth = this._drawMonth; |
|
1180 var drawYear = this._drawYear; |
|
1181 var drawHour = this._drawHour; |
|
1182 var drawMinute = this._drawMinute; |
|
1183 if (maxDate) { |
|
1184 var maxDraw = new Date(maxDate.getFullYear(), |
|
1185 maxDate.getMonth() - numMonths[1] + 1, maxDate.getDate()); |
|
1186 maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw); |
|
1187 while (new Date(drawYear, drawMonth, 1) > maxDraw) { |
|
1188 drawMonth--; |
|
1189 if (drawMonth < 0) { |
|
1190 drawMonth = 11; |
|
1191 drawYear--; |
|
1192 } |
|
1193 } |
|
1194 } |
|
1195 // controls and links |
|
1196 var prev = '<div class="datetimepicker_prev">' + (this._canAdjustMonth(-1, drawYear, drawMonth) ? |
|
1197 '<a onclick="jQuery.datetimepicker._adjustDate(' + this._id + ', -' + stepMonths + ', \'M\');"' + |
|
1198 (showStatus ? this._addStatus(this._get('prevStatus') || ' ') : '') + '>' + |
|
1199 this._get('prevText') + '</a>' : |
|
1200 (hideIfNoPrevNext ? '' : '<label>' + this._get('prevText') + '</label>')) + '</div>'; |
|
1201 var next = '<div class="datetimepicker_next">' + (this._canAdjustMonth(+1, drawYear, drawMonth) ? |
|
1202 '<a onclick="jQuery.datetimepicker._adjustDate(' + this._id + ', +' + stepMonths + ', \'M\');"' + |
|
1203 (showStatus ? this._addStatus(this._get('nextStatus') || ' ') : '') + '>' + |
|
1204 this._get('nextText') + '</a>' : |
|
1205 (hideIfNoPrevNext ? '>' : '<label>' + this._get('nextText') + '</label>')) + '</div>'; |
|
1206 var html = (prompt ? '<div class="datetimepicker_prompt">' + prompt + '</div>' : '') + |
|
1207 (closeAtTop && !this._inline ? controls : '') + |
|
1208 '<div class="datetimepicker_links">' + (isRTL ? next : prev) + |
|
1209 (this._isInRange(today) ? '<div class="datetimepicker_current">' + |
|
1210 '<a onclick="jQuery.datetimepicker._gotoToday(' + this._id + ');"' + |
|
1211 (showStatus ? this._addStatus(this._get('currentStatus') || ' ') : '') + '>' + |
|
1212 this._get('currentText') + '</a></div>' : '') + (isRTL ? prev : next) + '</div>'; |
|
1213 var showWeeks = this._get('showWeeks'); |
|
1214 for (var row = 0; row < numMonths[0]; row++) |
|
1215 for (var col = 0; col < numMonths[1]; col++) { |
|
1216 var selectedDate = new Date(drawYear, drawMonth, this._selectedDay, drawHour, drawMinute); |
|
1217 html += '<div class="datetimepicker_oneMonth' + (col == 0 ? ' datetimepicker_newRow' : '') + '">' + |
|
1218 this._generateMonthYearHeader(drawMinute,drawHour,drawMonth, drawYear, minDate, maxDate, |
|
1219 selectedDate, row > 0 || col > 0) + // draw month headers |
|
1220 '<table class="datetimepicker" cellpadding="0" cellspacing="0"><thead>' + |
|
1221 '<tr class="datetimepicker_titleRow">' + |
|
1222 (showWeeks ? '<td>' + this._get('weekHeader') + '</td>' : ''); |
|
1223 var firstDay = this._get('firstDay'); |
|
1224 var changeFirstDay = this._get('changeFirstDay'); |
|
1225 var dayNames = this._get('dayNames'); |
|
1226 var dayNamesShort = this._get('dayNamesShort'); |
|
1227 var dayNamesMin = this._get('dayNamesMin'); |
|
1228 for (var dow = 0; dow < 7; dow++) { // days of the week |
|
1229 var day = (dow + firstDay) % 7; |
|
1230 var status = this._get('dayStatus') || ' '; |
|
1231 status = (status.indexOf('DD') > -1 ? status.replace(/DD/, dayNames[day]) : |
|
1232 status.replace(/D/, dayNamesShort[day])); |
|
1233 html += '<td' + ((dow + firstDay + 6) % 7 >= 5 ? ' class="datetimepicker_weekEndCell"' : '') + '>' + |
|
1234 (!changeFirstDay ? '<span' : |
|
1235 '<a onclick="jQuery.datetimepicker._changeFirstDay(' + this._id + ', ' + day + ');"') + |
|
1236 (showStatus ? this._addStatus(status) : '') + ' title="' + dayNames[day] + '">' + |
|
1237 dayNamesMin[day] + (changeFirstDay ? '</a>' : '</span>') + '</td>'; |
|
1238 } |
|
1239 html += '</tr></thead><tbody>'; |
|
1240 var daysInMonth = this._getDaysInMonth(drawYear, drawMonth); |
|
1241 if (drawYear == this._selectedYear && drawMonth == this._selectedMonth) { |
|
1242 this._selectedDay = Math.min(this._selectedDay, daysInMonth); |
|
1243 } |
|
1244 var leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7; |
|
1245 var currentDate = (!this._currentDay ? new Date(9999, 9, 9) : |
|
1246 new Date(this._currentYear, this._currentMonth, this._currentDay)); |
|
1247 var endDate = this._endDay ? new Date(this._endYear, this._endMonth, this._endDay) : currentDate; |
|
1248 var printDate = new Date(drawYear, drawMonth, 1 - leadDays); |
|
1249 var numRows = (isMultiMonth ? 6 : Math.ceil((leadDays + daysInMonth) / 7)); // calculate the number of rows to generate |
|
1250 var beforeShowDay = this._get('beforeShowDay'); |
|
1251 var showOtherMonths = this._get('showOtherMonths'); |
|
1252 var calculateWeek = this._get('calculateWeek') || $.datetimepicker.iso8601Week; |
|
1253 var dateStatus = this._get('statusForDate') || $.datetimepicker.dateStatus; |
|
1254 for (var dRow = 0; dRow < numRows; dRow++) { // create date picker rows |
|
1255 html += '<tr class="datetimepicker_daysRow">' + |
|
1256 (showWeeks ? '<td class="datetimepicker_weekCol">' + calculateWeek(printDate) + '</td>' : ''); |
|
1257 for (var dow = 0; dow < 7; dow++) { // create date picker days |
|
1258 var daySettings = (beforeShowDay ? |
|
1259 beforeShowDay.apply((this._input ? this._input[0] : null), [printDate]) : [true, '']); |
|
1260 var otherMonth = (printDate.getMonth() != drawMonth); |
|
1261 var unselectable = otherMonth || !daySettings[0] || |
|
1262 (minDate && printDate < minDate) || (maxDate && printDate > maxDate); |
|
1263 html += '<td class="datetimepicker_daysCell' + |
|
1264 ((dow + firstDay + 6) % 7 >= 5 ? ' datetimepicker_weekEndCell' : '') + // highlight weekends |
|
1265 (otherMonth ? ' datetimepicker_otherMonth' : '') + // highlight days from other months |
|
1266 (printDate.getTime() == selectedDate.getTime() && drawMonth == this._selectedMonth ? |
|
1267 ' datetimepicker_daysCellOver' : '') + // highlight selected day |
|
1268 (unselectable ? ' datetimepicker_unselectable' : '') + // highlight unselectable days |
|
1269 (otherMonth && !showOtherMonths ? '' : ' ' + daySettings[1] + // highlight custom dates |
|
1270 (printDate.getTime() >= currentDate.getTime() && printDate.getTime() <= endDate.getTime() ? // in current range |
|
1271 ' datetimepicker_currentDay' : '') + // highlight selected day |
|
1272 (printDate.getTime() == today.getTime() ? ' datetimepicker_today' : '')) + '"' + // highlight today (if different) |
|
1273 (unselectable ? '' : ' onmouseover="jQuery(this).addClass(\'datetimepicker_daysCellOver\');' + |
|
1274 (!showStatus || (otherMonth && !showOtherMonths) ? '' : 'jQuery(\'#datetimepicker_status_' + |
|
1275 this._id + '\').html(\'' + (dateStatus.apply((this._input ? this._input[0] : null), |
|
1276 [printDate, this]) || ' ') +'\');') + '"' + |
|
1277 ' onmouseout="jQuery(this).removeClass(\'datetimepicker_daysCellOver\');' + |
|
1278 (!showStatus || (otherMonth && !showOtherMonths) ? '' : 'jQuery(\'#datetimepicker_status_' + |
|
1279 this._id + '\').html(\' \');') + '" onclick="jQuery.datetimepicker._selectDay(' + |
|
1280 this._id + ',' + drawMonth + ',' + drawYear + ', this);"') + '>' + // actions |
|
1281 (otherMonth ? (showOtherMonths ? printDate.getDate() : ' ') : // display for other months |
|
1282 (unselectable ? printDate.getDate() : '<a>' + printDate.getDate() + '</a>')) + '</td>'; // display for this month |
|
1283 printDate.setDate(printDate.getDate() + 1); |
|
1284 } |
|
1285 html += '</tr>'; |
|
1286 } |
|
1287 drawMonth++; |
|
1288 if (drawMonth > 11) { |
|
1289 drawMonth = 0; |
|
1290 drawYear++; |
|
1291 } |
|
1292 html += '</tbody></table></div>'; |
|
1293 } |
|
1294 html += (showStatus ? '<div style="clear: both;"></div><div id="datetimepicker_status_' + this._id + |
|
1295 '" class="datetimepicker_status">' + (this._get('initStatus') || ' ') + '</div>' : '') + |
|
1296 (!closeAtTop && !this._inline ? controls : '') + |
|
1297 '<div style="clear: both;"></div>' + |
|
1298 ($.browser.msie && parseInt($.browser.version) < 7 && !this._inline ? |
|
1299 '<iframe src="javascript:false;" class="datetimepicker_cover"></iframe>' : ''); |
|
1300 return html; |
|
1301 }, |
|
1302 |
|
1303 /* Generate the month and year header. */ |
|
1304 _generateMonthYearHeader: function(drawMinute,drawHour,drawMonth, drawYear, minDate, maxDate, selectedDate, secondary) { |
|
1305 minDate = (this._rangeStart && minDate && selectedDate < minDate ? selectedDate : minDate); |
|
1306 var showStatus = this._get('showStatus'); |
|
1307 var html = '<div class="datetimepicker_header">'; |
|
1308 // month selection |
|
1309 var monthNames = this._get('monthNames'); |
|
1310 if (secondary || !this._get('changeMonth')) |
|
1311 html += monthNames[drawMonth] + ' '; |
|
1312 |
|
1313 else { |
|
1314 var inMinYear = (minDate && minDate.getFullYear() == drawYear); |
|
1315 var inMaxYear = (maxDate && maxDate.getFullYear() == drawYear); |
|
1316 html += '<select class="datetimepicker_newMonth" ' + |
|
1317 'onchange="jQuery.datetimepicker._selectMonthYear(' + this._id + ', this, \'M\');" ' + |
|
1318 'onclick="jQuery.datetimepicker._clickMonthYear(' + this._id + ');"' + |
|
1319 (showStatus ? this._addStatus(this._get('monthStatus') || ' ') : '') + '>'; |
|
1320 for (var month = 0; month < 12; month++) { |
|
1321 if ((!inMinYear || month >= minDate.getMonth()) && |
|
1322 (!inMaxYear || month <= maxDate.getMonth())) { |
|
1323 html += '<option value="' + month + '"' + |
|
1324 (month == drawMonth ? ' selected="selected"' : '') + |
|
1325 '>' + monthNames[month] + '</option>'; |
|
1326 } |
|
1327 } |
|
1328 html += '</select>'; |
|
1329 } |
|
1330 // year selection |
|
1331 if (secondary || !this._get('changeYear')) |
|
1332 html += drawYear; |
|
1333 else { |
|
1334 // determine range of years to display |
|
1335 var years = this._get('yearRange').split(':'); |
|
1336 var year = 0; |
|
1337 var endYear = 0; |
|
1338 if (years.length != 2) { |
|
1339 year = drawYear - 10; |
|
1340 endYear = drawYear + 10; |
|
1341 } else if (years[0].charAt(0) == '+' || years[0].charAt(0) == '-') { |
|
1342 year = drawYear + parseInt(years[0], 10); |
|
1343 endYear = drawYear + parseInt(years[1], 10); |
|
1344 } else { |
|
1345 year = parseInt(years[0], 10); |
|
1346 endYear = parseInt(years[1], 10); |
|
1347 } |
|
1348 year = (minDate ? Math.max(year, minDate.getFullYear()) : year); |
|
1349 endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear); |
|
1350 html += '<select class="datetimepicker_newYear" ' + |
|
1351 'onchange="jQuery.datetimepicker._selectMonthYear(' + this._id + ', this, \'Y\');" ' + |
|
1352 'onclick="jQuery.datetimepicker._clickMonthYear(' + this._id + ');"' + |
|
1353 (showStatus ? this._addStatus(this._get('yearStatus') || ' ') : '') + '>'; |
|
1354 for (; year <= endYear; year++) { |
|
1355 html += '<option value="' + year + '"' + |
|
1356 (year == drawYear ? ' selected="selected"' : '') + |
|
1357 '>' + year + '</option>'; |
|
1358 } |
|
1359 html += '</select>'; |
|
1360 } |
|
1361 // if (this._get('changeTime')) |
|
1362 { |
|
1363 html += '<br />'; |
|
1364 html += '<select class="datetimepicker_newHour" ' + |
|
1365 'onchange="jQuery.datetimepicker._selectTime(' + this._id + ', this, \'H\');" ' + |
|
1366 'onclick="jQuery.datetimepicker._clickMonthYear(' + this._id + ');"' + |
|
1367 (showStatus ? this._addStatus(this._get('hourStatus') || ' ') : '') + '>'; |
|
1368 for (hour=0; hour < 24; hour++) { |
|
1369 html += '<option value="' + hour + '"' + |
|
1370 (hour == drawHour ? ' selected="selected"' : '') + |
|
1371 '>' + ((hour<10)?'0'+hour:hour) + '</option>'; |
|
1372 } |
|
1373 html += '</select>'; |
|
1374 html += ' : '; |
|
1375 html += '<select class="datetimepicker_newMinute" ' + |
|
1376 'onchange="jQuery.datetimepicker._selectTime(' + this._id + ', this, \'M\');" ' + |
|
1377 'onclick="jQuery.datetimepicker._clickMonthYear(' + this._id + ');"' + |
|
1378 (showStatus ? this._addStatus(this._get('minuteStatus') || ' ') : '') + '>'; |
|
1379 for (minute=0; minute < 60; minute++) { |
|
1380 html += '<option value="' + minute + '"' + |
|
1381 (minute == drawMinute ? ' selected="selected"' : '') + |
|
1382 '>' + ((minute<10)?'0'+minute:minute) + '</option>'; |
|
1383 } |
|
1384 html += '</select>'; |
|
1385 } |
|
1386 html += '</div>'; // Close datetimepicker_header |
|
1387 return html; |
|
1388 }, |
|
1389 |
|
1390 /* Provide code to set and clear the status panel. */ |
|
1391 _addStatus: function(text) { |
|
1392 return ' onmouseover="jQuery(\'#datetimepicker_status_' + this._id + '\').html(\'' + text + '\');" ' + |
|
1393 'onmouseout="jQuery(\'#datetimepicker_status_' + this._id + '\').html(\' \');"'; |
|
1394 }, |
|
1395 |
|
1396 /* Adjust one of the date sub-fields. */ |
|
1397 _adjustDate: function(offset, period) { |
|
1398 var year = this._drawYear + (period == 'Y' ? offset : 0); |
|
1399 var month = this._drawMonth + (period == 'M' ? offset : 0); |
|
1400 var day = Math.min(this._selectedDay, this._getDaysInMonth(year, month)) + |
|
1401 (period == 'D' ? offset : 0); |
|
1402 var hour = this._drawHour + (period == 'H' ? offset : 0); |
|
1403 var minute = this._drawMinute + (period == 'I' ? offset : 0); |
|
1404 var date = new Date(year, month, day, hour, minute); |
|
1405 // ensure it is within the bounds set |
|
1406 var minDate = this._getMinMaxDate('min', true); |
|
1407 var maxDate = this._getMinMaxDate('max'); |
|
1408 date = (minDate && date < minDate ? minDate : date); |
|
1409 date = (maxDate && date > maxDate ? maxDate : date); |
|
1410 this._selectedDay = date.getDate(); |
|
1411 this._drawMonth = this._selectedMonth = date.getMonth(); |
|
1412 this._drawYear = this._selectedYear = date.getFullYear(); |
|
1413 this._drawHour = this._selectedHour = date.getHours(); |
|
1414 this._drawMinute = this._selectedMinute = date.getMinutes(); |
|
1415 }, |
|
1416 |
|
1417 /* Determine the number of months to show. */ |
|
1418 _getNumberOfMonths: function() { |
|
1419 var numMonths = this._get('numberOfMonths'); |
|
1420 return (numMonths == null ? [1, 1] : (typeof numMonths == 'number' ? [1, numMonths] : numMonths)); |
|
1421 }, |
|
1422 |
|
1423 /* Determine the current maximum date - ensure no time components are set - may be overridden for a range. */ |
|
1424 _getMinMaxDate: function(minMax, checkRange) { |
|
1425 var date = this._determineDate(minMax + 'Date', null); |
|
1426 if (date) { |
|
1427 date.setHours(0); |
|
1428 date.setMinutes(0); |
|
1429 date.setSeconds(0); |
|
1430 date.setMilliseconds(0); |
|
1431 } |
|
1432 return date || (checkRange ? this._rangeStart : null); |
|
1433 }, |
|
1434 |
|
1435 /* Find the number of days in a given month. */ |
|
1436 _getDaysInMonth: function(year, month) { |
|
1437 return 32 - new Date(year, month, 32).getDate(); |
|
1438 }, |
|
1439 |
|
1440 /* Find the day of the week of the first of a month. */ |
|
1441 _getFirstDayOfMonth: function(year, month) { |
|
1442 return new Date(year, month, 1).getDay(); |
|
1443 }, |
|
1444 |
|
1445 /* Determines if we should allow a "next/prev" month display change. */ |
|
1446 _canAdjustMonth: function(offset, curYear, curMonth) { |
|
1447 var numMonths = this._getNumberOfMonths(); |
|
1448 var date = new Date(curYear, curMonth + (offset < 0 ? offset : numMonths[1]), 1); |
|
1449 if (offset < 0) |
|
1450 date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth())); |
|
1451 return this._isInRange(date); |
|
1452 }, |
|
1453 |
|
1454 /* Is the given date in the accepted range? */ |
|
1455 _isInRange: function(date) { |
|
1456 // during range selection, use minimum of selected date and range start |
|
1457 var newMinDate = (!this._rangeStart ? null : |
|
1458 new Date(this._selectedYear, this._selectedMonth, this._selectedDay)); |
|
1459 newMinDate = (newMinDate && this._rangeStart < newMinDate ? this._rangeStart : newMinDate); |
|
1460 var minDate = newMinDate || this._getMinMaxDate('min'); |
|
1461 var maxDate = this._getMinMaxDate('max'); |
|
1462 return ((!minDate || date >= minDate) && (!maxDate || date <= maxDate)); |
|
1463 }, |
|
1464 |
|
1465 /* Provide the configuration settings for formatting/parsing. */ |
|
1466 _getFormatConfig: function() { |
|
1467 var shortYearCutoff = this._get('shortYearCutoff'); |
|
1468 shortYearCutoff = (typeof shortYearCutoff != 'string' ? shortYearCutoff : |
|
1469 new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10)); |
|
1470 return {shortYearCutoff: shortYearCutoff, |
|
1471 dayNamesShort: this._get('dayNamesShort'), dayNames: this._get('dayNames'), |
|
1472 monthNamesShort: this._get('monthNamesShort'), monthNames: this._get('monthNames')}; |
|
1473 }, |
|
1474 |
|
1475 /* Format the given date for display. */ |
|
1476 _formatDateTime: function(day, month, year, hour, minute) { |
|
1477 if (!day) { |
|
1478 this._currentDay = this._selectedDay; |
|
1479 this._currentMonth = this._selectedMonth; |
|
1480 this._currentYear = this._selectedYear; |
|
1481 this._currentHour = this._selectedHour; |
|
1482 this._currentMinute = this._selectedMinute; |
|
1483 } |
|
1484 var date = (day ? (typeof day == 'object' ? day : new Date(year, month, day, hour, minute)) : |
|
1485 new Date(this._currentYear, this._currentMonth, this._currentDay, this._currentHour, this._currentMinute)); |
|
1486 return $.datetimepicker.formatDate(this._get('dateFormat')+' '+this._get('timeFormat'), date, this._getFormatConfig()); |
|
1487 } |
|
1488 }); |
|
1489 |
|
1490 /* jQuery extend now ignores nulls! */ |
|
1491 function extendRemove(target, props) { |
|
1492 $.extend(target, props); |
|
1493 for (var name in props) |
|
1494 if (props[name] == null) |
|
1495 target[name] = null; |
|
1496 return target; |
|
1497 }; |
|
1498 |
|
1499 /* Invoke the datetimepicker functionality. |
|
1500 @param options String - a command, optionally followed by additional parameters or |
|
1501 Object - settings for attaching new datetimepicker functionality |
|
1502 @return jQuery object */ |
|
1503 $.fn.datetimepicker = function(options){ |
|
1504 var otherArgs = Array.prototype.slice.call(arguments, 1); |
|
1505 if (typeof options == 'string' && (options == 'isDisabled' || options == 'getDate')) { |
|
1506 return $.datetimepicker['_' + options + 'Datepicker'].apply($.datetimepicker, [this[0]].concat(otherArgs)); |
|
1507 } |
|
1508 return this.each(function() { |
|
1509 typeof options == 'string' ? |
|
1510 $.datetimepicker['_' + options + 'Datepicker'].apply($.datetimepicker, [this].concat(otherArgs)) : |
|
1511 $.datetimepicker._attachDatepicker(this, options); |
|
1512 }); |
|
1513 }; |
|
1514 |
|
1515 $.datetimepicker = new DateTimepicker(); // singleton instance |
|
1516 |
|
1517 /* Initialise the date picker. */ |
|
1518 $(document).ready(function() { |
|
1519 $(document.body).append($.datetimepicker._datetimepickerDiv) |
|
1520 .mousedown($.datetimepicker._checkExternalClick); |
|
1521 }); |
|
1522 |
|
1523 })(jQuery); |