Spaces:
Running
Running
Update index.html
Browse files- index.html +229 -56
index.html
CHANGED
@@ -560,9 +560,12 @@
|
|
560 |
<!-- PDF.js library for PDF support -->
|
561 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.12.313/pdf.min.js"></script>
|
562 |
|
|
|
|
|
|
|
563 |
<script>
|
564 |
document.addEventListener('DOMContentLoaded', () => {
|
565 |
-
// Enhanced DOM Elements
|
566 |
const apiKeyInput = document.getElementById('apiKey');
|
567 |
const saveKeyBtn = document.getElementById('saveKeyBtn');
|
568 |
const uploadBtn = document.getElementById('uploadBtn');
|
@@ -588,7 +591,7 @@
|
|
588 |
const readingProgress = document.getElementById('readingProgress');
|
589 |
const currentPositionMarker = document.getElementById('currentPositionMarker');
|
590 |
|
591 |
-
// New feature elements
|
592 |
const darkModeToggle = document.getElementById('darkModeToggle');
|
593 |
const addBookmarkBtn = document.getElementById('addBookmarkBtn');
|
594 |
const showBookmarksBtn = document.getElementById('showBookmarksBtn');
|
@@ -602,6 +605,41 @@
|
|
602 |
let wordsPerMinute = 150; // Average reading speed
|
603 |
let isBookmarkPanelOpen = false;
|
604 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
605 |
// Caching & Smart Processing System
|
606 |
class TTSCache {
|
607 |
constructor() {
|
@@ -1450,51 +1488,168 @@
|
|
1450 |
// Download functionality
|
1451 |
initDownloadFeature();
|
1452 |
|
1453 |
-
|
1454 |
-
|
1455 |
-
|
1456 |
-
|
1457 |
-
|
1458 |
-
|
1459 |
-
|
1460 |
-
|
1461 |
-
|
1462 |
-
|
1463 |
-
document.getElementById('clearLibraryBtn').addEventListener('click', () => {
|
1464 |
-
if (confirm('Clear entire audio library? This cannot be undone.')) {
|
1465 |
-
audioLibrary.clearLibrary();
|
1466 |
-
showToast('Library cleared', 'success');
|
1467 |
}
|
1468 |
-
|
1469 |
-
|
1470 |
-
|
1471 |
-
|
1472 |
-
|
1473 |
-
|
1474 |
-
|
1475 |
-
|
1476 |
-
|
1477 |
-
|
1478 |
-
|
1479 |
-
|
1480 |
-
|
1481 |
-
|
1482 |
-
|
1483 |
-
|
1484 |
-
|
1485 |
-
|
1486 |
-
|
1487 |
-
|
1488 |
-
|
1489 |
-
|
1490 |
-
|
1491 |
-
|
1492 |
-
|
1493 |
-
|
1494 |
-
|
1495 |
-
|
1496 |
-
|
1497 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1498 |
}
|
1499 |
|
1500 |
// Dark Mode Implementation
|
@@ -1950,7 +2105,7 @@
|
|
1950 |
}
|
1951 |
}
|
1952 |
|
1953 |
-
// Toast notifications
|
1954 |
function showToast(message, type = 'success', duration = 3000) {
|
1955 |
const toast = document.createElement('div');
|
1956 |
toast.className = `toast ${type}`;
|
@@ -1963,10 +2118,13 @@
|
|
1963 |
}, duration);
|
1964 |
}
|
1965 |
|
1966 |
-
// Add slideOut animation
|
1967 |
-
|
1968 |
-
|
1969 |
-
|
|
|
|
|
|
|
1970 |
|
1971 |
// API Key Management
|
1972 |
saveKeyBtn.addEventListener('click', async () => {
|
@@ -2813,12 +2971,20 @@
|
|
2813 |
|
2814 |
// Word document reader (.doc/.docx)
|
2815 |
function readWordDocument(file) {
|
|
|
|
|
|
|
|
|
|
|
|
|
2816 |
const reader = new FileReader();
|
2817 |
reader.onload = async (e) => {
|
2818 |
try {
|
2819 |
// Use mammoth to convert docx to text
|
2820 |
const result = await mammoth.extractRawText({arrayBuffer: e.target.result});
|
2821 |
setDocumentContent(result.value);
|
|
|
|
|
2822 |
|
2823 |
if (result.messages.length > 0) {
|
2824 |
console.warn('Word processing warnings:', result.messages);
|
@@ -2826,10 +2992,12 @@
|
|
2826 |
} catch (error) {
|
2827 |
console.error('Error reading Word document:', error);
|
2828 |
showToast('Error reading Word document. Please try saving as a different format.', 'error');
|
|
|
2829 |
}
|
2830 |
};
|
2831 |
reader.onerror = () => {
|
2832 |
showToast('Error reading Word document', 'error');
|
|
|
2833 |
};
|
2834 |
reader.readAsArrayBuffer(file);
|
2835 |
}
|
@@ -3290,12 +3458,17 @@
|
|
3290 |
return SmartTextProcessor.optimizeChunks(text, maxChunkSize);
|
3291 |
}
|
3292 |
|
3293 |
-
// Event listeners
|
3294 |
-
refreshVoicesBtn.addEventListener('click', loadVoices);
|
3295 |
-
languageSelect.addEventListener('change', loadVoices);
|
3296 |
|
3297 |
-
// Initialize
|
3298 |
-
|
|
|
|
|
|
|
|
|
|
|
3299 |
});
|
3300 |
</script>
|
3301 |
</body>
|
|
|
560 |
<!-- PDF.js library for PDF support -->
|
561 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.12.313/pdf.min.js"></script>
|
562 |
|
563 |
+
<!-- Mammoth library for Word document support -->
|
564 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.4.2/mammoth.browser.min.js"></script>
|
565 |
+
|
566 |
<script>
|
567 |
document.addEventListener('DOMContentLoaded', () => {
|
568 |
+
// Enhanced DOM Elements with null checks
|
569 |
const apiKeyInput = document.getElementById('apiKey');
|
570 |
const saveKeyBtn = document.getElementById('saveKeyBtn');
|
571 |
const uploadBtn = document.getElementById('uploadBtn');
|
|
|
591 |
const readingProgress = document.getElementById('readingProgress');
|
592 |
const currentPositionMarker = document.getElementById('currentPositionMarker');
|
593 |
|
594 |
+
// New feature elements with null checks
|
595 |
const darkModeToggle = document.getElementById('darkModeToggle');
|
596 |
const addBookmarkBtn = document.getElementById('addBookmarkBtn');
|
597 |
const showBookmarksBtn = document.getElementById('showBookmarksBtn');
|
|
|
605 |
let wordsPerMinute = 150; // Average reading speed
|
606 |
let isBookmarkPanelOpen = false;
|
607 |
|
608 |
+
// Add missing functions
|
609 |
+
function formatFileSize(bytes) {
|
610 |
+
if (bytes === 0) return '0 Bytes';
|
611 |
+
const k = 1024;
|
612 |
+
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
613 |
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
614 |
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
615 |
+
}
|
616 |
+
|
617 |
+
function updateBookmarkCount() {
|
618 |
+
if (bookmarkCount) {
|
619 |
+
bookmarkCount.textContent = bookmarks.length.toString();
|
620 |
+
}
|
621 |
+
}
|
622 |
+
|
623 |
+
function removeBookmark(id) {
|
624 |
+
bookmarks = bookmarks.filter(b => b.id !== id);
|
625 |
+
localStorage.setItem('bookmarks', JSON.stringify(bookmarks));
|
626 |
+
renderBookmarks();
|
627 |
+
updateBookmarkCount();
|
628 |
+
showToast('Bookmark removed', 'success', 1000);
|
629 |
+
}
|
630 |
+
|
631 |
+
// Voice preset functions (needed for onclick handlers)
|
632 |
+
window.voicePresets = {
|
633 |
+
loadPreset: function(id) {
|
634 |
+
console.log('Voice preset loading not yet implemented');
|
635 |
+
showToast('Voice presets feature coming soon', 'info');
|
636 |
+
},
|
637 |
+
deletePreset: function(id) {
|
638 |
+
console.log('Voice preset deletion not yet implemented');
|
639 |
+
showToast('Voice presets feature coming soon', 'info');
|
640 |
+
}
|
641 |
+
};
|
642 |
+
|
643 |
// Caching & Smart Processing System
|
644 |
class TTSCache {
|
645 |
constructor() {
|
|
|
1488 |
// Download functionality
|
1489 |
initDownloadFeature();
|
1490 |
|
1491 |
+
// Enhanced Initialize function with proper error handling
|
1492 |
+
function init() {
|
1493 |
+
try {
|
1494 |
+
// Load saved API key
|
1495 |
+
if (apiKey) {
|
1496 |
+
apiKeyInput.value = apiKey;
|
1497 |
+
// Only load voices if we have elements
|
1498 |
+
if (voiceGrid && languageSelect) {
|
1499 |
+
loadVoices();
|
1500 |
+
}
|
|
|
|
|
|
|
|
|
1501 |
}
|
1502 |
+
|
1503 |
+
// Set PDF.js worker path
|
1504 |
+
if (typeof pdfjsLib !== 'undefined') {
|
1505 |
+
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.12.313/pdf.worker.min.js';
|
1506 |
+
}
|
1507 |
+
|
1508 |
+
// Initialize AudioContext on user interaction
|
1509 |
+
document.addEventListener('click', initAudioContext, { once: true });
|
1510 |
+
|
1511 |
+
// API Key visibility toggle
|
1512 |
+
const toggleKeyVisibility = document.getElementById('toggleKeyVisibility');
|
1513 |
+
const eyeIcon = document.getElementById('eyeIcon');
|
1514 |
+
|
1515 |
+
if (toggleKeyVisibility && eyeIcon && apiKeyInput) {
|
1516 |
+
toggleKeyVisibility.addEventListener('click', () => {
|
1517 |
+
if (apiKeyInput.type === 'password') {
|
1518 |
+
apiKeyInput.type = 'text';
|
1519 |
+
eyeIcon.className = 'fas fa-eye-slash';
|
1520 |
+
} else {
|
1521 |
+
apiKeyInput.type = 'password';
|
1522 |
+
eyeIcon.className = 'fas fa-eye';
|
1523 |
+
}
|
1524 |
+
});
|
1525 |
+
}
|
1526 |
+
|
1527 |
+
// Dark mode initialization
|
1528 |
+
if (darkModeToggle) {
|
1529 |
+
initDarkMode();
|
1530 |
+
}
|
1531 |
+
|
1532 |
+
// Keyboard shortcuts
|
1533 |
+
initKeyboardShortcuts();
|
1534 |
+
|
1535 |
+
// Bookmark system
|
1536 |
+
if (addBookmarkBtn && showBookmarksBtn) {
|
1537 |
+
initBookmarkSystem();
|
1538 |
+
}
|
1539 |
+
|
1540 |
+
// Progress indicator
|
1541 |
+
if (progressContainer) {
|
1542 |
+
initProgressIndicator();
|
1543 |
+
}
|
1544 |
+
|
1545 |
+
// Download functionality
|
1546 |
+
if (downloadBtn) {
|
1547 |
+
initDownloadFeature();
|
1548 |
+
}
|
1549 |
+
|
1550 |
+
// Initialize Audio Library
|
1551 |
+
const openLibraryBtn = document.getElementById('openLibraryBtn');
|
1552 |
+
const closeAudioLibrary = document.getElementById('closeAudioLibrary');
|
1553 |
+
const saveToLibraryBtn = document.getElementById('saveToLibraryBtn');
|
1554 |
+
const clearLibraryBtn = document.getElementById('clearLibraryBtn');
|
1555 |
+
const exportLibraryBtn = document.getElementById('exportLibraryBtn');
|
1556 |
+
|
1557 |
+
if (openLibraryBtn) {
|
1558 |
+
openLibraryBtn.addEventListener('click', () => {
|
1559 |
+
const panel = document.getElementById('audioLibraryPanel');
|
1560 |
+
if (panel) panel.classList.add('open');
|
1561 |
+
});
|
1562 |
+
}
|
1563 |
+
|
1564 |
+
if (closeAudioLibrary) {
|
1565 |
+
closeAudioLibrary.addEventListener('click', () => {
|
1566 |
+
const panel = document.getElementById('audioLibraryPanel');
|
1567 |
+
if (panel) panel.classList.remove('open');
|
1568 |
+
});
|
1569 |
+
}
|
1570 |
+
|
1571 |
+
if (saveToLibraryBtn) {
|
1572 |
+
saveToLibraryBtn.addEventListener('click', saveToLibrary);
|
1573 |
+
}
|
1574 |
+
|
1575 |
+
if (clearLibraryBtn) {
|
1576 |
+
clearLibraryBtn.addEventListener('click', () => {
|
1577 |
+
if (confirm('Clear entire audio library? This cannot be undone.')) {
|
1578 |
+
audioLibrary.clearLibrary();
|
1579 |
+
showToast('Library cleared', 'success');
|
1580 |
+
}
|
1581 |
+
});
|
1582 |
+
}
|
1583 |
+
|
1584 |
+
if (exportLibraryBtn) {
|
1585 |
+
exportLibraryBtn.addEventListener('click', () => {
|
1586 |
+
audioLibrary.exportLibrary();
|
1587 |
+
});
|
1588 |
+
}
|
1589 |
+
|
1590 |
+
// Initialize Budget System
|
1591 |
+
const openBudgetBtn = document.getElementById('openBudgetBtn');
|
1592 |
+
const closeBudgetPanel = document.getElementById('closeBudgetPanel');
|
1593 |
+
const monthlyBudget = document.getElementById('monthlyBudget');
|
1594 |
+
const alertThreshold = document.getElementById('alertThreshold');
|
1595 |
+
|
1596 |
+
if (openBudgetBtn) {
|
1597 |
+
openBudgetBtn.addEventListener('click', () => {
|
1598 |
+
const panel = document.getElementById('costBudgetPanel');
|
1599 |
+
if (panel) {
|
1600 |
+
panel.classList.add('open');
|
1601 |
+
budgetManager.updateBudgetDisplay();
|
1602 |
+
}
|
1603 |
+
});
|
1604 |
+
}
|
1605 |
+
|
1606 |
+
if (closeBudgetPanel) {
|
1607 |
+
closeBudgetPanel.addEventListener('click', () => {
|
1608 |
+
const panel = document.getElementById('costBudgetPanel');
|
1609 |
+
if (panel) panel.classList.remove('open');
|
1610 |
+
});
|
1611 |
+
}
|
1612 |
+
|
1613 |
+
if (monthlyBudget) {
|
1614 |
+
monthlyBudget.addEventListener('change', (e) => {
|
1615 |
+
budgetManager.setMonthlyBudget(parseFloat(e.target.value) || 0);
|
1616 |
+
});
|
1617 |
+
}
|
1618 |
+
|
1619 |
+
if (alertThreshold) {
|
1620 |
+
alertThreshold.addEventListener('input', (e) => {
|
1621 |
+
budgetManager.setAlertThreshold(parseInt(e.target.value));
|
1622 |
+
const thresholdValue = document.getElementById('alertThresholdValue');
|
1623 |
+
if (thresholdValue) {
|
1624 |
+
thresholdValue.textContent = `${e.target.value}%`;
|
1625 |
+
}
|
1626 |
+
});
|
1627 |
+
}
|
1628 |
+
|
1629 |
+
// Initialize cache system
|
1630 |
+
if (typeof ttsCache !== 'undefined') {
|
1631 |
+
ttsCache.init();
|
1632 |
+
}
|
1633 |
+
|
1634 |
+
// Update library display
|
1635 |
+
if (typeof audioLibrary !== 'undefined') {
|
1636 |
+
audioLibrary.updateLibraryDisplay();
|
1637 |
+
}
|
1638 |
+
|
1639 |
+
// Update bookmark count
|
1640 |
+
updateBookmarkCount();
|
1641 |
+
|
1642 |
+
// Cost breakdown modal
|
1643 |
+
const showCostBreakdown = document.getElementById('showCostBreakdown');
|
1644 |
+
if (showCostBreakdown) {
|
1645 |
+
showCostBreakdown.addEventListener('click', showCostBreakdownModal);
|
1646 |
+
}
|
1647 |
+
|
1648 |
+
} catch (error) {
|
1649 |
+
console.error('Error during initialization:', error);
|
1650 |
+
showToast('Some features may not work properly. Please refresh the page.', 'error');
|
1651 |
+
}
|
1652 |
+
}
|
1653 |
}
|
1654 |
|
1655 |
// Dark Mode Implementation
|
|
|
2105 |
}
|
2106 |
}
|
2107 |
|
2108 |
+
// Toast notifications function (moved up for early definition)
|
2109 |
function showToast(message, type = 'success', duration = 3000) {
|
2110 |
const toast = document.createElement('div');
|
2111 |
toast.className = `toast ${type}`;
|
|
|
2118 |
}, duration);
|
2119 |
}
|
2120 |
|
2121 |
+
// Add slideOut animation style if not exists
|
2122 |
+
if (!document.querySelector('style[data-toast-styles]')) {
|
2123 |
+
const style = document.createElement('style');
|
2124 |
+
style.setAttribute('data-toast-styles', 'true');
|
2125 |
+
style.textContent = '@keyframes slideOut { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } }';
|
2126 |
+
document.head.appendChild(style);
|
2127 |
+
}
|
2128 |
|
2129 |
// API Key Management
|
2130 |
saveKeyBtn.addEventListener('click', async () => {
|
|
|
2971 |
|
2972 |
// Word document reader (.doc/.docx)
|
2973 |
function readWordDocument(file) {
|
2974 |
+
// Check if mammoth library is available
|
2975 |
+
if (typeof mammoth === 'undefined') {
|
2976 |
+
showToast('Word document support not available. Mammoth library not loaded.', 'error');
|
2977 |
+
return;
|
2978 |
+
}
|
2979 |
+
|
2980 |
const reader = new FileReader();
|
2981 |
reader.onload = async (e) => {
|
2982 |
try {
|
2983 |
// Use mammoth to convert docx to text
|
2984 |
const result = await mammoth.extractRawText({arrayBuffer: e.target.result});
|
2985 |
setDocumentContent(result.value);
|
2986 |
+
showFileProgress(file.name, 'success');
|
2987 |
+
showToast(`Loaded Word document: ${formatFileSize(file.size)}`, 'success');
|
2988 |
|
2989 |
if (result.messages.length > 0) {
|
2990 |
console.warn('Word processing warnings:', result.messages);
|
|
|
2992 |
} catch (error) {
|
2993 |
console.error('Error reading Word document:', error);
|
2994 |
showToast('Error reading Word document. Please try saving as a different format.', 'error');
|
2995 |
+
showFileProgress(file.name, 'error');
|
2996 |
}
|
2997 |
};
|
2998 |
reader.onerror = () => {
|
2999 |
showToast('Error reading Word document', 'error');
|
3000 |
+
showFileProgress(file.name, 'error');
|
3001 |
};
|
3002 |
reader.readAsArrayBuffer(file);
|
3003 |
}
|
|
|
3458 |
return SmartTextProcessor.optimizeChunks(text, maxChunkSize);
|
3459 |
}
|
3460 |
|
3461 |
+
// Event listeners for main functionality
|
3462 |
+
if (refreshVoicesBtn) refreshVoicesBtn.addEventListener('click', loadVoices);
|
3463 |
+
if (languageSelect) languageSelect.addEventListener('change', loadVoices);
|
3464 |
|
3465 |
+
// Initialize everything when DOM is ready
|
3466 |
+
if (document.readyState === 'loading') {
|
3467 |
+
document.addEventListener('DOMContentLoaded', init);
|
3468 |
+
} else {
|
3469 |
+
// DOM is already loaded
|
3470 |
+
init();
|
3471 |
+
}
|
3472 |
});
|
3473 |
</script>
|
3474 |
</body>
|