diff --git a/client/components/cards/cardDate.js b/client/components/cards/cardDate.js index 468e7809b..e9f4c029e 100644 --- a/client/components/cards/cardDate.js +++ b/client/components/cards/cardDate.js @@ -3,13 +3,6 @@ import moment from 'moment/min/moment-with-locales'; import { TAPi18n } from '/imports/i18n'; import { DatePicker } from '/client/lib/datepicker'; -// Add this helper function at the top level -const formatDate = (date, format) => { - if (!date) return ''; - const normalizedDate = Utils.normalizeDigits(moment(date).format(format)); - return normalizedDate; -}; - // editCardReceivedDatePopup (class extends DatePicker { onCreated() { @@ -173,10 +166,6 @@ class CardReceivedDate extends CardDate { 'click .js-edit-date': Popup.open('editCardReceivedDate'), }); } - - showDate() { - return formatDate(this.date.get(), 'L'); - } } CardReceivedDate.register('cardReceivedDate'); @@ -212,10 +201,6 @@ class CardStartDate extends CardDate { 'click .js-edit-date': Popup.open('editCardStartDate'), }); } - - showDate() { - return formatDate(this.date.get(), 'L'); - } } CardStartDate.register('cardStartDate'); @@ -252,10 +237,6 @@ class CardDueDate extends CardDate { 'click .js-edit-date': Popup.open('editCardDueDate'), }); } - - showDate() { - return formatDate(this.date.get(), 'L'); - } } CardDueDate.register('cardDueDate'); @@ -287,10 +268,6 @@ class CardEndDate extends CardDate { 'click .js-edit-date': Popup.open('editCardEndDate'), }); } - - showDate() { - return formatDate(this.date.get(), 'L'); - } } CardEndDate.register('cardEndDate'); @@ -340,31 +317,31 @@ CardCustomFieldDate.register('cardCustomFieldDate'); (class extends CardReceivedDate { showDate() { - return formatDate(this.date.get(), 'L'); + return this.date.get().format('L'); } }.register('minicardReceivedDate')); (class extends CardStartDate { showDate() { - return formatDate(this.date.get(), 'YYYY-MM-DD HH:mm'); + return this.date.get().format('YYYY-MM-DD HH:mm'); } }.register('minicardStartDate')); (class extends CardDueDate { showDate() { - return formatDate(this.date.get(), 'YYYY-MM-DD HH:mm'); + return this.date.get().format('YYYY-MM-DD HH:mm'); } }.register('minicardDueDate')); (class extends CardEndDate { showDate() { - return formatDate(this.date.get(), 'YYYY-MM-DD HH:mm'); + return this.date.get().format('YYYY-MM-DD HH:mm'); } }.register('minicardEndDate')); (class extends CardCustomFieldDate { showDate() { - return formatDate(this.date.get(), 'L'); + return this.date.get().format('L'); } }.register('minicardCustomFieldDate')); @@ -381,7 +358,7 @@ class VoteEndDate extends CardDate { return classes; } showDate() { - return formatDate(this.date.get(), 'L LT'); + return this.date.get().format('L LT'); } showTitle() { return `${TAPi18n.__('card-end-on')} ${this.date.get().format('LLLL')}`; @@ -408,7 +385,7 @@ class PokerEndDate extends CardDate { return classes; } showDate() { - return formatDate(this.date.get(), 'L LT'); + return this.date.get().format('l LT'); } showTitle() { return `${TAPi18n.__('card-end-on')} ${this.date.get().format('LLLL')}`; diff --git a/client/lib/datepicker.js b/client/lib/datepicker.js index 59da73e46..84e4047f4 100644 --- a/client/lib/datepicker.js +++ b/client/lib/datepicker.js @@ -1,13 +1,16 @@ import { ReactiveCache } from '/imports/reactiveCache'; import { TAPi18n } from '/imports/i18n'; import moment from 'moment/min/moment-with-locales'; -import { Utils } from './utils'; -// Helper function to replace HH with H for 24 hours format +// Helper function to replace HH with H for 24 hours format, because H allows also single-digit hours function adjustedTimeFormat() { - return moment.localeData().longDateFormat('LT'); + return moment + .localeData() + .longDateFormat('LT'); } +// .replace(/HH/i, 'H'); + export class DatePicker extends BlazeComponent { template() { return 'datepicker'; @@ -37,15 +40,21 @@ export class DatePicker extends BlazeComponent { language: TAPi18n.getLanguage(), weekStart: this.startDayOfWeek(), calendarWeeks: true, - beforeParse: (value) => Utils.normalizeDigits(value), }) .on( 'changeDate', function(evt) { - const normalizedDate = moment(evt.date).format('L'); - this.find('#date').value = normalizedDate; + this.find('#date').value = moment(evt.date).format('L'); this.error.set(''); - this._handleTimeInput(evt); + const timeInput = this.find('#time'); + timeInput.focus(); + if (!timeInput.value && this.defaultTime) { + const currentHour = evt.date.getHours(); + const defaultMoment = moment( + currentHour > 0 ? evt.date : this.defaultTime, + ); // default to 8:00 am local time + timeInput.value = defaultMoment.format('LT'); + } }.bind(this), ); @@ -54,18 +63,6 @@ export class DatePicker extends BlazeComponent { } } - _handleTimeInput(evt) { - const timeInput = this.find('#time'); - timeInput.focus(); - if (!timeInput.value && this.defaultTime) { - const currentHour = evt.date.getHours(); - const defaultMoment = moment( - currentHour > 0 ? evt.date : this.defaultTime, - ); - timeInput.value = defaultMoment.format('LT'); - } - } - showDate() { if (this.date.get().isValid()) return this.date.get().format('L'); return ''; @@ -82,68 +79,67 @@ export class DatePicker extends BlazeComponent { } events() { - return [{ - 'keyup .js-date-field'() { - const rawValue = this.find('#date').value; - const normalizedValue = Utils.normalizeDigits(rawValue); - const dateMoment = moment(normalizedValue, 'L', true); + return [ + { + 'keyup .js-date-field'() { + // parse for localized date format in strict mode + const normalizedValue = Utils.normalizeDigits(this.find('#date').value); + const dateMoment = moment(normalizedValue, 'L', true); + if (dateMoment.isValid()) { + this.error.set(''); + this.$('.js-datepicker').datepicker('update', dateMoment.toDate()); + } + }, + 'keyup .js-time-field'() { + // parse for localized time format in strict mode + const normalizedValue = Utils.normalizeDigits(this.find('#time').value); + const dateMoment = moment( + normalizedValue, + adjustedTimeFormat(), + true, + ); + if (dateMoment.isValid()) { + this.error.set(''); + } + }, + 'submit .edit-date'(evt) { + evt.preventDefault(); - if (dateMoment.isValid()) { - this.error.set(''); - this.$('.js-datepicker').datepicker('update', dateMoment.toDate()); - } + // if no time was given, init with 12:00 + const timeValue = Utils.normalizeDigits(evt.target.time.value); + const time = + timeValue || + moment(new Date().setHours(12, 0, 0)).format('LT'); + const newTime = moment(time, adjustedTimeFormat(), true); + const dateValue = Utils.normalizeDigits(evt.target.date.value); + const newDate = moment(dateValue, 'L', true); + const dateString = `${dateValue} ${time}`; + const newCompleteDate = moment( + dateString, + `L ${adjustedTimeFormat()}`, + true, + ); + if (!newTime.isValid()) { + this.error.set('invalid-time'); + evt.target.time.focus(); + } + if (!newDate.isValid()) { + this.error.set('invalid-date'); + evt.target.date.focus(); + } + if (newCompleteDate.isValid()) { + this._storeDate(newCompleteDate.toDate()); + Popup.back(); + } else if (!this.error) { + this.error.set('invalid'); + } + }, + 'click .js-delete-date'(evt) { + evt.preventDefault(); + this._deleteDate(); + Popup.back(); + }, }, - - 'keyup .js-time-field'() { - const rawValue = this.find('#time').value; - const normalizedValue = Utils.normalizeDigits(rawValue); - const timeMoment = moment(normalizedValue, adjustedTimeFormat(), true); - - if (timeMoment.isValid()) { - this.error.set(''); - } - }, - - 'submit .edit-date'(evt) { - evt.preventDefault(); - - const dateValue = Utils.normalizeDigits(evt.target.date.value); - const timeValue = Utils.normalizeDigits(evt.target.time.value) || - moment(new Date().setHours(12, 0, 0)).format('LT'); - - const dateString = `${dateValue} ${timeValue}`; - const format = `L ${adjustedTimeFormat()}`; - const newDate = moment(dateString, format, true); - - if (!newDate.isValid()) { - this._handleDateTimeError(evt, dateValue, timeValue); - return; - } - - this._storeDate(newDate.toDate()); - Popup.back(); - }, - - 'click .js-delete-date'(evt) { - evt.preventDefault(); - this._deleteDate(); - Popup.back(); - } - }]; - } - - _handleDateTimeError(evt, dateValue, timeValue) { - const dateMoment = moment(dateValue, 'L', true); - const timeMoment = moment(timeValue, adjustedTimeFormat(), true); - - if (!timeMoment.isValid()) { - this.error.set('invalid-time'); - evt.target.time.focus(); - } else if (!dateMoment.isValid()) { - this.error.set('invalid-date'); - evt.target.date.focus(); - } else { - this.error.set('invalid'); - } + ]; } } diff --git a/client/lib/utils.js b/client/lib/utils.js index a2600ad4f..c2cd8886d 100644 --- a/client/lib/utils.js +++ b/client/lib/utils.js @@ -1,17 +1,6 @@ import { ReactiveCache } from '/imports/reactiveCache'; -import { Session } from 'meteor/session'; -import { FlowRouter } from 'meteor/kadira:flow-router'; -import { Tracker } from 'meteor/tracker'; -import { $ } from 'meteor/jquery'; -import { Meteor } from 'meteor/meteor'; -// Initialize global Utils first -if (typeof window.Utils === 'undefined') { - window.Utils = {}; -} - -// Create Utils object -const Utils = { +Utils = { setBackgroundImage(url) { const currentBoard = Utils.getCurrentBoard(); if (currentBoard.backgroundImageURL !== undefined) { @@ -26,19 +15,12 @@ const Utils = { // This helps with date parsing in non-English languages normalizeDigits(str) { if (!str) return str; - // Convert Persian and Arabic numbers to English - const persianNumbers = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹']; - const arabicNumbers = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩']; - - return str.split('') - .map(c => { - const pIndex = persianNumbers.indexOf(c); - const aIndex = arabicNumbers.indexOf(c); - if (pIndex >= 0) return pIndex.toString(); - if (aIndex >= 0) return aIndex.toString(); - return c; - }) - .join(''); + const persian = [/۰/g, /۱/g, /۲/g, /۳/g, /۴/g, /۵/g, /۶/g, /۷/g, /۸/g, /۹/g]; + const arabic = [/٠/g, /١/g, /٢/g, /٣/g, /٤/g, /٥/g, /٦/g, /٧/g, /٨/g, /٩/g]; + for (let i = 0; i < 10; i++) { + str = str.replace(persian[i], i).replace(arabic[i], i); + } + return str; }, /** returns the current board id *
  • returns the current board id or the board id of the popup card if set @@ -609,12 +591,6 @@ const Utils = { }, }; -// Update global Utils with all methods -Object.assign(window.Utils, Utils); - -// Export for ES modules -export { Utils }; - // A simple tracker dependency that we invalidate every time the window is // resized. This is used to reactively re-calculate the popup position in case // of a window resize. This is the equivalent of a "Signal" in some other