var pickmeup; var activePickmeup = null; $(document).ready(InitDateTimePickers); //Заглушка для совместимости function InitDateTimePickers() { DateTimePickerInitializer.init(); } class DateTimePickerInitializer { public static readonly defaultDateFormat = "d.m.Y"; public static readonly attrStartItem = "data-start-item-selector"; public static readonly attrEndItem = "data-end-item-selector"; public static readonly attrIsPaired = "data-date-is-paired"; static readonly CalendarLocales = { days: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'], daysShort: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'], daysMin: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'], months: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'], monthsShort: ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'] }; static initPickMeUpFlag = "pick-me-up-inited"; public static init() { $('.custom-date-time input').each((i, item) => { DateTimePickerInitializer.initElement(item); }); } public static initElement(dateTimeElement: HTMLElement) { DateTimePickerInitializer.InitInput(dateTimeElement); let label = $(dateTimeElement).closest(".custom-date-time").find("label").get(0); DateTimePickerInitializer.initLabel(label); } public static InitInput(dateTimeInput: HTMLElement) { var $this = $(dateTimeInput); if ($this.attr("disabled") === "disabled") { // return; } var dateFormat = $this.data("pmu-format"); let ageLimit: number = $this.data("age-limit"); let maxDate = $this.data("pmu-max"); let minDate = $this.data("pmu-min"); //инициализация обёртки DateTimePickerInitializer.initPickMeUp($this, dateFormat); // валидация и синхронность ограничений мин-макс для 2 связанных инпутов(начало события и конец). if ($this.attr(DateTimePickerInitializer.attrStartItem) && $this.attr(DateTimePickerInitializer.attrStartItem).indexOf("$index") > 0 || $this.attr(DateTimePickerInitializer.attrEndItem) && $this.attr(DateTimePickerInitializer.attrEndItem).indexOf("$index") > 0 || $this.attr(DateTimePickerInitializer.attrIsPaired)) { //задержка для отработки ангуляра setTimeout(() => { DateInputValidation.initValidation($this, dateFormat); }, 1100); } else { DateInputValidation.initValidation($this, dateFormat); } // для ограничения выбора дат в календаре setTimeout(() => DateTimePickerInitializer.initMinMaxDates($this), 900); if (ageLimit) { DateTimePickerInitializer.initByAgeLimit($this, ageLimit); } } public static initByAgeLimit(input, ageLimit) { let limitDate = new Date(); limitDate.setFullYear(limitDate.getFullYear() - ageLimit); $(input).attr("data-pmu-max", limitDate.toLocaleDateString()); input[0].__pickmeup.options.max = limitDate; pickmeup(input[0]).update(); pickmeup(input[0]).hide(); } public static initMinMaxDates(input) { let max = ConvertStringToDate($(input).attr("data-pmu-max")); let min = ConvertStringToDate($(input).attr("data-pmu-min")); let domInput = input[0]; $(input).attr("data-pmu-min", $(input).attr("data-pmu-min")); domInput.__pickmeup.options.min = min; $(input).attr("data-pmu-max", $(input).attr("data-pmu-max")); domInput.__pickmeup.options.max = max; pickmeup(domInput).update(); pickmeup(domInput).hide(); } /** * Инициализирует обёртку PickMeUp для инпута * @param element */ public static initPickMeUp(element: JQuery, dateFormat?: string) { if (element.data(this.initPickMeUpFlag)) { return; } // пометить инициализированным, чтобы не проинициализировать повторно element.data(this.initPickMeUpFlag, "true"); pickmeup.defaults.locales['ru'] = this.CalendarLocales; var selectDay = element.data("pmu-select-day"); var selectMonth = element.data("pmu-select-month"); // конвертирование в boolean selectDay = (selectDay.toLowerCase() == "true"); selectMonth = (selectMonth.toLowerCase() == "true"); if (!dateFormat) { dateFormat = this.defaultDateFormat; } // получение DOM-элемента для pickmeup var input = element[0]; pickmeup(input, { locale: 'ru', default_date: false, format: dateFormat, select_day: selectDay, select_month: selectMonth, hide_on_select: true, prev: '', next: '', }); input.addEventListener('pickmeup-show', function (e) { if (activePickmeup) { activePickmeup.hide(); } activePickmeup = pickmeup(e.target); }); input.addEventListener('pickmeup-hide', function (e) { activePickmeup = null; }); input.addEventListener('pickmeup-change', function (e) { // триггер события изменения значения инпута var element = e.target; var event = new Event("change"); element.dispatchEvent(event); }); input.addEventListener('focusout', function (e) { var element = e.target; var event = new Event("change"); element.dispatchEvent(event); }); } public static initLabel(label: HTMLElement) { $(label).click(event => { let datePicker = $(event.target).closest(".custom-date-time").find("input"); datePicker.show(); }); } }; class DateInputValidation { static initValidationFlag = "validation-inited"; private dateMinAbsolute: Date; private dateMaxAbsolute: Date; private constructor(public itemStart: JQuery, public itemEnd?: JQuery, public dateFormat = DateTimePickerInitializer.defaultDateFormat) { //Если уже была инициализация - выход if (itemStart && itemStart.data(DateInputValidation.initValidationFlag) || itemEnd && itemEnd.data(DateInputValidation.initValidationFlag)) { return; } this.dateMinAbsolute = ConvertStringToDate(itemStart.attr("data-pmu-min")) || new Date(1940, 1, 1); this.dateMaxAbsolute = ConvertStringToDate(itemStart.attr("data-pmu-max")) || new Date(2040, 1, 1); this.initStartItem(); if (this.itemEnd) { //Максимальное значение абсолютной даты, из начального инпута и второго. let dateEndItem = ConvertStringToDate(itemEnd.attr("data-pmu-max")); if (dateEndItem) { this.dateMaxAbsolute = new Date(Math.max(dateEndItem.getTime(), this.dateMaxAbsolute.getTime())); } this.initEndItem(); } } /** * Инициализирует маску ввода дат. Т.е только символы в формате маски * @param input */ public static initInputMask(input: JQuery, dateFormat: string) { let mask = dateFormat == DateTimePickerInitializer.defaultDateFormat ? "99.99.9999" : "9999"; let placeholder = dateFormat == DateTimePickerInitializer.defaultDateFormat ? "дд.мм.гггг" : "гггг"; (input as any).inputmask({ "alias": "date", "mask": mask, "placeholder": placeholder, "autoGroup": "true" }); } /** * Инициализирует валидацию инпутов(инпута) даты. * @param input */ public static initValidation(input: JQuery, dateFormat: string) { if (input.data(this.initValidationFlag)) { return; } //Обнуляем начало и конец, т.к не знаем он начало или конец var itemStart = input; var itemEnd = null; let startDateSelector = input.attr(DateTimePickerInitializer.attrStartItem); let endDateSelector = input.attr(DateTimePickerInitializer.attrEndItem); //А здесь нужный (начало или конец) записывает по селектору, а его брат - текущий if (startDateSelector) { itemStart = $(startDateSelector); itemEnd = input; } if (endDateSelector) { itemEnd = $(endDateSelector); itemStart = input; } //Поиск спаренного инпута в кастомной части //Там нельзя использовать startDateSelector, endDateSelector. //Возможно стоит избавится от startDateSelector, endDateSelector. //Дляэ того нужен одинаковый контейнер для датных инпутов if (input.attr(DateTimePickerInitializer.attrIsPaired) == "True") { let parent = input.parents(".row-group"); let inputs = parent.find(".date-time-input.custom[" + DateTimePickerInitializer.attrIsPaired + "=True]"); if (inputs && inputs.length == 2 && inputs.is(input)) { itemStart = $(inputs[0]) as any; itemEnd = $(inputs[1]); } } if (itemEnd) { //инициализация обёртки DateTimePickerInitializer.initPickMeUp(itemEnd); } new DateInputValidation(itemStart, itemEnd, dateFormat); } //Инит стартовый инпут, либо единственный, если нет пары private initStartItem() { this.itemStart.data(DateInputValidation.initValidationFlag, "true"); DateInputValidation.initInputMask(this.itemStart, this.dateFormat); this.itemStart.on("change", (event) => { var newStartDateVal = this.itemStart.val() as string; var newStartDate = ConvertStringToDate(newStartDateVal); //Дата из спаренного инпута макс даты var endDate = this.itemEnd ? ConvertStringToDate(this.itemEnd.val() as string) : null; //Введено значение, но оно не Дата if (newStartDate == null && newStartDateVal) { this.itemStart.val(this.dateToStringFormat(new Date())); event.stopImmediatePropagation(); } //Раньше, чем глобальный минимум(обычно даты конкурсов) if (newStartDate < this.dateMinAbsolute && this.dateMinAbsolute) { let dateStr = this.dateToStringFormat(this.dateMinAbsolute); this.itemStart.val(dateStr); MessageShower.showError("Нельзя установить дату, раньше чем " + dateStr + " !"); event.stopImmediatePropagation(); return false; } //Если больше чем дата в спаренном инпуте if (newStartDate > endDate && endDate) { let dateStr = this.dateToStringFormat(endDate); this.itemStart.val(dateStr); MessageShower.showError("Нельзя установить дату, позже чем " + dateStr + " !"); event.stopImmediatePropagation(); return; } //Если больше глобального максимума if (newStartDate > this.dateMaxAbsolute && this.dateMaxAbsolute) { let dateStr = this.dateToStringFormat(this.dateMaxAbsolute); this.itemStart.val(dateStr); MessageShower.showError("Нельзя установить дату, позже чем " + dateStr + " !"); event.stopImmediatePropagation(); return; } let element = event.currentTarget; element.dispatchEvent(new Event('pickmeup-force-update')); }); this.itemStart.on("set-date-limits", (event, minDate, maxDate) => { this.dateMaxAbsolute = ConvertStringToDate(maxDate); this.dateMinAbsolute = ConvertStringToDate(minDate); DateInputValidation.setDateMin($(event.target), minDate); DateInputValidation.setDateMax($(event.target), maxDate); }); } private validateInputChars(event) { var theEvent = event || window.event; // Handle paste if (theEvent.type === 'paste') { key = event.clipboardData.getData('text/plain'); } else { // Handle key press var key = theEvent.keyCode || theEvent.which; key = String.fromCharCode(key); } var regex = /[0-9]|\./; if (!regex.test(key)) { theEvent.returnValue = false; if (theEvent.preventDefault) theEvent.preventDefault(); } } //Инит парный второй макс дата инпут private initEndItem() { this.itemEnd.data(DateInputValidation.initValidationFlag, "true"); DateInputValidation.initInputMask(this.itemEnd, this.dateFormat); this.itemEnd.on("change", (event) => { var newEndDateVal = this.itemEnd.val() as string; var newEndDate = ConvertStringToDate(newEndDateVal); var startDate = this.itemStart ? ConvertStringToDate(this.itemStart.val() as string) : null; //Введено значение, но оно не Дата if (newEndDate == null && newEndDateVal) { this.itemEnd.val(this.dateToStringFormat(new Date())); event.stopImmediatePropagation(); } if (newEndDate > this.dateMaxAbsolute && this.dateMaxAbsolute) { let dateStr = this.dateToStringFormat(this.dateMaxAbsolute); this.itemEnd.val(dateStr); MessageShower.showError("Нельзя установить дату, позже чем " + dateStr + " !"); event.stopImmediatePropagation(); return; } if (newEndDate < startDate && startDate) { let dateStr = this.dateToStringFormat(startDate); this.itemEnd.val(dateStr); MessageShower.showError("Нельзя установить дату, раньше чем " + dateStr + " !"); event.stopImmediatePropagation(); return; } if (newEndDate < this.dateMinAbsolute && this.dateMinAbsolute) { let dateStr = this.dateToStringFormat(this.dateMinAbsolute); this.itemEnd.val(dateStr); MessageShower.showError("Нельзя установить дату, раньше чем " + dateStr + " !"); event.stopImmediatePropagation(); return; } }); this.itemEnd.on("set-date-limits", (event, minDate, maxDate) => { this.dateMaxAbsolute = ConvertStringToDate(maxDate); this.dateMinAbsolute = ConvertStringToDate(minDate); DateInputValidation.setDateMin($(event.target), minDate); DateInputValidation.setDateMax($(event.target), maxDate); }); } /** * Переводит дату в нужном формате инпута (ДД.ММ.ГОД или ГОД) * @param date */ private dateToStringFormat(date: Date): string { if (this.dateFormat === DateTimePickerInitializer.defaultDateFormat) { return (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + "." + ((date.getMonth() + 1) < 10 ? "0" + (date.getMonth() + 1) : (date.getMonth() + 1)) + "." + date.getFullYear(); } else { return date.getFullYear().toString(); } } /** * задать новое минимальное значение на основе даты окончания * @param item Элемент * @param newStartDateVal Новая дата */ public static setDateMin(item: JQuery, newStartDateVal: string) { let pureItem = (item[0] as any); item.attr("data-pmu-min", newStartDateVal); pureItem.__pickmeup.options.min = newStartDateVal; pickmeup(pureItem).update(); pickmeup(pureItem).hide(); } /** * задать новое максимальное значение на основе даты окончания * @param item Элемент * @param newEndDateVal Новая дата */ public static setDateMax(item: JQuery, newEndDateVal: string) { let pureItem = (item[0] as any); item.attr("data-pmu-max", newEndDateVal); pureItem.__pickmeup.options.max = newEndDateVal; pickmeup(pureItem).update(); pickmeup(pureItem).hide(); } }