refactor(reports): namespace the FTP browser partial behind window.FtpBrowser
Proper fix superseding the fb* prefix band-aid (1801d4e): wrap ftp_browser.html's script in an IIFE and expose only window.FtpBrowser. Its ~11 helpers no longer leak to global scope, so the partial is safe to co-load with other FTP-browsing partials (e.g. slm_live_view's Command Center) without name collisions in either direction. Inline onclick handlers call FtpBrowser.*; showFTPSettings stays global (it's from the included settings modal). Behaviour unchanged — verified full Jinja render + balanced delimiters.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -32,19 +32,19 @@
|
||||
</svg>
|
||||
Settings
|
||||
</button>
|
||||
<button onclick="fbEnableFTP('{{ unit_item.unit.id }}')"
|
||||
<button onclick="FtpBrowser.enableFTP('{{ unit_item.unit.id }}')"
|
||||
id="enable-ftp-{{ unit_item.unit.id }}"
|
||||
class="px-3 py-1 text-xs bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors"
|
||||
disabled>
|
||||
Enable FTP
|
||||
</button>
|
||||
<button onclick="disableFTP('{{ unit_item.unit.id }}')"
|
||||
<button onclick="FtpBrowser.disableFTP('{{ unit_item.unit.id }}')"
|
||||
id="disable-ftp-{{ unit_item.unit.id }}"
|
||||
class="px-3 py-1 text-xs bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors"
|
||||
disabled>
|
||||
Disable FTP
|
||||
</button>
|
||||
<button onclick="fbLoadFTPFiles('{{ unit_item.unit.id }}', '/NL-43')"
|
||||
<button onclick="FtpBrowser.loadFTPFiles('{{ unit_item.unit.id }}', '/NL-43')"
|
||||
id="browse-ftp-{{ unit_item.unit.id }}"
|
||||
class="px-3 py-1 text-xs bg-seismo-orange text-white rounded-lg hover:bg-seismo-navy transition-colors"
|
||||
disabled>
|
||||
@@ -61,7 +61,7 @@
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z"></path>
|
||||
</svg>
|
||||
<span id="current-path-{{ unit_item.unit.id }}" class="text-sm font-mono text-gray-600 dark:text-gray-400">/NL-43</span>
|
||||
<button onclick="fbLoadFTPFiles('{{ unit_item.unit.id }}', '/NL-43')"
|
||||
<button onclick="FtpBrowser.loadFTPFiles('{{ unit_item.unit.id }}', '/NL-43')"
|
||||
class="ml-auto text-xs px-2 py-1 bg-gray-100 dark:bg-gray-700 rounded hover:bg-gray-200 dark:hover:bg-gray-600">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
|
||||
@@ -87,6 +87,11 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Self-contained namespace: this partial is reusable (project-wide Data Files
|
||||
// tab AND per-NRL Data Files tab), and may be co-loaded with other FTP-browsing
|
||||
// partials (e.g. slm_live_view). Wrapping in an IIFE keeps its helpers off the
|
||||
// global scope; only window.FtpBrowser is exposed (see the export at the end).
|
||||
(function () {
|
||||
async function checkFTPStatus(unitId) {
|
||||
const statusSpan = document.getElementById(`ftp-status-${unitId}`);
|
||||
const enableBtn = document.getElementById(`enable-ftp-${unitId}`);
|
||||
@@ -117,7 +122,7 @@ async function checkFTPStatus(unitId) {
|
||||
}
|
||||
}
|
||||
|
||||
async function fbEnableFTP(unitId) {
|
||||
async function enableFTP(unitId) {
|
||||
const enableBtn = document.getElementById(`enable-ftp-${unitId}`);
|
||||
enableBtn.disabled = true;
|
||||
enableBtn.textContent = 'Enabling...';
|
||||
@@ -130,7 +135,7 @@ async function fbEnableFTP(unitId) {
|
||||
if (response.ok) {
|
||||
await checkFTPStatus(unitId);
|
||||
// Auto-load files after enabling
|
||||
setTimeout(() => fbLoadFTPFiles(unitId, '/NL-43'), 1000);
|
||||
setTimeout(() => loadFTPFiles(unitId, '/NL-43'), 1000);
|
||||
} else {
|
||||
alert('Failed to enable FTP');
|
||||
}
|
||||
@@ -169,7 +174,7 @@ async function disableFTP(unitId) {
|
||||
}
|
||||
}
|
||||
|
||||
async function fbLoadFTPFiles(unitId, path) {
|
||||
async function loadFTPFiles(unitId, path) {
|
||||
const fileListDiv = document.getElementById(`ftp-file-list-${unitId}`);
|
||||
const filesContainer = document.getElementById(`ftp-files-${unitId}`);
|
||||
const currentPathSpan = document.getElementById(`current-path-${unitId}`);
|
||||
@@ -220,7 +225,7 @@ async function fbLoadFTPFiles(unitId, path) {
|
||||
let html = '<div class="space-y-1">';
|
||||
for (const file of sorted) {
|
||||
const icon = getFileIcon(file);
|
||||
const sizeStr = file.is_dir ? '' : fbFormatFileSize(file.size);
|
||||
const sizeStr = file.is_dir ? '' : formatFileSize(file.size);
|
||||
const folderId = 'proj-folder-' + file.path.replace(/[^a-zA-Z0-9]/g, '-');
|
||||
|
||||
if (file.is_dir) {
|
||||
@@ -228,7 +233,7 @@ async function fbLoadFTPFiles(unitId, path) {
|
||||
html += `
|
||||
<div class="border border-gray-200 dark:border-gray-600 rounded mb-1">
|
||||
<div class="flex items-center justify-between p-3 hover:bg-gray-50 dark:hover:bg-gray-700 rounded transition-colors cursor-pointer"
|
||||
onclick="toggleFTPFolderProject('${unitId}', '${escapeForAttribute(file.path)}', '${folderId}', this)">
|
||||
onclick="FtpBrowser.toggleFTPFolderProject('${unitId}', '${escapeForAttribute(file.path)}', '${folderId}', this)">
|
||||
<div class="flex items-center flex-1">
|
||||
<svg class="w-4 h-4 mr-2 text-gray-400 transition-transform folder-chevron" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
||||
@@ -239,7 +244,7 @@ async function fbLoadFTPFiles(unitId, path) {
|
||||
</div>
|
||||
<div class="flex items-center gap-2 flex-shrink-0 ml-4">
|
||||
<span class="text-xs text-gray-500 hidden sm:inline">${file.modified || ''}</span>
|
||||
<button onclick="event.stopPropagation(); downloadFolderToServer('${unitId}', '${escapeForAttribute(file.path)}', '${escapeForAttribute(file.name)}')"
|
||||
<button onclick="event.stopPropagation(); FtpBrowser.downloadFolderToServer('${unitId}', '${escapeForAttribute(file.path)}', '${escapeForAttribute(file.name)}')"
|
||||
class="px-3 py-1 text-xs bg-seismo-orange text-white rounded hover:bg-seismo-navy transition-colors flex items-center"
|
||||
title="Download folder from device to server and add to project database">
|
||||
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -264,7 +269,7 @@ async function fbLoadFTPFiles(unitId, path) {
|
||||
<div class="flex items-center gap-3 flex-shrink-0 ml-4">
|
||||
<span class="text-xs text-gray-500 hidden sm:inline">${sizeStr}</span>
|
||||
<span class="text-xs text-gray-500 hidden md:inline">${file.modified || ''}</span>
|
||||
<button onclick="fbDownloadToServer('${unitId}', '${escapeForAttribute(file.path)}', '${escapeForAttribute(file.name)}')"
|
||||
<button onclick="FtpBrowser.downloadToServer('${unitId}', '${escapeForAttribute(file.path)}', '${escapeForAttribute(file.name)}')"
|
||||
class="px-3 py-1 text-xs bg-seismo-orange text-white rounded hover:bg-seismo-navy transition-colors flex items-center"
|
||||
title="Download file from device to server and add to project database">
|
||||
<svg class="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -371,7 +376,7 @@ async function toggleFTPFolderProject(unitId, folderPath, folderId, headerElemen
|
||||
files.forEach(file => {
|
||||
const fullPath = file.path || `${folderPath}/${file.name}`;
|
||||
const icon = getFileIcon(file);
|
||||
const sizeText = file.size ? fbFormatFileSize(file.size) : '';
|
||||
const sizeText = file.size ? formatFileSize(file.size) : '';
|
||||
const subFolderId = 'proj-folder-' + fullPath.replace(/[^a-zA-Z0-9]/g, '-');
|
||||
|
||||
if (file.is_dir) {
|
||||
@@ -379,7 +384,7 @@ async function toggleFTPFolderProject(unitId, folderPath, folderId, headerElemen
|
||||
html += `
|
||||
<div class="border border-gray-200 dark:border-gray-600 rounded">
|
||||
<div class="flex items-center justify-between p-2 hover:bg-gray-50 dark:hover:bg-gray-700 rounded transition-colors cursor-pointer text-sm"
|
||||
onclick="toggleFTPFolderProject('${unitId}', '${escapeForAttribute(fullPath)}', '${subFolderId}', this)">
|
||||
onclick="FtpBrowser.toggleFTPFolderProject('${unitId}', '${escapeForAttribute(fullPath)}', '${subFolderId}', this)">
|
||||
<div class="flex items-center flex-1">
|
||||
<svg class="w-3 h-3 mr-2 text-gray-400 transition-transform folder-chevron" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
||||
@@ -389,7 +394,7 @@ async function toggleFTPFolderProject(unitId, folderPath, folderId, headerElemen
|
||||
<span class="ml-2 text-xs text-gray-400 folder-status"></span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 flex-shrink-0 ml-2">
|
||||
<button onclick="event.stopPropagation(); downloadFolderToServer('${unitId}', '${escapeForAttribute(fullPath)}', '${escapeForAttribute(file.name)}')"
|
||||
<button onclick="event.stopPropagation(); FtpBrowser.downloadFolderToServer('${unitId}', '${escapeForAttribute(fullPath)}', '${escapeForAttribute(file.name)}')"
|
||||
class="px-2 py-1 bg-seismo-orange hover:bg-seismo-navy text-white text-xs rounded transition-colors flex items-center"
|
||||
title="Download folder to server">
|
||||
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -412,7 +417,7 @@ async function toggleFTPFolderProject(unitId, folderPath, folderId, headerElemen
|
||||
</div>
|
||||
<div class="flex items-center gap-2 flex-shrink-0 ml-2">
|
||||
<span class="text-xs text-gray-500 hidden sm:inline">${sizeText}</span>
|
||||
<button onclick="fbDownloadToServer('${unitId}', '${escapeForAttribute(fullPath)}', '${escapeForAttribute(file.name)}')"
|
||||
<button onclick="FtpBrowser.downloadToServer('${unitId}', '${escapeForAttribute(fullPath)}', '${escapeForAttribute(file.name)}')"
|
||||
class="px-2 py-1 bg-seismo-orange hover:bg-seismo-navy text-white text-xs rounded transition-colors"
|
||||
title="Download to server">
|
||||
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -436,7 +441,7 @@ async function toggleFTPFolderProject(unitId, folderPath, folderId, headerElemen
|
||||
}
|
||||
}
|
||||
|
||||
async function fbDownloadFTPFile(unitId, remotePath, fileName) {
|
||||
async function downloadFTPFile(unitId, remotePath, fileName) {
|
||||
const btn = event.target;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Downloading...';
|
||||
@@ -561,7 +566,7 @@ async function downloadFolderToServer(unitId, remotePath, folderName) {
|
||||
}
|
||||
}
|
||||
|
||||
async function fbDownloadToServer(unitId, remotePath, fileName) {
|
||||
async function downloadToServer(unitId, remotePath, fileName) {
|
||||
const btn = event.target;
|
||||
const originalText = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
@@ -588,7 +593,7 @@ async function fbDownloadToServer(unitId, remotePath, fileName) {
|
||||
|
||||
if (response.ok) {
|
||||
// Show success message
|
||||
const sizeLine = `\nSize: ${fbFormatFileSize(data.file_size)}`;
|
||||
const sizeLine = `\nSize: ${formatFileSize(data.file_size)}`;
|
||||
const msg = data.ingested
|
||||
? `✓ ${fileName} imported as measurement data!` + formatRunLength(data) + sizeLine
|
||||
: `✓ ${fileName} downloaded to server successfully!\n\nFile ID: ${data.file_id}` + sizeLine;
|
||||
@@ -607,7 +612,7 @@ async function fbDownloadToServer(unitId, remotePath, fileName) {
|
||||
}
|
||||
}
|
||||
|
||||
function fbFormatFileSize(bytes) {
|
||||
function formatFileSize(bytes) {
|
||||
if (bytes < 1024) return bytes + ' B';
|
||||
if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
|
||||
if (bytes < 1073741824) return (bytes / 1048576).toFixed(1) + ' MB';
|
||||
@@ -632,6 +637,17 @@ setTimeout(function() {
|
||||
checkFTPStatus('{{ unit_item.unit.id }}');
|
||||
{% endfor %}
|
||||
}, 100);
|
||||
|
||||
// The only global surface — the handlers referenced by inline onclick attributes.
|
||||
window.FtpBrowser = {
|
||||
loadFTPFiles,
|
||||
enableFTP,
|
||||
disableFTP,
|
||||
toggleFTPFolderProject,
|
||||
downloadFolderToServer,
|
||||
downloadToServer,
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
|
||||
<!-- Include the unified SLM Settings Modal -->
|
||||
|
||||
Reference in New Issue
Block a user