184 lines
6.0 KiB
JavaScript
184 lines
6.0 KiB
JavaScript
/**
|
|
* @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);
|