Spaces:
Paused
Paused
| // راهاندازی اولیه و هندلرهای رویداد اصلی | |
| window.onload = function() { | |
| startAnim('#animPreviewStatic'); | |
| initDB().then(() => { | |
| loadHome(); | |
| }); | |
| syncModeButtons(); | |
| initLanguagesList(); | |
| applyDemoLanguage('fa'); | |
| const timeline = document.getElementById('timelineScroll'); | |
| timeline.addEventListener('touchstart', () => { v.pause(); togglePlayIcon(false); }, {passive: true}); | |
| timeline.addEventListener('mousedown', () => { v.pause(); togglePlayIcon(false); }); | |
| // هماهنگسازی حرکت انیمیشن ویژوالایزرها با وضعیت پخش و توقف ویدیو | |
| v.addEventListener('play', () => { | |
| const vBars = tEl.querySelectorAll('.v-bar'); | |
| vBars.forEach(b => b.style.animationPlayState = 'running'); | |
| }); | |
| v.addEventListener('pause', () => { | |
| const vBars = tEl.querySelectorAll('.v-bar'); | |
| vBars.forEach(b => b.style.animationPlayState = 'paused'); | |
| }); | |
| }; | |
| window.onresize = fit; | |
| // --- موتور زمانسنج هوشمند (High-Precision Sync) جایگزین v.ontimeupdate --- | |
| function runFastSync() { | |
| // فقط وقتی ویدیو در حال پخش است یا دستی جابجا میشود اجرا شود | |
| if (!v.paused || manualOverride) { | |
| const cur = v.currentTime; | |
| const timeDisplay = document.getElementById('globalTimeDisplay'); | |
| if (timeDisplay) { | |
| timeDisplay.innerText = `${formatTimeSimple(cur)} / ${formatTimeSimple(v.duration || 0)}`; | |
| } | |
| if (!v.paused) manualOverride = false; | |
| let foundWord = false; | |
| // اضافه کردن 0.1 ثانیه تلورانس برای دیده شدن کلمات آخر جمله | |
| for (let s = 0; s < state.segs.length; s++) { | |
| const seg = state.segs[s]; | |
| if (cur >= seg.start && cur < (seg.end + 0.1)) { | |
| if (seg.words) { | |
| for (let w = 0; w < seg.words.length; w++) { | |
| const word = seg.words[w]; | |
| // بررسی فعال بودن کلمه با دقت بالا و تلورانس شروع و پایان | |
| if (cur >= (word.start - 0.02) && cur < (word.end + 0.05)) { | |
| const uid = `${s}-${w}`; | |
| if (activeWordId !== uid) { | |
| activeWordId = uid; | |
| document.querySelectorAll('.word-chip').forEach(c => c.classList.remove('active')); | |
| const el = document.getElementById(`w-${uid}`); | |
| if(el) { | |
| el.classList.add('active'); | |
| if(!manualOverride) el.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); | |
| } | |
| updateSplitButton(); | |
| } | |
| foundWord = true; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| if (foundWord) break; | |
| } | |
| if (!foundWord && !manualOverride) { | |
| document.querySelectorAll('.word-chip').forEach(c => c.classList.remove('active')); | |
| activeWordId = null; | |
| const toolbar = document.getElementById('toolbar'); | |
| if (toolbar) toolbar.classList.remove('show'); | |
| } | |
| updateOverlayContent(cur); | |
| } | |
| // تکرار مداوم برای دقت ۶۰ فریم در ثانیه | |
| requestAnimationFrame(runFastSync); | |
| } | |
| // شروع به کار موتور همزمان با لود شدن صفحه | |
| requestAnimationFrame(runFastSync); | |
| // --- سیستم لمسی --- | |
| // ۱. ساخت کادر نامرئی بزرگ برای راحتتر گرفتن متن و کادر سفید هنگام لمس | |
| const touchAreaStyle = document.createElement('style'); | |
| touchAreaStyle.innerHTML = ` | |
| /* ایجاد یک حاشیه نامرئی ۱۰۰ پیکسلی دور متن تا کاربر راحتتر آن را بگیرد */ | |
| #activeText::before { | |
| content: ''; | |
| position: absolute; | |
| top: -100px; bottom: -100px; left: -100px; right: -100px; | |
| z-index: 999; | |
| } | |
| /* مستطیل سفید که هنگام جابجایی ظاهر میشود */ | |
| #activeText.is-dragging { | |
| outline: 2px solid #ffffff; | |
| outline-offset: 20px; | |
| border-radius: 8px; | |
| transition: outline 0.1s; | |
| } | |
| `; | |
| document.head.appendChild(touchAreaStyle); | |
| // قفل کردن رفتار پیشفرض مرورگر روی این المان تا صفحه اسکرول نشود | |
| tEl.style.touchAction = 'none'; | |
| tEl.addEventListener('touchstart', (e) => { | |
| // متوقف کردن ویدیو در صورت پخش بودن به محض لمس کادر | |
| if (v && !v.paused) { | |
| v.pause(); | |
| if (typeof togglePlayIcon === 'function') togglePlayIcon(false); | |
| } | |
| // غیرفعال کردن اکشنهای بومی مرورگر از ریشه لمس برای فعالسازی زوم جاوااسکریپتی | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| if(e.touches.length === 1) { | |
| touchMode = 'drag'; | |
| initialY = e.touches[0].clientY; | |
| initialX = e.touches[0].clientX; | |
| initialBottom = state.st.y; | |
| initialXState = state.st.x || 0; | |
| tEl.classList.add('is-dragging'); | |
| } else if (e.touches.length === 2) { | |
| touchMode = 'pinch'; | |
| initialDist = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY); | |
| initialFontSize = state.st.fz; | |
| tEl.classList.add('is-dragging'); | |
| } | |
| }, {passive: false}); | |
| tEl.addEventListener('touchmove', (e) => { | |
| if (!touchMode) return; | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| // تشخیص هوشمند قرار گرفتن انگشت دوم در هر جای صفحه و سوییچ آنی به زوم | |
| if (e.touches.length === 2) { | |
| if (touchMode !== 'pinch') { | |
| touchMode = 'pinch'; | |
| initialDist = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY); | |
| initialFontSize = state.st.fz; | |
| } | |
| let dist = Math.hypot(e.touches[0].clientX - e.touches[1].clientX, e.touches[0].clientY - e.touches[1].clientY); | |
| if (initialDist > 0) { | |
| let newSize = initialFontSize * (dist / initialDist); | |
| state.st.fz = Math.round(Math.max(10, Math.min(150, newSize))); | |
| tEl.style.fontSize = state.st.fz + 'px'; | |
| syncUIWithState(); | |
| } | |
| } else if (touchMode === 'drag' && e.touches.length === 1) { | |
| let diffY = (initialY - e.touches[0].clientY) * 1.8; | |
| let newBottom = initialBottom + diffY; | |
| state.st.y = Math.round(newBottom); | |
| let diffX = (e.touches[0].clientX - initialX) * 1.5; | |
| let newX = initialXState + diffX; | |
| state.st.x = Math.round(newX); | |
| // اعمال تغییرات به صورت مستقیم و فوقروان | |
| tEl.style.bottom = state.st.y + 'px'; | |
| tEl.style.transform = `translateX(calc(-50% + ${state.st.x || 0}px))`; | |
| syncUIWithState(); | |
| } | |
| }, {passive: false}); | |
| tEl.addEventListener('touchend', () => { | |
| touchMode = null; | |
| // مخفی کردن کادر سفید | |
| tEl.classList.remove('is-dragging'); | |
| // ذخیره نهایی | |
| upd(); | |
| }); |