const timeFormatter = { roundToStep(hours, minutes, step) { let totalMinutes = hours * 60 + minutes; totalMinutes = Math.round(totalMinutes / step) * step; let roundedHours = Math.floor(totalMinutes / 60) % 24; let roundedMinutes = totalMinutes % 60; if (totalMinutes >= 1440) roundedHours = 0; return `${String(roundedHours).padStart(2, '0')}:${String(roundedMinutes).padStart(2, '0')}`; }, parseInput(val, step) { val = val.replace(/[^0-9]/g, ''); if (!val) return ''; // 빈 값이면 에러 없이 빈 문자열 반환 let hh = 0, mm = 0; if (val.length <= 2) { hh = parseInt(val) || 0; mm = 0; } else if (val.length === 3) { hh = parseInt(val.substring(0, 2)) || 0; mm = parseInt(val.substring(2)) * 10; } else if (val.length >= 4) { hh = parseInt(val.substring(0, 2)) || 0; mm = parseInt(val.substring(2, 4)) || 0; } if (hh >= 24 || mm >= 60) return ''; return this.roundToStep(hh, mm, step); } }; window.addEventListener('DOMContentLoaded', ()=>{ document.querySelectorAll('input.time-format').forEach(el => { if (el.dataset.btnEvent) { const btn = document.createElement('button'); btn.type = 'button'; btn.innerHTML = ''; btn.className = 'ppp_btn btn-gray'; btn.onclick = function() { try { const executeAction = new Function(el.dataset.btnEvent); executeAction.call(el); } catch (e) { console.error("이벤트 실행 중 오류 발생:", e); } }; el.parentNode.insertBefore(btn, el.nextSibling); } el.addEventListener('blur', function() { if (this.value.trim() === '') return; const step = parseInt(this.dataset.step) || 5; const formatted = timeFormatter.parseInput(this.value, step); this.value = formatted; }); el.addEventListener('focus', function(){ this.select(); }) }); }); const sc_timepicker = { targetEl: null, pickerEl: null, getStyle() { return ` position: absolute; background: white; border: 1px solid #ccc; box-shadow: 0 4px 8px rgba(0,0,0,0.1); padding: 8px; z-index: 9999; border-radius: 4px; display: flex; gap: 5px; align-items: center; font-family: sans-serif; font-size: 14px; white-space:nowrap; `; }, // 🌟 달력과 동일한 완벽한 좌표 계산 함수 적용 getObjPos(obj) { let left = 0; let top = 0; let width = obj.offsetWidth; let height = obj.offsetHeight; let elem = obj; while(elem) { left += elem.offsetLeft || 0; top += elem.offsetTop || 0; elem = elem.offsetParent; } elem = obj.parentNode; while(elem && elem.tagName !== 'BODY' && elem.tagName !== 'HTML') { if(elem.scrollTop) top -= elem.scrollTop; if(elem.scrollLeft) left -= elem.scrollLeft; elem = elem.parentNode; } return { left: left, top: top, width: width, height: height }; }, open(ele) { this.targetEl = ele; this.close(); const step = parseInt(ele.dataset.step) || 30; let curH, curM; if (ele.value && ele.value.includes(':')) { const parts = ele.value.split(':'); curH = parseInt(parts[0]) || 0; curM = parseInt(parts[1]) || 0; } else { const now = new Date(); curH = now.getHours(); curM = now.getMinutes(); curM = Math.round(curM / step) * step; if (curM >= 60) { curM = 0; curH = (curH + 1) % 24; } } const isPM = curH >= 12; const displayH = curH % 12; const picker = document.createElement('div'); this.pickerEl = picker; picker.style.cssText = this.getStyle() + 'visibility:hidden;'; let html = `
`; html += ` `; html += ` : `; html += ``; html += `
`; picker.innerHTML = html; document.body.appendChild(picker); // 🌟 달력과 동일한 경계선 및 위치 계산 로직 const t_ptr = this.getObjPos(ele); const m_w = picker.offsetWidth; const m_h = picker.offsetHeight; let _t = t_ptr.top + t_ptr.height + 2; let _l = t_ptr.left; const b_w = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth); const b_h = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight); if(_t + m_h > b_h) _t = t_ptr.top - m_h - 2; if(_l + m_w > b_w) _l = b_w - m_w - 5; if(_t < 0) _t = 0; if(_l < 0) _l = 0; picker.style.left = `${_l}px`; picker.style.top = `${_t}px`; picker.style.visibility = 'visible'; setTimeout(() => { document.addEventListener('click', this.outsideClick); }, 10); }, confirm() { const ampm = parseInt(document.getElementById('tp-ampm').value); const hour = parseInt(document.getElementById('tp-hour').value); const min = parseInt(document.getElementById('tp-min').value); let finalHour = ampm + hour; if (ampm === 0 && hour === 12) finalHour = 0; // 오전 12시는 00시 if (ampm === 12 && hour === 12) finalHour = 12; // 오후 12시는 12시 const timeStr = `${String(finalHour).padStart(2, '0')}:${String(min).padStart(2, '0')}`; this.targetEl.value = timeStr; this.targetEl.dispatchEvent(new Event('change')); this.close(); }, close() { if (this.pickerEl) { this.pickerEl.remove(); this.pickerEl = null; document.removeEventListener('click', this.outsideClick); } }, outsideClick(e) { if (sc_timepicker.pickerEl && !sc_timepicker.pickerEl.contains(e.target) && e.target !== sc_timepicker.targetEl) { sc_timepicker.close(); } } };