(function () { "use strict"; var monthNames = [ "", "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند" ]; var weekNames = ["ش", "ی", "د", "س", "چ", "پ", "ج"]; var openPicker = null; function div(a, b) { return Math.floor(a / b); } function gregorianToJalali(gy, gm, gd) { var gdm = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; var gy2 = gy + (gm > 2 ? 1 : 0); var days = 355666 + 365 * gy + div(gy2 + 3, 4) - div(gy2 + 99, 100) + div(gy2 + 399, 400) + gd + gdm[gm - 1]; var jy = -1595 + 33 * div(days, 12053); days %= 12053; jy += 4 * div(days, 1461); days %= 1461; if (days > 365) { jy += div(days - 1, 365); days = (days - 1) % 365; } if (days < 186) { return { year: jy, month: 1 + div(days, 31), day: 1 + (days % 31) }; } return { year: jy, month: 7 + div(days - 186, 30), day: 1 + ((days - 186) % 30) }; } function jalaliToGregorian(jy, jm, jd) { jy += 1595; var days = -355668 + 365 * jy + div(jy, 33) * 8 + div((jy % 33) + 3, 4) + jd; days += jm < 7 ? (jm - 1) * 31 : (jm - 7) * 30 + 186; var gy = 400 * div(days, 146097); days %= 146097; if (days > 36524) { gy += 100 * div(days - 1, 36524); days = (days - 1) % 36524; if (days >= 365) { days += 1; } } gy += 4 * div(days, 1461); days %= 1461; if (days > 365) { gy += div(days - 1, 365); days = (days - 1) % 365; } var gd = days + 1; var lengths = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; if ((gy % 4 === 0 && gy % 100 !== 0) || gy % 400 === 0) { lengths[2] = 29; } var gm = 1; while (gm <= 12 && gd > lengths[gm]) { gd -= lengths[gm]; gm += 1; } return { year: gy, month: gm, day: gd }; } function todayJalali() { var now = new Date(); return gregorianToJalali(now.getFullYear(), now.getMonth() + 1, now.getDate()); } function daysInMonth(year, month) { if (month <= 6) { return 31; } if (month <= 11) { return 30; } var current = jalaliToGregorian(year, 1, 1); var next = jalaliToGregorian(year + 1, 1, 1); var currentUTC = Date.UTC(current.year, current.month - 1, current.day); var nextUTC = Date.UTC(next.year, next.month - 1, next.day); return (nextUTC - currentUTC) / 86400000 === 366 ? 30 : 29; } function parseDate(value) { var match = /^\s*(\d{4})-(\d{2})-(\d{2})\s*$/.exec(value); if (!match) { return null; } var date = { year: Number(match[1]), month: Number(match[2]), day: Number(match[3]) }; if (date.year < 1200 || date.month < 1 || date.month > 12 || date.day < 1 || date.day > daysInMonth(date.year, date.month)) { return null; } return date; } function formatDate(date) { return String(date.year).padStart(4, "0") + "-" + String(date.month).padStart(2, "0") + "-" + String(date.day).padStart(2, "0"); } function sameDate(a, b) { return a && b && a.year === b.year && a.month === b.month && a.day === b.day; } function JalaliPicker(input) { this.input = input; this.field = input.closest(".jalali-field"); this.trigger = this.field.querySelector(".jalali-trigger"); this.selected = parseDate(input.value); var initial = this.selected || todayJalali(); this.year = initial.year; this.month = initial.month; this.popup = document.createElement("div"); this.popup.className = "jalali-picker"; this.popup.hidden = true; this.popup.setAttribute("role", "dialog"); this.popup.setAttribute("aria-label", "Persian date picker"); this.popup.setAttribute("dir", "rtl"); this.field.appendChild(this.popup); this.trigger.addEventListener("click", this.toggle.bind(this)); this.input.addEventListener("focus", this.open.bind(this)); this.input.addEventListener("change", this.sync.bind(this)); this.input.addEventListener("keydown", this.onKeyDown.bind(this)); } JalaliPicker.prototype.sync = function () { var parsed = parseDate(this.input.value); this.input.setCustomValidity(this.input.value && !parsed ? "Use a valid Persian date in YYYY-MM-DD format." : ""); if (parsed) { this.selected = parsed; this.year = parsed.year; this.month = parsed.month; if (!this.popup.hidden) { this.render(); } } }; JalaliPicker.prototype.onKeyDown = function (event) { if (event.key === "Escape") { this.close(); this.input.blur(); } else if (event.key === "ArrowDown" && this.popup.hidden) { event.preventDefault(); this.open(); } }; JalaliPicker.prototype.toggle = function () { if (this.popup.hidden) { this.open(); } else { this.close(); } }; JalaliPicker.prototype.open = function () { if (openPicker && openPicker !== this) { openPicker.close(); } this.sync(); this.popup.hidden = false; this.field.classList.add("picker-open"); this.trigger.setAttribute("aria-expanded", "true"); openPicker = this; this.render(); }; JalaliPicker.prototype.close = function () { this.popup.hidden = true; this.field.classList.remove("picker-open"); this.trigger.setAttribute("aria-expanded", "false"); if (openPicker === this) { openPicker = null; } }; JalaliPicker.prototype.moveMonth = function (amount) { this.month += amount; if (this.month < 1) { this.month = 12; this.year -= 1; } else if (this.month > 12) { this.month = 1; this.year += 1; } this.render(); }; JalaliPicker.prototype.choose = function (day) { this.selected = { year: this.year, month: this.month, day: day }; this.input.value = formatDate(this.selected); this.input.setCustomValidity(""); this.input.dispatchEvent(new Event("input", { bubbles: true })); this.input.dispatchEvent(new Event("change", { bubbles: true })); if (this.input.dataset.jalaliRole === "start") { var end = document.querySelector('[data-jalali-role="end"]'); var endDate = end && parseDate(end.value); if (end && (!endDate || end.value < this.input.value)) { end.value = this.input.value; end.dispatchEvent(new Event("change", { bubbles: true })); } } this.close(); this.input.focus(); }; JalaliPicker.prototype.render = function () { var picker = this; var today = todayJalali(); var selected = parseDate(this.input.value) || this.selected; var firstGregorian = jalaliToGregorian(this.year, this.month, 1); var offset = (new Date(firstGregorian.year, firstGregorian.month - 1, firstGregorian.day).getDay() + 1) % 7; var count = daysInMonth(this.year, this.month); this.popup.innerHTML = '