Weird behavior with HTML content

Hi all.
I am building an AC remote controller with a Nano ESP32, inspired by the examples found on this tutorial.

It should gather temperature and RH data and show it on a simple web page, with a couple of controls to interact with my AC unit.

I'm currently stumped because I stored the HTML code for the web page in a string:

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  <style>
    html {
     font-family: Arial;
     display: inline-block;
     margin: 0px auto;
     text-align: center;
    }
    h2 { font-size: 3.0rem; }
    p { font-size: 3.0rem; }
    .units { font-size: 1.2rem; }
    .dht-labels{
      font-size: 1.5rem;
      vertical-align:middle;
      padding-bottom: 15px;
    }
    .slider { -webkit-appearance: none; margin: 14px; width: 360px; height: 25px; background: #FFD65C;
      outline: none; -webkit-transition: .2s; transition: opacity .2s;}
    .slider::-webkit-slider-thumb {-webkit-appearance: none; appearance: none; width: 35px; height: 35px; background: #003249; cursor: pointer;}
    .slider::-moz-range-thumb { width: 35px; height: 35px; background: #003249; cursor: pointer; } 
  </style>
</head>
<body>
  <h2>ESP32 AC Control</h2>
  <p>
    <i class="fas fa-thermometer-half" style="color:#059e8a;"></i> 
    <span class="dht-labels">Temperature</span> 
    <span id="temperature">%TEMPERATURE%</span>
    <sup class="units">&deg;C</sup>
  </p>
  <p>
    <i class="fas fa-tint" style="color:#00add6;"></i> 
    <span class="dht-labels">Humidity</span>
    <span id="humidity">%HUMIDITY%</span>
    <sup class="units">&percnt;</sup>
  </p>
  <p><span id="textSliderValue">Target: %SETTEMP%</span></p>
  <p><input type="range" onchange="updateTempSlider(this)" id="tempSlider" min="18" max="28" value="%SETTEMP%" step="1" class="slider"></p>
  <p>
  	<select name="Mode" id="setMode" onchange="updateCommand(this)")>
		<option value="0">Spento</option> 
		<option value="1">Riscaldamento</option>
		<option value="2">Raffreddamento</option>
		<option value="3">Deumidificatore</option> 
  	</select>
  	<select name="Fan" id="setFan" onchange="updateCommand(this)")>
		<option value="0">Auto</option> 
		<option value="1">Silenzioso</option>
		<option value="2">1/4</option>
		<option value="3">2/4</option>
		<option value="4">3/4</option>
		<option value="5">4/4</option> 
  	</select>
  </p>
</body>
<script>
function updateCommand(element) {
var xhr = new XMLHttpRequest();
  xhr.open("GET", "/sendCommand?setMode="+document.getElementById("setMode").value +"&setTemp="+document.getElementById("tempSlider").value + "&setFan="+document.getElementById("setFan").value, true);
  xhr.send();
}
function updateTempSlider(element) {
  var sliderValue = document.getElementById("tempSlider").value;
  document.getElementById("textSliderValue").innerHTML = "Target: " + sliderValue;
  if (document.getElementById("setMode").value="0") {
  	if (sliderValue>24) {
  		document.getElementById("setMode").value="2";
  		}
  	else {
  		document.getElementById("setMode").value="1"	;	
  		}
  	}  
  updateCommand();
}
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("temperature").innerHTML = this.responseText;
    }
  };
  xhttp.open("GET", "/temperature", true);
  xhttp.send();
}, 10000 ) ;
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("humidity").innerHTML = this.responseText;
    }
  };
  xhttp.open("GET", "/humidity", true);
  xhttp.send();
}, 10000 ) ;
</script>
</html>)rawliteral";

but the js part doesn't seem to work; inspecting the web page with whe browser's debugger I found a couple of weird lines that should not be there, right after the \<script\> tag:

