193 lines
7.6 KiB
JavaScript
193 lines
7.6 KiB
JavaScript
import {
|
|
TEST_PM_TIME,
|
|
formatMilliseconds,
|
|
parseMillisecondsIntoInteger,
|
|
parseDigitsIntoInteger,
|
|
getAmString,
|
|
getPmString,
|
|
getSeparator,
|
|
searchAmOrPmToken
|
|
} from './helpers.js';
|
|
import { TimePicker } from '@vaadin/time-picker/src/vaadin-time-picker.js';
|
|
|
|
(function () {
|
|
const tryCatchWrapper = function (callback) {
|
|
return window.Vaadin.Flow.tryCatchWrapper(callback, 'Vaadin Time Picker');
|
|
};
|
|
|
|
// Execute callback when predicate returns true.
|
|
// Try again later if predicate returns false.
|
|
function when(predicate, callback, timeout = 0) {
|
|
if (predicate()) {
|
|
callback();
|
|
} else {
|
|
setTimeout(() => when(predicate, callback, 200), timeout);
|
|
}
|
|
}
|
|
|
|
function parseISO(text) {
|
|
// The default i18n parser of the web component is ISO 8601 compliant.
|
|
const timeObject = TimePicker.properties.i18n.value().parseTime(text);
|
|
|
|
// The web component returns an object with string values
|
|
// while the connector expects number values.
|
|
return {
|
|
hours: parseInt(timeObject.hours || 0),
|
|
minutes: parseInt(timeObject.minutes || 0),
|
|
seconds: parseInt(timeObject.seconds || 0),
|
|
milliseconds: parseInt(timeObject.milliseconds || 0)
|
|
}
|
|
};
|
|
|
|
window.Vaadin.Flow.timepickerConnector = {
|
|
initLazy: (timepicker) =>
|
|
tryCatchWrapper(function (timepicker) {
|
|
// Check whether the connector was already initialized for the timepicker
|
|
if (timepicker.$connector) {
|
|
return;
|
|
}
|
|
|
|
timepicker.$connector = {};
|
|
|
|
timepicker.$connector.setLocale = tryCatchWrapper(function (locale) {
|
|
// capture previous value if any
|
|
let previousValueObject;
|
|
if (timepicker.value && timepicker.value !== '') {
|
|
previousValueObject = parseISO(timepicker.value);
|
|
}
|
|
|
|
try {
|
|
// Check whether the locale is supported by the browser or not
|
|
TEST_PM_TIME.toLocaleTimeString(locale);
|
|
} catch (e) {
|
|
locale = 'en-US';
|
|
// FIXME should do a callback for server to throw an exception ?
|
|
throw new Error(
|
|
'vaadin-time-picker: The locale ' +
|
|
locale +
|
|
' is not supported, falling back to default locale setting(en-US).'
|
|
);
|
|
}
|
|
|
|
// 1. 24 or 12 hour clock, if latter then what are the am/pm strings ?
|
|
const pmString = getPmString(locale);
|
|
const amString = getAmString(locale);
|
|
|
|
// 2. What is the separator ?
|
|
const separator = getSeparator(locale);
|
|
|
|
const includeSeconds = function () {
|
|
return timepicker.step && timepicker.step < 60;
|
|
};
|
|
|
|
const includeMilliSeconds = function () {
|
|
return timepicker.step && timepicker.step < 1;
|
|
};
|
|
|
|
let cachedTimeString;
|
|
let cachedTimeObject;
|
|
|
|
timepicker.i18n = {
|
|
formatTime: tryCatchWrapper(function (timeObject) {
|
|
if (!timeObject) return;
|
|
|
|
const timeToBeFormatted = new Date();
|
|
timeToBeFormatted.setHours(timeObject.hours);
|
|
timeToBeFormatted.setMinutes(timeObject.minutes);
|
|
timeToBeFormatted.setSeconds(timeObject.seconds !== undefined ? timeObject.seconds : 0);
|
|
|
|
// the web component expects the correct granularity used for the time string,
|
|
// thus need to format the time object in correct granularity by passing the format options
|
|
let localeTimeString = timeToBeFormatted.toLocaleTimeString(locale, {
|
|
hour: 'numeric',
|
|
minute: 'numeric',
|
|
second: includeSeconds() ? 'numeric' : undefined
|
|
});
|
|
|
|
// milliseconds not part of the time format API
|
|
if (includeMilliSeconds()) {
|
|
localeTimeString = formatMilliseconds(localeTimeString, timeObject.milliseconds, amString, pmString);
|
|
}
|
|
|
|
return localeTimeString;
|
|
}),
|
|
|
|
parseTime: tryCatchWrapper(function (timeString) {
|
|
if (timeString && timeString === cachedTimeString && cachedTimeObject) {
|
|
return cachedTimeObject;
|
|
}
|
|
|
|
if (!timeString) {
|
|
// when nothing is returned, the component shows the invalid state for the input
|
|
return;
|
|
}
|
|
|
|
const amToken = searchAmOrPmToken(timeString, amString);
|
|
const pmToken = searchAmOrPmToken(timeString, pmString);
|
|
|
|
const numbersOnlyTimeString = timeString
|
|
.replace(amToken || '', '')
|
|
.replace(pmToken || '', '')
|
|
.trim();
|
|
|
|
// A regexp that allows to find the numbers with optional separator and continuing searching after it.
|
|
const numbersRegExp = new RegExp('([\\d\\u0660-\\u0669]){1,2}(?:' + separator + ')?', 'g');
|
|
|
|
let hours = numbersRegExp.exec(numbersOnlyTimeString);
|
|
if (hours) {
|
|
hours = parseDigitsIntoInteger(hours[0].replace(separator, ''));
|
|
// handle 12 am -> 0
|
|
// do not do anything if am & pm are not used or if those are the same,
|
|
// as with locale bg-BG there is always ч. at the end of the time
|
|
if (amToken !== pmToken) {
|
|
if (hours === 12 && amToken) {
|
|
hours = 0;
|
|
}
|
|
if (hours !== 12 && pmToken) {
|
|
hours += 12;
|
|
}
|
|
}
|
|
const minutes = numbersRegExp.exec(numbersOnlyTimeString);
|
|
const seconds = minutes && numbersRegExp.exec(numbersOnlyTimeString);
|
|
// detecting milliseconds from input, expects am/pm removed from end, eg. .0 or .00 or .000
|
|
const millisecondRegExp = /[[\.][\d\u0660-\u0669]{1,3}$/;
|
|
// reset to end or things can explode
|
|
let milliseconds = seconds && includeMilliSeconds() && millisecondRegExp.exec(numbersOnlyTimeString);
|
|
// handle case where last numbers are seconds and . is the separator (invalid regexp match)
|
|
if (milliseconds && milliseconds['index'] <= seconds['index']) {
|
|
milliseconds = undefined;
|
|
}
|
|
// hours is a number at this point, others are either arrays or null
|
|
// the string in [0] from the arrays includes the separator too
|
|
cachedTimeObject = hours !== undefined && {
|
|
hours: hours,
|
|
minutes: minutes ? parseDigitsIntoInteger(minutes[0].replace(separator, '')) : 0,
|
|
seconds: seconds ? parseDigitsIntoInteger(seconds[0].replace(separator, '')) : 0,
|
|
milliseconds:
|
|
minutes && seconds && milliseconds
|
|
? parseMillisecondsIntoInteger(milliseconds[0].replace('.', ''))
|
|
: 0
|
|
};
|
|
cachedTimeString = timeString;
|
|
return cachedTimeObject;
|
|
}
|
|
})
|
|
};
|
|
|
|
if (previousValueObject) {
|
|
when(
|
|
() => timepicker.$,
|
|
() => {
|
|
const newValue = timepicker.i18n.formatTime(previousValueObject);
|
|
// FIXME works but uses private API, needs fixes in web component
|
|
if (timepicker.inputElement.value !== newValue) {
|
|
timepicker.inputElement.value = newValue;
|
|
timepicker.$.comboBox.value = newValue;
|
|
}
|
|
}
|
|
);
|
|
}
|
|
});
|
|
})(timepicker)
|
|
};
|
|
})();
|