diff --git a/NEWS b/NEWS index f1e4392b1..d4d16ce52 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ Bug fixes - [web] properly encode rawsource of events and tasks to avoid XSS issues (#3718) - [web] properly encode rawsource of cards to avoid XSS issues - [web] fixed all-day events covering a timezone change (#3457) + - [web] sgTimePicker parser now respects the user's time format and language - [core] properly handle sorted/deleted calendars (#3723) - [core] properly handle flattened timezone definitions (#2690) diff --git a/UI/WebServerResources/js/Common/sgTimepicker.directive.js b/UI/WebServerResources/js/Common/sgTimepicker.directive.js index 12ddabc8d..534da5ef3 100644 --- a/UI/WebServerResources/js/Common/sgTimepicker.directive.js +++ b/UI/WebServerResources/js/Common/sgTimepicker.directive.js @@ -711,38 +711,22 @@ */ TimePickerCtrl.prototype.handleInputEvent = function(self) { var inputString = this.inputElement.value; - var arr = inputString.split(/[\.:]/); + var parsedTime = inputString ? this.dateLocale.parseTime(inputString) : null; - if (inputString === '') { - this.ngModelCtrl.$setViewValue(null); - this.time = null; - this.inputContainer.classList.remove(INVALID_CLASS); - } - else if (arr.length < 2) { - arr = /(\d{1,2})(\d{2})/i.exec(inputString); - if (arr) - arr.splice(0, 1); // only keep text captured by parenthesis + // An input string is valid if it is either empty (representing no date) + // or if it parses to a valid time that the user is allowed to select. + var isValidInput = inputString === '' || this.dateUtil.isValidDate(parsedTime); + + // The datepicker's model is only updated when there is a valid input. + if (isValidInput) { + var updated = new Date(this.time); + updated.setHours(parsedTime.getHours()); + updated.setMinutes(parsedTime.getMinutes()); + this.ngModelCtrl.$setViewValue(updated); + this.time = updated; } - if (!arr || arr.length < 2) { - this.inputContainer.classList.toggle(INVALID_CLASS, inputString); - } - else { - var h = Number(arr[0]); - var m = Number(arr[1]); - var newVal = new Date(this.time); - if (h && h >= 0 && h <= 23 && m && m >= 0 && m <= 59 && angular.isDate(newVal)) { - newVal.setHours(h); - newVal.setMinutes(m); - this.ngModelCtrl.$setViewValue(newVal); - this.time = newVal; - this.inputElement.value = this.dateLocale.formatTime(newVal); - this.inputContainer.classList.remove(INVALID_CLASS); - } - else { - this.inputContainer.classList.toggle(INVALID_CLASS, inputString); - } - } + this.updateErrorState(parsedTime); }; /** Position and attach the floating calendar to the document. */ diff --git a/UI/WebServerResources/js/Common/utils.js b/UI/WebServerResources/js/Common/utils.js index d69630847..839565b1c 100644 --- a/UI/WebServerResources/js/Common/utils.js +++ b/UI/WebServerResources/js/Common/utils.js @@ -152,12 +152,14 @@ String.prototype.parseDate = function(localeProvider, format) { var string, formattingTokens, tokens, token, now, date, regexes, i, parsedInput, matchesCount; string = '' + this; - formattingTokens = /%[dembByY]/g; + formattingTokens = /%[dembByYHIMp]/g; now = new Date(); date = { - year: -1, - month: -1, - day: -1 + year: now.getYear() + 1900, + month: now.getMonth(), + day: now.getDate(), + hour: 0, + minute: 0 }; regexes = { '%d': [/\d\d/, function(input) { @@ -173,13 +175,13 @@ String.prototype.parseDate = function(localeProvider, format) { return (date.month < 12); }], '%b': [/[^\d\s\.\/\-]{2,}/, function(input) { - var i = _.indexOf(localeProvider.shortMonths, input); + var i = _.indexOf(_.map(localeProvider.shortMonths, _.toLower), _.toLower(input)); if (i >= 0) date.month = i; return (i >= 0); }], '%B': [/[^\d\s\.\/\-]{2,}/, function(input) { - var i = _.indexOf(localeProvider.months, input); + var i = _.indexOf(_.map(localeProvider.months, _.toLower), _.toLower(input)); if (i >= 0) date.month = i; return (i >= 0); @@ -194,7 +196,25 @@ String.prototype.parseDate = function(localeProvider, format) { '%Y': [/[12]\d\d\d/, function(input) { date.year = parseInt(input); return true; - }] + }], + '%H': [/\d{1,2}/, function(input) { + date.hour = parseInt(input); + return (date.hour < 24); + }], + '%I': [/\d{1,2}/, function(input) { + date.hour = parseInt(input); + return (date.hour <= 12); + }], + '%M': [/[0-5]\d/, function(input) { + date.minute = parseInt(input); + return (date.minute < 60 ); + }], + '%p': [/[^\d\s\/\-]+/, function(input) { + var linput = _.toLower(input), am = _.toLower(l('AM')), pm = _.toLower(l('PM')); + if (linput == pm) + date.hour += 12; + return (linput == am || linput == pm); + }], }; tokens = format.match(formattingTokens) || []; matchesCount = 0; @@ -211,7 +231,7 @@ String.prototype.parseDate = function(localeProvider, format) { if (tokens.length === matchesCount) { // console.debug(this + ' + ' + format + ' = ' + JSON.stringify(date)); - return new Date(date.year, date.month, date.day); + return new Date(date.year, date.month, date.day, date.hour, date.minute); } else return new Date(NaN); diff --git a/UI/WebServerResources/js/Preferences/Preferences.service.js b/UI/WebServerResources/js/Preferences/Preferences.service.js index b0e6f46e0..005ba2bf1 100644 --- a/UI/WebServerResources/js/Preferences/Preferences.service.js +++ b/UI/WebServerResources/js/Preferences/Preferences.service.js @@ -92,6 +92,9 @@ _this.$mdDateLocaleProvider.formatDate = function(date) { return date? date.format(_this.$mdDateLocaleProvider, data.SOGoShortDateFormat) : ''; }; + _this.$mdDateLocaleProvider.parseTime = function(timeString) { + return timeString? timeString.parseDate(_this.$mdDateLocaleProvider, data.SOGoTimeFormat) : new Date(NaN); + }; _this.$mdDateLocaleProvider.formatTime = function(date) { return date? date.format(_this.$mdDateLocaleProvider, data.SOGoTimeFormat) : ''; };