5 * @see http://wiki.civicrm.org/confluence/display/CRMDOC/crmDatepicker
7 $.fn
.crmDatepicker = function(options
) {
8 return $(this).each(function() {
9 if ($(this).is('.crm-form-date-wrapper .crm-hidden-date')) {
10 // Already initialized - destroy
12 .off('.crmDatepicker')
14 .removeClass('crm-hidden-date')
18 if (options
=== 'destroy') {
22 $dataField
= $(this).wrap('<span class="crm-form-date-wrapper" />'),
23 settings
= _
.cloneDeep(options
|| {}),
27 hasDatepicker
= settings
.date
!== false && settings
.date
!== 'yy',
28 type
= hasDatepicker
? 'text' : 'number';
30 if (settings
.allowClear
!== undefined ? settings
.allowClear
: !$dataField
.is('.required, [required]')) {
31 $clearLink
= $('<a class="crm-hover-button crm-clear-link" title="'+ _
.escape(ts('Clear')) +'"><i class="crm-i fa-times"></i></a>')
32 .insertAfter($dataField
);
34 if (settings
.time
!== false) {
35 $timeField
= $('<input>').insertAfter($dataField
);
36 CRM
.utils
.copyAttributes($dataField
, $timeField
, ['class', 'disabled']);
38 .addClass('crm-form-text crm-form-time')
39 .attr('placeholder', $dataField
.attr('time-placeholder') === undefined ? '\uf017' : $dataField
.attr('time-placeholder'))
40 .attr('aria-label', $dataField
.attr('time-placeholder') === undefined ? ts('Time') : $dataField
.attr('time-placeholder'))
41 .change(updateDataField
)
44 show24Hours
: settings
.time
=== true || settings
.time
=== undefined ? CRM
.config
.timeIs24Hr
: settings
.time
== '24'
47 if (settings
.date
!== false) {
48 // Render "number" field for year-only format, calendar popup for all other formats
49 $dateField
= $('<input type="' + type
+ '">').insertAfter($dataField
);
50 CRM
.utils
.copyAttributes($dataField
, $dateField
, ['placeholder', 'style', 'class', 'disabled', 'aria-label']);
51 $dateField
.addClass('crm-form-' + type
);
53 settings
.minDate
= settings
.minDate
? CRM
.utils
.makeDate(settings
.minDate
) : null;
54 settings
.maxDate
= settings
.maxDate
? CRM
.utils
.makeDate(settings
.maxDate
) : null;
55 settings
.dateFormat
= typeof settings
.date
=== 'string' ? settings
.date
: CRM
.config
.dateInputFormat
;
56 settings
.changeMonth
= _
.includes(settings
.dateFormat
, 'm');
57 settings
.changeYear
= _
.includes(settings
.dateFormat
, 'y');
58 if (!settings
.yearRange
&& settings
.minDate
!== null && settings
.maxDate
!== null) {
59 settings
.yearRange
= '' + CRM
.utils
.formatDate(settings
.minDate
, 'yy') + ':' + CRM
.utils
.formatDate(settings
.maxDate
, 'yy');
61 // Set placeholder as calendar icon (`fa-calendar` is Unicode f073)
63 $dateField
.addClass('crm-form-date').attr({placeholder
: '\uF073'}).datepicker(settings
);
65 $dateField
.attr('min', settings
.minDate
? CRM
.utils
.formatDate(settings
.minDate
, 'yy') : '1000');
66 $dateField
.attr('max', settings
.maxDate
? CRM
.utils
.formatDate(settings
.maxDate
, 'yy') : '4000');
68 $dateField
.change(updateDataField
);
70 // Rudimentary validation. TODO: Roll into use of jQUery validate and ui.datepicker.validation
71 function isValidDate() {
72 // FIXME: parseDate doesn't work with incomplete date formats; skip validation if no month, day or year in format
73 var lowerFormat
= settings
.dateFormat
.toLowerCase();
74 if (lowerFormat
.indexOf('y') < 0 || lowerFormat
.indexOf('m') < 0 || !dateHasDay()) {
78 $.datepicker
.parseDate(settings
.dateFormat
, $dateField
.val());
86 * Does the date format contain the day.
90 function dateHasDay() {
91 var lowerFormat
= settings
.dateFormat
.toLowerCase();
92 if (lowerFormat
.indexOf('d') < 0) {
97 function updateInputFields(e
, context
) {
98 var val
= $dataField
.val(),
100 if (context
!== 'userInput' && context
!== 'crmClear') {
102 $dateField
.datepicker('setDate', _
.includes(val
, '-') ? $.datepicker
.parseDate('yy-mm-dd', val
) : null);
103 } else if ($dateField
.length
) {
104 $dateField
.val(val
.slice(0, 4));
106 if ($timeField
.length
) {
107 if (val
.length
=== 8) {
109 } else if (val
.length
=== 19) {
110 time
= val
.split(' ')[1];
112 $timeField
.timeEntry('setTime', time
);
115 $clearLink
.css('visibility', val
? 'visible' : 'hidden');
117 function updateDataField(e
, context
) {
118 // The crmClear event wipes all the field values anyway, so no need to respond
119 if (context
!== 'crmClear') {
121 if ($dateField
.val()) {
122 if (hasDatepicker
&& isValidDate() && dateHasDay()) {
123 val
= $.datepicker
.formatDate('yy-mm-dd', $dateField
.datepicker('getDate'));
124 $dateField
.removeClass('crm-error');
125 } else if (!hasDatepicker
) {
126 val
= $dateField
.val() + '-01-01';
128 else if (!dateHasDay()) {
129 // This would be a Year-month date (yyyy-mm)
130 // it could be argued it should not use a datepicker....
131 val
= $dateField
.val() + '-01';
133 $dateField
.addClass('crm-error');
136 if ($timeField
.val()) {
137 val
+= (val
? ' ' : '') + $timeField
.timeEntry('getTime').toTimeString().substr(0, 8);
139 $dataField
.val(val
).trigger('change', ['userInput']);
142 $dataField
.hide().addClass('crm-hidden-date').on('change.crmDatepicker', updateInputFields
);
146 })(jQuery
, CRM
, CRM
._
);