Toowired commited on
Commit
42f611e
·
verified ·
1 Parent(s): adf79eb

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +84 -224
index.html CHANGED
@@ -560,12 +560,27 @@
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,7 +606,7 @@
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,7 +620,7 @@
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;
@@ -614,32 +629,6 @@
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,168 +1477,51 @@
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
@@ -1918,6 +1790,9 @@
1918
  showToast('Bookmark removed', 'success', 1000);
1919
  }
1920
 
 
 
 
1921
  function updateBookmarkCount() {
1922
  bookmarkCount.textContent = bookmarks.length.toString();
1923
  }
@@ -2105,7 +1980,7 @@
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,17 +1993,14 @@
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 () => {
2131
- const key = apiKeyInput.value.trim();
2132
  if (key) {
2133
  // Show loading state
2134
  saveKeyBtn.innerHTML = '<div class="loading-spinner inline-block w-4 h-4 mr-2"></div>Validating...';
@@ -2310,7 +2182,10 @@
2310
  const languageCode = languageSelect.value;
2311
 
2312
  // Clean and validate API key
2313
- const cleanApiKey = apiKey.trim();
 
 
 
2314
 
2315
  // Method 1: Try with API key in header (preferred)
2316
  let response;
@@ -2971,20 +2846,12 @@
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,12 +2859,10 @@
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,17 +3323,12 @@
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>
 
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
+ // Global error handler
566
+ window.addEventListener('error', (e) => {
567
+ console.error('Global error caught:', e.error);
568
+ console.error('Error stack:', e.error?.stack);
569
+
570
+ // Don't show toast for every error, just log them
571
+ if (e.error?.message?.includes('currentText') ||
572
+ e.error?.message?.includes('formatFileSize') ||
573
+ e.error?.message?.includes('apikey.trim')) {
574
+ console.error('Handled error:', e.error.message);
575
+ }
576
+ });
577
+
578
+ // Global unhandled promise rejection handler
579
+ window.addEventListener('unhandledrejection', (e) => {
580
+ console.error('Unhandled promise rejection:', e.reason);
581
+ e.preventDefault(); // Prevent default browser error logging
582
+ });
583
+ // Enhanced DOM Elements
584
  const apiKeyInput = document.getElementById('apiKey');
585
  const saveKeyBtn = document.getElementById('saveKeyBtn');
586
  const uploadBtn = document.getElementById('uploadBtn');
 
606
  const readingProgress = document.getElementById('readingProgress');
607
  const currentPositionMarker = document.getElementById('currentPositionMarker');
608
 
609
+ // New feature elements
610
  const darkModeToggle = document.getElementById('darkModeToggle');
611
  const addBookmarkBtn = document.getElementById('addBookmarkBtn');
612
  const showBookmarksBtn = document.getElementById('showBookmarksBtn');
 
620
  let wordsPerMinute = 150; // Average reading speed
621
  let isBookmarkPanelOpen = false;
622
 
623
+ // Utility function for file size formatting
624
  function formatFileSize(bytes) {
625
  if (bytes === 0) return '0 Bytes';
626
  const k = 1024;
 
629
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
630
  }
631
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
632
  // Caching & Smart Processing System
633
  class TTSCache {
634
  constructor() {
 
1477
  // Download functionality
1478
  initDownloadFeature();
1479
 
1480
+ // Initialize Audio Library
1481
+ document.getElementById('openLibraryBtn').addEventListener('click', () => {
1482
+ document.getElementById('audioLibraryPanel').classList.add('open');
1483
+ });
1484
+
1485
+ document.getElementById('closeAudioLibrary').addEventListener('click', () => {
1486
+ document.getElementById('audioLibraryPanel').classList.remove('open');
1487
+ });
1488
+
1489
+ document.getElementById('saveToLibraryBtn').addEventListener('click', saveToLibrary);
1490
+ document.getElementById('clearLibraryBtn').addEventListener('click', () => {
1491
+ if (confirm('Clear entire audio library? This cannot be undone.')) {
1492
+ audioLibrary.clearLibrary();
1493
+ showToast('Library cleared', 'success');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1494
  }
1495
+ });
1496
+
1497
+ document.getElementById('exportLibraryBtn').addEventListener('click', () => {
1498
+ audioLibrary.exportLibrary();
1499
+ });
1500
+
1501
+ // Initialize Budget System
1502
+ document.getElementById('openBudgetBtn').addEventListener('click', () => {
1503
+ document.getElementById('costBudgetPanel').classList.add('open');
1504
+ budgetManager.updateBudgetDisplay();
1505
+ });
1506
+
1507
+ document.getElementById('closeBudgetPanel').addEventListener('click', () => {
1508
+ document.getElementById('costBudgetPanel').classList.remove('open');
1509
+ });
1510
+
1511
+ document.getElementById('monthlyBudget').addEventListener('change', (e) => {
1512
+ budgetManager.setMonthlyBudget(parseFloat(e.target.value) || 0);
1513
+ });
1514
+
1515
+ document.getElementById('alertThreshold').addEventListener('input', (e) => {
1516
+ budgetManager.setAlertThreshold(parseInt(e.target.value));
1517
+ document.getElementById('alertThresholdValue').textContent = `${e.target.value}%`;
1518
+ });
1519
+
1520
+ // Initialize cache system
1521
+ ttsCache.init();
1522
+
1523
+ // Update library display
1524
+ audioLibrary.updateLibraryDisplay();
1525
  }
1526
 
1527
  // Dark Mode Implementation
 
1790
  showToast('Bookmark removed', 'success', 1000);
1791
  }
1792
 
1793
+ // Make removeBookmark globally accessible
1794
+ window.removeBookmark = removeBookmark;
1795
+
1796
  function updateBookmarkCount() {
1797
  bookmarkCount.textContent = bookmarks.length.toString();
1798
  }
 
1980
  }
1981
  }
