Spaces:
Running
Running
Upload folder using huggingface_hub
Browse files- index.html +149 -122
index.html
CHANGED
|
@@ -693,8 +693,7 @@ https://www.douyin.com/video/7123456789012345680`;
|
|
| 693 |
// Handle URL input
|
| 694 |
function handleUrlInput() {
|
| 695 |
const input = document.getElementById('urlInput').value;
|
| 696 |
-
const urlList = input.split('
|
| 697 |
-
').filter(url => url.trim());
|
| 698 |
urls = urlList;
|
| 699 |
document.getElementById('urlCount').textContent = urlList.length;
|
| 700 |
updateUrlList();
|
|
@@ -736,13 +735,12 @@ https://www.douyin.com/video/7123456789012345680`;
|
|
| 736 |
// Remove URL
|
| 737 |
function removeUrl(index) {
|
| 738 |
urls.splice(index, 1);
|
| 739 |
-
document.getElementById('urlInput').value = urls.join('
|
| 740 |
-
');
|
| 741 |
handleUrlInput();
|
| 742 |
showToast('info', 'URL Removida', 'A URL foi removida da lista');
|
| 743 |
}
|
| 744 |
|
| 745 |
-
// Process URLs
|
| 746 |
function processUrls() {
|
| 747 |
if (urls.length === 0) {
|
| 748 |
showToast('warning', 'Sem URLs', 'Adicione algumas URLs Douyin para processar');
|
|
@@ -752,14 +750,23 @@ https://www.douyin.com/video/7123456789012345680`;
|
|
| 752 |
// Mark all as processing
|
| 753 |
document.querySelectorAll('.url-item').forEach(item => {
|
| 754 |
item.classList.add('processing');
|
| 755 |
-
item.querySelector('.status-badge')
|
| 756 |
-
|
|
|
|
|
|
|
|
|
|
| 757 |
});
|
| 758 |
|
| 759 |
-
// Create result inputs
|
| 760 |
const resultsList = document.getElementById('resultsList');
|
| 761 |
-
resultsList.innerHTML =
|
| 762 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 763 |
<div class="flex items-center justify-between mb-2">
|
| 764 |
<h4 class="font-semibold text-sm">URL ${index + 1}</h4>
|
| 765 |
<span class="text-xs text-gray-500">${url}</span>
|
|
@@ -769,25 +776,102 @@ https://www.douyin.com/video/7123456789012345680`;
|
|
| 769 |
rows="2"
|
| 770 |
placeholder="Cole o link v3 extraído aqui..."
|
| 771 |
class="w-full px-3 py-2 border border-gray-200 rounded focus:border-purple-500 focus:outline-none resize-none font-mono text-xs"
|
| 772 |
-
onchange="saveResult(${index}, this.value)"
|
| 773 |
></textarea>
|
| 774 |
<div class="flex gap-2 mt-2">
|
| 775 |
-
<button
|
| 776 |
<i class="fas fa-copy mr-1"></i>Copiar
|
| 777 |
</button>
|
| 778 |
-
<button
|
| 779 |
<i class="fas fa-download mr-1"></i>Baixar
|
| 780 |
</button>
|
| 781 |
</div>
|
| 782 |
-
|
| 783 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 784 |
|
| 785 |
showToast('info', 'URLs Processadas', 'Copie o código e execute nas páginas Douyin');
|
| 786 |
|
| 787 |
-
// Copy extraction code automatically
|
| 788 |
copyExtractionCode();
|
| 789 |
}
|
| 790 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 791 |
// Save result
|
| 792 |
function saveResult(index, value) {
|
| 793 |
if (!results[index]) {
|
|
@@ -799,10 +883,15 @@ https://www.douyin.com/video/7123456789012345680`;
|
|
| 799 |
if (value.trim()) {
|
| 800 |
// Update URL item status
|
| 801 |
const urlItem = document.querySelector(`.url-item[data-index="${index}"]`);
|
| 802 |
-
|
| 803 |
-
|
| 804 |
-
|
| 805 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 806 |
|
| 807 |
extractedCount++;
|
| 808 |
updateStats();
|
|
@@ -810,33 +899,49 @@ https://www.douyin.com/video/7123456789012345680`;
|
|
| 810 |
}
|
| 811 |
}
|
| 812 |
|
| 813 |
-
// Copy extraction code
|
| 814 |
-
function copyExtractionCode() {
|
| 815 |
-
const code = document.getElementById('extractionCode').textContent;
|
| 816 |
-
navigator.clipboard.writeText(code).then(() => {
|
| 817 |
-
showToast('success', 'Código Copiado!', 'Cole no console da página Douyin');
|
| 818 |
-
});
|
| 819 |
-
}
|
| 820 |
-
|
| 821 |
// Copy result
|
| 822 |
function copyResult(index) {
|
| 823 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 824 |
if (!value) {
|
| 825 |
showToast('warning', 'Nenhum Link', 'Cole um link v3 antes de copiar');
|
| 826 |
return;
|
| 827 |
}
|
| 828 |
-
|
| 829 |
-
|
| 830 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 831 |
}
|
| 832 |
|
| 833 |
// Download result
|
| 834 |
function downloadResult(index) {
|
| 835 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 836 |
if (!value) {
|
| 837 |
showToast('warning', 'Nenhum Link', 'Cole um link v3 antes de baixar');
|
| 838 |
return;
|
| 839 |
}
|
|
|
|
| 840 |
const blob = new Blob([value], { type: 'text/plain' });
|
| 841 |
const url = URL.createObjectURL(blob);
|
| 842 |
const a = document.createElement('a');
|
|
@@ -849,98 +954,20 @@ https://www.douyin.com/video/7123456789012345680`;
|
|
| 849 |
|
| 850 |
// Export all results
|
| 851 |
function exportResults() {
|
| 852 |
-
|
| 853 |
-
|
| 854 |
-
return;
|
| 855 |
-
}
|
| 856 |
-
|
| 857 |
-
let exportData = results.map((result, index) => {
|
| 858 |
-
return `URL ${index + 1}: ${result.originalUrl}
|
| 859 |
-
Link v3: ${result.v3Link || 'N/A'}
|
| 860 |
-
`;
|
| 861 |
-
}).join('
|
| 862 |
-
');
|
| 863 |
-
|
| 864 |
-
const blob = new Blob([exportData], { type: 'text/plain' });
|
| 865 |
-
const url = URL.createObjectURL(blob);
|
| 866 |
-
const a = document.createElement('a');
|
| 867 |
-
a.href = url;
|
| 868 |
-
a.download = 'douyin_links_export.txt';
|
| 869 |
-
a.click();
|
| 870 |
-
URL.revokeObjectURL(url);
|
| 871 |
-
showToast('success', 'Exportação Concluída', 'Resultados exportados com sucesso');
|
| 872 |
-
}
|
| 873 |
-
|
| 874 |
-
// Copy all links
|
| 875 |
-
function copyAllLinks() {
|
| 876 |
-
if (results.length === 0) {
|
| 877 |
showToast('warning', 'Sem Resultados', 'Nenhum link v3 foi extraído ainda');
|
| 878 |
return;
|
| 879 |
}
|
| 880 |
|
| 881 |
-
|
| 882 |
-
|
| 883 |
-
|
| 884 |
-
|
| 885 |
-
|
| 886 |
-
|
| 887 |
-
|
| 888 |
-
return;
|
| 889 |
-
}
|
| 890 |
-
|
| 891 |
-
navigator.clipboard.writeText(allLinks).then(() => {
|
| 892 |
-
showToast('success', 'Todos os Links Copiados!', `${results.length} links copiados para área de transferência`);
|
| 893 |
});
|
| 894 |
-
}
|
| 895 |
-
|
| 896 |
-
// Download all links
|
| 897 |
-
function downloadAll() {
|
| 898 |
-
if (results.length === 0) {
|
| 899 |
-
showToast('warning', 'Sem Resultados', 'Nenhum link v3 foi extraído ainda');
|
| 900 |
-
return;
|
| 901 |
-
}
|
| 902 |
-
|
| 903 |
-
const allLinks = results.map((result, index) => {
|
| 904 |
-
return result.v3Link || '';
|
| 905 |
-
}).filter(link => link).join('
|
| 906 |
-
');
|
| 907 |
-
|
| 908 |
-
if (!allLinks) {
|
| 909 |
-
showToast('warning', 'Nenhum Link', 'Nenhum link v3 disponível para baixar');
|
| 910 |
-
return;
|
| 911 |
-
}
|
| 912 |
|
| 913 |
-
|
| 914 |
-
|
| 915 |
-
const a = document.createElement('a');
|
| 916 |
-
a.href = url;
|
| 917 |
-
a.download = 'douyin_all_links.txt';
|
| 918 |
-
a.click();
|
| 919 |
-
URL.revokeObjectURL(url);
|
| 920 |
-
showToast('success', 'Download Iniciado', 'Todos os links foram baixados');
|
| 921 |
-
}
|
| 922 |
-
|
| 923 |
-
// Clear all results
|
| 924 |
-
function clearResults() {
|
| 925 |
-
results = [];
|
| 926 |
-
extractedCount = 0;
|
| 927 |
-
updateStats();
|
| 928 |
-
document.getElementById('resultsList').innerHTML = `
|
| 929 |
-
<div class="text-center py-8 text-gray-400">
|
| 930 |
-
<i class="fas fa-clipboard-list text-4xl mb-2"></i>
|
| 931 |
-
<p class="text-sm">Nenhum resultado ainda</p>
|
| 932 |
-
<p class="text-xs mt-1">Processando URLs e cole os links v3 aqui</p>
|
| 933 |
-
</div>
|
| 934 |
-
`;
|
| 935 |
-
// Reset URL statuses
|
| 936 |
-
document.querySelectorAll('.url-item').forEach(item => {
|
| 937 |
-
item.classList.remove('processing', 'success');
|
| 938 |
-
item.querySelector('.status-badge').className = 'status-badge text-xs px-2 py-1 rounded-full bg-gray-200 text-gray-600';
|
| 939 |
-
item.querySelector('.status-badge').textContent = 'Pendente';
|
| 940 |
-
});
|
| 941 |
-
showToast('info', 'Resultados Limpos', 'Todos os resultados foram removidos');
|
| 942 |
-
}
|
| 943 |
-
</script>
|
| 944 |
-
</body>
|
| 945 |
-
|
| 946 |
-
</html>```
|
|
|
|
| 693 |
// Handle URL input
|
| 694 |
function handleUrlInput() {
|
| 695 |
const input = document.getElementById('urlInput').value;
|
| 696 |
+
const urlList = input.split('\n').filter(url => url.trim());
|
|
|
|
| 697 |
urls = urlList;
|
| 698 |
document.getElementById('urlCount').textContent = urlList.length;
|
| 699 |
updateUrlList();
|
|
|
|
| 735 |
// Remove URL
|
| 736 |
function removeUrl(index) {
|
| 737 |
urls.splice(index, 1);
|
| 738 |
+
document.getElementById('urlInput').value = urls.join('\n');
|
|
|
|
| 739 |
handleUrlInput();
|
| 740 |
showToast('info', 'URL Removida', 'A URL foi removida da lista');
|
| 741 |
}
|
| 742 |
|
| 743 |
+
// Process URLs - CORRECTED VERSION
|
| 744 |
function processUrls() {
|
| 745 |
if (urls.length === 0) {
|
| 746 |
showToast('warning', 'Sem URLs', 'Adicione algumas URLs Douyin para processar');
|
|
|
|
| 750 |
// Mark all as processing
|
| 751 |
document.querySelectorAll('.url-item').forEach(item => {
|
| 752 |
item.classList.add('processing');
|
| 753 |
+
const badge = item.querySelector('.status-badge');
|
| 754 |
+
if (badge) {
|
| 755 |
+
badge.className = 'status-badge text-xs px-2 py-1 rounded-full bg-yellow-100 text-yellow-600';
|
| 756 |
+
badge.textContent = 'Processando';
|
| 757 |
+
}
|
| 758 |
});
|
| 759 |
|
| 760 |
+
// Create result inputs with proper event binding
|
| 761 |
const resultsList = document.getElementById('resultsList');
|
| 762 |
+
resultsList.innerHTML = '';
|
| 763 |
+
|
| 764 |
+
urls.forEach((url, index) => {
|
| 765 |
+
const resultDiv = document.createElement('div');
|
| 766 |
+
resultDiv.className = 'bg-gray-50 p-4 rounded-lg slide-in';
|
| 767 |
+
resultDiv.style.animationDelay = `${index * 0.1}s`;
|
| 768 |
+
|
| 769 |
+
resultDiv.innerHTML = `
|
| 770 |
<div class="flex items-center justify-between mb-2">
|
| 771 |
<h4 class="font-semibold text-sm">URL ${index + 1}</h4>
|
| 772 |
<span class="text-xs text-gray-500">${url}</span>
|
|
|
|
| 776 |
rows="2"
|
| 777 |
placeholder="Cole o link v3 extraído aqui..."
|
| 778 |
class="w-full px-3 py-2 border border-gray-200 rounded focus:border-purple-500 focus:outline-none resize-none font-mono text-xs"
|
|
|
|
| 779 |
></textarea>
|
| 780 |
<div class="flex gap-2 mt-2">
|
| 781 |
+
<button data-action="copy" data-index="${index}" class="flex-1 px-3 py-1 bg-green-100 hover:bg-green-200 text-green-700 rounded text-xs transition">
|
| 782 |
<i class="fas fa-copy mr-1"></i>Copiar
|
| 783 |
</button>
|
| 784 |
+
<button data-action="download" data-index="${index}" class="flex-1 px-3 py-1 bg-purple-100 hover:bg-purple-200 text-purple-700 rounded text-xs transition">
|
| 785 |
<i class="fas fa-download mr-1"></i>Baixar
|
| 786 |
</button>
|
| 787 |
</div>
|
| 788 |
+
`;
|
| 789 |
+
|
| 790 |
+
resultsList.appendChild(resultDiv);
|
| 791 |
+
});
|
| 792 |
+
|
| 793 |
+
// Add event listeners for dynamically created buttons
|
| 794 |
+
resultsList.addEventListener('click', handleResultButtonClick);
|
| 795 |
|
| 796 |
showToast('info', 'URLs Processadas', 'Copie o código e execute nas páginas Douyin');
|
| 797 |
|
| 798 |
+
// Copy extraction code automatically with enhanced error handling
|
| 799 |
copyExtractionCode();
|
| 800 |
}
|
| 801 |
|
| 802 |
+
// Handle result button clicks - CORRECTED VERSION
|
| 803 |
+
function handleResultButtonClick(event) {
|
| 804 |
+
const button = event.target.closest('button[data-action]');
|
| 805 |
+
if (!button) return;
|
| 806 |
+
|
| 807 |
+
const action = button.getAttribute('data-action');
|
| 808 |
+
const index = parseInt(button.getAttribute('data-index'));
|
| 809 |
+
|
| 810 |
+
event.preventDefault();
|
| 811 |
+
event.stopPropagation();
|
| 812 |
+
|
| 813 |
+
switch (action) {
|
| 814 |
+
case 'copy':
|
| 815 |
+
copyResult(index);
|
| 816 |
+
break;
|
| 817 |
+
case 'download':
|
| 818 |
+
downloadResult(index);
|
| 819 |
+
break;
|
| 820 |
+
}
|
| 821 |
+
}
|
| 822 |
+
|
| 823 |
+
// Enhanced copy extraction code - CORRECTED VERSION
|
| 824 |
+
function copyExtractionCode() {
|
| 825 |
+
const preElement = document.getElementById('extractionCode');
|
| 826 |
+
if (!preElement) {
|
| 827 |
+
showToast('error', 'Erro', 'Elemento de código não encontrado');
|
| 828 |
+
return;
|
| 829 |
+
}
|
| 830 |
+
|
| 831 |
+
// Get the exact text content
|
| 832 |
+
const codeText = preElement.textContent;
|
| 833 |
+
|
| 834 |
+
// Enhanced clipboard API with fallback
|
| 835 |
+
if (navigator.clipboard && window.isSecureContext) {
|
| 836 |
+
navigator.clipboard.writeText(codeText)
|
| 837 |
+
.then(() => {
|
| 838 |
+
showToast('success', 'Código Copiado!', 'Cole no console da página Douyin');
|
| 839 |
+
})
|
| 840 |
+
.catch(err => {
|
| 841 |
+
console.error('Clipboard API failed:', err);
|
| 842 |
+
fallbackCopyText(codeText);
|
| 843 |
+
});
|
| 844 |
+
} else {
|
| 845 |
+
fallbackCopyText(codeText);
|
| 846 |
+
}
|
| 847 |
+
}
|
| 848 |
+
|
| 849 |
+
// Fallback copy method
|
| 850 |
+
function fallbackCopyText(text) {
|
| 851 |
+
try {
|
| 852 |
+
const textArea = document.createElement('textarea');
|
| 853 |
+
textArea.value = text;
|
| 854 |
+
textArea.style.position = 'fixed';
|
| 855 |
+
textArea.style.left = '-999999px';
|
| 856 |
+
textArea.style.top = '-999999px';
|
| 857 |
+
document.body.appendChild(textArea);
|
| 858 |
+
textArea.focus();
|
| 859 |
+
textArea.select();
|
| 860 |
+
|
| 861 |
+
const successful = document.execCommand('copy');
|
| 862 |
+
textArea.remove();
|
| 863 |
+
|
| 864 |
+
if (successful) {
|
| 865 |
+
showToast('success', 'Código Copiado!', 'Método alternativo usado - cole no console da página Douyin');
|
| 866 |
+
} else {
|
| 867 |
+
throw new Error('Fallback copy failed');
|
| 868 |
+
}
|
| 869 |
+
} catch (err) {
|
| 870 |
+
console.error('All copy methods failed:', err);
|
| 871 |
+
showToast('error', 'Falha ao Copiar', 'Não foi possível copiar o código automaticamente. Copie manualmente.');
|
| 872 |
+
}
|
| 873 |
+
}
|
| 874 |
+
|
| 875 |
// Save result
|
| 876 |
function saveResult(index, value) {
|
| 877 |
if (!results[index]) {
|
|
|
|
| 883 |
if (value.trim()) {
|
| 884 |
// Update URL item status
|
| 885 |
const urlItem = document.querySelector(`.url-item[data-index="${index}"]`);
|
| 886 |
+
if (urlItem) {
|
| 887 |
+
urlItem.classList.remove('processing');
|
| 888 |
+
urlItem.classList.add('success');
|
| 889 |
+
const badge = urlItem.querySelector('.status-badge');
|
| 890 |
+
if (badge) {
|
| 891 |
+
badge.className = 'status-badge text-xs px-2 py-1 rounded-full bg-green-100 text-green-600';
|
| 892 |
+
badge.textContent = 'Extraído';
|
| 893 |
+
}
|
| 894 |
+
}
|
| 895 |
|
| 896 |
extractedCount++;
|
| 897 |
updateStats();
|
|
|
|
| 899 |
}
|
| 900 |
}
|
| 901 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 902 |
// Copy result
|
| 903 |
function copyResult(index) {
|
| 904 |
+
const textarea = document.getElementById(`result-${index}`);
|
| 905 |
+
if (!textarea) {
|
| 906 |
+
showToast('error', 'Erro', 'Campo de resultado não encontrado');
|
| 907 |
+
return;
|
| 908 |
+
}
|
| 909 |
+
|
| 910 |
+
const value = textarea.value.trim();
|
| 911 |
if (!value) {
|
| 912 |
showToast('warning', 'Nenhum Link', 'Cole um link v3 antes de copiar');
|
| 913 |
return;
|
| 914 |
}
|
| 915 |
+
|
| 916 |
+
// Enhanced clipboard API with fallback
|
| 917 |
+
if (navigator.clipboard && window.isSecureContext) {
|
| 918 |
+
navigator.clipboard.writeText(value)
|
| 919 |
+
.then(() => {
|
| 920 |
+
showToast('success', 'Link Copiado!', `Link da URL ${index + 1} copiado para área de transferência`);
|
| 921 |
+
})
|
| 922 |
+
.catch(err => {
|
| 923 |
+
console.error('Clipboard failed:', err);
|
| 924 |
+
fallbackCopyText(value);
|
| 925 |
+
});
|
| 926 |
+
} else {
|
| 927 |
+
fallbackCopyText(value);
|
| 928 |
+
}
|
| 929 |
}
|
| 930 |
|
| 931 |
// Download result
|
| 932 |
function downloadResult(index) {
|
| 933 |
+
const textarea = document.getElementById(`result-${index}`);
|
| 934 |
+
if (!textarea) {
|
| 935 |
+
showToast('error', 'Erro', 'Campo de resultado não encontrado');
|
| 936 |
+
return;
|
| 937 |
+
}
|
| 938 |
+
|
| 939 |
+
const value = textarea.value.trim();
|
| 940 |
if (!value) {
|
| 941 |
showToast('warning', 'Nenhum Link', 'Cole um link v3 antes de baixar');
|
| 942 |
return;
|
| 943 |
}
|
| 944 |
+
|
| 945 |
const blob = new Blob([value], { type: 'text/plain' });
|
| 946 |
const url = URL.createObjectURL(blob);
|
| 947 |
const a = document.createElement('a');
|
|
|
|
| 954 |
|
| 955 |
// Export all results
|
| 956 |
function exportResults() {
|
| 957 |
+
const allResults = Array.from(document.querySelectorAll('[id^="result-"]'));
|
| 958 |
+
if (allResults.length === 0) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 959 |
showToast('warning', 'Sem Resultados', 'Nenhum link v3 foi extraído ainda');
|
| 960 |
return;
|
| 961 |
}
|
| 962 |
|
| 963 |
+
let exportData = '';
|
| 964 |
+
allResults.forEach((textarea, index) => {
|
| 965 |
+
const value = textarea.value.trim();
|
| 966 |
+
if (value) {
|
| 967 |
+
const url = urls[index] || 'Unknown URL';
|
| 968 |
+
exportData += `URL ${index + 1}: ${url}\nLink v3: ${value}\n\n`;
|
| 969 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 970 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 971 |
|
| 972 |
+
if (!exportData.trim()) {
|
| 973 |
+
showToast('warning
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|