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 = `