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