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
|
/**
* @file
* Polyfill for HTML5 date input.
*/
(function ($, Modernizr, Drupal) {
/**
* Attach datepicker fallback on date elements.
*
* @type {Drupal~behavior}
*
* @prop {Drupal~behaviorAttach} attach
* Attaches the behavior. Adds a class that hides formatting instructions
* on date/time fields when the browser supports a native datepicker.
*/
Drupal.behaviors.date = {
attach(context, settings) {
const dataFieldElements = 'data-drupal-field-elements';
const dataDatepickerProcessed = 'data-datepicker-is-processed';
/**
* Returns a CSS selector for a date field to process.
*
* The dataDatepickerProcessed attribute prevents a field from being
* selected and processed more than once.
*
* @param {string} elements
* The data attribute value.
*
* @return {string}
* A CSS Selector.
*/
const getDateSelector = (elements) =>
[
`[${dataFieldElements}="${elements}"]`,
`:not([${dataDatepickerProcessed}="${elements}"])`,
].join('');
// If the browser does not support a native datepicker, add date
// formatting instructions on date/time fields.
if (Modernizr.inputtypes.date === false) {
Array.prototype.forEach.call(
document.querySelectorAll(getDateSelector('date-time')),
(dateTime) => {
const dateInput = dateTime.querySelector('input[type="date"]');
const timeInput = dateTime.querySelector('input[type="time"]');
const help = Drupal.theme.dateTimeHelp({
dateId: `${dateInput.id}--description`,
dateDesc: dateInput.dataset.help,
timeId: `${timeInput.id}--description`,
timeDesc: timeInput.dataset.help,
});
[dateInput, timeInput].forEach((input) => {
input.setAttribute(
'aria-describedby',
`${input.id}--description`,
);
// If the browser does not support date or time inputs, the input
// is treated as the type "text". The type attribute should be
// changed to reflect this.
input.setAttribute('type', 'text');
});
Drupal.DatepickerPolyfill.attachDescription(dateTime, help);
// Set attribute to prevent element from being processed again.
dateTime.setAttribute(dataDatepickerProcessed, 'date-time');
},
);
Array.prototype.forEach.call(
document.querySelectorAll(getDateSelector('date')),
(date) => {
const dateInput = date.querySelector('input[type="date"]');
const help = Drupal.theme.dateHelp({
dateDesc: dateInput.dataset.help,
});
// Date-only input will be described by description directly.
const id = `${date.id}--description`;
dateInput.setAttribute('aria-describedby', id);
// If the browser does not support date inputs, the input is treated
// as the type "text". The type attribute should be changed to
// changed to reflect this.
dateInput.setAttribute('type', 'text');
Drupal.DatepickerPolyfill.attachDescription(date, help, id);
// Set attribute to prevent element from selection on next run.
date.setAttribute(dataDatepickerProcessed, 'date');
},
);
}
},
};
/**
* Provides overridable utility functions for the datepicker polyfill.
*/
Drupal.DatepickerPolyfill = class {
/**
* Adds help text to polyfilled date/time elements.
*
* The help text is added to existing description elements when present.
* If a description element is not present, one is created.
*
* @param {HTMLElement} element
* The input element.
* @param {string} help
* The help text.
* @param {string} id
* The input id.
*/
static attachDescription(element, help, id) {
let description = element.nextElementSibling;
// If no description element exists, create one.
if (
!(
description &&
description.getAttribute('data-drupal-field-elements') ===
'description'
)
) {
description = Drupal.DatepickerPolyfill.descriptionWrapperElement(id);
element.parentNode.insertBefore(description, element.nextSibling);
}
description.insertAdjacentHTML('beforeend', help);
}
/**
* Creates a description wrapper element.
*
* @param {string} id
* The id of the input being described.
*
* @return {HTMLElement}
* The description wrapper DOM element.
*/
static descriptionWrapperElement(id) {
const description = document.createElement('div');
description.classList.add('description');
description.setAttribute('data-drupal-field-elements', 'description');
if (id) {
description.setAttribute('id', id);
}
return description;
}
};
/**
* Theme function for no-native-datepicker date input help text.
*
* @param {string} dateDesc
* The help text.
*
* @return {string}
* The HTML markup for the help text.
*/
Drupal.theme.dateHelp = ({ dateDesc }) =>
`<div class="no-native-datepicker-help">${dateDesc}</div>`;
/**
* Theme function for no-native-datepicker date+time inputs help text.
*
* @param {string} dateId
* The date input aria-describedby value.
* @param {string} timeId
* The time input aria-describedby value.
* @param {string} dateDesc
* The date help text.
* @param {string} timeDesc
* The time help text.
*
* @return {string}
* The HTML markup for the help text.
*/
Drupal.theme.dateTimeHelp = ({ dateId, timeId, dateDesc, timeDesc }) =>
`<div class="no-native-datepicker-help">
<span id="${dateId}">${dateDesc}</span> <span id="${timeId}">${timeDesc}</span>
</div>`;
})(jQuery, Modernizr, Drupal);
|