1/** 2 * Persian Calendar Component - Standalone Version 3 * @version 1.2.7 4 * @license MIT 5 */ 6(function(){'use strict';const PERSIAN_CALENDAR_STYLES=` 7 /* Persian Calendar Popup Container */ 8 .persian-date-popup { 9 background: #fff !important; 10 border: 1px solid #ccc !important; 11 box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15) !important; 12 border-radius: 8px !important; 13 padding: 16px !important; 14 width: 290px !important; 15 box-sizing: border-box !important; 16 font-family: 'VazirMatn', 'Vazir', 'VazirmatnVF', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important; 17 -webkit-animation: fpFadeInDown 300ms cubic-bezier(0.23, 1, 0.32, 1); 18 animation: fpFadeInDown 300ms cubic-bezier(0.23, 1, 0.32, 1); 19 } 20 .persian-date-popup * { 21 box-sizing: border-box !important; 22 font-family: 'VazirMatn', 'Vazir', 'VazirmatnVF', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important; 23 } 24 @-webkit-keyframes fpFadeInDown { 25 from { opacity: 0; -webkit-transform: translate3d(0, -20px, 0); transform: translate3d(0, -20px, 0); } 26 to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 27 } 28 @keyframes fpFadeInDown { 29 from { opacity: 0; -webkit-transform: translate3d(0, -20px, 0); transform: translate3d(0, -20px, 0); } 30 to { opacity: 1; -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } 31 } 32 33 /* Calendar Wrapper Reset */ 34 .persian-calendar-wrapper { 35 background: transparent !important; 36 border: none !important; 37 box-shadow: none !important; 38 padding: 0 !important; 39 direction: rtl; 40 text-align: right; 41 position: relative; 42 max-width: 100% !important; 43 } 44 45 /* Month and Year Header Navigation */ 46 .persian-calendar-nav { 47 display: flex !important; 48 gap: 6px !important; 49 align-items: center !important; 50 justify-content: space-between !important; 51 margin-bottom: 12px !important; 52 padding: 0 !important; 53 height: 32px !important; 54 } 55 56 /* Header Clickable Labels (No border) */ 57 .persian-calendar-header-labels { 58 display: flex !important; 59 gap: 6px !important; 60 align-items: center !important; 61 } 62 .persian-calendar-month-label, 63 .persian-calendar-year-label, 64 .persian-calendar-year-label-view, 65 .persian-calendar-years-range-label { 66 font-size: 14px !important; 67 font-weight: 600 !important; 68 color: #1e1e1e !important; 69 padding: 4px 8px !important; 70 border-radius: 4px !important; 71 user-select: none !important; 72 transition: background-color 0.15s ease, color 0.15s ease !important; 73 } 74 .persian-calendar-month-label[role="button"], 75 .persian-calendar-year-label[role="button"], 76 .persian-calendar-year-label-view[role="button"] { 77 cursor: pointer !important; 78 } 79 .persian-calendar-month-label[role="button"]:hover, 80 .persian-calendar-year-label[role="button"]:hover, 81 .persian-calendar-year-label-view[role="button"]:hover { 82 background-color: #f7f9fe !important; 83 color: #3858e9 !important; 84 } 85 86 /* Prev & Next Buttons */ 87 .persian-calendar-prev, 88 .persian-calendar-next, 89 .persian-calendar-prev-year, 90 .persian-calendar-next-year, 91 .persian-calendar-prev-years, 92 .persian-calendar-next-years { 93 background: transparent !important; 94 border: none !important; 95 box-shadow: none !important; 96 padding: 0 !important; 97 margin: 0 !important; 98 cursor: pointer !important; 99 box-sizing: border-box !important; 100 text-shadow: none !important; 101 width: 32px !important; 102 height: 32px !important; 103 display: inline-flex !important; 104 align-items: center !important; 105 justify-content: center !important; 106 font-size: 20px !important; 107 color: #3858e9 !important; 108 border-radius: 2px !important; 109 transition: background-color 0.1s ease !important; 110 } 111 .persian-calendar-prev:hover, 112 .persian-calendar-next:hover, 113 .persian-calendar-prev-year:hover, 114 .persian-calendar-next-year:hover, 115 .persian-calendar-prev-years:hover, 116 .persian-calendar-next-years:hover { 117 background-color: #f7f9fe !important; 118 color: #183ad6 !important; 119 } 120 .persian-calendar-prev:focus, 121 .persian-calendar-prev:active, 122 .persian-calendar-next:focus, 123 .persian-calendar-next:active { 124 background-color: #eff2fe !important; 125 color: #183ad6 !important; 126 outline: none !important; 127 } 128 129 /* Grid & Weekdays */ 130 .persian-calendar-grid { margin-top: 12px; } 131 .persian-calendar-weekdays { 132 display: grid; 133 grid-template-columns: repeat(7, 1fr); 134 gap: 4px; 135 margin-bottom: 8px; 136 } 137 .persian-calendar-weekday { 138 text-align: center; 139 font-size: 12px; 140 font-weight: 500; 141 color: #757575; 142 padding: 4px; 143 } 144 .persian-calendar-days { 145 display: grid; 146 grid-template-columns: repeat(7, 1fr); 147 gap: 4px; 148 } 149 150 /* Day Display */ 151 .persian-calendar-day { 152 padding: 0 !important; 153 height: 28px !important; 154 width: 28px !important; 155 display: flex !important; 156 align-items: center !important; 157 justify-content: center !important; 158 box-sizing: border-box !important; 159 border-radius: 50% !important; 160 margin: 0 auto !important; 161 font-size: 14px !important; 162 cursor: pointer !important; 163 transition: none !important; 164 position: relative; 165 } 166 .persian-calendar-day:not(.empty):hover { 167 color: #3858e9 !important; 168 } 169 .persian-calendar-day.today { 170 background: #e0e0e0 !important; 171 font-weight: 500 !important; 172 } 173 .persian-calendar-day.selected { 174 background: #3858e9 !important; 175 color: white !important; 176 font-weight: 600 !important; 177 } 178 .persian-calendar-day.selected:hover { 179 color: white !important; 180 } 181 .persian-calendar-day.empty { 182 cursor: default; 183 pointer-events: none; 184 } 185 186 /* Months and Years Selection Grids */ 187 .persian-calendar-months-grid, 188 .persian-calendar-years-grid { 189 display: grid !important; 190 grid-template-columns: repeat(3, 1fr) !important; 191 gap: 8px !important; 192 margin-top: 12px !important; 193 } 194 .persian-calendar-month-item, 195 .persian-calendar-year-item { 196 display: flex !important; 197 align-items: center !important; 198 justify-content: center !important; 199 height: 40px !important; 200 border-radius: 6px !important; 201 font-size: 13px !important; 202 cursor: pointer !important; 203 background: #fdfdfd !important; 204 border: none !important; 205 transition: all 0.15s ease !important; 206 user-select: none !important; 207 color: #1e1e1e !important; 208 } 209 .persian-calendar-month-item:hover, 210 .persian-calendar-year-item:hover { 211 background: #f7f9fe !important; 212 color: #3858e9 !important; 213 } 214 .persian-calendar-month-item.system-current, 215 .persian-calendar-year-item.system-current { 216 color: #1e1e1e !important; 217 font-weight: 600 !important; 218 background: #e0e0e0 !important; 219 } 220 .persian-calendar-month-item.active, 221 .persian-calendar-year-item.active { 222 background: #3858e9 !important; 223 color: white !important; 224 font-weight: 600 !important; 225 border: none !important; 226 } 227 228 /* Time Picker Styles (Inputs with Up/Down Arrows) */ 229 .persian-calendar-time-picker { 230 display: flex !important; 231 align-items: center !important; 232 justify-content: center !important; 233 gap: 8px !important; 234 margin-top: 10px !important; 235 padding-top: 10px !important; 236 border-top: 1px solid #eee !important; 237 direction: rtl !important; 238 } 239 .persian-calendar-time-title { 240 font-size: 13px !important; 241 font-weight: 600 !important; 242 color: #333 !important; 243 margin-left: 2px !important; 244 } 245 .persian-calendar-time-box { 246 display: inline-flex !important; 247 align-items: center !important; 248 border: 1px solid #ccc !important; 249 border-radius: 6px !important; 250 background: #fff !important; 251 overflow: hidden !important; 252 height: 32px !important; 253 direction: ltr !important; 254 } 255 .persian-calendar-time-input { 256 width: 36px !important; 257 height: 100% !important; 258 border: none !important; 259 outline: none !important; 260 background: transparent !important; 261 text-align: center !important; 262 font-size: 14px !important; 263 font-weight: 600 !important; 264 color: #1e1e1e !important; 265 font-family: inherit !important; 266 padding: 0 !important; 267 margin: 0 !important; 268 -moz-appearance: textfield !important; 269 } 270 .persian-calendar-time-input::-webkit-outer-spin-button, 271 .persian-calendar-time-input::-webkit-inner-spin-button { 272 -webkit-appearance: none !important; 273 margin: 0 !important; 274 } 275 .persian-calendar-time-arrows { 276 display: flex !important; 277 flex-direction: column !important; 278 height: 100% !important; 279 width: 18px !important; 280 border-left: 1px solid #eee !important; 281 background: #f9f9f9 !important; 282 } 283 .persian-calendar-time-arrow { 284 flex: 1 !important; 285 border: none !important; 286 background: transparent !important; 287 cursor: pointer !important; 288 font-size: 8px !important; 289 color: #555 !important; 290 display: flex !important; 291 align-items: center !important; 292 justify-content: center !important; 293 padding: 0 !important; 294 margin: 0 !important; 295 line-height: 1 !important; 296 user-select: none !important; 297 transition: background-color 0.1s ease, color 0.1s ease !important; 298 } 299 .persian-calendar-time-arrow:hover { 300 background-color: #eef2ff !important; 301 color: #3858e9 !important; 302 } 303 .persian-calendar-time-arrow:active {
304 background-color: #e0e7ff !important; 305 } 306 .persian-calendar-time-separator { 307 font-weight: bold !important; 308 font-size: 16px !important; 309 color: #333 !important; 310 } 311 312 @media (max-width: 480px) { 313 .persian-calendar-wrapper { max-width: 100%; margin: 0 auto; } 314 } 315 @media (prefers-contrast: high) { 316 .persian-calendar-wrapper { border: 2px solid #000; } 317 .persian-calendar-day.selected { border: 2px solid #000; } 318 } 319 @media (prefers-reduced-motion: reduce) { 320 .persian-calendar-day, .persian-calendar-prev, .persian-calendar-next { transition: none; } 321 } 322 `;let stylesInjected=!1;function injectStyles(){if(stylesInjected)return;const styleElement=document.createElement('style');styleElement.id='persian-calendar-styles';styleElement.textContent=PERSIAN_CALENDAR_STYLES;document.head.appendChild(styleElement);stylesInjected=!0} 323const G_DAYS_IN_MONTH_NON_LEAP=[0,31,59,90,120,151,181,212,243,273,304,334];const JALALI_EPOCH_DIFFERENCE=355666;const JALALI_33_YEAR_CYCLE_DAYS=12053;const GREGORIAN_4_YEAR_CYCLE_DAYS=1461;const JALALI_YEAR_START_OFFSET=-1595;const GREGORIAN_EPOCH_DIFFERENCE=-355668;const JALALI_33_YEAR_CYCLE_LEAP_DAYS=8;const GREGORIAN_400_YEAR_CYCLE_DAYS=146097;const GREGORIAN_100_YEAR_CYCLE_DAYS=36524;const isValidGregorian=(gy,gm,gd)=>{if(!Number.isInteger(gy)||!Number.isInteger(gm)||!Number.isInteger(gd))return!1;return!(gy<1||gy>3000||gm<1||gm>12||gd<1||gd>31)};const isValidJalali=(jy,jm,jd)=>{if(!Number.isInteger(jy)||!Number.isInteger(jm)||!Number.isInteger(jd))return!1;return!(jy<1||jy>3000||jm<1||jm>12||jd<1||jd>31)};const gregorianToJalali=(gy,gm,gd)=>{if(!isValidGregorian(gy,gm,gd))return[0,0,0];const gy2=gm>2?(gy+1):gy;let days=JALALI_EPOCH_DIFFERENCE+(365*gy)+Math.floor((gy2+3)/4)-Math.floor((gy2+99)/100)+Math.floor((gy2+399)/400)+gd+G_DAYS_IN_MONTH_NON_LEAP[gm-1];let jy=JALALI_YEAR_START_OFFSET+33*Math.floor(days/JALALI_33_YEAR_CYCLE_DAYS);days%=JALALI_33_YEAR_CYCLE_DAYS;jy+=4*Math.floor(days/GREGORIAN_4_YEAR_CYCLE_DAYS);days%=GREGORIAN_4_YEAR_CYCLE_DAYS;if(days>365){jy+=Math.floor((days-1)/365);days=(days-1)%365} 324let jm,jd;if(days<186){jm=1+Math.floor(days/31);jd=1+(days%31)}else{jm=7+Math.floor((days-186)/30);jd=1+((days-186)%30)} 325return[jy,jm,jd]};const jalaliToGregorian=(jy,jm,jd)=>{if(!isValidJalali(jy,jm,jd))return[0,0,0];const jy_adj=jy+1595;let days=GREGORIAN_EPOCH_DIFFERENCE+(365*jy_adj)+(Math.floor(jy_adj/33)*JALALI_33_YEAR_CYCLE_LEAP_DAYS)+Math.floor(((jy_adj%33)+3)/4)+jd;if(jm<7)days+=(jm-1)*31;else days+=(jm-7)*30+186;let gy=400*Math.floor(days/GREGORIAN_400_YEAR_CYCLE_DAYS);days%=GREGORIAN_400_YEAR_CYCLE_DAYS;if(days>GREGORIAN_100_YEAR_CYCLE_DAYS){gy+=100*Math.floor(--days/GREGORIAN_100_YEAR_CYCLE_DAYS);days%=GREGORIAN_100_YEAR_CYCLE_DAYS;if(days>=365)days++} 326gy+=4*Math.floor(days/GREGORIAN_4_YEAR_CYCLE_DAYS);days%=GREGORIAN_4_YEAR_CYCLE_DAYS;if(days>365){gy+=Math.floor((days-1)/365);days=(days-1)%365} 327let gd=days+1;const isLeap=((gy%4===0)&&(gy%100!==0))||(gy%400===0);const G_DAYS_IN_MONTH=[0,31,isLeap?29:28,31,30,31,30,31,31,30,31,30,31];let gm;for(gm=1;gm<=12;gm++){if(gd<=G_DAYS_IN_MONTH[gm])break;gd-=G_DAYS_IN_MONTH[gm]} 328return[gy,gm,gd]};const safeParseInt=(value,defaultValue=0,min=null,max=null)=>{const parsed=parseInt(value,10);if(isNaN(parsed))return defaultValue;if(min!==null&&parsed<min)return min;if(max!==null&&parsed>max)return max;return parsed};const isValidJalaliDate=(year,month,day)=>year>=1&&year<=3000&&month>=1&&month<=12&&day>=1&&day<=31;const PERSIAN_MONTHS=['ÙØ±ÙردÛÙ','Ø§Ø±Ø¯ÛØ¨Ùشت','خرداد','ØªÛØ±','٠رداد','Ø´ÙØ±ÛÙØ±','Ù ÙØ±','آباÙ','آذر','دÛ','بÙÙ Ù','اسÙÙØ¯'];const PERSIAN_WEEKDAYS=['Ø´','Û','د','س','Ú','Ù¾','ج'];const PERSIAN_DIGITS=['Û°','Û±','Û²','Û³','Û´','Ûµ','Û¶','Û·','Û¸','Û¹'];const toPersianDigits=(str)=>String(str).replace(/[0-9]/g,(d)=>PERSIAN_DIGITS[d]);const toAsciiDigits=(str)=>String(str).replace(/[Û°-Û¹]/g,(d)=>PERSIAN_DIGITS.indexOf(d).toString());const getDaysInJalaliMonth=(jy,jm)=>{if(jm<=6)return 31;if(jm<=11)return 30;const leapYears=[1,5,9,13,17,22,26,30];return leapYears.includes(jy%33)?30:29};const IRAN_OFFSET_MINUTES=210;const getIranLocalDate=()=>{const now=new Date();const browserOffsetMinutes=-now.getTimezoneOffset();const diffMinutes=IRAN_OFFSET_MINUTES-browserOffsetMinutes;return new Date(now.getTime()+diffMinutes*60*1000)};class PersianCalendar{constructor(container,options={}){if(!container||!(container instanceof Element))throw new Error('PersianCalendar: Invalid container element');injectStyles();this.container=container;this.options={selectedDate:(options.selectedDate instanceof Date)?options.selectedDate:new Date(),enableTime:!!options.enableTime,selectedHour:typeof options.selectedHour==='number'?options.selectedHour:null,selectedMinute:typeof options.selectedMinute==='number'?options.selectedMinute:null,onDateSelect:(typeof options.onDateSelect==='function')?o
328ptions.onDateSelect:()=>{},onClose:(typeof options.onClose==='function')?options.onClose:()=>{},title:options.title||'Ø§ÙØªØ®Ø§Ø¨ ØªØ§Ø±ÛØ®',...options};let initialDate=this.options.selectedDate;const browserOffsetMinutes=-initialDate.getTimezoneOffset();const diffMinutes=IRAN_OFFSET_MINUTES-browserOffsetMinutes;initialDate=new Date(initialDate.getTime()+diffMinutes*60*1000);const[jy,jm,jd]=gregorianToJalali(initialDate.getFullYear(),initialDate.getMonth()+1,initialDate.getDate());this.currentYear=jy;this.currentMonth=jm;this.selectedDate={year:jy,month:jm,day:jd};this.selectedHour=this.options.selectedHour!==null?this.options.selectedHour:initialDate.getHours();this.selectedMinute=this.options.selectedMinute!==null?this.options.selectedMinute:initialDate.getMinutes();this.viewMode='days';this._boundClickHandler=null;this.render();this.attachEventListeners()} 329render(){this.container.textContent='';const wrapper=document.createElement('div');wrapper.className='persian-calendar-wrapper';this.container.appendChild(wrapper);this.updateCalendarView()} 330createDaysFragment(){const daysInMonth=getDaysInJalaliMonth(this.currentYear,this.currentMonth);const[gy,gm,gd]=jalaliToGregorian(this.currentYear,this.currentMonth,1);const jsDay=new Date(Date.UTC(gy,gm-1,gd)).getUTCDay();const startDay=(jsDay+1)%7;const today=getIranLocalDate();const[todayJy,todayJm,todayJd]=gregorianToJalali(today.getFullYear(),today.getMonth()+1,today.getDate());const isTodayMonth=(this.currentMonth===todayJm&&this.currentYear===todayJy);const isSelectedMonth=(this.currentMonth===this.selectedDate.month&&this.currentYear===this.selectedDate.year);const fragment=document.createDocumentFragment();for(let i=0;i<startDay;i++){const emptyDay=document.createElement('div');emptyDay.className='persian-calendar-day empty';emptyDay.setAttribute('aria-hidden','true');fragment.appendChild(emptyDay)} 331for(let day=1;day<=daysInMonth;day++){const dayElement=document.createElement('div');dayElement.className='persian-calendar-day';dayElement.setAttribute('data-day',day.toString());dayElement.setAttribute('role','gridcell');dayElement.setAttribute('tabindex','0');dayElement.textContent=toPersianDigits(day);if(isTodayMonth&&day===todayJd){dayElement.classList.add('today');dayElement.setAttribute('aria-current','date')} 332if(isSelectedMonth&&day===this.selectedDate.day){dayElement.classList.add('selected');dayElement.setAttribute('aria-selected','true')} 333fragment.appendChild(dayElement)} 334return fragment} 335attachEventListeners(){this._boundClickHandler=(e)=>{const target=e.target;if(target.matches('.persian-calendar-day:not(.empty)')){const day=safeParseInt(target.dataset.day,1,1,31);if(isValidJalaliDate(this.currentYear,this.currentMonth,day))this.selectDate(this.currentYear,this.currentMonth,day);}else if(target.matches('.persian-calendar-prev')){this.previousMonth()}else if(target.matches('.persian-calendar-next')){this.nextMonth()}else if(target.closest('.persian-calendar-month-label')){this.viewMode='months';this.updateCalendarView()}else if(target.closest('.persian-calendar-year-label')||target.closest('.persian-calendar-year-label-view')){this.viewMode='years';this.updateCalendarView()}else if(target.matches('.persian-calendar-prev-year')){this.currentYear--;this.updateCalendarView()}else if(target.matches('.persian-calendar-next-year')){this.currentYear++;this.updateCalendarView()}else if(target.closest('.persian-calendar-month-item')){const item=target.closest('.persian-calendar-month-item');this.currentMonth=parseInt(item.dataset.month,10);this.viewMode='days';this.updateCalendarView()}else if(target.matches('.persian-calendar-prev-years')){this.currentYear-=12;this.updateCalendarView()}else if(target.matches('.persian-calendar-next-years')){this.currentYear+=12;this.updateCalendarView()}else if(target.closest('.persian-calendar-year-item')){const item=target.closest('.persian-calendar-year-item');this.currentYear=parseInt(item.dataset.year,10);this.viewMode='days';this.updateCalendarView()}};this.container.addEventListener('click',this._boundClickHandler)} 336selectDate(year,month,day){this.selectedDate={year,month,day};this.updateCalendarView();this.notifyDateChange()} 337previousMonth(){this.currentMonth--;if(this.currentMonth<1){this.currentMonth=12;this.currentYear--} 338this.updateCalendarView()} 339nextMonth(){this.currentMonth++;
339if(this.currentMonth>12){this.currentMonth=1;this.currentYear++} 340this.updateCalendarView()} 341updateCalendarView(){const wrapper=this.container.querySelector('.persian-calendar-wrapper');if(!wrapper)return;wrapper.textContent='';const datePicker=document.createElement('div');datePicker.className='persian-calendar-date-picker';const nav=document.createElement('div');nav.className='persian-calendar-nav';const body=document.createElement('div');body.className='persian-calendar-body';if(this.viewMode==='days'){nav.innerHTML=` 342 <button class="persian-calendar-prev" type="button" aria-label="Ù Ø§Ù ÙØ¨Ù">â¹</button> 343 <div class="persian-calendar-header-labels"> 344 <span class="persian-calendar-month-label" role="button" tabindex="0">${PERSIAN_MONTHS[this.currentMonth - 1]}</span> 345 <span class="persian-calendar-year-label" role="button" tabindex="0">${toPersianDigits(this.currentYear)}</span> 346 </div> 347 <button class="persian-calendar-next" type="button" aria-label="٠ا٠بعد">âº</button> 348 `;body.innerHTML=` 349 <div class="persian-calendar-grid"> 350 <div class="persian-calendar-weekdays">${PERSIAN_WEEKDAYS.map(day => `<div class="persian-calendar-weekday">${day}</div>`).join('')}</div> 351 <div class="persian-calendar-days" role="grid" aria-label="تÙÙÛÙ "></div> 352 </div> 353 `;datePicker.appendChild(nav);datePicker.appendChild(body);const daysContainer=body.querySelector('.persian-calendar-days');daysContainer.appendChild(this.createDaysFragment());if(this.options.enableTime){const timePicker=document.createElement('div');timePicker.className='persian-calendar-time-picker';const hStr=String(this.selectedHour).padStart(2,'0');const mStr=String(this.selectedMinute).padStart(2,'0');timePicker.innerHTML=` 354 <span class="persian-calendar-time-title">ز٠اÙ:</span> 355 <div class="persian-calendar-time-box"> 356 <input type="number" class="persian-calendar-time-input persian-calendar-minute-input" min="0" max="59" value="${mStr}" dir="ltr"> 357 <div class="persian-calendar-time-arrows"> 358 <button type="button" class="persian-calendar-time-arrow arrow-up-minute" tabindex="-1">â²</button> 359 <button type="button" class="persian-calendar-time-arrow arrow-down-minute" tabindex="-1">â¼</button> 360 </div> 361 </div> 362 <span class="persian-calendar-time-separator">:</span> 363 <div class="persian-calendar-time-box"> 364 <input type="number" class="persian-calendar-time-input persian-calendar-hour-input" min="0" max="23" value="${hStr}" dir="ltr"> 365 <div class="persian-calendar-time-arrows"> 366 <button type="button" class="persian-calendar-time-arrow arrow-up-hour" tabindex="-1">â²</button> 367 <button type="button" class="persian-calendar-time-arrow arrow-down-hour" tabindex="-1">â¼</button> 368 </div> 369 </div> 370 `;datePicker.appendChild(timePicker);const hourInput=timePicker.querySelector('.persian-calendar-hour-input');const minInput=timePicker.querySelector('.persian-calendar-minute-input');const upHour=timePicker.querySelector('.arrow-up-hour');const downHour=timePicker.querySelector('.arrow-down-hour');const upMin=timePicker.querySelector('.arrow-up-minute');const downMin=timePicker.querySelector('.arrow-down-minute');const updateHour=(val)=>{let h=parseInt(val,10);if(isNaN(h))h=0;if(h>23)h=0;if(h<0)h=23;this.selectedHour=h;hourInput.value=String(h).padStart(2,'0');this.notifyDateChange()};const updateMin=(val)=>{let m=parseInt(val,10);if(isNaN(m))m=0;if(m>59)m=0;if(m<0)m=59;this.selectedMinute=m;minInput.value=String(m).padStart(2,'0');this.notifyDateChange()};hourInput.addEventListener('change',(e)=>updateHour(e.target.value));hourInput.addEventListener('input',(e)=>{if(e.target.value.length>=2)updateHour(e.target.value);});minInput.addEventListener('change',(e)=>updateMin(e.target.value));minInput.addEventListener('input',(e)=>{if(e.target.value.length>=2)updateMin(e.target.value);});upHour.addEventListener('click',(e)=>{e.preventDefault();e.stopPropagation();updateHour(this.selectedHour+1)});downHour.addEventListener('click',(e)=>{e.preventDefault();e.stopPropagation();updateHour(this.selectedHour-1)});upMin.addEventListener('click',(e)=>{e.preventDefault();e.stopPropagation();updateMin(this.selectedMinute+1)});downMin.addEventListener('click',(e)=>{e.preventDefault();e.stopPropagation();updateMin(this.selectedMinute-1)})}}else if(this.viewMode==='months'){nav.innerHTML=` 371 <button class="persian-calendar-prev-year" type="button" aria-label="Ø³Ø§Ù ÙØ¨Ù">â¹</button>
372 <span class="persian-calendar-year-label-view" role="button" tabindex="0">${toPersianDigits(this.currentYear)}</span> 373 <button class="persian-calendar-next-year" type="button" aria-label="سا٠بعد">âº</button> 374 `;const today=getIranLocalDate();const[todayJy,todayJm,todayJd]=gregorianToJalali(today.getFullYear(),today.getMonth()+1,today.getDate());const monthsGrid=document.createElement('div');monthsGrid.className='persian-calendar-months-grid';PERSIAN_MONTHS.forEach((m,idx)=>{const monthVal=idx+1;const monthBtn=document.createElement('div');monthBtn.className='persian-calendar-month-item';monthBtn.setAttribute('role','button');monthBtn.setAttribute('tabindex','0');monthBtn.setAttribute('data-month',monthVal);monthBtn.textContent=m;if(this.currentMonth===monthVal){monthBtn.classList.add('active')} 375if(monthVal===todayJm){monthBtn.classList.add('system-current')} 376monthsGrid.appendChild(monthBtn)});body.appendChild(monthsGrid);datePicker.appendChild(nav);datePicker.appendChild(body)}else if(this.viewMode==='years'){const startYear=this.currentYear-(this.currentYear%12);const endYear=startYear+11;nav.innerHTML=` 377 <button class="persian-calendar-prev-years" type="button" aria-label="Û±Û² Ø³Ø§Ù ÙØ¨Ù">â¹</button> 378 <span class="persian-calendar-years-range-label">${toPersianDigits(startYear)} - ${toPersianDigits(endYear)}</span> 379 <button class="persian-calendar-next-years" type="button" aria-label="Û±Û² سا٠بعد">âº</button> 380 `;const today=getIranLocalDate();const[todayJy,todayJm,todayJd]=gregorianToJalali(today.getFullYear(),today.getMonth()+1,today.getDate());const yearsGrid=document.createElement('div');yearsGrid.className='persian-calendar-years-grid';for(let y=startYear;y<=endYear;y++){const yearBtn=document.createElement('div');yearBtn.className='persian-calendar-year-item';yearBtn.setAttribute('role','button');yearBtn.setAttribute('tabindex','0');yearBtn.setAttribute('data-year',y);yearBtn.textContent=toPersianDigits(y);if(this.currentYear===y){yearBtn.classList.add('active')} 381if(y===todayJy){yearBtn.classList.add('system-current')} 382yearsGrid.appendChild(yearBtn)} 383body.appendChild(yearsGrid);datePicker.appendChild(nav);datePicker.appendChild(body)} 384wrapper.appendChild(datePicker)} 385notifyDateChange(){const[gy,gm,gd]=jalaliToGregorian(this.selectedDate.year,this.selectedDate.month,this.selectedDate.day);const hour=this.selectedHour||0;const minute=this.selectedMinute||0;const dateObj=new Date(gy,gm-1,gd,hour,minute,0);this.options.onDateSelect({jalali:{...this.selectedDate,hour,minute},gregorian:{year:gy,month:gm,day:gd,hour,minute},date:dateObj})} 386getSelectedDate(){const[gy,gm,gd]=jalaliToGregorian(this.selectedDate.year,this.selectedDate.month,this.selectedDate.day);const hour=this.selectedHour||0;const minute=this.selectedMinute||0;return new Date(gy,gm-1,gd,hour,minute,0)} 387destroy(){if(this._boundClickHandler)this.container.removeEventListener('click',this._boundClickHandler);this.container.textContent='';this.dom=null;this._boundClickHandler=null}} 388window.PersianElementorDateConverter={gregorianToJalali,jalaliToGregorian,isValidGregorian,isValidJalali,toPersianDigits,toAsciiDigits,getDaysInJalaliMonth,PERSIAN_MONTHS,PERSIAN_WEEKDAYS};window.PersianElementorCalendar=PersianCalendar})() 389;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.