| 1 | (function($, CRM, _) { |
| 2 | "use strict"; |
| 3 | |
| 4 | /** |
| 5 | * @see http://wiki.civicrm.org/confluence/display/CRMDOC/crmDatepicker |
| 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" />'), |
| 23 | settings = _.cloneDeep(options || {}), |
| 24 | $dateField = $(), |
| 25 | $timeField = $(), |
| 26 | $clearLink = $(), |
| 27 | hasDatepicker = settings.date !== false && settings.date !== 'yy', |
| 28 | type = hasDatepicker ? 'text' : 'number'; |
| 29 | |
| 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); |
| 33 | } |
| 34 | if (settings.time !== false) { |
| 35 | $timeField = $('<input>').insertAfter($dataField); |
| 36 | CRM.utils.copyAttributes($dataField, $timeField, ['class', 'disabled']); |
| 37 | $timeField |
| 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) |
| 42 | .timeEntry({ |
| 43 | spinnerImage: '', |
| 44 | show24Hours: settings.time === true || settings.time === undefined ? CRM.config.timeIs24Hr : settings.time == '24' |
| 45 | }); |
| 46 | } |
| 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); |
| 52 | if (!settings.minDate && !_.isUndefined(settings.start_date_years)) { |
| 53 | settings.minDate = '' + (new Date().getFullYear() - settings.start_date_years) + '-01-01'; |
| 54 | } |
| 55 | if (!settings.maxDate && !_.isUndefined(settings.end_date_years)) { |
| 56 | settings.maxDate = '' + (new Date().getFullYear() + settings.end_date_years) + '-12-31'; |
| 57 | } |
| 58 | if (hasDatepicker) { |
| 59 | settings.minDate = settings.minDate ? CRM.utils.makeDate(settings.minDate) : null; |
| 60 | settings.maxDate = settings.maxDate ? CRM.utils.makeDate(settings.maxDate) : null; |
| 61 | settings.dateFormat = typeof settings.date === 'string' ? settings.date : CRM.config.dateInputFormat; |
| 62 | settings.changeMonth = _.includes(settings.dateFormat, 'm'); |
| 63 | settings.changeYear = _.includes(settings.dateFormat, 'y'); |
| 64 | if (!settings.yearRange && settings.minDate !== null && settings.maxDate !== null) { |
| 65 | settings.yearRange = '' + CRM.utils.formatDate(settings.minDate, 'yy') + ':' + CRM.utils.formatDate(settings.maxDate, 'yy'); |
| 66 | } |
| 67 | // Set placeholder as calendar icon (`fa-calendar` is Unicode f073) |
| 68 | // and add datepicker |
| 69 | $dateField.addClass('crm-form-date').attr({placeholder: '\uF073'}).datepicker(settings); |
| 70 | } else { |
| 71 | $dateField.attr('min', settings.minDate ? CRM.utils.formatDate(settings.minDate, 'yy') : '1000'); |
| 72 | $dateField.attr('max', settings.maxDate ? CRM.utils.formatDate(settings.maxDate, 'yy') : '4000'); |
| 73 | } |
| 74 | $dateField.change(updateDataField); |
| 75 | } |
| 76 | // Rudimentary validation. TODO: Roll into use of jQUery validate and ui.datepicker.validation |
| 77 | function isValidDate() { |
| 78 | // FIXME: parseDate doesn't work with incomplete date formats; skip validation if no month, day or year in format |
| 79 | var lowerFormat = settings.dateFormat.toLowerCase(); |
| 80 | if (lowerFormat.indexOf('y') < 0 || lowerFormat.indexOf('m') < 0 || !dateHasDay()) { |
| 81 | return true; |
| 82 | } |
| 83 | try { |
| 84 | $.datepicker.parseDate(settings.dateFormat, $dateField.val()); |
| 85 | return true; |
| 86 | } catch (e) { |
| 87 | return false; |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | /** |
| 92 | * Does the date format contain the day. |
| 93 | * |
| 94 | * @returns {boolean} |
| 95 | */ |
| 96 | function dateHasDay() { |
| 97 | var lowerFormat = settings.dateFormat.toLowerCase(); |
| 98 | if (lowerFormat.indexOf('d') < 0) { |
| 99 | return false; |
| 100 | } |
| 101 | return true; |
| 102 | } |
| 103 | function updateInputFields(e, context) { |
| 104 | var val = $dataField.val(), |
| 105 | time = null; |
| 106 | if (context !== 'userInput' && context !== 'crmClear') { |
| 107 | if (hasDatepicker) { |
| 108 | $dateField.datepicker('setDate', _.includes(val, '-') ? $.datepicker.parseDate('yy-mm-dd', val) : null); |
| 109 | } else if ($dateField.length) { |
| 110 | $dateField.val(val.slice(0, 4)); |
| 111 | } |
| 112 | if ($timeField.length) { |
| 113 | if (val.length === 8) { |
| 114 | time = val; |
| 115 | } else if (val.length === 19) { |
| 116 | time = val.split(' ')[1]; |
| 117 | } |
| 118 | $timeField.timeEntry('setTime', time); |
| 119 | } |
| 120 | } |
| 121 | $clearLink.css('visibility', val ? 'visible' : 'hidden'); |
| 122 | } |
| 123 | function updateDataField(e, context) { |
| 124 | // The crmClear event wipes all the field values anyway, so no need to respond |
| 125 | if (context !== 'crmClear') { |
| 126 | var val = ''; |
| 127 | if ($dateField.val()) { |
| 128 | if (hasDatepicker && isValidDate() && dateHasDay()) { |
| 129 | val = $.datepicker.formatDate('yy-mm-dd', $dateField.datepicker('getDate')); |
| 130 | $dateField.removeClass('crm-error'); |
| 131 | } else if (!hasDatepicker) { |
| 132 | val = $dateField.val() + '-01-01'; |
| 133 | } |
| 134 | else if (!dateHasDay()) { |
| 135 | // This would be a Year-month date (yyyy-mm) |
| 136 | // it could be argued it should not use a datepicker.... |
| 137 | val = $dateField.val() + '-01'; |
| 138 | } else { |
| 139 | $dateField.addClass('crm-error'); |
| 140 | } |
| 141 | } |
| 142 | if ($timeField.val()) { |
| 143 | val += (val ? ' ' : '') + $timeField.timeEntry('getTime').toTimeString().substr(0, 8); |
| 144 | } |
| 145 | $dataField.val(val).trigger('change', ['userInput']); |
| 146 | } |
| 147 | } |
| 148 | $dataField.hide().addClass('crm-hidden-date').on('change.crmDatepicker', updateInputFields); |
| 149 | updateInputFields(); |
| 150 | }); |
| 151 | }; |
| 152 | })(jQuery, CRM, CRM._); |