Files
personotes/frontend/src/file-tree.js

833 lines
30 KiB
JavaScript

import { debug, debugError } from './debug.js';
/**
* FileTree - Gère l'arborescence hiérarchique avec drag & drop
* Utilise la délégation d'événements pour éviter les problèmes de listeners perdus
*/
class FileTree {
constructor() {
this.draggedPath = null;
this.draggedType = null;
this.init();
}
init() {
this.setupEventListeners();
// Restaurer l'état des dossiers au démarrage
setTimeout(() => this.restoreFolderStates(), 500);
debug('FileTree initialized with event delegation');
}
setupEventListeners() {
// Utiliser la délégation d'événements sur le conteneur de la sidebar
// Cela évite de perdre les listeners après les swaps htmx
const sidebar = document.getElementById('sidebar');
if (!sidebar) {
console.error('FileTree: sidebar not found');
return;
}
// Supprimer les anciens listeners s'ils existent
if (this.clickHandler) {
sidebar.removeEventListener('click', this.clickHandler);
}
// Créer et stocker le handler pour pouvoir le supprimer plus tard
this.clickHandler = (e) => {
// Ignorer les clics sur les checkboxes
if (e.target.classList.contains('selection-checkbox')) {
return;
}
// Vérifier d'abord si c'est un folder-header ou un de ses enfants
const folderHeader = e.target.closest('.folder-header');
if (folderHeader && !e.target.closest('.file-item')) {
e.preventDefault();
e.stopPropagation();
this.toggleFolder(folderHeader);
return;
}
// Event listener délégué pour les clics sur les fichiers
const fileItem = e.target.closest('.file-item');
if (fileItem && !folderHeader) {
// Laisser HTMX gérer le chargement via l'attribut hx-get
// Ne pas bloquer la propagation pour les fichiers
return;
}
};
// Attacher le handler
sidebar.addEventListener('click', this.clickHandler);
// Event listeners délégués pour le drag & drop
this.setupDelegatedDragAndDrop(sidebar);
}
toggleFolder(header) {
const folderItem = header.parentElement;
const children = folderItem.querySelector('.folder-children');
const toggle = header.querySelector('.folder-toggle');
const icon = header.querySelector('.folder-icon');
const folderPath = folderItem.getAttribute('data-path');
if (children.style.display === 'none') {
// Ouvrir le dossier
children.style.display = 'block';
toggle.classList.add('expanded');
icon.innerHTML = '<i data-lucide="folder-open" class="icon-sm"></i>';
this.saveFolderState(folderPath, true);
} else {
// Fermer le dossier
children.style.display = 'none';
toggle.classList.remove('expanded');
icon.innerHTML = '<i data-lucide="folder" class="icon-sm"></i>';
this.saveFolderState(folderPath, false);
}
}
saveFolderState(folderPath, isExpanded) {
if (!folderPath) return;
const expandedFolders = this.getExpandedFolders();
if (isExpanded) {
expandedFolders.add(folderPath);
} else {
expandedFolders.delete(folderPath);
}
localStorage.setItem('expanded-folders', JSON.stringify([...expandedFolders]));
}
getExpandedFolders() {
const saved = localStorage.getItem('expanded-folders');
return saved ? new Set(JSON.parse(saved)) : new Set();
}
restoreFolderStates() {
const expandedFolders = this.getExpandedFolders();
document.querySelectorAll('.folder-item').forEach(folderItem => {
const folderPath = folderItem.getAttribute('data-path');
if (folderPath && expandedFolders.has(folderPath)) {
const header = folderItem.querySelector('.folder-header');
const children = folderItem.querySelector('.folder-children');
const toggle = header?.querySelector('.folder-toggle');
const icon = header?.querySelector('.folder-icon');
if (children && toggle && icon) {
children.style.display = 'block';
toggle.classList.add('expanded');
icon.innerHTML = '<i data-lucide="folder-open" class="icon-sm"></i>';
}
}
});
// Réinitialiser les icônes Lucide
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
}
setupDelegatedDragAndDrop(sidebar) {
// Supprimer les anciens handlers s'ils existent
if (this.dragStartHandler) {
sidebar.removeEventListener('dragstart', this.dragStartHandler);
sidebar.removeEventListener('dragend', this.dragEndHandler);
sidebar.removeEventListener('dragover', this.dragOverHandler);
sidebar.removeEventListener('dragleave', this.dragLeaveHandler);
sidebar.removeEventListener('drop', this.dropHandler);
}
// Drag start - délégué pour fichiers et dossiers
this.dragStartHandler = (e) => {
const fileItem = e.target.closest('.file-item');
const folderHeader = e.target.closest('.folder-header');
if (fileItem && fileItem.draggable) {
this.handleDragStart(e, 'file', fileItem);
} else if (folderHeader && folderHeader.draggable) {
this.handleDragStart(e, 'folder', folderHeader);
}
};
// Drag end - délégué
this.dragEndHandler = (e) => {
const fileItem = e.target.closest('.file-item');
const folderHeader = e.target.closest('.folder-header');
if (fileItem || folderHeader) {
this.handleDragEnd(e);
}
};
// Drag over - délégué sur les folder-headers et la racine
this.dragOverHandler = (e) => {
const folderHeader = e.target.closest('.folder-header');
const rootHeader = e.target.closest('.sidebar-section-header[data-section="notes"]');
const target = folderHeader || rootHeader;
if (target) {
this.handleDragOver(e, target);
}
};
// Drag leave - délégué
this.dragLeaveHandler = (e) => {
const folderHeader = e.target.closest('.folder-header');
const rootHeader = e.target.closest('.sidebar-section-header[data-section="notes"]');
const target = folderHeader || rootHeader;
if (target) {
this.handleDragLeave(e, target);
}
};
// Drop - délégué
this.dropHandler = (e) => {
const folderHeader = e.target.closest('.folder-header');
const rootHeader = e.target.closest('.sidebar-section-header[data-section="notes"]');
const target = folderHeader || rootHeader;
if (target) {
this.handleDrop(e, target);
}
};
// Attacher les handlers
sidebar.addEventListener('dragstart', this.dragStartHandler);
sidebar.addEventListener('dragend', this.dragEndHandler);
sidebar.addEventListener('dragover', this.dragOverHandler);
sidebar.addEventListener('dragleave', this.dragLeaveHandler);
sidebar.addEventListener('drop', this.dropHandler);
// Rendre les dossiers draggables (sauf racine)
this.updateDraggableAttributes();
// Écouter les événements HTMX pour mettre à jour les attributs après les swaps
// Plus performant et plus cohérent qu'un MutationObserver
document.body.addEventListener('htmx:afterSwap', (event) => {
// Vérifier si le swap concerne le file-tree
const target = event.detail?.target;
if (target && (target.id === 'file-tree' || target.closest('#file-tree'))) {
debug('FileTree: afterSwap detected, updating attributes and restoring folder states...');
this.updateDraggableAttributes();
setTimeout(() => this.restoreFolderStates(), 50);
}
});
// Écouter aussi les swaps out-of-band (oob) qui mettent à jour le file-tree
document.body.addEventListener('htmx:oobAfterSwap', (event) => {
const target = event.detail?.target;
// Ignorer les swaps de statut (auto-save-status, save-status)
if (target && target.id === 'file-tree') {
debug('FileTree: oobAfterSwap detected, updating attributes and restoring folder states...');
this.updateDraggableAttributes();
setTimeout(() => this.restoreFolderStates(), 50);
}
});
// Écouter les restaurations d'historique (bouton retour du navigateur)
document.body.addEventListener('htmx:historyRestore', () => {
debug('FileTree: History restored, re-initializing event listeners...');
// Réinitialiser complètement les event listeners après restauration de l'historique
setTimeout(() => {
this.setupEventListeners();
this.updateDraggableAttributes();
this.restoreFolderStates();
}, 50);
});
}
updateDraggableAttributes() {
// Mettre à jour l'attribut draggable pour les dossiers non-racine
const folderItems = document.querySelectorAll('.folder-item');
folderItems.forEach(folder => {
const header = folder.querySelector('.folder-header');
const isRoot = folder.dataset.isRoot === 'true';
if (header && !isRoot) {
header.setAttribute('draggable', 'true');
}
});
}
handleDragStart(e, type, item) {
item.classList.add('dragging');
let path, name;
if (type === 'file') {
path = item.dataset.path;
name = path.split('/').pop();
} else if (type === 'folder') {
const folderItem = item.closest('.folder-item');
path = folderItem.dataset.path;
name = folderItem.querySelector('.folder-name').textContent.trim();
}
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/plain', path);
e.dataTransfer.setData('application/note-path', path);
e.dataTransfer.setData('application/note-type', type);
e.dataTransfer.setData('application/note-name', name);
// Stocker le chemin source pour validation
this.draggedPath = path;
this.draggedType = type;
debug('Drag start:', { type, path, name });
}
handleDragEnd(e) {
// Trouver l'élément draggé (fichier ou folder-header)
const fileItem = e.target.closest('.file-item');
const folderHeader = e.target.closest('.folder-header');
const item = fileItem || folderHeader;
if (item) {
item.classList.remove('dragging');
}
// Supprimer les highlights de tous les dossiers
document.querySelectorAll('.folder-item.drag-over').forEach(f => {
f.classList.remove('drag-over');
});
// Supprimer l'indicateur de destination
const indicator = document.getElementById('drag-destination-indicator');
if (indicator) {
indicator.remove();
}
this.draggedPath = null;
this.draggedType = null;
}
handleDragOver(e, target) {
e.preventDefault();
e.stopPropagation();
// Gérer soit un folder-header dans un folder-item, soit la racine (sidebar-section-header)
const isRoot = target.classList.contains('sidebar-section-header');
const targetElement = isRoot ? target : target.closest('.folder-item');
if (!targetElement) return;
const targetPath = targetElement.dataset.path;
// Empêcher de déplacer un dossier dans lui-même ou dans ses enfants
if (this.draggedType === 'folder' && this.draggedPath) {
if (targetPath === this.draggedPath || targetPath.startsWith(this.draggedPath + '/')) {
e.dataTransfer.dropEffect = 'none';
targetElement.classList.remove('drag-over');
this.removeDestinationIndicator();
return;
}
}
e.dataTransfer.dropEffect = 'move';
if (targetElement && !targetElement.classList.contains('drag-over')) {
// Retirer la classe des autres dossiers et de la racine
document.querySelectorAll('.folder-item.drag-over, .sidebar-section-header.drag-over').forEach(f => {
if (f !== targetElement) {
f.classList.remove('drag-over');
}
});
targetElement.classList.add('drag-over');
// Afficher l'indicateur de destination
this.showDestinationIndicator(targetElement, targetPath, isRoot);
}
}
handleDragLeave(e, target) {
// Gérer soit un folder-header dans un folder-item, soit la racine (sidebar-section-header)
const isRoot = target.classList.contains('sidebar-section-header');
const targetElement = isRoot ? target : target.closest('.folder-item');
if (!targetElement) return;
// Vérifier que la souris a vraiment quitté l'élément
const rect = target.getBoundingClientRect();
if (e.clientX < rect.left || e.clientX >= rect.right ||
e.clientY < rect.top || e.clientY >= rect.bottom) {
targetElement.classList.remove('drag-over');
this.removeDestinationIndicator();
}
}
showDestinationIndicator(targetElement, targetPath, isRoot) {
let indicator = document.getElementById('drag-destination-indicator');
if (!indicator) {
indicator = document.createElement('div');
indicator.id = 'drag-destination-indicator';
indicator.className = 'drag-destination-indicator';
document.body.appendChild(indicator);
}
const folderName = targetElement.querySelector('.folder-name').textContent.trim();
const displayPath = isRoot ? 'notes/' : targetPath;
indicator.innerHTML = `
<span class="indicator-icon">📥</span>
<span class="indicator-text">Déplacer vers: <strong>${folderName}</strong></span>
<span class="indicator-path">${displayPath}</span>
`;
indicator.style.display = 'flex';
}
removeDestinationIndicator() {
const indicator = document.getElementById('drag-destination-indicator');
if (indicator) {
indicator.style.display = 'none';
}
}
handleDrop(e, target) {
e.preventDefault();
e.stopPropagation();
// Gérer soit un folder-header dans un folder-item, soit la racine (sidebar-section-header)
const isRoot = target.classList.contains('sidebar-section-header');
const targetElement = isRoot ? target : target.closest('.folder-item');
if (!targetElement) return;
targetElement.classList.remove('drag-over');
// Supprimer l'indicateur de destination
this.removeDestinationIndicator();
const sourcePath = e.dataTransfer.getData('application/note-path') ||
e.dataTransfer.getData('text/plain');
const sourceType = e.dataTransfer.getData('application/note-type');
const targetFolderPath = targetElement.dataset.path;
debug('Drop event:', {
sourcePath,
sourceType,
targetFolderPath,
dataTransfer: e.dataTransfer.types
});
// Validation : sourcePath doit exister, targetFolderPath peut être vide (racine)
if (!sourcePath || targetFolderPath === undefined || targetFolderPath === null) {
console.error('Chemins invalides pour le drag & drop', {
sourcePath,
targetFolderPath
});
alert(`Erreur: source='${sourcePath}', destination='${targetFolderPath}'`);
return;
}
// Empêcher de déplacer un dossier dans lui-même ou dans ses enfants
if (sourceType === 'folder') {
if (targetFolderPath === sourcePath || targetFolderPath.startsWith(sourcePath + '/')) {
alert('Impossible de déplacer un dossier dans lui-même ou dans un de ses sous-dossiers');
return;
}
}
// Ne pas déplacer si c'est déjà dans le même dossier parent
const sourceDir = sourcePath.includes('/') ?
sourcePath.substring(0, sourcePath.lastIndexOf('/')) : '';
if (sourceDir === targetFolderPath) {
debug('Déjà dans le même dossier parent, rien à faire');
return;
}
// Extraire le nom du fichier/dossier
const itemName = sourcePath.includes('/') ?
sourcePath.substring(sourcePath.lastIndexOf('/') + 1) :
sourcePath;
// Construire le chemin de destination
// Si targetFolderPath est vide (racine), ne pas ajouter de slash
const destinationPath = targetFolderPath === '' ? itemName : targetFolderPath + '/' + itemName;
debug(`Déplacement: ${sourcePath}${destinationPath}`);
this.moveFile(sourcePath, destinationPath);
}
async moveFile(sourcePath, destinationPath) {
debug('moveFile called:', { sourcePath, destinationPath });
try {
// Utiliser htmx.ajax() au lieu de fetch() manuel
// HTMX gère automatiquement les swaps oob et le traitement du HTML
// Les attributs draggables seront mis à jour automatiquement via htmx:oobAfterSwap
htmx.ajax('POST', '/api/files/move', {
values: { source: sourcePath, destination: destinationPath },
swap: 'none' // On ne swap rien directement, le serveur utilise hx-swap-oob
}).then(() => {
debug(`Fichier déplacé: ${sourcePath} -> ${destinationPath}`);
}).catch((error) => {
console.error('Erreur lors du déplacement:', error);
alert('Erreur lors du déplacement du fichier');
});
} catch (error) {
console.error('Erreur lors du déplacement:', error);
alert('Erreur lors du déplacement du fichier: ' + error.message);
}
}
}
/**
* Gestion de la création de notes
*/
window.showNewNoteModal = function() {
const modal = document.getElementById('new-note-modal');
modal.style.display = 'flex';
setTimeout(() => {
document.getElementById('note-name').focus();
}, 100);
}
window.hideNewNoteModal = function() {
const modal = document.getElementById('new-note-modal');
modal.style.display = 'none';
document.getElementById('note-name').value = '';
}
window.handleNewNote = function(event) {
event.preventDefault();
let noteName = document.getElementById('note-name').value.trim();
if (!noteName) {
alert('Veuillez entrer un nom de note');
return;
}
// Ajouter .md si pas déjà présent
if (!noteName.endsWith('.md')) {
noteName += '.md';
}
// Valider le nom (pas de caractères dangereux)
if (noteName.includes('..') || noteName.includes('\\')) {
alert('Nom de note invalide. Évitez les caractères \\ et ..');
return;
}
// Fermer la modale
window.hideNewNoteModal();
// Utiliser HTMX pour charger la note (au lieu de fetch manuel)
const editorContainer = document.getElementById('editor-container');
// Supprimer temporairement le trigger "load" pour éviter qu'il se redéclenche
editorContainer.removeAttribute('hx-trigger');
// Utiliser l'API HTMX pour charger la note
if (typeof htmx !== 'undefined') {
htmx.ajax('GET', `/api/notes/${encodeURIComponent(noteName)}`, {
target: '#editor-container',
swap: 'innerHTML',
pushUrl: true
});
}
}
/**
* Gestion de la création de dossiers
*/
window.showNewFolderModal = function() {
const modal = document.getElementById('new-folder-modal');
modal.style.display = 'flex';
setTimeout(() => {
document.getElementById('folder-name').focus();
}, 100);
}
window.hideNewFolderModal = function() {
const modal = document.getElementById('new-folder-modal');
modal.style.display = 'none';
document.getElementById('folder-name').value = '';
}
window.handleNewFolder = async function(event) {
event.preventDefault();
let folderName = document.getElementById('folder-name').value.trim();
if (!folderName) {
alert('Veuillez entrer un nom de dossier');
return;
}
// Valider le nom (pas de caractères dangereux)
if (folderName.includes('..') || folderName.includes('\\')) {
alert('Nom de dossier invalide. Évitez les caractères \\ et ..');
return;
}
try {
// Utiliser htmx.ajax() au lieu de fetch() manuel
// HTMX gère automatiquement les swaps oob et le traitement du HTML
// Les attributs draggables seront mis à jour automatiquement via htmx:oobAfterSwap
htmx.ajax('POST', '/api/folders/create', {
values: { path: folderName },
swap: 'none' // On ne swap rien directement, le serveur utilise hx-swap-oob
}).then(() => {
window.hideNewFolderModal();
debug(`Dossier créé: ${folderName}`);
}).catch((error) => {
console.error('Erreur lors de la création du dossier:', error);
alert('Erreur lors de la création du dossier');
});
} catch (error) {
console.error('Erreur lors de la création du dossier:', error);
alert('Erreur lors de la création du dossier: ' + error.message);
}
}
// Fermer les modales avec Escape
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
const folderModal = document.getElementById('new-folder-modal');
const noteModal = document.getElementById('new-note-modal');
if (folderModal && folderModal.style.display === 'flex') {
window.hideNewFolderModal();
}
if (noteModal && noteModal.style.display === 'flex') {
window.hideNewNoteModal();
}
}
});
/**
* Initialisation automatique
*/
document.addEventListener('DOMContentLoaded', () => {
window.fileTree = new FileTree();
window.selectionManager = new SelectionManager();
});
/**
* SelectionManager - Gère le mode sélection et la suppression en masse
*/
class SelectionManager {
constructor() {
this.isSelectionMode = false;
this.selectedPaths = new Set();
this.init();
}
init() {
// Écouter les événements HTMX pour réinitialiser les listeners après les swaps
document.body.addEventListener('htmx:afterSwap', (event) => {
if (event.detail.target.id === 'file-tree' || event.detail.target.closest('#file-tree')) {
this.attachCheckboxListeners();
if (this.isSelectionMode) {
this.showCheckboxes();
}
}
});
document.body.addEventListener('htmx:oobAfterSwap', (event) => {
if (event.detail.target.id === 'file-tree') {
this.attachCheckboxListeners();
if (this.isSelectionMode) {
this.showCheckboxes();
}
}
});
// Attacher les listeners initiaux
setTimeout(() => this.attachCheckboxListeners(), 500);
}
attachCheckboxListeners() {
const checkboxes = document.querySelectorAll('.selection-checkbox');
checkboxes.forEach(checkbox => {
// Retirer l'ancien listener s'il existe
checkbox.removeEventListener('change', this.handleCheckboxChange);
// Ajouter le nouveau listener
checkbox.addEventListener('change', (e) => this.handleCheckboxChange(e));
});
}
handleCheckboxChange(e) {
const checkbox = e.target;
const path = checkbox.dataset.path;
if (checkbox.checked) {
window.selectionManager.selectedPaths.add(path);
} else {
window.selectionManager.selectedPaths.delete(path);
}
window.selectionManager.updateToolbar();
}
toggleSelectionMode() {
this.isSelectionMode = !this.isSelectionMode;
if (this.isSelectionMode) {
this.showCheckboxes();
document.getElementById('toggle-selection-mode')?.classList.add('active');
} else {
this.hideCheckboxes();
this.clearSelection();
document.getElementById('toggle-selection-mode')?.classList.remove('active');
}
}
showCheckboxes() {
const checkboxes = document.querySelectorAll('.selection-checkbox');
checkboxes.forEach(checkbox => {
checkbox.style.display = 'inline-block';
});
}
hideCheckboxes() {
const checkboxes = document.querySelectorAll('.selection-checkbox');
checkboxes.forEach(checkbox => {
checkbox.style.display = 'none';
checkbox.checked = false;
});
}
clearSelection() {
this.selectedPaths.clear();
this.updateToolbar();
}
updateToolbar() {
const toolbar = document.getElementById('selection-toolbar');
const countSpan = document.getElementById('selection-count');
if (this.selectedPaths.size > 0) {
toolbar.style.display = 'flex';
countSpan.textContent = `${this.selectedPaths.size} élément(s) sélectionné(s)`;
} else {
toolbar.style.display = 'none';
}
}
showDeleteConfirmationModal() {
const modal = document.getElementById('delete-confirmation-modal');
const countSpan = document.getElementById('delete-count');
const itemsList = document.getElementById('delete-items-list');
countSpan.textContent = this.selectedPaths.size;
// Générer la liste des éléments à supprimer
itemsList.innerHTML = '';
const ul = document.createElement('ul');
ul.style.margin = '0';
ul.style.padding = '0 0 0 1.5rem';
ul.style.color = 'var(--text-primary)';
this.selectedPaths.forEach(path => {
const li = document.createElement('li');
li.style.marginBottom = '0.5rem';
// Déterminer si c'est un dossier
const checkbox = document.querySelector(`.selection-checkbox[data-path="${path}"]`);
const isDir = checkbox?.dataset.isDir === 'true';
li.innerHTML = `${isDir ? '<i data-lucide="folder" class="icon-sm"></i>' : '<i data-lucide="file-text" class="icon-sm"></i>'} <code>${path}</code>`;
ul.appendChild(li);
});
itemsList.appendChild(ul);
modal.style.display = 'flex';
}
hideDeleteConfirmationModal() {
const modal = document.getElementById('delete-confirmation-modal');
modal.style.display = 'none';
}
async deleteSelectedItems() {
const paths = Array.from(this.selectedPaths);
if (paths.length === 0) {
alert('Aucun élément sélectionné');
return;
}
try {
// Construire le corps de la requête au format query string
// Le backend attend: paths[]=path1&paths[]=path2
const params = new URLSearchParams();
paths.forEach(path => {
params.append('paths[]', path);
});
// Utiliser fetch() avec le corps en query string
const response = await fetch('/api/files/delete-multiple', {
method: 'DELETE',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: params.toString()
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const html = await response.text();
// Parser le HTML pour trouver les éléments avec hx-swap-oob
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
// Traiter les swaps out-of-band manuellement
doc.querySelectorAll('[hx-swap-oob]').forEach(element => {
const targetId = element.id;
const target = document.getElementById(targetId);
if (target) {
target.innerHTML = element.innerHTML;
// Déclencher l'événement htmx pour que les listeners se réattachent
htmx.process(target);
}
});
debug(`${paths.length} élément(s) supprimé(s)`);
// Fermer la modale
this.hideDeleteConfirmationModal();
// Réinitialiser la sélection et garder le mode sélection actif
this.clearSelection();
// Réattacher les listeners sur les nouvelles checkboxes
setTimeout(() => {
this.attachCheckboxListeners();
if (this.isSelectionMode) {
this.showCheckboxes();
}
}, 100);
} catch (error) {
console.error('Erreur lors de la suppression:', error);
alert('Erreur lors de la suppression des éléments: ' + error.message);
}
}
}
/**
* Fonctions globales pour les boutons
*/
window.toggleSelectionMode = function() {
window.selectionManager.toggleSelectionMode();
};
window.deleteSelected = function() {
window.selectionManager.showDeleteConfirmationModal();
};
window.cancelSelection = function() {
window.selectionManager.toggleSelectionMode();
};
window.hideDeleteConfirmationModal = function() {
window.selectionManager.hideDeleteConfirmationModal();
};
window.confirmDelete = function() {
window.selectionManager.deleteSelectedItems();
};