1// ================= ÐÐСТРÐÐÐÐ ÐÐ ÐÐÐÐÐ Ð ÐÐУÐÐ ÐÐÐÐÐÐ ================= 2const POPUP_CONFIG = { 3 startHour: 9, // Ð§Ð°Ñ Ð¾ÑкÑÑÑÐ¸Ñ (0-23) 4 startMinute: 0, // ÐинÑÑа оÑкÑÑÑÐ¸Ñ (0-59) 5 durationMinutes: 1, // ÐлиÑелÑноÑÑÑ Ð¿Ð¾ÐºÐ°Ð·Ð° в минÑÑÐ°Ñ 6 enableMetronome: true, // ÐклÑÑиÑÑ Ð¼ÐµÑÑоном глобалÑно в коде (true/false) 7 soundOnByDefault: true // ÐклÑÑаÑÑ Ð»Ð¸ звÑк ÑÑÐ°Ð·Ñ Ð¿Ñи оÑкÑÑÑии (еÑли ÑазÑеÑÐ¸Ñ Ð±ÑаÑзеÑ) 8}; 9// ==================================================================== 10 11function addPopupStyle(){ 12 const styleTag = document.createElement('style'); 13 styleTag.textContent = 14`/* Фон-подложка на веÑÑ ÑкÑан */ 15.popup-overlay { 16 position: fixed; top: 0; left: 0; width: 100vw; height: 100dvh; 17 background-color: rgba(0, 0, 0, 0.75); backdrop-filter: blur(5px); 18 display: flex; justify-content: center; align-items: center; z-index: 9999; 19} 20/* Само белое окоÑко по ÑенÑÑÑ */ 21.popup-content { 22 position: relative; 23 font-size: 1.25rem; background: #ffffff; padding: 35px 30px 30px; border-radius: 12px; 24 max-width: 475px; width: 95%; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); text-align: center; 25} 26.popup-content p { padding: 5px 0px; margin: 0; line-height: 1.4; } 27.no-scroll { overflow: hidden; } 28/* ÐонÑÐµÐ¹Ð½ÐµÑ ÑаймеÑа */ 29.timer-overlay { display: flex; justify-content: center; align-items: center; margin-top: 20px; } 30.timer__items { display: flex; font-size: 2rem; font-weight: bold; } 31.timer__item { position: relative; min-width: 60px; margin-left: 15px; margin-right: 15px; padding-bottom: 20px; text-align: center; } 32.timer__item::before { 33 content: attr(data-title); display: block; position: absolute; left: 50%; bottom: 0; 34 transform: translateX(-50%); font-size: 12px; font-weight: normal; color: #666; 35} 36.timer__item:not(:last-child)::after { content: ':'; position: absolute; right: -18px; top: -2px; } 37 38/* Ðнопка ÑпÑÐ°Ð²Ð»ÐµÐ½Ð¸Ñ Ð·Ð²Ñком */ 39.popup-audio-toggle { 40 position: absolute; top: 15px; right: 15px; 41 background: none; border: none; font-size: 1.5rem; cursor: pointer; 42 padding: 5px; opacity: 0.6; transition: opacity 0.2s; 43} 44.popup-audio-toggle:hover { opacity: 1; }`; 45 46 document.head.appendChild(styleTag); 47} 48 49let countdownInterval = null; 50let audioCtx = null; 51let isSoundMuted = !POPUP_CONFIG.soundOnByDefault; 52 53/** 54 * ÐенеÑаÑÐ¸Ñ Ð·Ð²Ñка ÑÑÑка меÑÑонома без иÑполÑÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ mp3 Ñайлов 55 */ 56function playMetronomeTick() { 57 if (!POPUP_CONFIG.enableMetronome || isSoundMuted) return; 58 59 try { 60 if (!audioCtx) { 61 audioCtx = new (window.AudioContext || window.webkitAudioContext)(); 62 } 63 64 // ÐÑли бÑаÑÐ·ÐµÑ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¸Ñовал аÑдио из-за полиÑики autoplay, пÑобÑем возобновиÑÑ 65 if (audioCtx.state === 'suspended') { 66 audioCtx.resume(); 67 } 68 69 const osc = audioCtx.createOscillator(); 70 const gain = audioCtx.createGain(); 71 72 osc.connect(gain); 73 gain.connect(audioCtx.destination); 74 75 // ÐмиÑиÑÑем глÑÑ Ð¾Ð¹ деÑевÑннÑй ÑÑÑк 76 osc.type = 'triangle'; 77 osc.frequency.setValueAtTime(120, audioCtx.currentTime); // ÐÐ¸Ð·ÐºÐ°Ñ ÑаÑÑоÑа ÑÑÑка 78 79 // ÐÑÑÑÑое заÑÑÑ Ð°Ð½Ð¸Ðµ звÑка за 0.08 ÑекÑÐ½Ð´Ñ 80 gain.gain.setValueAtTime(0.6, audioCtx.currentTime); 81 gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.08); 82 83 osc.start(audioCtx.currentTime); 84 osc.stop(audioCtx.currentTime + 0.08); 85 } catch (e) { 86 console.log("ÐÑаÑÐ·ÐµÑ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¸Ñовал авÑомаÑиÑеÑкий звÑк до пеÑвого клика:", e); 87 } 88} 89 90function getPopupDeadline() { 91 const deadline = new Date();
92 deadline.setHours(POPUP_CONFIG.startHour, POPUP_CONFIG.startMinute + POPUP_CONFIG.durationMinutes, 0, 0); 93 return deadline; 94} 95 96function isItPopupTime() { 97 const now = new Date(); 98 const startTime = new Date(); 99 startTime.setHours(POPUP_CONFIG.startHour, POPUP_CONFIG.startMinute, 0, 0); 100 const endTime = getPopupDeadline(); 101 return now >= startTime && now < endTime; 102} 103 104/** 105 * Создание и оÑкÑÑÑие попапа 106 */ 107function openPopup() { 108 document.body.classList.add('no-scroll'); 109 110 const targetDate = getPopupDeadline(); 111 const timestamp = targetDate.getTime(); 112 113 const overlay = document.createElement('div'); 114 overlay.className = 'popup-overlay'; 115 overlay.innerHTML = ` 116 <div class="popup-content"> 117 <!-- Ðнопка звÑка --> 118 ${POPUP_CONFIG.enableMetronome ? `<button class="popup-audio-toggle" id="popupAudioBtn" title="Ðкл/ÐÑкл звÑк">${isSoundMuted ? 'ð' : 'ð'}</button>` : ''} 119 120 <p>Ð¨Ð°Ð½Ð¾Ð²Ð½Ñ Ð²ÑдвÑдÑваÑÑ ÑайÑÑ!</p> 121 <p>ЩоÑÐ°Ð½ÐºÑ Ð¾ <b>${String(POPUP_CONFIG.startHour).padStart(2, '0')}
121:${String(POPUP_CONFIG.startMinute).padStart(2, '0')}</b><br> ми зÑпинÑÑмоÑÑ Ð½Ð° Ñ Ð²Ð¸Ð»Ð¸Ð½Ñ.</p> 122 <p>Щоб згадаÑи ÑÐ¸Ñ ,<br> Ñ Ñо вÑддав жиÑÑÑ Ð·Ð° УкÑаÑнÑ.</p> 123 <p>Щоб подÑкÑваÑи й збеÑегÑи ÑвÑÑÐ»Ñ Ð¿Ð°Ð¼'ÑÑÑ.</p> 124 <div class="timer-overlay"> 125 <div id="timer" class="timer" data-dead-line="${timestamp}"> 126 <div class="timer__items"> 127 <div class="timer__item timer__minutes" data-title="Ñ Ð²">00</div> 128 <div class="timer__item timer__seconds" data-title="Ñек">00</div> 129 </div> 130 </div> 131 </div> 132 </div> 133 `; 134 document.body.appendChild(overlay); 135 136 const minutesEl = overlay.querySelector('.timer__minutes'); 137 const secondsEl = overlay.querySelector('.timer__seconds'); 138 const audioBtn = overlay.querySelector('#popupAudioBtn'); 139 140 // ÐеÑеклÑÑение звÑка по ÐºÐ»Ð¸ÐºÑ Ð½Ð° Ð¸ÐºÐ¾Ð½ÐºÑ Ð² попапе 141 if (audioBtn) { 142 audioBtn.addEventListener('click', () => { 143 isSoundMuted = !isSoundMuted; 144 audioBtn.textContent = isSoundMuted ? 'ð' : 'ð'; 145 146 // ÐкÑивиÑÑем конÑекÑÑ, еÑли ÑÑо бÑл пеÑвÑй клик полÑзоваÑÐµÐ»Ñ 147 if (!isSoundMuted && !audioCtx) { 148 audioCtx = new (window.AudioContext || window.webkitAudioContext)(); 149 } 150 }); 151 } 152 153 function updateTimer() { 154 const now = new Date(); 155 const diff = targetDate - now; 156 157 if (diff <= 0) { 158 closePopup(overlay); 159 return; 160 } 161 162 const minutes = Math.floor((diff / 1000 / 60) % 60); 163 const seconds = Math.floor((diff / 1000) % 60); 164 165 minutesEl.textContent = String(minutes).padStart(2, '0'); 166 secondsEl.textContent = String(seconds).padStart(2, '0'); 167 168 // ð¥ ÐаждÑй Ñик ÑаймеÑа (Ñаз в ÑекÑндÑ) воÑпÑоизводим ÑÐ´Ð°Ñ Ð¼ÐµÑÑонома 169 playMetronomeTick(); 170 } 171 172 updateTimer(); 173 countdownInterval = setInterval(updateTimer, 1000); 174 175 overlay.addEventListener('needClouse', () => { 176 closePopup(overlay); 177 }); 178 179 return overlay; 180} 181 182function closePopup(overlayElement) { 183 if (countdownInterval) clearInterval(countdownInterval); 184 if (overlayElement) overlayElement.remove(); 185 document.body.classList.remove('no-scroll'); 186 if (audioCtx) { 187 audioCtx.close(); 188 audioCtx = null; 189 } 190} 191 192function checkTimeAndRun() { 193 setInterval(() => { 194 if (isItPopupTime()) { 195 if (!document.querySelector('.popup-overlay')) { 196 openPopup(); 197 } 198 } 199 }, 1000); 200} 201 202$(function(){ 203 addPopupStyle(); 204 205 if (isItPopupTime()) { 206 openPopup(); 207 } 208 209 checkTimeAndRun(); 210}); 211
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.