(nothing weird before this line)
<script>
#line 110 "/home/max/.arduino15/RemoteSketchbook/ArduinoCloud/111621719352800137854/Sensore_TLC/Sensore_TLC.ino"
function updateTempSlider(element);
#line 105 "/home/max/.arduino15/RemoteSketchbook/ArduinoCloud/111621719352800137854/Sensore_TLC/Sensore_TLC.ino"
function updateCommand(element) {
(nothing weird after)

What happened?

How do I get rid of these two lines that crash my web page?

Here is the full sketch, should it be useful:

#include <Adafruit_SSD1306.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Fonts/FreeSans9pt7b.h>
#include <WiFi.h>
#include <WiFiUdp.h>
#include <DHT.h>
#include <WiFiClient.h>
#include <MDNS_Generic.h>
#include <ESPAsyncWebServer.h>

#define DHTPIN 2
#define DHTTYPE DHT11


#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32

const char* ssid     = "(my SSID)";
const char* password = "(my pwd)"; 
const char* hostname = "espnano";

const char* cmdMode = "setMode";
const char* cmdTemp = "setTemp";
const char* cmdFan = "setFan";

void connect(long timeout); 
void updateReadings(int* T, int* HR);
void drawDisplay(); 
String processor(const String& var);
String readDHTTemperature();
String readDHTHumidity();


Adafruit_SSD1306 display(128, 32, &Wire, -1);
DHT dht(DHTPIN, DHTTYPE);
AsyncWebServer webServer(80);

WiFiUDP udp;
MDNS mdns(udp);


const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  <style>
    html {
     font-family: Arial;
     display: inline-block;
     margin: 0px auto;
     text-align: center;
    }
    h2 { font-size: 3.0rem; }
    p { font-size: 3.0rem; }
    .units { font-size: 1.2rem; }
    .dht-labels{
      font-size: 1.5rem;
      vertical-align:middle;
      padding-bottom: 15px;
    }
    .slider { -webkit-appearance: none; margin: 14px; width: 360px; height: 25px; background: #FFD65C;
      outline: none; -webkit-transition: .2s; transition: opacity .2s;}
    .slider::-webkit-slider-thumb {-webkit-appearance: none; appearance: none; width: 35px; height: 35px; background: #003249; cursor: pointer;}
    .slider::-moz-range-thumb { width: 35px; height: 35px; background: #003249; cursor: pointer; } 
  </style>
</head>
<body>
  <h2>ESP32 AC Control</h2>
  <p>
    <i class="fas fa-thermometer-half" style="color:#059e8a;"></i> 
    <span class="dht-labels">Temperature</span> 
    <span id="temperature">%TEMPERATURE%</span>
    <sup class="units">&deg;C</sup>
  </p>
  <p>
    <i class="fas fa-tint" style="color:#00add6;"></i> 
    <span class="dht-labels">Humidity</span>
    <span id="humidity">%HUMIDITY%</span>
    <sup class="units">&percnt;</sup>
  </p>
  <p><span id="textSliderValue">Target: %SETTEMP%</span></p>
  <p><input type="range" onchange="updateTempSlider(this)" id="tempSlider" min="18" max="28" value="%SETTEMP%" step="1" class="slider"></p>
  <p>
  	<select name="Mode" id="setMode" onchange="updateCommand(this)")>
		<option value="0">Spento</option> 
		<option value="1">Riscaldamento</option>
		<option value="2">Raffreddamento</option>
		<option value="3">Deumidificatore</option> 
  	</select>
  	<select name="Fan" id="setFan" onchange="updateCommand(this)")>
		<option value="0">Auto</option> 
		<option value="1">Silenzioso</option>
		<option value="2">1/4</option>
		<option value="3">2/4</option>
		<option value="4">3/4</option>
		<option value="5">4/4</option> 
  	</select>
  </p>
</body>
<script>
function updateCommand(element) {
var xhr = new XMLHttpRequest();
  xhr.open("GET", "/sendCommand?setMode="+document.getElementById("setMode").value +"&setTemp="+document.getElementById("tempSlider").value + "&setFan="+document.getElementById("setFan").value, true);
  xhr.send();
}
function updateTempSlider(element) {
  var sliderValue = document.getElementById("tempSlider").value;
  document.getElementById("textSliderValue").innerHTML = "Target: " + sliderValue;
  if (document.getElementById("setMode").value="0") {
  	if (sliderValue>24) {
  		document.getElementById("setMode").value="2";
  		}
  	else {
  		document.getElementById("setMode").value="1"	;	
  		}
  	}  
  updateCommand();
}
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("temperature").innerHTML = this.responseText;
    }
  };
  xhttp.open("GET", "/temperature", true);
  xhttp.send();
}, 10000 ) ;
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      document.getElementById("humidity").innerHTML = this.responseText;
    }
  };
  xhttp.open("GET", "/humidity", true);
  xhttp.send();
}, 10000 ) ;
</script>
</html>)rawliteral";


unsigned long lastReading;
int HR,T, setTemp, ACMode, fanSpeed;

void setup() {

  Serial.begin(9600);
  
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  WiFi.begin(ssid, password); 
  dht.begin();                
    
  delay(2000);
  
  updateReadings(&T,&HR);
  drawDisplay();              // Disegna l'interfaccia
  connect(10);                // Connessione iniziale al wifi
  
  // Inizializza il server web
  webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });
  webServer.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readDHTTemperature().c_str());
  });
  webServer.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readDHTHumidity().c_str());
  });
  webServer.on("/sendCommand", HTTP_GET, [] (AsyncWebServerRequest *request) {

    Serial.print("Parameter: "); Serial.print(request->getParam(0)->name());Serial.print(" value: "); Serial.println(request->getParam(0)->value());
    
    String inputMessage;
    if (request->hasParam(cmdTemp)) {
      inputMessage = request->getParam(cmdTemp)->value();
      
      setTemp = inputMessage.toInt();
      Serial.print("Temperature set to "); Serial.println(setTemp);
    }
    if (request->hasParam(cmdMode)) {
      inputMessage = request->getParam(cmdMode)->value();
      
      ACMode = inputMessage.toInt();
      Serial.print("Mode set to "); Serial.println(ACMode);
    }
    if (request->hasParam(cmdFan)) {
      inputMessage = request->getParam(cmdFan)->value();
      
      fanSpeed = inputMessage.toInt();
      Serial.print("Fan speed set to "); Serial.println(fanSpeed);
    }
    

    request->send(200, "text/plain", "OK");
  });

  // Start server
  webServer.begin();

  // Registra il servizio sulla rete
  
  mdns.begin(WiFi.localIP(), hostname);
  mdns.addServiceRecord("Arduino mDNS Webserver._http", 80, MDNSServiceTCP);



}

