Files
minio-explorer/script.js
Bertrand Benjamin e097d19f07
All checks were successful
Deploy MinIO Explorer / deploy (push) Successful in 34s
fix: nothing is shown
2025-09-05 12:05:31 +02:00

870 lines
28 KiB
JavaScript

// Configuration globale
let config = {
baseUrl: '',
currentPath: ''
};
// Initialisation de l'application
document.addEventListener('DOMContentLoaded', function() {
initializeApp();
setupEventListeners();
});
function initializeApp() {
// Tentative de configuration automatique depuis l'URL courante
autoConfigureFromURL();
// Affichage du formulaire de développement
showDevForm();
}
function setupEventListeners() {
// Formulaire de développement
const urlForm = document.getElementById('url-form');
if (urlForm) {
urlForm.addEventListener('submit', handleUrlFormSubmit);
}
// Gestion de la navigation arrière/avant du navigateur
window.addEventListener('popstate', handlePopState);
// Gestionnaires pour la prévisualisation
const closePreview = document.getElementById('close-preview');
if (closePreview) {
closePreview.addEventListener('click', hidePreview);
}
const downloadFile = document.getElementById('download-file');
if (downloadFile) {
downloadFile.addEventListener('click', downloadCurrentFile);
}
}
function handlePopState(event) {
console.log('Navigation arrière/avant détectée:', event.state);
if (event.state && event.state.baseUrl) {
// Restaurer la configuration
config.baseUrl = event.state.baseUrl;
config.currentPath = event.state.path || '';
updateConfigDisplay();
// Charger le contenu sans ajouter à l'historique (pour éviter les boucles)
loadBucketContents(config.currentPath, false);
}
}
function autoConfigureFromURL() {
const currentUrl = window.location.href;
const url = new URL(currentUrl);
// Configuration simplifiée : on prend juste le base URL sans le bucket
config.baseUrl = `${url.protocol}//${url.host}${url.pathname.replace('/index.html', '')}`;
// Vérifier s'il y a un fragment d'URL pour définir le chemin initial
if (url.hash) {
const hashPath = url.hash.replace('#/', '');
config.currentPath = hashPath;
}
updateConfigDisplay();
console.log('Configuration automatique:', config);
// Charger automatiquement le contenu si on a une config valide
if (config.baseUrl) {
loadBucketContents(config.currentPath, false);
}
}
function handleUrlFormSubmit(event) {
event.preventDefault();
const urlInput = document.getElementById('minio-url');
if (!urlInput) return;
const minioUrl = urlInput.value.trim();
if (!minioUrl) {
showError('Veuillez saisir une URL MinIO');
return;
}
parseMinioURL(minioUrl);
}
function parseMinioURL(url) {
try {
const urlObj = new URL(url);
// Configuration simplifiée : on prend l'URL complète comme base
config.baseUrl = url.replace(/\/$/, ''); // Supprimer le slash final si présent
config.currentPath = '';
updateConfigDisplay();
// Ajouter l'état initial à l'historique
const initialState = { path: '', baseUrl: config.baseUrl };
history.replaceState(initialState, '', window.location.href);
loadBucketContents();
} catch (error) {
showError('URL invalide: ' + error.message);
console.error('Erreur de parsing URL:', error);
}
}
function updateConfigDisplay() {
const configInfo = document.getElementById('config-info');
if (configInfo) {
configInfo.innerHTML = `
<strong>Configuration actuelle:</strong><br>
URL de base: ${config.baseUrl}<br>
Chemin actuel: /${config.currentPath}
`;
}
}
function showDevForm() {
const devForm = document.getElementById('dev-form');
if (devForm) {
devForm.style.display = 'block';
}
}
function showError(message) {
const errorSection = document.getElementById('error-section');
const errorMessage = document.getElementById('error-message');
if (errorMessage) {
errorMessage.textContent = message;
}
if (errorSection) {
errorSection.style.display = 'block';
// Masquer l'erreur après 5 secondes
setTimeout(() => {
errorSection.style.display = 'none';
}, 5000);
}
}
function showLoading(show = true) {
const loading = document.getElementById('loading');
const table = document.getElementById('files-table');
if (loading) {
loading.style.display = show ? 'block' : 'none';
}
if (table) {
table.style.display = show ? 'none' : 'table';
}
}
function updateCurrentPathDisplay() {
const pathDisplay = document.getElementById('current-path');
if (pathDisplay) {
pathDisplay.textContent = '/' + config.currentPath;
}
}
async function loadBucketContents(path = '', pushToHistory = true) {
console.log('Debug loadBucketContents - path received:', JSON.stringify(path));
if (!config.baseUrl) {
showError('Configuration URL manquante');
return;
}
showLoading(true);
config.currentPath = path;
updateCurrentPathDisplay();
// Ajouter à l'historique du navigateur si demandé
if (pushToHistory) {
const state = { path: path, baseUrl: config.baseUrl };
const url = path ? `#/${path}` : '#/';
history.pushState(state, '', url);
}
try {
// Construction de l'URL API avec delimiter
let apiUrl = `${config.baseUrl}?list-type=2&delimiter=/`;
// Construire le prefix pour l'API MinIO
if (path) {
// Pour un sous-dossier
const cleanPath = path.startsWith('/') ? path.substring(1) : path;
const fullPrefix = cleanPath + '/';
apiUrl += `&prefix=${encodeURIComponent(fullPrefix)}`;
}
console.log('Appel API MinIO:', apiUrl);
const response = await fetch(apiUrl);
if (!response.ok) {
throw new Error(`Erreur HTTP: ${response.status}`);
}
const xmlText = await response.text();
console.log('Réponse XML reçue:', xmlText);
const contents = parseMinioXML(xmlText);
displayContents(contents);
} catch (error) {
console.error('Erreur lors du chargement:', error);
showError('Erreur de chargement: ' + error.message);
} finally {
showLoading(false);
}
}
function parseMinioXML(xmlText) {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlText, 'text/xml');
const contents = {
folders: [],
files: []
};
// Déterminer le préfixe attendu basé sur la navigation actuelle
let expectedPrefix = '';
if (config.currentPath) {
expectedPrefix = config.currentPath + '/';
}
// Parse CommonPrefixes (dossiers)
const prefixes = xmlDoc.getElementsByTagName('CommonPrefixes');
for (let prefix of prefixes) {
const prefixElement = prefix.getElementsByTagName('Prefix')[0];
if (prefixElement) {
const fullPrefix = prefixElement.textContent;
// Extraire le nom du dossier (dernière partie avant le /)
const folderName = fullPrefix.replace(/\/$/, '').split('/').pop();
if (folderName) {
// Construire le chemin pour la navigation
let folderPath;
if (config.currentPath) {
folderPath = config.currentPath + '/' + folderName;
} else {
folderPath = folderName;
}
console.log('Debug parsing - fullPrefix:', fullPrefix, 'folderName:', folderName, 'currentPath:', config.currentPath, 'folderPath:', folderPath);
contents.folders.push({
name: folderName,
path: folderPath
});
}
}
}
// Parse Contents (fichiers)
const objects = xmlDoc.getElementsByTagName('Contents');
for (let obj of objects) {
const keyElement = obj.getElementsByTagName('Key')[0];
const sizeElement = obj.getElementsByTagName('Size')[0];
const modifiedElement = obj.getElementsByTagName('LastModified')[0];
if (keyElement) {
const fullKey = keyElement.textContent;
const fileName = fullKey.split('/').pop();
// Vérifier que le fichier correspond au niveau de navigation actuel
let shouldInclude = false;
if (config.currentPath) {
// Dans un sous-dossier : vérifier que la clé commence par le bon préfixe
const currentPrefix = config.currentPath + '/';
if (fullKey.startsWith(currentPrefix)) {
// S'assurer qu'on n'affiche que les fichiers directs (pas dans des sous-sous-dossiers)
const relativePath = fullKey.replace(currentPrefix, '');
if (!relativePath.includes('/')) {
shouldInclude = true;
}
}
} else {
// À la racine : afficher tous les fichiers qui ne sont pas dans des dossiers
if (!fullKey.includes('/')) {
shouldInclude = true;
}
}
// Exclure les fichiers de l'application
if (shouldInclude && fileName && !['index.html', 'style.css', 'script.js'].includes(fileName)) {
contents.files.push({
name: fileName,
size: sizeElement ? parseInt(sizeElement.textContent) : 0,
modified: modifiedElement ? new Date(modifiedElement.textContent) : new Date(),
fullKey: fullKey
});
}
}
}
return contents;
}
function displayContents(contents) {
const tbody = document.getElementById('files-tbody');
if (!tbody) return;
tbody.innerHTML = '';
// Bouton parent directory si on n'est pas à la racine
if (config.currentPath) {
const parentPath = config.currentPath.split('/').slice(0, -1).join('/');
const parentRow = createParentRow(parentPath);
tbody.appendChild(parentRow);
}
// Afficher les dossiers
contents.folders.forEach(folder => {
const row = createFolderRow(folder);
tbody.appendChild(row);
});
// Afficher les fichiers
contents.files.forEach(file => {
const row = createFileRow(file);
tbody.appendChild(row);
});
// Afficher le tableau
const filesTable = document.getElementById('files-table');
if (filesTable) {
filesTable.style.display = 'table';
}
// Chercher et ouvrir automatiquement index.rst s'il existe
autoOpenIndexRst(contents.files);
}
function createParentRow(parentPath) {
const row = document.createElement('tr');
row.innerHTML = `
<td>
<span class="icon-parent"></span>
<a href="#" class="folder-link parent-dir" data-path="${parentPath}">..</a>
</td>
<td>-</td>
<td>-</td>
<td>-</td>
`;
const link = row.querySelector('.folder-link');
link.addEventListener('click', (e) => {
e.preventDefault();
loadBucketContents(parentPath, true);
});
return row;
}
function createFolderRow(folder) {
const row = document.createElement('tr');
row.innerHTML = `
<td>
<span class="icon-folder"></span>
<a href="#" class="folder-link" data-path="${folder.path}">${folder.name}/</a>
</td>
<td>-</td>
<td>-</td>
<td>-</td>
`;
const link = row.querySelector('.folder-link');
link.addEventListener('click', (e) => {
e.preventDefault();
loadBucketContents(folder.path, true);
});
return row;
}
function createFileRow(file) {
const row = document.createElement('tr');
const fileUrl = `${config.baseUrl}/${file.fullKey}`;
row.innerHTML = `
<td>
<span class="icon-file"></span>
<a href="#" class="file-link" data-file-url="${fileUrl}" data-file-name="${file.name}">${file.name}</a>
</td>
<td class="file-size">${formatFileSize(file.size)}</td>
<td class="file-date">${formatDate(file.modified)}</td>
<td>
<button class="copy-btn" data-url="${fileUrl}">Copier lien</button>
</td>
`;
const fileLink = row.querySelector('.file-link');
fileLink.addEventListener('click', (e) => {
e.preventDefault();
showPreview(fileUrl, file.name);
});
const copyBtn = row.querySelector('.copy-btn');
copyBtn.addEventListener('click', () => copyToClipboard(fileUrl));
return row;
}
function formatFileSize(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
}
function formatDate(date) {
return date.toLocaleString('fr-FR', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
}
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
console.log('Lien copié:', text);
// Feedback visuel temporaire
const btn = event.target;
const originalText = btn.textContent;
btn.textContent = 'Copié !';
setTimeout(() => {
btn.textContent = originalText;
}, 1000);
} catch (error) {
console.error('Erreur copie clipboard:', error);
// Fallback pour les navigateurs plus anciens
const textArea = document.createElement('textarea');
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
}
}
// Variables globales pour la prévisualisation
let currentPreviewFile = {
url: '',
name: ''
};
function detectFileType(fileName) {
const extension = fileName.toLowerCase().split('.').pop();
const fileTypes = {
images: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp'],
text: ['txt', 'json', 'xml', 'csv', 'log'],
code: ['js', 'css', 'html', 'htm', 'py', 'java', 'cpp', 'c', 'php', 'rb', 'go', 'rs', 'ts', 'jsx', 'tsx', 'vue', 'svelte', 'tex', 'latex'],
markdown: ['md', 'markdown'],
rst: ['rst', 'rest'],
pdf: ['pdf'],
video: ['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm', 'mkv'],
audio: ['mp3', 'wav', 'ogg', 'aac', 'flac', 'm4a'],
archive: ['zip', 'rar', '7z', 'tar', 'gz', 'bz2']
};
for (const [type, extensions] of Object.entries(fileTypes)) {
if (extensions.includes(extension)) {
return type;
}
}
return 'unknown';
}
function showPreview(fileUrl, fileName) {
currentPreviewFile = { url: fileUrl, name: fileName };
const previewSection = document.getElementById('preview-section');
const previewTitle = document.getElementById('preview-title');
const previewContent = document.getElementById('preview-content');
if (!previewSection || !previewTitle || !previewContent) {
console.error('Éléments de prévisualisation non trouvés');
return;
}
previewTitle.textContent = `Prévisualisation: ${fileName}`;
previewContent.innerHTML = '<div style="text-align: center; padding: 20px;">Chargement...</div>';
previewSection.style.display = 'flex';
const fileType = detectFileType(fileName);
switch (fileType) {
case 'images':
showImagePreview(fileUrl, previewContent);
break;
case 'text':
case 'code':
showTextPreview(fileUrl, previewContent);
break;
case 'markdown':
showMarkdownPreview(fileUrl, previewContent);
break;
case 'rst':
showRstPreview(fileUrl, previewContent);
break;
case 'pdf':
showPdfPreview(fileUrl, previewContent);
break;
case 'video':
showVideoPreview(fileUrl, previewContent);
break;
case 'audio':
showAudioPreview(fileUrl, previewContent);
break;
default:
showUnsupportedPreview(fileName, previewContent);
}
}
function showImagePreview(fileUrl, container) {
const img = document.createElement('img');
img.src = fileUrl;
img.alt = 'Prévisualisation image';
img.onload = () => {
container.innerHTML = '';
container.appendChild(img);
};
img.onerror = () => {
container.innerHTML = '<div class="preview-error">Erreur: Impossible de charger l\'image</div>';
};
}
function showTextPreview(fileUrl, container) {
fetch(fileUrl)
.then(response => {
if (!response.ok) {
throw new Error(`Erreur HTTP: ${response.status}`);
}
return response.text();
})
.then(text => {
const pre = document.createElement('pre');
pre.textContent = text;
container.innerHTML = '';
container.appendChild(pre);
})
.catch(error => {
console.error('Erreur chargement texte:', error);
container.innerHTML = '<div class="preview-error">Erreur: Impossible de charger le fichier texte</div>';
});
}
function showPdfPreview(fileUrl, container) {
const iframe = document.createElement('iframe');
iframe.src = fileUrl;
iframe.style.width = '100%';
iframe.style.height = '500px';
container.innerHTML = '';
container.appendChild(iframe);
}
function showVideoPreview(fileUrl, container) {
const video = document.createElement('video');
video.src = fileUrl;
video.controls = true;
video.style.maxWidth = '100%';
container.innerHTML = '';
container.appendChild(video);
}
function showAudioPreview(fileUrl, container) {
const audio = document.createElement('audio');
audio.src = fileUrl;
audio.controls = true;
audio.style.width = '100%';
container.innerHTML = '';
container.appendChild(audio);
}
function showUnsupportedPreview(fileName, container) {
const extension = fileName.toLowerCase().split('.').pop();
container.innerHTML = `
<div class="preview-error">
<p>Prévisualisation non disponible pour ce type de fichier (.${extension})</p>
<p>Utilisez le bouton "Télécharger" ci-dessous pour obtenir le fichier.</p>
</div>
`;
}
function hidePreview() {
const previewSection = document.getElementById('preview-section');
if (previewSection) {
previewSection.style.display = 'none';
}
currentPreviewFile = { url: '', name: '' };
}
function downloadCurrentFile() {
if (currentPreviewFile.url) {
const link = document.createElement('a');
link.href = currentPreviewFile.url;
link.download = currentPreviewFile.name;
link.target = '_blank';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
// Fonction pour ouvrir automatiquement index.rst s'il existe
function autoOpenIndexRst(files) {
// Chercher un fichier index.rst dans la liste
const indexRstFile = files.find(file =>
file.name.toLowerCase() === 'index.rst'
);
if (indexRstFile) {
// Construire l'URL complète du fichier
const fileUrl = `${config.baseUrl}/${indexRstFile.fullKey}`;
// Ouvrir automatiquement la prévisualisation
console.log('Auto-ouverture de index.rst trouvé:', indexRstFile.name);
showPreview(fileUrl, indexRstFile.name);
} else {
// Pas d'index.rst trouvé, fermer le preview s'il est ouvert
console.log('Aucun index.rst trouvé, fermeture du preview');
hidePreview();
}
}
// Fonction simple de parsing Markdown vers HTML
function parseMarkdown(text) {
let html = text;
// Headers
html = html.replace(/^### (.*$)/gim, '<h3>$1</h3>');
html = html.replace(/^## (.*$)/gim, '<h2>$1</h2>');
html = html.replace(/^# (.*$)/gim, '<h1>$1</h1>');
// Bold
html = html.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
html = html.replace(/__(.*?)__/g, '<strong>$1</strong>');
// Italic
html = html.replace(/\*(.*?)\*/g, '<em>$1</em>');
html = html.replace(/_(.*?)_/g, '<em>$1</em>');
// Code inline
html = html.replace(/`(.*?)`/g, '<code>$1</code>');
// Code blocks
html = html.replace(/```([\s\S]*?)```/g, '<pre><code>$1</code></pre>');
// Links
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>');
// Images
html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img alt="$1" src="$2" style="max-width: 100%; height: auto;">');
// Lists
html = html.replace(/^\* (.*$)/gim, '<li>$1</li>');
html = html.replace(/^- (.*$)/gim, '<li>$1</li>');
html = html.replace(/^\d+\. (.*$)/gim, '<li>$1</li>');
// Wrap consecutive <li> items in <ul>
html = html.replace(/(<li>.*<\/li>)/gs, (match) => {
return '<ul>' + match + '</ul>';
});
// Line breaks
html = html.replace(/\n\n/g, '</p><p>');
html = '<p>' + html + '</p>';
// Clean up empty paragraphs
html = html.replace(/<p><\/p>/g, '');
html = html.replace(/<p>(<h[1-6]>)/g, '$1');
html = html.replace(/(<\/h[1-6]>)<\/p>/g, '$1');
html = html.replace(/<p>(<ul>)/g, '$1');
html = html.replace(/(<\/ul>)<\/p>/g, '$1');
html = html.replace(/<p>(<pre>)/g, '$1');
html = html.replace(/(<\/pre>)<\/p>/g, '$1');
return html;
}
// Fonction simple de parsing reStructuredText vers HTML
function parseRst(text) {
let html = text;
// Créer un système de placeholders pour éviter les liens imbriqués
const linkPlaceholders = new Map();
let placeholderIndex = 0;
// Fonction pour créer un placeholder unique
const createLinkPlaceholder = (linkText, linkUrl) => {
const placeholder = `__LINK_PLACEHOLDER_${placeholderIndex++}__`;
linkPlaceholders.set(placeholder, `<a href="${linkUrl}" target="_blank">${linkText}</a>`);
return placeholder;
};
// Headers (RST style with underlines) - ordre important pour éviter les conflits
html = html.replace(/^(.*)\n#{3,}$/gim, '<h1>$1</h1>');
html = html.replace(/^(.*)\n={3,}$/gim, '<h2>$1</h2>');
html = html.replace(/^(.*)\n-{3,}$/gim, '<h3>$1</h3>');
html = html.replace(/^(.*)\n~{3,}$/gim, '<h4>$1</h4>');
html = html.replace(/^(.*)\n\^{3,}$/gim, '<h5>$1</h5>');
// Metadata fields
html = html.replace(/^:([^:]+):\s*(.*)$/gim, (match, key, value) => {
// Remplacer les liens par des placeholders dans les métadonnées
const processedValue = value.replace(/`([^<]+) <([^>]+)>`_/g, (match, text, url) => {
return createLinkPlaceholder(text, url);
});
return `<div class="rst-metadata"><span class="metadata-key">${key}:</span> <span class="metadata-value">${processedValue}</span></div>`;
});
// Custom directives (.. directive::)
html = html.replace(/^\.\. ([^:]+)::\s*(.*)$/gim, '<div class="rst-directive rst-directive-$1" data-directive="$1" data-content="$2">');
// Directive options (:option: value)
html = html.replace(/^ :([^:]+):\s*(.*)$/gim, (match, key, value) => {
// Remplacer les liens par des placeholders dans les options
const processedValue = value.replace(/`([^<]+) <([^>]+)>`_/g, (match, text, url) => {
return createLinkPlaceholder(text, url);
});
return `<div class="directive-option" data-option="${key}" data-value="${value}"><span class="option-key">${key}:</span> <span class="option-value">${processedValue}</span></div>`;
});
// Special handling for big_button directive
html = html.replace(/<div class="rst-directive rst-directive-big_button"[^>]*>/g, '<div class="rst-big-button">');
// Process directive content blocks (indented text after directive)
html = html.replace(/(<div class="rst-directive[^>]*>)\n((?: [^\n]*\n?)*)/gim, (match, directive, content) => {
if (content.trim()) {
const cleanContent = content.replace(/^ /gm, '').trim();
// Remplacer les liens par des placeholders dans le contenu
const processedContent = cleanContent.replace(/`([^<]+) <([^>]+)>`_/g, (match, text, url) => {
return createLinkPlaceholder(text, url);
});
return directive + '<div class="directive-content">' + processedContent + '</div></div>';
}
return directive + '</div>';
});
// Bold
html = html.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
// Italic
html = html.replace(/\*(.*?)\*/g, '<em>$1</em>');
// Code inline
html = html.replace(/``(.*?)``/g, '<code>$1</code>');
// Code blocks (:: at end of line followed by indented block)
html = html.replace(/::\s*\n\n((?: .*\n?)+)/g, '<pre><code>$1</code></pre>');
// Links restants dans le texte principal
html = html.replace(/`([^<]+) <([^>]+)>`_/g, (match, text, url) => {
return createLinkPlaceholder(text, url);
});
// Lists
html = html.replace(/^\* (.*$)/gim, '<li>$1</li>');
html = html.replace(/^- (.*$)/gim, '<li>$1</li>');
html = html.replace(/^\d+\. (.*$)/gim, '<li>$1</li>');
// Wrap consecutive <li> items in <ul>
html = html.replace(/(<li>.*<\/li>)/gs, (match) => {
return '<ul>' + match + '</ul>';
});
// Line breaks
html = html.replace(/\n\n/g, '</p><p>');
html = '<p>' + html + '</p>';
// Clean up empty paragraphs and fix structure
html = html.replace(/<p><\/p>/g, '');
html = html.replace(/<p>(<h[1-6]>)/g, '$1');
html = html.replace(/(<\/h[1-6]>)<\/p>/g, '$1');
html = html.replace(/<p>(<div class="rst-)/g, '<div class="rst-');
html = html.replace(/(<\/div>)<\/p>/g, '$1');
html = html.replace(/<p>(<ul>)/g, '$1');
html = html.replace(/(<\/ul>)<\/p>/g, '$1');
html = html.replace(/<p>(<pre>)/g, '$1');
html = html.replace(/(<\/pre>)<\/p>/g, '$1');
// Restaurer tous les liens à partir des placeholders
for (const [placeholder, linkHtml] of linkPlaceholders) {
html = html.replace(new RegExp(placeholder, 'g'), linkHtml);
}
return html;
}
function showMarkdownPreview(fileUrl, container) {
fetch(fileUrl)
.then(response => {
if (!response.ok) {
throw new Error(`Erreur HTTP: ${response.status}`);
}
return response.text();
})
.then(text => {
const htmlContent = parseMarkdown(text);
const div = document.createElement('div');
div.className = 'markdown-content';
div.innerHTML = htmlContent;
container.innerHTML = '';
container.appendChild(div);
})
.catch(error => {
console.error('Erreur chargement markdown:', error);
container.innerHTML = '<div class="preview-error">Erreur: Impossible de charger le fichier Markdown</div>';
});
}
function showRstPreview(fileUrl, container) {
fetch(fileUrl)
.then(response => {
if (!response.ok) {
throw new Error(`Erreur HTTP: ${response.status}`);
}
return response.text();
})
.then(text => {
const htmlContent = parseRst(text);
const div = document.createElement('div');
div.className = 'rst-content';
div.innerHTML = htmlContent;
container.innerHTML = '';
container.appendChild(div);
})
.catch(error => {
console.error('Erreur chargement RST:', error);
container.innerHTML = '<div class="preview-error">Erreur: Impossible de charger le fichier reStructuredText</div>';
});
}