Commit | Line | Data |
---|---|---|
403c52ac | 1 | (function($, CRM, _) { |
2 | "use strict"; | |
3 | ||
4 | /** | |
6f55e2a8 | 5 | * @see https://docs.civicrm.org/dev/en/latest/framework/ui/#date-picker |
403c52ac | 6 | */ |
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 | |
11 | $(this) | |
12 | .off('.crmDatepicker') | |
13 | .css('display', '') | |
14 | .removeClass('crm-hidden-date') | |
15 | .siblings().remove(); | |
16 | $(this).unwrap(); | |
17 | } | |
18 | if (options === 'destroy') { | |
19 | return; | |
20 | } | |
21 | var | |
22 | $dataField = $(this).wrap('<span class="crm-form-date-wrapper" />'), | |
ff2eb9e8 | 23 | settings = _.cloneDeep(options || {}), |
403c52ac | 24 | $dateField = $(), |
25 | $timeField = $(), | |
26 | $clearLink = $(), | |
351719bf | 27 | placeholder, |
403c52ac | 28 | hasDatepicker = settings.date !== false && settings.date !== 'yy', |
29 | type = hasDatepicker ? 'text' : 'number'; | |
30 | ||
31 | if (settings.allowClear !== undefined ? settings.allowClear : !$dataField.is('.required, [required]')) { | |
13a3d214 | 32 | $clearLink = $('<a class="crm-hover-button crm-clear-link" title="'+ _.escape(ts('Clear')) +'"><i class="crm-i fa-times" aria-hidden="true"></i></a>') |
403c52ac | 33 | .insertAfter($dataField); |
34 | } | |
35 | if (settings.time !== false) { | |
36 | $timeField = $('<input>').insertAfter($dataField); | |
351719bf | 37 | placeholder = settings.timePlaceholder || $dataField.attr('time-placeholder'); |
403c52ac | 38 | CRM.utils.copyAttributes($dataField, $timeField, ['class', 'disabled']); |
39 | $timeField | |
40 | .addClass('crm-form-text crm-form-time') | |
351719bf CW |
41 | // Set default placeholder as clock icon (`fa-clock` is Unicode f017) |
42 | .attr('placeholder', placeholder === undefined ? '\uf017' : placeholder) | |
43 | .attr('aria-label', placeholder === undefined ? ts('Time') : placeholder) | |
403c52ac | 44 | .change(updateDataField) |
45 | .timeEntry({ | |
46 | spinnerImage: '', | |
47 | show24Hours: settings.time === true || settings.time === undefined ? CRM.config.timeIs24Hr : settings.time == '24' | |
48 | }); | |
351719bf CW |
49 | if (!placeholder) { |
50 | $timeField.addClass('crm-placeholder-icon'); | |
51 | } | |
403c52ac | 52 | } |
53 | if (settings.date !== false) { | |
54 | // Render "number" field for year-only format, calendar popup for all other formats | |
55 | $dateField = $('<input type="' + type + '">').insertAfter($dataField); | |
351719bf CW |
56 | CRM.utils.copyAttributes($dataField, $dateField, ['style', 'class', 'disabled', 'aria-label']); |
57 | placeholder = settings.placeholder || $dataField.attr('placeholder'); | |
403c52ac | 58 | $dateField.addClass('crm-form-' + type); |
1c32ff27 CW |
59 | if (!settings.minDate && !_.isUndefined(settings.start_date_years)) { |
60 | settings.minDate = '' + (new Date().getFullYear() - settings.start_date_years) + '-01-01'; | |
61 | } | |
62 | if (!settings.maxDate && !_.isUndefined(settings.end_date_years)) { | |
63 | settings.maxDate = '' + (new Date().getFullYear() + settings.end_date_years) + '-12-31'; | |
64 | } | |
403c52ac | 65 | if (hasDatepicker) { |
66 | settings.minDate = settings.minDate ? CRM.utils.makeDate(settings.minDate) : null; | |
67 | settings.maxDate = settings.maxDate ? CRM.utils.makeDate(settings.maxDate) : null; | |
68 | settings.dateFormat = typeof settings.date === 'string' ? settings.date : CRM.config.dateInputFormat; | |
ff2eb9e8 CW |
69 | settings.changeMonth = _.includes(settings.dateFormat, 'm'); |
70 | settings.changeYear = _.includes(settings.dateFormat, 'y'); | |
403c52ac | 71 | if (!settings.yearRange && settings.minDate !== null && settings.maxDate !== null) { |
72 | settings.yearRange = '' + CRM.utils.formatDate(settings.minDate, 'yy') + ':' + CRM.utils.formatDate(settings.maxDate, 'yy'); | |
73 | } | |
351719bf | 74 | $dateField.addClass('crm-form-date').datepicker(settings); |
403c52ac | 75 | } else { |
76 | $dateField.attr('min', settings.minDate ? CRM.utils.formatDate(settings.minDate, 'yy') : '1000'); | |
77 | $dateField.attr('max', settings.maxDate ? CRM.utils.formatDate(settings.maxDate, 'yy') : '4000'); | |
5b7dd006 | 78 | placeholder = null; |
403c52ac | 79 | } |
351719bf CW |
80 | // Set placeholder as calendar icon (`fa-calendar` is Unicode f073) |
81 | $dateField.attr({placeholder: placeholder === undefined ? '\uF073' : placeholder}).change(updateDataField); | |
82 | if (!placeholder) { | |
83 | $dateField.addClass('crm-placeholder-icon'); | |
84 | } | |
403c52ac | 85 | } |
86 | // Rudimentary validation. TODO: Roll into use of jQUery validate and ui.datepicker.validation | |
87 | function isValidDate() { | |
88 | // FIXME: parseDate doesn't work with incomplete date formats; skip validation if no month, day or year in format | |
89 | var lowerFormat = settings.dateFormat.toLowerCase(); | |
90 | if (lowerFormat.indexOf('y') < 0 || lowerFormat.indexOf('m') < 0 || !dateHasDay()) { | |
91 | return true; | |
92 | } | |
93 | try { | |
94 | $.datepicker.parseDate(settings.dateFormat, $dateField.val()); | |
95 | return true; | |
96 | } catch (e) { | |
97 | return false; | |
98 | } | |
99 | } | |
100 | ||
101 | /** | |
102 | * Does the date format contain the day. | |
103 | * | |
104 | * @returns {boolean} | |
105 | */ | |
106 | function dateHasDay() { | |
107 | var lowerFormat = settings.dateFormat.toLowerCase(); | |
108 | if (lowerFormat.indexOf('d') < 0) { | |
109 | return false; | |
110 | } | |
111 | return true; | |
112 | } | |
113 | function updateInputFields(e, context) { | |
114 | var val = $dataField.val(), | |
115 | time = null; | |
116 | if (context !== 'userInput' && context !== 'crmClear') { | |
117 | if (hasDatepicker) { | |
ff2eb9e8 | 118 | $dateField.datepicker('setDate', _.includes(val, '-') ? $.datepicker.parseDate('yy-mm-dd', val) : null); |
403c52ac | 119 | } else if ($dateField.length) { |
120 | $dateField.val(val.slice(0, 4)); | |
121 | } | |
122 | if ($timeField.length) { | |
123 | if (val.length === 8) { | |
124 | time = val; | |
125 | } else if (val.length === 19) { | |
126 | time = val.split(' ')[1]; | |
127 | } | |
128 | $timeField.timeEntry('setTime', time); | |
129 | } | |
130 | } | |
131 | $clearLink.css('visibility', val ? 'visible' : 'hidden'); | |
132 | } | |
133 | function updateDataField(e, context) { | |
134 | // The crmClear event wipes all the field values anyway, so no need to respond | |
135 | if (context !== 'crmClear') { | |
136 | var val = ''; | |
137 | if ($dateField.val()) { | |
138 | if (hasDatepicker && isValidDate() && dateHasDay()) { | |
139 | val = $.datepicker.formatDate('yy-mm-dd', $dateField.datepicker('getDate')); | |
140 | $dateField.removeClass('crm-error'); | |
141 | } else if (!hasDatepicker) { | |
142 | val = $dateField.val() + '-01-01'; | |
143 | } | |
144 | else if (!dateHasDay()) { | |
145 | // This would be a Year-month date (yyyy-mm) | |
146 | // it could be argued it should not use a datepicker.... | |
147 | val = $dateField.val() + '-01'; | |
148 | } else { | |
149 | $dateField.addClass('crm-error'); | |
150 | } | |
151 | } | |
152 | if ($timeField.val()) { | |
153 | val += (val ? ' ' : '') + $timeField.timeEntry('getTime').toTimeString().substr(0, 8); | |
154 | } | |
155 | $dataField.val(val).trigger('change', ['userInput']); | |
156 | } | |
157 | } | |
158 | $dataField.hide().addClass('crm-hidden-date').on('change.crmDatepicker', updateInputFields); | |
159 | updateInputFields(); | |
160 | }); | |
161 | }; | |
162 | })(jQuery, CRM, CRM._); |