1(function($, CRM, _) { 2 "use strict"; 3 4 /** 5 * @see https://docs.civicrm.org/dev/en/latest/framework/ui/#date-picker 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 placeholder, 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]')) { 32 $clearLink = $('<a class="crm-hover-button crm-clear-link" title="'+ _.escape(ts('Clear')) +'"><i class="crm-i fa-times" role="img" aria-hidden="true"></i></a>') 33 .insertAfter($dataField); 34 } 35 if (settings.time !== false) { 36 $timeField = $('<input>').insertAfter($dataField); 37 placeholder = settings.timePlaceholder || $dataField.attr('time-placeholder'); 38 CRM.utils.copyAttributes($dataField, $timeField, ['class', 'disabled', 'required']); 39 $timeField.removeClass(stripNgClasses); 40 $timeField 41 .removeClass('two four eight twelve twenty medium big huge crm-auto-width') 42 .addClass('crm-form-text crm-form-time six') 43 // Set default placeholder as clock icon (`fa-clock` is Unicode f017) 44 .attr('placeholder', placeholder === undefined ? '\uf017' : placeholder) 45 .attr('aria-label', placeholder === undefined ? ts('Time') : placeholder) 46 .change(updateDataField) 47 .timeEntry({ 48 spinnerImage: '', 49 useMouseWheel: false, 50 defaultTime: new Date(new Date().setMinutes(0)), 51 show24Hours: settings.time === true || settings.time === undefined ? CRM.config.timeIs24Hr : settings.time == '24' 52 }); 53 if (!placeholder) { 54 $timeField.addClass('crm-placeholder-icon'); 55 } 56 } 57 if (settings.date !== false) { 58 // Render "number" field for year-only format, calendar popup for all other formats 59 $dateField = $('<input type="' + type + '">').insertAfter($dataField); 60 CRM.utils.copyAttributes($dataField, $dateField, ['style', 'class', 'disabled', 'aria-label', 'required']); 61 $dateField.removeClass(stripNgClasses); 62 placeholder = settings.placeholder || $dataField.attr('placeholder'); 63 $dateField.addClass('crm-form-' + type); 64 if (!settings.minDate && isInt(settings.start_date_years)) { 65 settings.minDate = '' + (new Date().getFullYear() - settings.start_date_years) + '-01-01'; 66 } 67 if (!settings.maxDate && isInt(settings.end_date_years)) { 68 settings.maxDate = '' + (new Date().getFullYear() + settings.end_date_years) + '-12-31'; 69 } 70 if (hasDatepicker) { 71 settings.minDate = settings.minDate ? CRM.utils.makeDate(settings.minDate) : null; 72 settings.maxDate = settings.maxDate ? CRM.utils.makeDate(settings.maxDate) : null; 73 settings.dateFormat = typeof settings.date === 'string' ? settings.date : CRM.config.dateInputFormat; 74 settings.changeMonth = _.includes(settings.dateFormat, 'm'); 75 settings.changeYear = _.includes(settings.dateFormat, 'y'); 76 if (!settings.yearRange && settings.minDate !== null && settings.maxDate !== null) { 77 settings.yearRange = '' + CRM.utils.formatDate(settings.minDate, 'yy') + ':' + CRM.utils.formatDate(settings.maxDate, 'yy'); 78 } 79 settings.onSelect = function (dateText, inst) { 80 updateDataField(); 81 if (settings.time !== false) { 82 $timeField.focus(); 83 } else { 84 $(this).focus(); 85 } 86 }; 87 $dateField.addClass('crm-form-date').datepicker(settings); 88 } else { 89 $dateField.attr('min', settings.minDate ? CRM.utils.formatDate(settings.minDate, 'yy') : '1000'); 90 $dateField.attr('max', settings.maxDate ? CRM.utils.formatDate(settings.maxDate, 'yy') : '4000'); 91 placeholder = null; 92 } 93 // Set placeholder as calendar icon (`fa-calendar` is Unicode f073) 94 $dateField.attr({placeholder: placeholder === undefined ? '\uF073' : placeholder}).change(updateDataField); 95 if (!placeholder) { 96 $dateField.addClass('crm-placeholder-icon').attr('aria-label', ts('Select Date')); 97 } 98 else { 99 $dateField.attr('aria-label', placeholder); 100 } 101 } 102 // Rudimentary validation. TODO: Roll into use of jQUery validate and ui.datepicker.validation 103 function isValidDate() { 104 // FIXME: parseDate doesn't work with incomplete date formats; skip validation if no month, day or year in format 105 var lowerFormat = settings.dateFormat.toLowerCase(); 106 if (lowerFormat.indexOf('y') < 0 || lowerFormat.indexOf('m') < 0 || !dateHasDay()) { 107 return true; 108 } 109 try { 110 $.datepicker.parseDate(settings.dateFormat, $dateField.val()); 111 return true; 112 } catch (e) { 113 return false;
114 } 115 } 116 117 /** 118 * Does the date format contain the day. 119 * 120 * @returns {boolean} 121 */ 122 function dateHasDay() { 123 var lowerFormat = settings.dateFormat.toLowerCase(); 124 return lowerFormat.indexOf('d') >= 0; 125 } 126 function updateInputFields(e, context) { 127 var val = $dataField.val(), 128 time = null; 129 if (context !== 'userInput' && context !== 'crmClear') { 130 if (hasDatepicker) { 131 $dateField.datepicker('setDate', _.includes(val, '-') ? $.datepicker.parseDate('yy-mm-dd', val) : null); 132 } else if ($dateField.length) { 133 $dateField.val(val.slice(0, 4)); 134 } 135 if ($timeField.length) { 136 if (val.length === 8) { 137 time = val; 138 } else if (val.length === 19) { 139 time = val.split(' ')[1]; 140 } 141 $timeField.timeEntry('setTime', time); 142 } 143 } 144 $clearLink.css('visibility', val ? 'visible' : 'hidden'); 145 } 146 function updateDataField(e, context) { 147 // The crmClear event wipes all the field values anyway, so no need to respond 148 if (context !== 'crmClear') { 149 var val = ''; 150 if ($dateField.val()) { 151 if (hasDatepicker && isValidDate() && dateHasDay()) { 152 val = $.datepicker.formatDate('yy-mm-dd', $dateField.datepicker('getDate')); 153 $dateField.removeClass('crm-error'); 154 } else if (!hasDatepicker) { 155 val = $dateField.val() + '-01-01'; 156 } 157 else if (!dateHasDay()) { 158 // This would be a Year-month date (yyyy-mm) 159 // it could be argued it should not use a datepicker.... 160 val = $dateField.val() + '-01'; 161 } else { 162 $dateField.addClass('crm-error'); 163 } 164 } 165 if ($timeField.val()) { 166 val += (val ? ' ' : '') + $timeField.timeEntry('getTime').toTimeString().substr(0, 8); 167 } 168 $dataField.val(val).trigger('change', ['userInput']); 169 } 170 } 171 $dataField.hide().addClass('crm-hidden-date').on('change.crmDatepicker', updateInputFields); 172 updateInputFields(); 173 }); 174 }; 175 176 function isInt(value) { 177 if (isNaN(value)) { 178 return false; 179 } 180 var x = parseFloat(value); 181 return (x | 0) === x; 182 } 183 184 // jQuery removeClass callback: strips Angular's transient state classes (ng-*). 185 // copyAttributes() snapshots the source field's `class` verbatim onto the generated 186 // display inputs, which are NOT ng-model-bound, so any ng-invalid/ng-pristine/etc. froze 187 // there as a "phantom" that shadowed real invalid fields during afForm validation. 188 function stripNgClasses(i, cls) { 189 return cls.split(/\s+/).filter(function(c) { return c.indexOf('ng-') === 0; }).join(' '); 190 } 191 192})(jQuery, CRM, CRM._);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.