`;
routineList.appendChild(li);
});
}
// --- DRAG & DROP LOGIC ---
function getDragAfterElement(container, y) {
const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')];
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect();
const offset = y - box.top - box.height / 2;
if (offset < 0 && offset > closest.offset) {
return { offset: offset, element: child };
} else {
return closest;
}
}, { offset: Number.NEGATIVE_INFINITY }).element;
}
function updateRoutineOrder() {
const newOrderIds = [...routineList.querySelectorAll('.draggable')].map(el => parseInt(el.dataset.id));
routine.sort((a, b) => newOrderIds.indexOf(a.id) - newOrderIds.indexOf(b.id));
}
// --- PLAYER LOGIC ---
function setupPlayer() {
playerState.currentPoseIndex = 0;
playerState.isRunning = false;
clearInterval(playerState.interval);
if (routine.length > 0) {
playerState.timeRemaining = routine[0].duration;
updatePlayerDisplay();
startRoutineBtn.disabled = false;
} else {
playerPoseName.textContent = 'Build a routine first.';
playerTimer.textContent = '00:00';
playerNextPose.textContent = 'Next: -';
startRoutineBtn.disabled = true;
}
}
function updatePlayerDisplay() {
const currentPose = routine[playerState.currentPoseIndex];
const nextPose = routine[playerState.currentPoseIndex + 1];
playerPoseName.textContent = currentPose ? currentPose.name : "Routine Complete!";
const minutes = Math.floor(playerState.timeRemaining / 60).toString().padStart(2, '0');
const seconds = (playerState.timeRemaining % 60).toString().padStart(2, '0');
playerTimer.textContent = `${minutes}:${seconds}`;
playerNextPose.textContent = nextPose ? `Next: ${nextPose.name}` : "Next: -";
}
function handleStartStopRoutine() {
initializeAudio();
if (playerState.isRunning) { // Pause
clearInterval(playerState.interval);
playerState.isRunning = false;
startRoutineBtn.textContent = "Resume";
} else { // Start/Resume
if (routine.length === 0) return;
playerState.isRunning = true;
startRoutineBtn.textContent = "Pause";
playerState.interval = setInterval(tick, 1000);
}
}
function tick() {
playerState.timeRemaining--;
if (playerState.timeRemaining < 0) {
if (synth) synth.triggerAttackRelease("C5", "0.5s");
playerState.currentPoseIndex++;
if (playerState.currentPoseIndex >= routine.length) {
// End of routine
clearInterval(playerState.interval);
playerState.isRunning = false;
startRoutineBtn.textContent = "Start Routine";
playerPoseName.textContent = "Routine Complete!";
return;
}
playerState.timeRemaining = routine[playerState.currentPoseIndex].duration;
}
updatePlayerDisplay();
}
function resetPlayer() {
setupPlayer();
}
// --- TAB NAVIGATION & SUMMARY ---
window.changeTab = function(tabNumber) {
if (tabNumber === currentTab) return;
if (tabNumber === 2) {
renderSummary();
setupPlayer();
}
document.getElementById(`tab-panel-${currentTab}`).classList.add('hidden');
document.getElementById(`tab-btn-${currentTab}`).classList.remove('active');
document.getElementById(`tab-btn-${currentTab}`).classList.add('inactive');
document.getElementById(`tab-panel-${tabNumber}`).classList.remove('hidden');
document.getElementById(`tab-btn-${tabNumber}`).classList.add('active');
document.getElementById(`tab-btn-${tabNumber}`).classList.remove('inactive');
currentTab = tabNumber;
updateNavButtons();
};
window.navigateTabs = function(direction) {
const newTab = direction === 'next' ? currentTab + 1 : currentTab - 1;
if (newTab >= 1 && newTab <= 2) {
changeTab(newTab);
}
};
function updateNavButtons() {
prevBtn.disabled = (currentTab === 1);
nextBtn.disabled = (currentTab === 2);
}
function renderSummary() {
summaryTableBody.innerHTML = '';
if (routine.length === 0) {
summaryTableBody.innerHTML = 'Your routine is empty. ';
totalTimeDisplay.textContent = 'Total time: 0 minutes';
return;
}
let totalSeconds = 0;
routine.forEach(pose => {
totalSeconds += pose.duration;
const row = summaryTableBody.insertRow();
row.innerHTML = `
${pose.name}
${pose.duration}s
`;
});
const totalMinutes = Math.ceil(totalSeconds / 60);
totalTimeDisplay.textContent = `Total time: ~${totalMinutes} minutes`;
}
// --- PDF GENERATION ---
function generatePdf() {
const contentToPrint = document.getElementById('pdf-content-container');
const pdfButtonWrapper = document.getElementById('pdf-button-wrapper');
if (routine.length === 0) {
alert("Routine is empty. There is nothing to download.");
return;
}
if (pdfButtonWrapper) pdfButtonWrapper.style.display = 'none';
html2canvas(contentToPrint, { scale: 2, useCORS: true, logging: false })
.then(canvas => {
if (pdfButtonWrapper) pdfButtonWrapper.style.display = 'block';
const imgData = canvas.toDataURL('image/png');
const { jsPDF } = window.jspdf;
const pdf = new jsPDF({ orientation: 'portrait', unit: 'pt', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const canvasAspectRatio = canvas.width / canvas.height;
const imgWidth = pdfWidth - 80;
const imgHeight = imgWidth / canvasAspectRatio;
pdf.addImage(imgData, 'PNG', 40, 40, imgWidth, imgHeight);
pdf.save('My-Yoga-Routine.pdf');
}).catch(err => {
if (pdfButtonWrapper) pdfButtonWrapper.style.display = 'block';
console.error("Error generating PDF:", err);
alert("Sorry, an error occurred while creating the PDF.");
});
}
// --- START THE APP ---
initialize();
});
