Refactor WiFi scanning JavaScript to use raw string literal template

- Remove async parameter from WiFi_scanNetworks() method signatures
- Move JavaScript code to templates/WiFiPollingJS.h using R"rawliteral" format
- Fix JavaScript syntax errors by eliminating quote escaping issues
- Change scan status route from /wifi/scanstatus to /wifistatus to avoid route conflicts
- Cleaner code structure with JavaScript in separate template file
This commit is contained in:
2025-11-05 20:47:10 +10:00
parent d533f4d754
commit a0a7b7e263
4 changed files with 115 additions and 109 deletions
-2
View File
@@ -782,8 +782,6 @@ protected:
String WiFi_psk(bool persistent = true) const; String WiFi_psk(bool persistent = true) const;
bool WiFi_scanNetworks(); bool WiFi_scanNetworks();
bool WiFi_scanNetworks(bool force); // Always async - returns false if scan started but not complete bool WiFi_scanNetworks(bool force); // Always async - returns false if scan started but not complete
bool WiFi_scanNetworks(bool force,bool async); // async parameter ignored - always async
bool WiFi_scanNetworks(unsigned int cachetime,bool async); // async parameter ignored - always async
bool WiFi_scanNetworks(unsigned int cachetime); bool WiFi_scanNetworks(unsigned int cachetime);
void WiFi_scanComplete(int networksFound); void WiFi_scanComplete(int networksFound);
bool WiFiSetCountry(); bool WiFiSetCountry();
@@ -0,0 +1,112 @@
/**
* WiFiPollingJS.h
* JavaScript code for WiFi network scanning with AJAX polling
* This is embedded in the WiFi configuration page for async WiFi scanning
*/
#ifndef _WIFI_POLLING_JS_H_
#define _WIFI_POLLING_JS_H_
#include <Arduino.h>
const char PROGMEM WIFI_POLLING_JS[] = R"rawliteral(
<script>
let scanPollInterval = null;
let isPolling = false;
function refreshScan() {
// Trigger scan via refresh parameter
fetch('/wifi?refresh=1', {method: 'GET'}).then(() => {
startPolling();
});
}
function startPolling() {
if(isPolling) return;
isPolling = true;
document.getElementById('refresh-btn').disabled = true;
document.getElementById('refresh-btn').textContent = 'Scanning...';
updateScanStatus();
scanPollInterval = setInterval(updateScanStatus, 1000);
}
function stopPolling() {
if(scanPollInterval) {
clearInterval(scanPollInterval);
scanPollInterval = null;
}
isPolling = false;
document.getElementById('refresh-btn').disabled = false;
document.getElementById('refresh-btn').textContent = 'Refresh';
}
function updateScanStatus() {
fetch('/wifistatus')
.then(response => response.json())
.then(data => {
if(data.scanning) {
// Still scanning, show status
document.getElementById('scan-results').innerHTML = 'Scanning for networks...<br/><br/>';
} else {
// Scan complete, update network list
stopPolling();
updateNetworkList(data);
}
})
.catch(error => {
console.error('Error polling scan status:', error);
stopPolling();
});
}
function updateNetworkList(data) {
let html = '';
if(data.count === 0) {
html = 'No networks found. Refresh to scan again.<br/><br/>';
} else if(data.networks && data.networks.length > 0) {
data.networks.forEach(function(network) {
let qualityPercent = network.quality + '%';
let encrypted = network.enc_type !== 0 ? '<span class="l">🔒</span>' : '';
let ssidEscaped = escapeHtml(network.ssid);
html += '<div><a href="#p" onclick="c(this)" data-ssid="' + ssidEscaped + '">' + ssidEscaped + '</a>';
html += '<div role="img" aria-label="' + qualityPercent + '" title="' + qualityPercent + '" class="q q-' + network.quality + '"></div>';
html += '<div class="q">' + qualityPercent + '</div>';
html += encrypted;
html += '</div>';
});
}
document.getElementById('scan-results').innerHTML = html;
}
function escapeHtml(text) {
var map = {};
map['&'] = '&amp;';
map['<'] = '&lt;';
map['>'] = '&gt;';
map['"'] = '&quot;';
map["'"] = '&#039;';
return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}
function getQualityIcon(quality) {
if(quality >= 75) return '▂▄▆█';
if(quality >= 50) return '▂▄▆▁';
if(quality >= 25) return '▂▄▁▁';
return '▂▁▁▁';
}
// Check if scan is in progress on page load
window.addEventListener('load', function() {
fetch('/wifistatus')
.then(response => response.json())
.then(data => {
if(data.scanning) {
startPolling();
}
});
});
</script>
)rawliteral";
#endif // _WIFI_POLLING_JS_H_
+1 -1
View File
@@ -59,7 +59,7 @@ const char R_exit[] PROGMEM = "/exit";
const char R_close[] PROGMEM = "/close"; const char R_close[] PROGMEM = "/close";
const char R_erase[] PROGMEM = "/erase"; const char R_erase[] PROGMEM = "/erase";
const char R_status[] PROGMEM = "/status"; const char R_status[] PROGMEM = "/status";
const char R_scanstatus[] PROGMEM = "/wifi/scanstatus"; const char R_scanstatus[] PROGMEM = "/wifistatus";
const char R_update[] PROGMEM = "/update"; const char R_update[] PROGMEM = "/update";
const char R_updatedone[] PROGMEM = "/u"; const char R_updatedone[] PROGMEM = "/u";
+2 -106
View File
@@ -11,6 +11,7 @@
*/ */
#include "WiFiManager.h" #include "WiFiManager.h"
#include "templates/WiFiPollingJS.h"
#if defined(ESP8266) || defined(ESP32) #if defined(ESP8266) || defined(ESP32)
@@ -1501,104 +1502,7 @@ void WiFiManager::handleWifi(AsyncWebServerRequest *request, boolean scan) {
reportStatus(page); reportStatus(page);
// Add JavaScript for AJAX polling // Add JavaScript for AJAX polling
page += F("<script>"); page += FPSTR(WIFI_POLLING_JS);
page += F("let scanPollInterval = null;");
page += F("let isPolling = false;");
page += F("");
page += F("function refreshScan() {");
page += F(" // Trigger scan via refresh parameter");
page += F(" fetch('/wifi?refresh=1', {method: 'GET'}).then(() => {");
page += F(" startPolling();");
page += F(" });");
page += F("}");
page += F("");
page += F("function startPolling() {");
page += F(" if(isPolling) return;");
page += F(" isPolling = true;");
page += F(" document.getElementById('refresh-btn').disabled = true;");
page += F(" document.getElementById('refresh-btn').textContent = 'Scanning...';");
page += F(" updateScanStatus();");
page += F(" scanPollInterval = setInterval(updateScanStatus, 1000);");
page += F("}");
page += F("");
page += F("function stopPolling() {");
page += F(" if(scanPollInterval) {");
page += F(" clearInterval(scanPollInterval);");
page += F(" scanPollInterval = null;");
page += F(" }");
page += F(" isPolling = false;");
page += F(" document.getElementById('refresh-btn').disabled = false;");
page += F(" document.getElementById('refresh-btn').textContent = 'Refresh';");
page += F("}");
page += F("");
page += F("function updateScanStatus() {");
page += F(" fetch('/wifi/scanstatus')");
page += F(" .then(response => response.json())");
page += F(" .then(data => {");
page += F(" if(data.scanning) {");
page += F(" // Still scanning, show status");
page += F(" document.getElementById('scan-results').innerHTML = 'Scanning for networks...<br/><br/>';");
page += F(" } else {");
page += F(" // Scan complete, update network list");
page += F(" stopPolling();");
page += F(" updateNetworkList(data);");
page += F(" }");
page += F(" })");
page += F(" .catch(error => {");
page += F(" console.error('Error polling scan status:', error);");
page += F(" stopPolling();");
page += F(" });");
page += F("}");
page += F("");
page += F("function updateNetworkList(data) {");
page += F(" let html = '';");
page += F(" if(data.count === 0) {");
page += F(" html = 'No networks found. Refresh to scan again.<br/><br/>';");
page += F(" } else if(data.networks && data.networks.length > 0) {");
page += F(" data.networks.forEach(function(network) {");
page += F(" // Match existing HTML structure from getScanItemOut()");
page += F(" let qualityIcon = getQualityIcon(network.quality);");
page += F(" let qualityPercent = network.quality + '%';");
page += F(" let encrypted = network.enc_type !== 0 ? '<span class=\"l\">🔒</span>' : '';");
page += F(" html += '<div><a href=\"#p\" onclick=\"c(this)\" data-ssid=\"' + escapeHtml(network.ssid) + '\">' + escapeHtml(network.ssid) + '</a>';");
page += F(" html += '<div role=\"img\" aria-label=\"' + qualityPercent + '\" title=\"' + qualityPercent + '\" class=\"q q-' + network.quality + '\"></div>';");
page += F(" html += '<div class=\"q\">' + qualityPercent + '</div>';");
page += F(" html += encrypted;");
page += F(" html += '</div>';");
page += F(" });");
page += F(" }");
page += F(" document.getElementById('scan-results').innerHTML = html;");
page += F("}");
page += F("");
page += F("function escapeHtml(text) {");
page += F(" const map = {");
page += F(" '&': '&amp;',");
page += F(" '<': '&lt;',");
page += F(" '>': '&gt;',");
page += F(" '\"': '&quot;',");
page += F(" \"'\": '&#039;'");
page += F(" };");
page += F(" return text.replace(/[&<>\"']/g, m => map[m]);");
page += F("}");
page += F("");
page += F("function getQualityIcon(quality) {");
page += F(" if(quality >= 75) return '▂▄▆█';");
page += F(" if(quality >= 50) return '▂▄▆▁';");
page += F(" if(quality >= 25) return '▂▄▁▁';");
page += F(" return '▂▁▁▁';");
page += F("}");
page += F("");
page += F("// Check if scan is in progress on page load");
page += F("window.addEventListener('load', function() {");
page += F(" fetch('/wifi/scanstatus')");
page += F(" .then(response => response.json())");
page += F(" .then(data => {");
page += F(" if(data.scanning) {");
page += F(" startPolling();");
page += F(" }");
page += F(" });");
page += F("});");
page += F("</script>");
page += getHTTPEnd(); page += getHTTPEnd();
@@ -1675,17 +1579,9 @@ bool WiFiManager::WiFi_scanNetworks(){
return WiFi_scanNetworks(false); return WiFi_scanNetworks(false);
} }
bool WiFiManager::WiFi_scanNetworks(unsigned int cachetime,bool async){
// async parameter ignored - always async now
return WiFi_scanNetworks(millis()-_lastscan > cachetime);
}
bool WiFiManager::WiFi_scanNetworks(unsigned int cachetime){ bool WiFiManager::WiFi_scanNetworks(unsigned int cachetime){
return WiFi_scanNetworks(millis()-_lastscan > cachetime); return WiFi_scanNetworks(millis()-_lastscan > cachetime);
} }
bool WiFiManager::WiFi_scanNetworks(bool force,bool async){
// async parameter ignored - always async now
return WiFi_scanNetworks(force);
}
bool WiFiManager::WiFi_scanNetworks(bool force){ bool WiFiManager::WiFi_scanNetworks(bool force){
#ifdef WM_DEBUG_LEVEL #ifdef WM_DEBUG_LEVEL
// DEBUG_WM(WM_DEBUG_DEV,_numNetworks,(millis()-_lastscan )); // DEBUG_WM(WM_DEBUG_DEV,_numNetworks,(millis()-_lastscan ));