Fix JS syntax errors: convert qq{} to heredocs in restore.live.cgi

Perl's qq{} delimiter matches balanced braces, which conflicted with
JavaScript curly braces, producing empty function bodies. Converted
_print_step1_js and _print_step2_js to heredoc blocks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
shuki
2026-03-05 02:16:58 +02:00
parent d26a327595
commit 8822f93438

View File

@@ -232,83 +232,85 @@ sub handle_step1 {
sub _print_step1_js { sub _print_step1_js {
my ($esc_type, $next_step) = @_; my ($esc_type, $next_step) = @_;
print qq{<script>\n}; print <<"END_JS";
print qq{var gnizaType = '$esc_type';\n}; <script>
print qq{var gnizaNextStep = '$next_step';\n}; var gnizaType = '$esc_type';
print qq{\n}; var gnizaNextStep = '$next_step';
print qq{function gnizaLoadSnapshots() {\n};
print qq{ var remote = document.getElementById('remote').value;\n}; function gnizaLoadSnapshots() {
print qq{ var sel = document.getElementById('timestamp');\n}; var remote = document.getElementById('remote').value;
print qq{ var btn = document.getElementById('next-btn');\n}; var sel = document.getElementById('timestamp');
print qq{\n}; var btn = document.getElementById('next-btn');
print qq{ if (!remote) {\n};
print qq{ _setSelectPlaceholder(sel, '-- Select remote first --');\n}; if (!remote) {
print qq{ sel.disabled = true;\n}; _setSelectPlaceholder(sel, '-- Select remote first --');
print qq{ btn.disabled = true;\n}; sel.disabled = true;
print qq{ return;\n}; btn.disabled = true;
print qq{ }\n}; return;
print qq{\n}; }
print qq{ _setSelectPlaceholder(sel, 'Loading...');\n};
print qq{ sel.disabled = true;\n}; _setSelectPlaceholder(sel, 'Loading...');
print qq{ btn.disabled = true;\n}; sel.disabled = true;
print qq{\n}; btn.disabled = true;
print qq{ var url = 'restore.live.cgi?step=fetch_snapshots&remote=' + encodeURIComponent(remote);\n};
print qq{ var xhr = new XMLHttpRequest();\n}; var url = 'restore.live.cgi?step=fetch_snapshots&remote=' + encodeURIComponent(remote);
print qq{ xhr.open('GET', url, true);\n}; var xhr = new XMLHttpRequest();
print qq{ xhr.onreadystatechange = function() {\n}; xhr.open('GET', url, true);
print qq{ if (xhr.readyState !== 4) return;\n}; xhr.onreadystatechange = function() {
print qq{ if (xhr.status === 200) {\n}; if (xhr.readyState !== 4) return;
print qq{ try {\n}; if (xhr.status === 200) {
print qq{ var data = JSON.parse(xhr.responseText);\n}; try {
print qq{ if (data.error) {\n}; var data = JSON.parse(xhr.responseText);
print qq{ _setSelectPlaceholder(sel, 'Error: ' + data.error);\n}; if (data.error) {
print qq{ } else if (data.snapshots && data.snapshots.length > 0) {\n}; _setSelectPlaceholder(sel, 'Error: ' + data.error);
print qq{ _populateSelect(sel, data.snapshots);\n}; } else if (data.snapshots && data.snapshots.length > 0) {
print qq{ sel.disabled = false;\n}; _populateSelect(sel, data.snapshots);
print qq{ btn.disabled = false;\n}; sel.disabled = false;
print qq{ } else {\n}; btn.disabled = false;
print qq{ _setSelectPlaceholder(sel, 'No snapshots found');\n}; } else {
print qq{ }\n}; _setSelectPlaceholder(sel, 'No snapshots found');
print qq{ } catch(e) {\n}; }
print qq{ _setSelectPlaceholder(sel, 'Failed to parse response');\n}; } catch(e) {
print qq{ }\n}; _setSelectPlaceholder(sel, 'Failed to parse response');
print qq{ } else {\n}; }
print qq{ _setSelectPlaceholder(sel, 'Request failed');\n}; } else {
print qq{ }\n}; _setSelectPlaceholder(sel, 'Request failed');
print qq{ };\n}; }
print qq{ xhr.send();\n}; };
print qq{}\n}; xhr.send();
print qq{\n}; }
print qq{function _setSelectPlaceholder(sel, text) {\n};
print qq{ while (sel.options.length) sel.remove(0);\n}; function _setSelectPlaceholder(sel, text) {
print qq{ var opt = document.createElement('option');\n}; while (sel.options.length) sel.remove(0);
print qq{ opt.value = '';\n}; var opt = document.createElement('option');
print qq{ opt.textContent = text;\n}; opt.value = '';
print qq{ sel.appendChild(opt);\n}; opt.textContent = text;
print qq{}\n}; sel.appendChild(opt);
print qq{\n}; }
print qq{function _populateSelect(sel, values) {\n};
print qq{ while (sel.options.length) sel.remove(0);\n}; function _populateSelect(sel, values) {
print qq{ for (var i = 0; i < values.length; i++) {\n}; while (sel.options.length) sel.remove(0);
print qq{ var opt = document.createElement('option');\n}; for (var i = 0; i < values.length; i++) {
print qq{ opt.value = values[i];\n}; var opt = document.createElement('option');
print qq{ opt.textContent = values[i];\n}; opt.value = values[i];
print qq{ sel.appendChild(opt);\n}; opt.textContent = values[i];
print qq{ }\n}; sel.appendChild(opt);
print qq{}\n}; }
print qq{\n}; }
print qq{function gnizaGoNext() {\n};
print qq{ var remote = document.getElementById('remote').value;\n}; function gnizaGoNext() {
print qq{ var timestamp = document.getElementById('timestamp').value;\n}; var remote = document.getElementById('remote').value;
print qq{ if (!remote || !timestamp) return;\n}; var timestamp = document.getElementById('timestamp').value;
print qq{\n}; if (!remote || !timestamp) return;
print qq{ var url = 'restore.live.cgi?step=' + gnizaNextStep\n};
print qq{ + '&type=' + encodeURIComponent(gnizaType)\n}; var url = 'restore.live.cgi?step=' + gnizaNextStep
print qq{ + '&remote=' + encodeURIComponent(remote)\n}; + '&type=' + encodeURIComponent(gnizaType)
print qq{ + '&timestamp=' + encodeURIComponent(timestamp);\n}; + '&remote=' + encodeURIComponent(remote)
print qq{ window.location.href = url;\n}; + '&timestamp=' + encodeURIComponent(timestamp);
print qq{}\n}; window.location.href = url;
print qq{</script>\n}; }
</script>
END_JS
} }
# ── Step 2: Select specific item ───────────────────────────── # ── Step 2: Select specific item ─────────────────────────────
@@ -409,280 +411,282 @@ sub _render_file_picker {
sub _print_step2_js { sub _print_step2_js {
my ($esc_type, $esc_remote, $esc_timestamp) = @_; my ($esc_type, $esc_remote, $esc_timestamp) = @_;
print qq{<script>\n}; print <<"END_JS";
print qq{var gnizaType = '$esc_type';\n}; <script>
print qq{var gnizaRemote = '$esc_remote';\n}; var gnizaType = '$esc_type';
print qq{var gnizaTimestamp = '$esc_timestamp';\n}; var gnizaRemote = '$esc_remote';
print qq{var fbCache = {};\n}; var gnizaTimestamp = '$esc_timestamp';
print qq{var fbSelected = '';\n}; var fbCache = {};
print qq{\n}; var fbSelected = '';
print qq{(function() {\n};
print qq{ var listTypes = ['database','dbusers','mailbox','domains','ssl','cron'];\n}; (function() {
print qq{ if (listTypes.indexOf(gnizaType) >= 0) {\n}; var listTypes = ['database','dbusers','mailbox','domains','ssl','cron'];
print qq{ loadOptions();\n}; if (listTypes.indexOf(gnizaType) >= 0) {
print qq{ }\n}; loadOptions();
print qq{})();\n}; }
print qq{\n}; })();
print qq{function loadOptions() {\n};
print qq{ var url = 'restore.live.cgi?step=fetch_options'\n}; function loadOptions() {
print qq{ + '&remote=' + encodeURIComponent(gnizaRemote)\n}; var url = 'restore.live.cgi?step=fetch_options'
print qq{ + '&timestamp=' + encodeURIComponent(gnizaTimestamp)\n}; + '&remote=' + encodeURIComponent(gnizaRemote)
print qq{ + '&type=' + encodeURIComponent(gnizaType);\n}; + '&timestamp=' + encodeURIComponent(gnizaTimestamp)
print qq{\n}; + '&type=' + encodeURIComponent(gnizaType);
print qq{ var xhr = new XMLHttpRequest();\n};
print qq{ xhr.open('GET', url, true);\n}; var xhr = new XMLHttpRequest();
print qq{ xhr.onreadystatechange = function() {\n}; xhr.open('GET', url, true);
print qq{ if (xhr.readyState !== 4) return;\n}; xhr.onreadystatechange = function() {
print qq{ var container = document.getElementById('item-list');\n}; if (xhr.readyState !== 4) return;
print qq{ if (xhr.status === 200) {\n}; var container = document.getElementById('item-list');
print qq{ try {\n}; if (xhr.status === 200) {
print qq{ var data = JSON.parse(xhr.responseText);\n}; try {
print qq{ if (data.error) {\n}; var data = JSON.parse(xhr.responseText);
print qq{ container.textContent = 'Error: ' + data.error;\n}; if (data.error) {
print qq{ } else if (gnizaType === 'cron') {\n}; container.textContent = 'Error: ' + data.error;
print qq{ populatePreview(container, data.options);\n}; } else if (gnizaType === 'cron') {
print qq{ } else {\n}; populatePreview(container, data.options);
print qq{ populateChecklist(container, data.options);\n}; } else {
print qq{ }\n}; populateChecklist(container, data.options);
print qq{ } catch(e) {\n}; }
print qq{ container.textContent = 'Failed to parse response';\n}; } catch(e) {
print qq{ }\n}; container.textContent = 'Failed to parse response';
print qq{ } else {\n}; }
print qq{ container.textContent = 'Request failed';\n}; } else {
print qq{ }\n}; container.textContent = 'Request failed';
print qq{ };\n}; }
print qq{ xhr.send();\n}; };
print qq{}\n}; xhr.send();
print qq{\n}; }
print qq{function populateChecklist(container, options) {\n};
print qq{ var hidden = document.getElementById('selected_items');\n}; function populateChecklist(container, options) {
print qq{ container.textContent = '';\n}; var hidden = document.getElementById('selected_items');
print qq{ if (!options || options.length === 0) {\n}; container.textContent = '';
print qq{ container.textContent = '(none found)';\n}; if (!options || options.length === 0) {
print qq{ return;\n}; container.textContent = '(none found)';
print qq{ }\n}; return;
print qq{\n}; }
print qq{ var allLabels = {database:'All Databases',dbusers:'All Database Users',mailbox:'All Mailboxes',domains:'All Domains',ssl:'All Certificates'};\n};
print qq{ var allLabel = allLabels[gnizaType] || 'All';\n}; var allLabels = {database:'All Databases',dbusers:'All Database Users',mailbox:'All Mailboxes',domains:'All Domains',ssl:'All Certificates'};
print qq{\n}; var allLabel = allLabels[gnizaType] || 'All';
print qq{ var allRow = _makeCheckRow(allLabel, '', true);\n};
print qq{ allRow.querySelector('input').setAttribute('data-all', '1');\n}; var allRow = _makeCheckRow(allLabel, '', true);
print qq{ allRow.querySelector('input').onchange = function() { toggleAll(this.checked); };\n}; allRow.querySelector('input').setAttribute('data-all', '1');
print qq{ allRow.querySelector('span').className = 'text-sm font-semibold';\n}; allRow.querySelector('input').onchange = function() { toggleAll(this.checked); };
print qq{ container.appendChild(allRow);\n}; allRow.querySelector('span').className = 'text-sm font-semibold';
print qq{\n}; container.appendChild(allRow);
print qq{ for (var i = 0; i < options.length; i++) {\n};
print qq{ var row = _makeCheckRow(options[i], options[i], false);\n}; for (var i = 0; i < options.length; i++) {
print qq{ row.querySelector('input').setAttribute('data-item', '1');\n}; var row = _makeCheckRow(options[i], options[i], false);
print qq{ row.querySelector('input').onchange = function() { syncHidden(); };\n}; row.querySelector('input').setAttribute('data-item', '1');
print qq{ container.appendChild(row);\n}; row.querySelector('input').onchange = function() { syncHidden(); };
print qq{ }\n}; container.appendChild(row);
print qq{}\n}; }
print qq{\n}; }
print qq{function _makeCheckRow(labelText, value, isAll) {\n};
print qq{ var label = document.createElement('label');\n}; function _makeCheckRow(labelText, value, isAll) {
print qq{ label.className = 'flex items-center gap-2 cursor-pointer';\n}; var label = document.createElement('label');
print qq{ var cb = document.createElement('input');\n}; label.className = 'flex items-center gap-2 cursor-pointer';
print qq{ cb.type = 'checkbox';\n}; var cb = document.createElement('input');
print qq{ cb.className = 'checkbox checkbox-sm';\n}; cb.type = 'checkbox';
print qq{ if (value) cb.value = value;\n}; cb.className = 'checkbox checkbox-sm';
print qq{ var span = document.createElement('span');\n}; if (value) cb.value = value;
print qq{ span.className = 'text-sm';\n}; var span = document.createElement('span');
print qq{ span.textContent = labelText;\n}; span.className = 'text-sm';
print qq{ label.appendChild(cb);\n}; span.textContent = labelText;
print qq{ label.appendChild(span);\n}; label.appendChild(cb);
print qq{ return label;\n}; label.appendChild(span);
print qq{}\n}; return label;
print qq{\n}; }
print qq{function toggleAll(checked) {\n};
print qq{ var container = document.getElementById('item-list');\n}; function toggleAll(checked) {
print qq{ var hidden = document.getElementById('selected_items');\n}; var container = document.getElementById('item-list');
print qq{ var items = container.querySelectorAll('input[data-item]');\n}; var hidden = document.getElementById('selected_items');
print qq{ for (var i = 0; i < items.length; i++) {\n}; var items = container.querySelectorAll('input[data-item]');
print qq{ items[i].disabled = checked;\n}; for (var i = 0; i < items.length; i++) {
print qq{ if (checked) items[i].checked = false;\n}; items[i].disabled = checked;
print qq{ }\n}; if (checked) items[i].checked = false;
print qq{ hidden.value = checked ? '__ALL__' : '';\n}; }
print qq{}\n}; hidden.value = checked ? '__ALL__' : '';
print qq{\n}; }
print qq{function syncHidden() {\n};
print qq{ var container = document.getElementById('item-list');\n}; function syncHidden() {
print qq{ var hidden = document.getElementById('selected_items');\n}; var container = document.getElementById('item-list');
print qq{ var items = container.querySelectorAll('input[data-item]:checked');\n}; var hidden = document.getElementById('selected_items');
print qq{ var vals = [];\n}; var items = container.querySelectorAll('input[data-item]:checked');
print qq{ for (var i = 0; i < items.length; i++) {\n}; var vals = [];
print qq{ vals.push(items[i].value);\n}; for (var i = 0; i < items.length; i++) {
print qq{ }\n}; vals.push(items[i].value);
print qq{ hidden.value = vals.join(',');\n}; }
print qq{}\n}; hidden.value = vals.join(',');
print qq{\n}; }
print qq{function populatePreview(container, options) {\n};
print qq{ container.textContent = '';\n}; function populatePreview(container, options) {
print qq{ if (!options || options.length === 0) {\n}; container.textContent = '';
print qq{ container.textContent = '(none found)';\n}; if (!options || options.length === 0) {
print qq{ return;\n}; container.textContent = '(none found)';
print qq{ }\n}; return;
print qq{ var pre = document.createElement('pre');\n}; }
print qq{ pre.className = 'text-xs font-mono bg-base-200 p-3 rounded-lg overflow-x-auto';\n}; var pre = document.createElement('pre');
print qq{ pre.textContent = options.join('\\n');\n}; pre.className = 'text-xs font-mono bg-base-200 p-3 rounded-lg overflow-x-auto';
print qq{ container.appendChild(pre);\n}; pre.textContent = options.join('\\n');
print qq{}\n}; container.appendChild(pre);
print qq{\n}; }
print qq{function gnizaGoConfirm() {\n};
print qq{ var url = 'restore.live.cgi?step=3'\n}; function gnizaGoConfirm() {
print qq{ + '&type=' + encodeURIComponent(gnizaType)\n}; var url = 'restore.live.cgi?step=3'
print qq{ + '&remote=' + encodeURIComponent(gnizaRemote)\n}; + '&type=' + encodeURIComponent(gnizaType)
print qq{ + '&timestamp=' + encodeURIComponent(gnizaTimestamp);\n}; + '&remote=' + encodeURIComponent(gnizaRemote)
print qq{\n}; + '&timestamp=' + encodeURIComponent(gnizaTimestamp);
print qq{ if (gnizaType === 'files') {\n};
print qq{ var path = document.getElementById('path') ? document.getElementById('path').value : '';\n}; if (gnizaType === 'files') {
print qq{ url += '&path=' + encodeURIComponent(path);\n}; var path = document.getElementById('path') ? document.getElementById('path').value : '';
print qq{ } else if (document.getElementById('selected_items')) {\n}; url += '&path=' + encodeURIComponent(path);
print qq{ url += '&items=' + encodeURIComponent(document.getElementById('selected_items').value);\n}; } else if (document.getElementById('selected_items')) {
print qq{ }\n}; url += '&items=' + encodeURIComponent(document.getElementById('selected_items').value);
print qq{\n}; }
print qq{ window.location.href = url;\n};
print qq{}\n}; window.location.href = url;
print qq{\n}; }
print qq{function gnizaOpenBrowser() {\n};
print qq{ fbSelected = '';\n}; function gnizaOpenBrowser() {
print qq{ document.getElementById('fb-select-btn').disabled = true;\n}; fbSelected = '';
print qq{ document.getElementById('fb-modal').showModal();\n}; document.getElementById('fb-select-btn').disabled = true;
print qq{ gnizaLoadDir('');\n}; document.getElementById('fb-modal').showModal();
print qq{}\n}; gnizaLoadDir('');
print qq{\n}; }
print qq{function gnizaLoadDir(path) {\n};
print qq{ var cacheKey = path;\n}; function gnizaLoadDir(path) {
print qq{ if (fbCache[cacheKey]) {\n}; var cacheKey = path;
print qq{ gnizaRenderFileList(path, fbCache[cacheKey]);\n}; if (fbCache[cacheKey]) {
print qq{ return;\n}; gnizaRenderFileList(path, fbCache[cacheKey]);
print qq{ }\n}; return;
print qq{\n}; }
print qq{ document.getElementById('fb-loading').hidden = false;\n};
print qq{ document.getElementById('fb-error').hidden = true;\n}; document.getElementById('fb-loading').hidden = false;
print qq{ document.getElementById('fb-tbody').textContent = '';\n}; document.getElementById('fb-error').hidden = true;
print qq{\n}; document.getElementById('fb-tbody').textContent = '';
print qq{ var url = 'restore.live.cgi?step=fetch_options'\n};
print qq{ + '&remote=' + encodeURIComponent(gnizaRemote)\n}; var url = 'restore.live.cgi?step=fetch_options'
print qq{ + '&timestamp=' + encodeURIComponent(gnizaTimestamp)\n}; + '&remote=' + encodeURIComponent(gnizaRemote)
print qq{ + '&type=files'\n}; + '&timestamp=' + encodeURIComponent(gnizaTimestamp)
print qq{ + (path ? '&path=' + encodeURIComponent(path) : '');\n}; + '&type=files'
print qq{\n}; + (path ? '&path=' + encodeURIComponent(path) : '');
print qq{ var xhr = new XMLHttpRequest();\n};
print qq{ xhr.open('GET', url, true);\n}; var xhr = new XMLHttpRequest();
print qq{ xhr.onreadystatechange = function() {\n}; xhr.open('GET', url, true);
print qq{ if (xhr.readyState !== 4) return;\n}; xhr.onreadystatechange = function() {
print qq{ document.getElementById('fb-loading').hidden = true;\n}; if (xhr.readyState !== 4) return;
print qq{ if (xhr.status === 200) {\n}; document.getElementById('fb-loading').hidden = true;
print qq{ try {\n}; if (xhr.status === 200) {
print qq{ var data = JSON.parse(xhr.responseText);\n}; try {
print qq{ if (data.error) {\n}; var data = JSON.parse(xhr.responseText);
print qq{ document.getElementById('fb-error').textContent = data.error;\n}; if (data.error) {
print qq{ document.getElementById('fb-error').hidden = false;\n}; document.getElementById('fb-error').textContent = data.error;
print qq{ } else {\n}; document.getElementById('fb-error').hidden = false;
print qq{ fbCache[cacheKey] = data.options;\n}; } else {
print qq{ gnizaRenderFileList(path, data.options);\n}; fbCache[cacheKey] = data.options;
print qq{ }\n}; gnizaRenderFileList(path, data.options);
print qq{ } catch(e) {\n}; }
print qq{ document.getElementById('fb-error').textContent = 'Failed to parse response';\n}; } catch(e) {
print qq{ document.getElementById('fb-error').hidden = false;\n}; document.getElementById('fb-error').textContent = 'Failed to parse response';
print qq{ }\n}; document.getElementById('fb-error').hidden = false;
print qq{ }\n}; }
print qq{ };\n}; }
print qq{ xhr.send();\n}; };
print qq{}\n}; xhr.send();
print qq{\n}; }
print qq{function gnizaRenderBreadcrumbs(path) {\n};
print qq{ var ul = document.createElement('ul');\n}; function gnizaRenderBreadcrumbs(path) {
print qq{ var li = document.createElement('li');\n}; var ul = document.createElement('ul');
print qq{ var a = document.createElement('a');\n}; var li = document.createElement('li');
print qq{ a.textContent = 'homedir';\n}; var a = document.createElement('a');
print qq{ a.href = '#';\n}; a.textContent = 'homedir';
print qq{ a.onclick = function(e) { e.preventDefault(); gnizaLoadDir(''); };\n}; a.href = '#';
print qq{ li.appendChild(a);\n}; a.onclick = function(e) { e.preventDefault(); gnizaLoadDir(''); };
print qq{ ul.appendChild(li);\n}; li.appendChild(a);
print qq{\n}; ul.appendChild(li);
print qq{ if (path) {\n};
print qq{ var parts = path.replace(/\\/\$/, '').split('/');\n}; if (path) {
print qq{ var built = '';\n}; var parts = path.replace(/\\/\$/, '').split('/');
print qq{ for (var i = 0; i < parts.length; i++) {\n}; var built = '';
print qq{ built += (i > 0 ? '/' : '') + parts[i];\n}; for (var i = 0; i < parts.length; i++) {
print qq{ li = document.createElement('li');\n}; built += (i > 0 ? '/' : '') + parts[i];
print qq{ if (i < parts.length - 1) {\n}; li = document.createElement('li');
print qq{ a = document.createElement('a');\n}; if (i < parts.length - 1) {
print qq{ a.textContent = parts[i];\n}; a = document.createElement('a');
print qq{ a.href = '#';\n}; a.textContent = parts[i];
print qq{ (function(p) { a.onclick = function(e) { e.preventDefault(); gnizaLoadDir(p); }; })(built);\n}; a.href = '#';
print qq{ li.appendChild(a);\n}; (function(p) { a.onclick = function(e) { e.preventDefault(); gnizaLoadDir(p); }; })(built);
print qq{ } else {\n}; li.appendChild(a);
print qq{ li.textContent = parts[i];\n}; } else {
print qq{ }\n}; li.textContent = parts[i];
print qq{ ul.appendChild(li);\n}; }
print qq{ }\n}; ul.appendChild(li);
print qq{ }\n}; }
print qq{\n}; }
print qq{ var bc = document.getElementById('fb-breadcrumbs');\n};
print qq{ bc.textContent = '';\n}; var bc = document.getElementById('fb-breadcrumbs');
print qq{ bc.appendChild(ul);\n}; bc.textContent = '';
print qq{}\n}; bc.appendChild(ul);
print qq{\n}; }
print qq{function gnizaRenderFileList(currentPath, entries) {\n};
print qq{ gnizaRenderBreadcrumbs(currentPath);\n}; function gnizaRenderFileList(currentPath, entries) {
print qq{ fbSelected = '';\n}; gnizaRenderBreadcrumbs(currentPath);
print qq{ document.getElementById('fb-select-btn').disabled = true;\n}; fbSelected = '';
print qq{\n}; document.getElementById('fb-select-btn').disabled = true;
print qq{ var tbody = document.getElementById('fb-tbody');\n};
print qq{ tbody.textContent = '';\n}; var tbody = document.getElementById('fb-tbody');
print qq{\n}; tbody.textContent = '';
print qq{ if (!entries || entries.length === 0) {\n};
print qq{ var emptyTr = document.createElement('tr');\n}; if (!entries || entries.length === 0) {
print qq{ var emptyTd = document.createElement('td');\n}; var emptyTr = document.createElement('tr');
print qq{ emptyTd.className = 'text-center text-base-content/60 py-4';\n}; var emptyTd = document.createElement('td');
print qq{ emptyTd.textContent = '(empty directory)';\n}; emptyTd.className = 'text-center text-base-content/60 py-4';
print qq{ emptyTr.appendChild(emptyTd);\n}; emptyTd.textContent = '(empty directory)';
print qq{ tbody.appendChild(emptyTr);\n}; emptyTr.appendChild(emptyTd);
print qq{ return;\n}; tbody.appendChild(emptyTr);
print qq{ }\n}; return;
print qq{\n}; }
print qq{ for (var i = 0; i < entries.length; i++) {\n};
print qq{ var entry = entries[i];\n}; for (var i = 0; i < entries.length; i++) {
print qq{ var isDir = entry.endsWith('/');\n}; var entry = entries[i];
print qq{ var fullPath = currentPath ? currentPath.replace(/\\/\$/, '') + '/' + entry : entry;\n}; var isDir = entry.endsWith('/');
print qq{\n}; var fullPath = currentPath ? currentPath.replace(/\\/\$/, '') + '/' + entry : entry;
print qq{ var tr = document.createElement('tr');\n};
print qq{ tr.className = 'cursor-pointer hover';\n}; var tr = document.createElement('tr');
print qq{ tr.setAttribute('data-path', fullPath);\n}; tr.className = 'cursor-pointer hover';
print qq{\n}; tr.setAttribute('data-path', fullPath);
print qq{ var td = document.createElement('td');\n};
print qq{ td.className = 'py-1';\n}; var td = document.createElement('td');
print qq{ var icon = isDir ? '\\uD83D\\uDCC1 ' : '\\uD83D\\uDCC4 ';\n}; td.className = 'py-1';
print qq{ td.textContent = icon + entry;\n}; var icon = isDir ? '\\uD83D\\uDCC1 ' : '\\uD83D\\uDCC4 ';
print qq{ tr.appendChild(td);\n}; td.textContent = icon + entry;
print qq{\n}; tr.appendChild(td);
print qq{ (function(row, path, dir) {\n};
print qq{ row.onclick = function() {\n}; (function(row, path, dir) {
print qq{ var rows = document.getElementById('fb-tbody').querySelectorAll('tr');\n}; row.onclick = function() {
print qq{ for (var j = 0; j < rows.length; j++) rows[j].classList.remove('bg-primary/10');\n}; var rows = document.getElementById('fb-tbody').querySelectorAll('tr');
print qq{ row.classList.add('bg-primary/10');\n}; for (var j = 0; j < rows.length; j++) rows[j].classList.remove('bg-primary/10');
print qq{ fbSelected = path;\n}; row.classList.add('bg-primary/10');
print qq{ document.getElementById('fb-select-btn').disabled = false;\n}; fbSelected = path;
print qq{ };\n}; document.getElementById('fb-select-btn').disabled = false;
print qq{ if (dir) {\n}; };
print qq{ row.ondblclick = function() { gnizaLoadDir(path.replace(/\\/\$/, '')); };\n}; if (dir) {
print qq{ }\n}; row.ondblclick = function() { gnizaLoadDir(path.replace(/\\/\$/, '')); };
print qq{ })(tr, fullPath, isDir);\n}; }
print qq{\n}; })(tr, fullPath, isDir);
print qq{ tbody.appendChild(tr);\n};
print qq{ }\n}; tbody.appendChild(tr);
print qq{}\n}; }
print qq{\n}; }
print qq{function gnizaSelectPath() {\n};
print qq{ if (fbSelected) {\n}; function gnizaSelectPath() {
print qq{ document.getElementById('path').value = fbSelected;\n}; if (fbSelected) {
print qq{ }\n}; document.getElementById('path').value = fbSelected;
print qq{ document.getElementById('fb-modal').close();\n}; }
print qq{}\n}; document.getElementById('fb-modal').close();
print qq{</script>\n}; }
</script>
END_JS
} }
# ── Step 3: Confirmation ───────────────────────────────────── # ── Step 3: Confirmation ─────────────────────────────────────