1982
 
1983
+ // Toast notifications
1984
  function showToast(message, type = 'success', duration = 3000) {
1985
  const toast = document.createElement('div');
1986
  toast.className = `toast ${type}`;
 
1993
  }, duration);
1994
  }
1995
 
1996
+ // Add slideOut animation
1997
+ const style = document.createElement('style');
1998
+ style.textContent = '@keyframes slideOut { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } }';
1999
+ document.head.appendChild(style);
 
 
 
2000
 
2001
+ // API Key Management
2002
  saveKeyBtn.addEventListener('click', async () => {
2003
+ const key = apiKeyInput.value ? apiKeyInput.value.trim() : '';
2004
  if (key) {
2005
  // Show loading state
2006
  saveKeyBtn.innerHTML = '<div class="loading-spinner inline-block w-4 h-4 mr-2"></div>Validating...';
 
2182
  const languageCode = languageSelect.value;
2183
 
2184
  // Clean and validate API key
2185
+ const cleanApiKey = apiKey && apiKey.trim ? apiKey.trim() : (apiKey || '');
2186
+ if (!cleanApiKey) {
2187
+ throw new Error('API key is empty or invalid');
2188
+ }
2189
 
2190
  // Method 1: Try with API key in header (preferred)
2191
  let response;
 
2846
 
2847
  // Word document reader (.doc/.docx)
2848
  function readWordDocument(file) {
 
 
 
 
 
 
2849
  const reader = new FileReader();
2850
  reader.onload = async (e) => {
2851
  try {
2852
  // Use mammoth to convert docx to text
2853
  const result = await mammoth.extractRawText({arrayBuffer: e.target.result});
2854
  setDocumentContent(result.value);
 
 
2855
 
2856
  if (result.messages.length > 0) {
2857
  console.warn('Word processing warnings:', result.messages);
 
2859
  } catch (error) {
2860
  console.error('Error reading Word document:', error);
2861
  showToast('Error reading Word document. Please try saving as a different format.', 'error');
 
2862
  }
2863
  };
2864
  reader.onerror = () => {
2865
  showToast('Error reading Word document', 'error');
 
2866
  };
2867
  reader.readAsArrayBuffer(file);
2868
  }
 
3323
  return SmartTextProcessor.optimizeChunks(text, maxChunkSize);
3324
  }
3325
 
3326
+ // Event listeners
3327
+ refreshVoicesBtn.addEventListener('click', loadVoices);
3328
+ languageSelect.addEventListener('change', loadVoices);
3329
 
3330
+ // Initialize the application
3331
+ init();
 
 
 
 
 
3332
  });
3333
  </script>
3334
  </body>