Refactor event binding in domUtils & fileListView
This commit is contained in:
19
CHANGELOG.md
19
CHANGELOG.md
@@ -24,6 +24,25 @@
|
|||||||
- Removed the inline `onclick` attributes from `#cancelDownloadFile` and `#confirmSingleDownloadButton` in the HTML
|
- Removed the inline `onclick` attributes from `#cancelDownloadFile` and `#confirmSingleDownloadButton` in the HTML
|
||||||
- Ensured all file-action modals (delete, download, extract, copy, move, rename) now use JS event handlers instead of inline code
|
- Ensured all file-action modals (delete, download, extract, copy, move, rename) now use JS event handlers instead of inline code
|
||||||
|
|
||||||
|
### 5. domUtils.js
|
||||||
|
|
||||||
|
- **Removed** all inline `onclick` and `onchange` attributes from:
|
||||||
|
- `buildSearchAndPaginationControls` (advanced search toggle, prev/next buttons, items-per-page selector)
|
||||||
|
- `buildFileTableHeader` (select-all checkbox)
|
||||||
|
- `buildFileTableRow` (download, edit, preview, rename buttons)
|
||||||
|
- **Retained** all original logic (file-type icon detection, shift-select, debounce, custom confirm modal, etc.)
|
||||||
|
|
||||||
|
### 6. fileListView.js
|
||||||
|
|
||||||
|
- **Stopped** generating inline `onclick` handlers in both table and gallery views.
|
||||||
|
- **Added** `data-` attributes on actionable elements:
|
||||||
|
- `data-download-name`, `data-download-folder`
|
||||||
|
- `data-edit-name`, `data-edit-folder`
|
||||||
|
- `data-rename-name`, `data-rename-folder`
|
||||||
|
- `data-preview-url`, `data-preview-name`
|
||||||
|
- IDs on controls: `#advancedSearchToggle`, `#searchInput`, `#prevPageBtn`, `#nextPageBtn`, `#selectAll`, `#itemsPerPageSelect`
|
||||||
|
- **Introduced** `attachListControlListeners()` to bind all events via `addEventListener` immediately after rendering, preserving every interaction without inline code.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Changes 4/25/2025
|
## Changes 4/25/2025
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { sendRequest } from './networkUtils.js';
|
|||||||
import { t, applyTranslations, setLocale } from './i18n.js';
|
import { t, applyTranslations, setLocale } from './i18n.js';
|
||||||
import { loadAdminConfigFunc } from './auth.js';
|
import { loadAdminConfigFunc } from './auth.js';
|
||||||
|
|
||||||
const version = "v1.2.5"; // Update this version string as needed
|
const version = "v1.2.6"; // Update this version string as needed
|
||||||
const adminTitle = `${t("admin_panel")} <small style="font-size: 12px; color: gray;">${version}</small>`;
|
const adminTitle = `${t("admin_panel")} <small style="font-size: 12px; color: gray;">${version}</small>`;
|
||||||
|
|
||||||
let lastLoginData = null;
|
let lastLoginData = null;
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export function showToast(message, duration = 3000) {
|
|||||||
export function buildSearchAndPaginationControls({ currentPage, totalPages, searchTerm }) {
|
export function buildSearchAndPaginationControls({ currentPage, totalPages, searchTerm }) {
|
||||||
const safeSearchTerm = escapeHTML(searchTerm);
|
const safeSearchTerm = escapeHTML(searchTerm);
|
||||||
// Choose the placeholder text based on advanced search mode
|
// Choose the placeholder text based on advanced search mode
|
||||||
const placeholderText = window.advancedSearchEnabled
|
const placeholderText = window.advancedSearchEnabled
|
||||||
? t("search_placeholder_advanced")
|
? t("search_placeholder_advanced")
|
||||||
: t("search_placeholder");
|
: t("search_placeholder");
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ export function buildSearchAndPaginationControls({ currentPage, totalPages, sear
|
|||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<!-- Advanced Search Toggle Button -->
|
<!-- Advanced Search Toggle Button -->
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<button id="advancedSearchToggle" class="btn btn-outline-secondary btn-icon" onclick="toggleAdvancedSearch()" title="${window.advancedSearchEnabled ? t("basic_search_tooltip") : t("advanced_search_tooltip")}">
|
<button id="advancedSearchToggle" class="btn btn-outline-secondary btn-icon" title="${window.advancedSearchEnabled ? t("basic_search_tooltip") : t("advanced_search_tooltip")}">
|
||||||
<i class="material-icons">${window.advancedSearchEnabled ? "filter_alt_off" : "filter_alt"}</i>
|
<i class="material-icons">${window.advancedSearchEnabled ? "filter_alt_off" : "filter_alt"}</i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -117,9 +117,9 @@ export function buildSearchAndPaginationControls({ currentPage, totalPages, sear
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-4 text-left">
|
<div class="col-12 col-md-4 text-left">
|
||||||
<div class="d-flex justify-content-center justify-content-md-start align-items-center">
|
<div class="d-flex justify-content-center justify-content-md-start align-items-center">
|
||||||
<button class="custom-prev-next-btn" ${currentPage === 1 ? "disabled" : ""} onclick="changePage(${currentPage - 1})">${t("prev")}</button>
|
<button id="prevPageBtn" class="custom-prev-next-btn" ${currentPage === 1 ? "disabled" : ""}>${t("prev")}</button>
|
||||||
<span class="page-indicator">${t("page")} ${currentPage} ${t("of")} ${totalPages || 1}</span>
|
<span class="page-indicator">${t("page")} ${currentPage} ${t("of")} ${totalPages || 1}</span>
|
||||||
<button class="custom-prev-next-btn" ${currentPage === totalPages ? "disabled" : ""} onclick="changePage(${currentPage + 1})">${t("next")}</button>
|
<button id="nextPageBtn" class="custom-prev-next-btn" ${currentPage === totalPages ? "disabled" : ""}>${t("next")}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -131,7 +131,7 @@ export function buildFileTableHeader(sortOrder) {
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="checkbox-col"><input type="checkbox" id="selectAll" onclick="toggleAllCheckboxes(this)"></th>
|
<th class="checkbox-col"><input type="checkbox" id="selectAll"></th>
|
||||||
<th data-column="name" class="sortable-col">${t("file_name")} ${sortOrder.column === "name" ? (sortOrder.ascending ? "▲" : "▼") : ""}</th>
|
<th data-column="name" class="sortable-col">${t("file_name")} ${sortOrder.column === "name" ? (sortOrder.ascending ? "▲" : "▼") : ""}</th>
|
||||||
<th data-column="modified" class="hide-small sortable-col">${t("date_modified")} ${sortOrder.column === "modified" ? (sortOrder.ascending ? "▲" : "▼") : ""}</th>
|
<th data-column="modified" class="hide-small sortable-col">${t("date_modified")} ${sortOrder.column === "modified" ? (sortOrder.ascending ? "▲" : "▼") : ""}</th>
|
||||||
<th data-column="uploaded" class="hide-small hide-medium sortable-col">${t("upload_date")} ${sortOrder.column === "uploaded" ? (sortOrder.ascending ? "▲" : "▼") : ""}</th>
|
<th data-column="uploaded" class="hide-small hide-medium sortable-col">${t("upload_date")} ${sortOrder.column === "uploaded" ? (sortOrder.ascending ? "▲" : "▼") : ""}</th>
|
||||||
@@ -162,15 +162,15 @@ export function buildFileTableRow(file, folderPath) {
|
|||||||
} else if (/\.(mp3|wav|m4a|ogg|flac|aac|wma|opus)$/i.test(file.name)) {
|
} else if (/\.(mp3|wav|m4a|ogg|flac|aac|wma|opus)$/i.test(file.name)) {
|
||||||
previewIcon = `<i class="material-icons">audiotrack</i>`;
|
previewIcon = `<i class="material-icons">audiotrack</i>`;
|
||||||
}
|
}
|
||||||
previewButton = `<button class="btn btn-sm btn-info preview-btn" onclick="event.stopPropagation(); previewFile('${folderPath + encodeURIComponent(file.name)}', '${safeFileName}')">
|
previewButton = `<button class="btn btn-sm btn-info preview-btn" data-preview-url="${folderPath + encodeURIComponent(file.name)}?t=${Date.now()}" data-preview-name="${safeFileName}">
|
||||||
${previewIcon}
|
${previewIcon}
|
||||||
</button>`;
|
</button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<tr onclick="toggleRowSelection(event, '${safeFileName}')" class="clickable-row">
|
<tr class="clickable-row">
|
||||||
<td>
|
<td>
|
||||||
<input type="checkbox" class="file-checkbox" value="${safeFileName}" onclick="event.stopPropagation(); updateRowHighlight(this);">
|
<input type="checkbox" class="file-checkbox" value="${safeFileName}">
|
||||||
</td>
|
</td>
|
||||||
<td class="file-name-cell">${safeFileName}</td>
|
<td class="file-name-cell">${safeFileName}</td>
|
||||||
<td class="hide-small nowrap">${safeModified}</td>
|
<td class="hide-small nowrap">${safeModified}</td>
|
||||||
@@ -179,22 +179,16 @@ export function buildFileTableRow(file, folderPath) {
|
|||||||
<td class="hide-small hide-medium nowrap">${safeUploader}</td>
|
<td class="hide-small hide-medium nowrap">${safeUploader}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="button-wrap" style="display: flex; justify-content: left; gap: 5px;">
|
<div class="button-wrap" style="display: flex; justify-content: left; gap: 5px;">
|
||||||
<button type="button" class="btn btn-sm btn-success download-btn"
|
<button type="button" class="btn btn-sm btn-success download-btn" data-download-name="${file.name}" data-download-folder="${file.folder || 'root'}" title="${t('download')}">
|
||||||
onclick="openDownloadModal('${file.name}', '${file.folder || 'root'}')"
|
|
||||||
title="${t('download')}">
|
|
||||||
<i class="material-icons">file_download</i>
|
<i class="material-icons">file_download</i>
|
||||||
</button>
|
</button>
|
||||||
${file.editable ? `
|
${file.editable ? `
|
||||||
<button class="btn btn-sm edit-btn"
|
<button class="btn btn-sm edit-btn" data-edit-name="${file.name}" data-edit-folder="${file.folder || 'root'}" title="${t('edit')}">
|
||||||
onclick='editFile(${JSON.stringify(file.name)}, ${JSON.stringify(file.folder || "root")})'
|
|
||||||
title="${t('edit')}">
|
|
||||||
<i class="material-icons">edit</i>
|
<i class="material-icons">edit</i>
|
||||||
</button>
|
</button>
|
||||||
` : ""}
|
` : ""}
|
||||||
${previewButton}
|
${previewButton}
|
||||||
<button class="btn btn-sm btn-warning rename-btn"
|
<button class="btn btn-sm btn-warning rename-btn" data-rename-name="${file.name}" data-rename-folder="${file.folder || 'root'}" title="${t('rename')}">
|
||||||
onclick='renameFile(${JSON.stringify(file.name)}, ${JSON.stringify(file.folder || "root")})'
|
|
||||||
title="${t('rename')}">
|
|
||||||
<i class="material-icons">drive_file_rename_outline</i>
|
<i class="material-icons">drive_file_rename_outline</i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -207,10 +201,10 @@ export function buildBottomControls(itemsPerPageSetting) {
|
|||||||
return `
|
return `
|
||||||
<div class="d-flex align-items-center mt-3 bottom-controls">
|
<div class="d-flex align-items-center mt-3 bottom-controls">
|
||||||
<label class="label-inline mr-2 mb-0">${t("show")}</label>
|
<label class="label-inline mr-2 mb-0">${t("show")}</label>
|
||||||
<select class="form-control bottom-select" onchange="changeItemsPerPage(this.value)">
|
<select class="form-control bottom-select" id="itemsPerPageSelect">
|
||||||
${[10, 20, 50, 100]
|
${[10, 20, 50, 100]
|
||||||
.map(num => `<option value="${num}" ${num === itemsPerPageSetting ? "selected" : ""}>${num}</option>`)
|
.map(num => `<option value="${num}" ${num === itemsPerPageSetting ? "selected" : ""}>${num}</option>`)
|
||||||
.join("")}
|
.join("")}
|
||||||
</select>
|
</select>
|
||||||
<span class="items-per-page-text ml-2 mb-0">${t("items_per_page")}</span>
|
<span class="items-per-page-text ml-2 mb-0">${t("items_per_page")}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -345,4 +339,7 @@ export function showCustomConfirmModal(message) {
|
|||||||
yesBtn.addEventListener("click", onYes);
|
yesBtn.addEventListener("click", onYes);
|
||||||
noBtn.addEventListener("click", onNo);
|
noBtn.addEventListener("click", onNo);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
window.toggleRowSelection = toggleRowSelection;
|
||||||
|
window.updateRowHighlight = updateRowHighlight;
|
||||||
@@ -340,6 +340,48 @@ export function renderFileTable(folder, container) {
|
|||||||
|
|
||||||
fileListContent.innerHTML = combinedTopHTML + headerHTML + rowsHTML + bottomControlsHTML;
|
fileListContent.innerHTML = combinedTopHTML + headerHTML + rowsHTML + bottomControlsHTML;
|
||||||
|
|
||||||
|
// 1) Row-click selects the row
|
||||||
|
fileListContent.querySelectorAll("tbody tr").forEach(row => {
|
||||||
|
row.addEventListener("click", e => {
|
||||||
|
// grab the underlying checkbox value
|
||||||
|
const cb = row.querySelector(".file-checkbox");
|
||||||
|
if (!cb) return;
|
||||||
|
toggleRowSelection(e, cb.value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2) Download buttons
|
||||||
|
fileListContent.querySelectorAll(".download-btn").forEach(btn => {
|
||||||
|
btn.addEventListener("click", e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
openDownloadModal(btn.dataset.downloadName, btn.dataset.downloadFolder);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3) Edit buttons
|
||||||
|
fileListContent.querySelectorAll(".edit-btn").forEach(btn => {
|
||||||
|
btn.addEventListener("click", e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
editFile(btn.dataset.editName, btn.dataset.editFolder);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4) Rename buttons
|
||||||
|
fileListContent.querySelectorAll(".rename-btn").forEach(btn => {
|
||||||
|
btn.addEventListener("click", e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
renameFile(btn.dataset.renameName, btn.dataset.renameFolder);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 5) Preview buttons (if you still have a .preview-btn)
|
||||||
|
fileListContent.querySelectorAll(".preview-btn").forEach(btn => {
|
||||||
|
btn.addEventListener("click", e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
previewFile(btn.dataset.previewUrl, btn.dataset.previewName);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
createViewToggleButton();
|
createViewToggleButton();
|
||||||
|
|
||||||
// Setup event listeners.
|
// Setup event listeners.
|
||||||
@@ -529,9 +571,9 @@ export function renderGalleryView(folder, container) {
|
|||||||
<label for="cb-${idSafe}"
|
<label for="cb-${idSafe}"
|
||||||
style="position:absolute; top:5px; left:5px; width:16px; height:16px;"></label>
|
style="position:absolute; top:5px; left:5px; width:16px; height:16px;"></label>
|
||||||
|
|
||||||
<div class="gallery-preview"
|
<div class="gallery-preview" style="cursor:pointer;"
|
||||||
style="cursor:pointer;"
|
data-preview-url="${folderPath+encodeURIComponent(file.name)}?t=${Date.now()}"
|
||||||
onclick="previewFile('${folderPath + encodeURIComponent(file.name)}?t='+Date.now(), '${file.name}')">
|
data-preview-name="${file.name}">
|
||||||
${thumbnail}
|
${thumbnail}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -543,20 +585,21 @@ export function renderGalleryView(folder, container) {
|
|||||||
${tagBadgesHTML}
|
${tagBadgesHTML}
|
||||||
|
|
||||||
<div class="button-wrap" style="display:flex; justify-content:center; gap:5px; margin-top:5px;">
|
<div class="button-wrap" style="display:flex; justify-content:center; gap:5px; margin-top:5px;">
|
||||||
<button type="button" class="btn btn-sm btn-success download-btn"
|
<button … class="download-btn"
|
||||||
onclick="openDownloadModal('${file.name}', '${file.folder || "root"}')"
|
data-download-name="${file.name}"
|
||||||
|
data-download-folder="${file.folder||"root"}">
|
||||||
title="${t('download')}">
|
title="${t('download')}">
|
||||||
<i class="material-icons">file_download</i>
|
<i class="material-icons">file_download</i>
|
||||||
</button>
|
</button>
|
||||||
${file.editable ? `
|
${file.editable ? `
|
||||||
<button class="btn btn-sm edit-btn"
|
<button … class="edit-btn"
|
||||||
onclick='editFile(${JSON.stringify(file.name)}, ${JSON.stringify(file.folder || "root")})'
|
data-edit-name="${file.name}"
|
||||||
title="${t('Edit')}">
|
data-edit-folder="${file.folder||"root"}">
|
||||||
<i class="material-icons">edit</i>
|
<i class="material-icons">edit</i>
|
||||||
</button>` : ""}
|
</button>` : ""}
|
||||||
<button class="btn btn-sm btn-warning rename-btn"
|
<button … class="rename-btn"
|
||||||
onclick='renameFile(${JSON.stringify(file.name)}, ${JSON.stringify(file.folder || "root")})'
|
data-rename-name="${file.name}"
|
||||||
title="${t('rename')}">
|
data-rename-folder="${file.folder||"root"}">
|
||||||
<i class="material-icons">drive_file_rename_outline</i>
|
<i class="material-icons">drive_file_rename_outline</i>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-sm btn-secondary share-btn"
|
<button class="btn btn-sm btn-secondary share-btn"
|
||||||
@@ -578,6 +621,39 @@ export function renderGalleryView(folder, container) {
|
|||||||
|
|
||||||
// render
|
// render
|
||||||
fileListContent.innerHTML = galleryHTML;
|
fileListContent.innerHTML = galleryHTML;
|
||||||
|
|
||||||
|
// Preview clicks
|
||||||
|
document.querySelectorAll(".gallery-preview").forEach(el => {
|
||||||
|
el.addEventListener("click", () => {
|
||||||
|
const url = el.dataset.previewUrl;
|
||||||
|
const name = el.dataset.previewName;
|
||||||
|
previewFile(url, name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Download clicks
|
||||||
|
document.querySelectorAll(".download-btn").forEach(btn => {
|
||||||
|
btn.addEventListener("click", e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
openDownloadModal(btn.dataset.downloadName, btn.dataset.downloadFolder);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Edit clicks
|
||||||
|
document.querySelectorAll(".edit-btn").forEach(btn => {
|
||||||
|
btn.addEventListener("click", e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
editFile(btn.dataset.editName, btn.dataset.editFolder);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rename clicks
|
||||||
|
document.querySelectorAll(".rename-btn").forEach(btn => {
|
||||||
|
btn.addEventListener("click", e => {
|
||||||
|
e.stopPropagation();
|
||||||
|
renameFile(btn.dataset.renameName, btn.dataset.renameFolder);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// ensure toggle button
|
// ensure toggle button
|
||||||
createViewToggleButton();
|
createViewToggleButton();
|
||||||
|
|||||||
Reference in New Issue
Block a user