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>
|