var decodeEntities = (function() { // this prevents any overhead from creating the object each time var element = document.createElement('div'); function decodeHTMLEntities (str) { if(str && typeof str === 'string') { // strip script/html tags str = str.replace(/]*>([\S\s]*?)<\/script>/gmi, ''); str = str.replace(/<\/?\w(?:[^"'>]|"[^"]*"|'[^']*')*>/gmi, ''); element.innerHTML = str; str = element.textContent; element.textContent = ''; } return str; } return decodeHTMLEntities; })(); $(document).ready(function () { // Export content as HTML $('#editDownloadHtml').click(function () { var htmlContent = $('#editFrameTest'); downloadFile(htmlContent, 'content.html', 'text/html'); }); // Export content as plain text $('#editDownloadText').click(function () { //var plainTextContent = $('#editor').trumbowyg('html').replace(/<[^>]*>/g, ''); var plainTextContent = decodeEntities($('#editFrameTest')); downloadFile(plainTextContent, 'content.txt', 'text/plain'); }); // Quit back to Filemanager. $('#editQuit').click(function () { history.back(); }); // Save buffer and continue editting. $('#editSave').click(function () { saveFile(); }); // Save buffer and exit. $('#editExit').click(function () { saveFile(); history.back(); }); // Save buffer and apply configuration. $('#editApply').click(function () { const statusDiv = document.getElementById('saveStatus'); // Updated changes. saveFile(); // Reload ESP32, if the config has changed then it will initiate an RP2350 config reload. setTimeout(function() { const statusDiv = document.getElementById('saveStatus'); if (statusDiv) { statusDiv.style.backgroundColor = "#fff3cd"; statusDiv.style.color = "#856404"; statusDiv.innerHTML = "Requesting ESP32 reload configuration,,,)"; } setTimeout(function() { window.location.href="/reboot/esp32"; }, 2000); }, 2000); }); // Method to save the contents of the textarea buffer into a file on the SD card (AJAX style) async function saveFile() { const editFileInput = document.getElementById('editFile'); const textarea = document.getElementById('editFrameText'); const statusDiv = document.getElementById('saveStatus'); if (!editFileInput || !textarea) { alert("Editor elements not found!"); return; } let fullInput = editFileInput.value.trim(); if (!fullInput) { alert("No file path/filename specified!"); return; } // Clean path (same logic as before) let cleanedPath = fullInput .replace(/^\/(sdcard|sdpath)\/?/i, '') .replace(/^sd(card|path)\/?/i, '') .replace(/^[\\/]+/, ''); if (!cleanedPath) { alert("No valid path after removing SD prefix!"); return; } // Split → dir + filename let dirPart = ""; let filenameOnly = cleanedPath; const lastSlash = Math.max(cleanedPath.lastIndexOf('/'), cleanedPath.lastIndexOf('\\')); if (lastSlash >= 0) { dirPart = cleanedPath.substring(0, lastSlash).replace(/^[\\/]+|[\\/]+$/g, ''); filenameOnly = cleanedPath.substring(lastSlash + 1); } if (!filenameOnly) { alert("No valid filename found!"); return; } if (filenameOnly.includes(' ')) { alert("Filename cannot contain spaces!"); return; } // Prepare UI for saving const saveButton = document.getElementById('editSave'); if (saveButton) saveButton.disabled = true; textarea.disabled = true; editFileInput.disabled = true; if (statusDiv) { statusDiv.style.backgroundColor = "#fff3cd"; statusDiv.style.color = "#856404"; statusDiv.innerHTML = "Saving... (creating backup if needed)"; } // Attempt backup. Keep same name as server will rename to ; let backupDone = false; let backupMsg = ""; try { const renameParams = new URLSearchParams(); if (dirPart) renameParams.set("dir", dirPart); renameParams.set("oldname", filenameOnly); renameParams.set("name", filenameOnly); const renameUrl = `/data/rename?${renameParams.toString()}`; const renameRes = await fetch(renameUrl, { method: "GET" }); backupDone = renameRes.ok; backupMsg = backupDone ? "Backup created. " : "No backup created (may already exist). "; } catch (err) { console.warn("Backup rename failed:", err); backupMsg = "Backup attempt failed. "; } // Save the current content const content = textarea.value; let saveUrl = `/data/upload/${encodeURIComponent(filenameOnly)}`; if (dirPart) { saveUrl += `?dir=${encodeURIComponent(dirPart)}`; } try { const saveRes = await fetch(saveUrl, { method: "POST", headers: { "Content-Type": "text/plain; charset=utf-8" }, body: content }); if (!saveRes.ok) { const errorText = await saveRes.text().catch(() => "(no details)"); throw new Error(`Save failed (${saveRes.status}) – ${errorText}`); } // Success path if (statusDiv) { statusDiv.style.backgroundColor = "#d4edda"; statusDiv.style.color = "#155724"; statusDiv.innerHTML = `✓ Saved successfully. ${backupMsg}`; } // Optional: small delay then fade or clear status setTimeout(() => { if (statusDiv) statusDiv.style.opacity = "0.7"; }, 2000); } catch (err) { console.error("Save error:", err); if (statusDiv) { statusDiv.style.backgroundColor = "#f8d7da"; statusDiv.style.color = "#721c24"; statusDiv.innerHTML = `✗ Save failed: ${err.message}`; } alert("Save failed!\n" + err.message); // keep alert as fallback } finally { // Re-enable controls if (saveButton) saveButton.disabled = false; textarea.disabled = false; editFileInput.disabled = false; } } // Function to download file function downloadFile(content, filename, contentType) { var blob = new Blob([ content ], { type: contentType }); var url = window.URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); } });