void loop() {

mdns.run();

if (millis()<5000 and lastReading>5000) {
  lastReading=0;
}

if (millis()>lastReading+10000) {
  drawDisplay();
  updateReadings(&T,&HR);
  lastReading=millis();
}

  
}


String readDHTTemperature() {
  return String(T);
  }


String readDHTHumidity() {
  return String(HR);
  }


void updateReadings(int* T, int* HR) {
  
  int readT = int(dht.readTemperature());
  int readHR = int(dht.readHumidity());

  *T = (isnan(readT)?0:readT);
  *HR = (isnan(readHR)?0:readHR);

}


void connect(long timeout) {
  long startConnect;

  startConnect=millis();  

  display.fillRect(0,21,93,8,BLACK);
  display.setCursor(0,21);
  display.setFont();
    
  while (WiFi.status() != WL_CONNECTED and millis()<startConnect+(timeout*1000)) {
    delay(250);
  }

  if (WiFi.status()==WL_CONNECTED) {
    display.print(WiFi.localIP());
  }
  else {
    display.print(F("DISCONNECTED"));
    }

  display.display();
    
}

void drawDisplay() {
  display.setTextSize(1);
  display.setTextColor(WHITE);
  
  display.clearDisplay();
  display.setFont(&FreeSans9pt7b);
  display.setCursor(0,13);
  display.print(F("T:"));
  display.drawCircle(40,3,2,WHITE);

  display.setCursor(62,13);
  display.print(F("HR:"));
  display.setFont();
  display.setCursor(114,6);
  display.print(F("%"));
  
  display.setFont(&FreeSans9pt7b);
  
  display.setCursor(17,13);
  display.print(T);
  
  display.setCursor(93,13);
  display.print(HR);
  
  display.setCursor(0,28);
  display.setFont();
  if (WiFi.status()==WL_CONNECTED) {
    display.print(WiFi.localIP());
  }
  else {
    display.print(F("DISCONNECTED"));
  }
  
  display.display();
}

String processor(const String& var){
  
  if(var == "TEMPERATURE"){
    return String(T);
  }
  else if(var == "HUMIDITY"){
    return String(HR);
  }
  else if(var == "SETTEMP"){
    return String(setTemp);
  }
  
  return String();
}

I think it's a known issue with the Arduino builder. Take your HTML content and place it in a new tab (file) called e.g. html.h or myWebPage.h. Include that file (using double quotes instead if <>) in the ino file.

The builder will not touch files that don't have the ino extension.

One other workaround to try: make the JavaScript embedded in the HTML look less like C++. With what you have now:

function is not a keyword in C++, so it could be just another type, and therefore this is a function declaration that the .ino-"magic" is trying to "fix up" by inserting the forward declaration and #line directives. element could also be a type with no corresponding variable name, which is allowed. That argument is not used anyway, so it can be removed.

So instead, with arrow function expressions assigned to variables (with destructuring in some parameters):

const updateCommand = () => {
  fetch("/sendCommand?" + [
    ["setMode"],
    ["setTemp", "tempSlider"],
    ["setFan"],
  ].map(([name, id]) => name + "=" + document.getElementById(id || name).value).join("&"));
};
const updateTempSlider = () => {
  var sliderValue = document.getElementById("tempSlider").value;
  document.getElementById("textSliderValue").innerHTML = "Target: " + sliderValue;
  if (document.getElementById("setMode").value == "0") {  // was `value = "0"`
    if (sliderValue > 24) {
      document.getElementById("setMode").value = "2";
    }
    else {
      document.getElementById("setMode").value = "1";
    }
  }
  updateCommand();
}
setInterval(() => {
  fetch("/temperature").then(async (response) => {
    if (response.ok) {
      document.getElementById("temperature").innerHTML = await response.text();
    }
  });
  fetch("/humidity").then(async (response) => {
    if (response.ok) {
      document.getElementById("humidity").innerHTML = await response.text();
    }
  });
}, 10000) ;

Also, using fetch instead of XMLHttpRequest makes the code shorter and simpler; although it does introduce the concepts of async/await and promises. You can use fetch without the function declaration changes.

Separately, there was what looked like a typo: using a single = in an if condition instead of == to compare. The same bug is allowed in both JavaScript and C++.