{"id":8302,"date":"2025-03-17T14:43:35","date_gmt":"2025-03-17T13:43:35","guid":{"rendered":"https:\/\/willem.aandewiel.nl\/?p=8302"},"modified":"2025-03-26T13:15:54","modified_gmt":"2025-03-26T12:15:54","slug":"bibliotheken-om-het-leven-makkelijker-te-maken-part-2","status":"publish","type":"post","link":"https:\/\/willem.aandewiel.nl\/index.php\/2025\/03\/17\/bibliotheken-om-het-leven-makkelijker-te-maken-part-2\/","title":{"rendered":"Bibliotheken om het leven makkelijker te maken (part 2)"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">Single Page Application<\/h1>\n\n\n\n<p>[ 1,205 keer bekeken \/ views ]<br><br>Dit is de tweede post over bibliotheken die ik heb geschreven om het bouwen van een applicatie voor esp32 bordjes te vereenvoudigen.<\/p>\n\n\n\n<p>Deel 1: <a href=\"https:\/\/willem.aandewiel.nl\/index.php\/2025\/03\/15\/bibliotheken-om-het-leven-makkelijker-te-maken\/\">Networking<\/a><br>Deel 2: Single Page Application (GUI)<br>Deel 3: <a href=\"https:\/\/willem.aandewiel.nl\/index.php\/2025\/03\/25\/bibliotheken-om-het-leven-makkelijker-te-maken-part-3\/\">Filesystem manager<\/a><\/p>\n\n\n\n<p>Deze post gaat dus over de SPAmanager. Een bibliotheek waarmee op een eenvoudige manier een GUI kan worden geschreven voor je volgende esp32 project.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Wat is een Single Page Application (SPA)?<\/h1>\n\n\n\n<p>Een Single Page Application (SPA) is een webapplicatie of website die volledig binnen \u00e9\u00e9n enkele webpagina werkt. In plaats van elke keer een nieuwe pagina van de server op te halen wanneer een gebruiker navigeert, laadt een SPA eenmalig de essenti\u00eble HTML-, CSS- en JavaScript-bestanden en beheert daarna de navigatie en interacties via JavaScript.<\/p>\n\n\n\n<p>De SPAmanager bibliotheek zorgt ervoor dat de layout van de webapplicatie netjes voor je geregeld wordt. Hij ziet er altijd hetzelfde uit.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/basicLayout-1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"856\" height=\"498\" src=\"https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/basicLayout-1.png\" alt=\"\" class=\"wp-image-8307\" srcset=\"https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/basicLayout-1.png 856w, https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/basicLayout-1-300x175.png 300w, https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/basicLayout-1-768x447.png 768w\" sizes=\"auto, (max-width: 856px) 100vw, 856px\" \/><\/a><\/figure>\n\n\n\n<p>De bovenste regel is gereserveerd voor (van links naar rechts) ruimte voor drop-down menu&#8217;s, een titel en de datum\/tijd.<\/p>\n\n\n\n<p>Op de onderste regel kunnen status- en fout-meldingen worden weergegeven.<\/p>\n\n\n\n<p>Alles daartussen is gereserveerd voor pagina&#8217;s met informatie van jouw project.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/basicSPA.webp\" alt=\"\"\/><\/figure>\n\n\n\n<p>Je ontkomt er niet aan om voor iedere pagina die je nodig hebt kleine stukjes html te schrijven maar dit blijft beperkt tot alleen de zaken die je in jouw project nodig hebt. Hierbij zorgt de SPAmanager ervoor dat de layout consistent blijft.<\/p>\n\n\n\n<p>Om je project van pagina&#8217;s en menu&#8217;s te voorzien heeft de bibliotheek een aantal methoden beschikbaar. We zullen de belangrijkste \u00e9\u00e9n voor \u00e9\u00e9n behandelen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">addPage() <\/h2>\n\n\n\n<p>Met deze methode kun je een pagina aan je SPA toevoegen. Iedere pagina heeft een naam en een (klein) stukje html. <\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/-- Voeg een eenvoudige hoofdpagina toe\nspaManager.addPage(\"Home\", R\"(\n  &lt;div>\n    &lt;h1>Welkom bij mijn ESP32 Project&lt;\/h1>\n    &lt;p>Huidige temperatuur: &lt;span id=\"temp\">--&lt;\/span> \u00b0C&lt;\/p>\n    &lt;button id=\"refreshBtn\">Vernieuwen&lt;\/button>\n  &lt;\/div>\n)\");<\/pre>\n\n\n\n<p>Bovenstaande code heeft dit resultaat:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"846\" height=\"497\" src=\"https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/addPage_Home.png\" alt=\"\" class=\"wp-image-8308\" srcset=\"https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/addPage_Home.png 846w, https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/addPage_Home-300x176.png 300w, https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/addPage_Home-768x451.png 768w\" sizes=\"auto, (max-width: 846px) 100vw, 846px\" \/><\/figure>\n\n\n\n<p>Bestaat je applicatie uit meerdere pagina&#8217;s, dan kun je \u00e9\u00e9n daarvan &#8220;actief&#8221; maken met de methode <code>activatePage()<\/code>. Met deze methode worden alle andere pagina&#8217;s automatisch naar de achtergrond gestuurd.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">addMenu() <\/h2>\n\n\n\n<p>Met deze methode voeg je een drop-down menu toe aan de pagina.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/-- Voeg een hoofdmenu toe aan de homepagina\nspaManager.addMenu(\"Home\", \"Hoofdmenu\");\n\n\/\/-- Voeg een instellingenmenu toe aan de instellingenpagina\nspaManager.addMenu(\"Home\", \"Instellingenmenu\");<\/pre>\n\n\n\n<p>Bovenstaande code heeft dit als resultaat:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"844\" height=\"498\" src=\"https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/addMenus.png\" alt=\"\" class=\"wp-image-8309\" srcset=\"https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/addMenus.png 844w, https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/addMenus-300x177.png 300w, https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/addMenus-768x453.png 768w\" sizes=\"auto, (max-width: 844px) 100vw, 844px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">addMenuItem() <\/h2>\n\n\n\n<p>Binnen het met addMenu() aangemaakte drop-down menu kun je keuzen plaatsen met addMenuItem(). Zo&#8217;n menu-item kan een URL starten, een functie aanroepen of een popup-menu activeren.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/-- Definieer een callback functie\nvoid ledAanzetten() \n{\n  digitalWrite(LED_PIN, HIGH);\n  Serial.println(\"LED aangezet\");\n}\n\n\/\/-- Voeg een menu-item toe dat de functie aanroept wanneer erop geklikt wordt\nspaManager.addMenuItem(\"Home\", \"Hoofdmenu\", \"LED AAN\", ledAanzetten);\n\n\/\/-- Voeg een menu-item toe dat naar een externe website navigeert\nspaManager.addMenuItem(\"Home\", \"Hoofdmenu\", \"Documentatie\", \"https:\/\/example.com\/docs\");<\/pre>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"947\" height=\"796\" src=\"https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/addMenuItems.jpeg\" alt=\"\" class=\"wp-image-8312\" style=\"width:240px;height:auto\" srcset=\"https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/addMenuItems.jpeg 947w, https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/addMenuItems-300x252.jpeg 300w, https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/addMenuItems-768x646.jpeg 768w\" sizes=\"auto, (max-width: 947px) 100vw, 947px\" \/><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">disableMenuItem() <\/h2>\n\n\n\n<p>Mocht, in een bepaalde context een menu-item geen functie hebben, dan kan dat menu-item met deze methode &#8220;ge-grayed-out&#8221; gemaakt worden. Het menu-item blijft dan wel in de drop-down lijst staan, maar je kunt er niet meer op klikken.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/-- Schakel het \"Reset Apparaat\" menu-item uit\nspaManager.disableMenuItem(\"Home\", \"Hoofdmenu\", \"LED AAN\");\n\n\/\/-- Je kunt items uitschakelen op basis van voorwaarden\nif (!isWifiConnected) \n{\n  spaManager.disableMenuItem(\"Home\", \"Hoofdmenu\", \"LED AAN\");\n}<\/pre>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"923\" height=\"695\" src=\"https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/disableMenuItem.jpeg\" alt=\"\" class=\"wp-image-8314\" style=\"width:240px;height:auto\" srcset=\"https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/disableMenuItem.jpeg 923w, https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/disableMenuItem-300x226.jpeg 300w, https:\/\/willem.aandewiel.nl\/wp-content\/uploads\/2025\/03\/disableMenuItem-768x578.jpeg 768w\" sizes=\"auto, (max-width: 923px) 100vw, 923px\" \/><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">enableMenuItem() <\/h2>\n\n\n\n<p>Met deze methode kan een menu-item weer actief gemaakt worden.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/-- Schakel het \"Reset Apparaat\" menu-item in\nspaManager.enableMenuItem(\"Instellingen\", \"Instellingenmenu\", \"Reset Apparaat\");\n\n\/\/-- Je kunt items inschakelen op basis van voorwaarden\nif (isWifiConnected) \n{\n  spaManager.enableMenuItem(\"Home\", \"Hoofdmenu\", \"Cloud Upload\");\n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">includeJsFile() <\/h2>\n\n\n\n<p>Eventuele extra javascript functies kunnen met de methode includeJsFile() in de, in de browser aanwezige, code worden ge\u00efnjecteerd. Hierdoor is het mogelijk om functionaliteit die niet standaard in de SPA aanwezig is, toe te voegen. Na het aanroepen van deze methode zal het nieuwe Javascript bestand niet direct naar de browser gestuurd worden. Dit zal pas gebeuren als de esp32 van de browser &#8220;hoort&#8221; dat de html pagina volledig is ingeladen.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void onPageLoaded() \n{\n  \/\/-- Initialiseer het dashboard na het laden van scripts\n  spaManager.callJsFunction(\"initDashboard\");\n}\n\nvoid setup() \n{\n  \/\/-- Stel de display manager in\n  spaManager.begin(\"\/SYS\");\n\n  \/\/-- Registreer de callback voor wanneer de pagina is geladen\n  spaManager.pageIsLoaded(onPageLoaded);\n\n  \/\/-- Voeg JavaScript bestanden toe\n  \/\/-- deze bestanden worden door de SPAmanager geladen\n  \/\/-- zodra de html pagina volledig is geladen\n  spaManager.includeJsFile(\"\/scripts\/utils.js\");\n  spaManager.includeJsFile(\"\/scripts\/charts.js\");\n  spaManager.includeJsFile(\"\/scripts\/sensors.js\");  \n}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">includeCssFile() <\/h2>\n\n\n\n<p>Deze methode doet hetzelfde als includeJsFile() maar dan voor style-sheets.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void setup() \n{\n  \/\/-- Stel de display manager in\n  spaManager.begin(\"\/SYS\");\n\n  \/\/-- Voeg CSS bestanden toe\n  spaManager.includeCssFile(\"\/styles\/main.css\");\n  spaManager.includeCssFile(\"\/styles\/dashboard.css\");\n\n  \/\/-- Voeg conditioneel een thema CSS toe\n  if (darkModeEnabled) \n  {\n    spaManager.includeCssFile(\"\/styles\/dark-theme.css\");\n  } \n  else \n  {\n    spaManager.includeCssFile(\"\/styles\/light-theme.css\");\n  }  \n}<\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">Hoe werkt deze SPAmanager intern<\/h1>\n\n\n\n<p>Bij het opstarten zal de esp32 intern een html-raamwerk opbouwen.<br>Zodra er een client (lees: webbrowser) verbinding maakt met de interne webserver zal dit raamwerk (SPAmanager.html) naar de browser worden gestuurd, samen met het bijbehorende style-sheet (SPAmanager.css) en de nodige Javascript (SPAmanager.js). Als de pagina volledig in de browser is geladen stuurt de browser een signaal naar de SPAmanager die vervolgens de door jouw opgegeven pagina&#8217;s met menu&#8217;s injecteert in de, in de browser reeds aanwezige, code. Ook kunnen extra Javascript en style-sheets worden ge\u00efnjecteerd. Vervolgens draait de hele SPA in de browser. Via web-sockets vindt er communicatie plaats tussen de browser en de interne webserver.<\/p>\n\n\n\n<div style=\"height:53px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h1 class=\"wp-block-heading\">Voorbeelden<\/h1>\n\n\n\n<p>Alle in deze post gepresenteerde voorbeelden staan in de <code>examples<\/code> folder van de <a href=\"https:\/\/github.com\/mrWheel\/SPAmanager\" target=\"_blank\" rel=\"noreferrer noopener\">github repository<\/a>.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity is-style-wide\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">API Reference<\/h1>\n\n\n\n<h3 class=\"wp-block-heading\">SPAmanager(uint16_t port = 80)<\/h3>\n\n\n\n<p>Constructor for the SPAmanager class. <\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>port: The port number for the web server (default is 80).<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/\/-- Create a SPAmanager instance with default port (80)\nSPAmanager spaManager;\n\n\/\/-- Or specify a custom port\nSPAmanager spaManager(8080);<\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">begin(const char* systemPath, Stream* debugOut = nullptr)<\/h3>\n\n\n\n<p>Initializes the SPAmanager with system files path and optional debug output.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>systemPath: Path to the system files (HTML, CSS, JS)<\/li>\n\n\n\n<li>debugOut: Optional parameter for debug output stream.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void setup()\n{\n  Serial.begin(115200);\n  \/\/-- Initialize with system files in \/SYS directory\n  \/\/-- and debug output to Serial\n  spaManager.begin(\"\/SYS\", &amp;Serial);\n\n  \/\/-- Or initialize without debug output\n  \/\/spaManager.begin(\"\/SYS\");\n}<\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">addPage(const char* pageName, const char* html)<\/h3>\n\n\n\n<p>Adds a web page to the display manager.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pageName: The name of the page.<\/li>\n\n\n\n<li>html: The HTML content of the page.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> \/\/-- Add a simple home page \n spaManager.addPage(\"Home\", R\"( &lt;div>\n &lt;h1>Welcome to my ESP32 Web Server&lt;\/h1> \n &lt;p>Current temperature: &lt;span id=\"temp\">--&lt;\/span> \u00b0C&lt;\/p> \n &lt;button id=\"refreshBtn\">Refresh&lt;\/button> &lt;\/div> \n )\");<\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">activatePage(const char* pageName)<\/h3>\n\n\n\n<p>Activates a web page, making it the current page being displayed.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pageName: The name of the page to activate.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"> \/\/-- Switch to the settings page\n spaManager.activatePage(\"Settings\"); <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">setPageTitle(const char* pageName, const char* title)<\/h3>\n\n\n\n<p>Sets the title of a specific web page.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pageName: The name of the page.<\/li>\n\n\n\n<li>title: The new title for the page.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/\/-- Set the title of the home page\n  spaManager.setPageTitle(\"Home\", \"Smart Home Dashboard\");\n\n  \/\/-- Update title based on sensor status \n  if (alarmTriggered) \n  {\n    spaManager.setPageTitle(\"Home\", \"ALERT: Motion Detected!\"); \n  } <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">addMenu(const char* pageName, const char* menuName)<\/h3>\n\n\n\n<p>Adds a menu to a specific web page.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pageName: The name of the page to add the menu to.<\/li>\n\n\n\n<li>menuName: The name of the menu.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/\/-- Add a main menu to the home page \n  spaManager.addMenu(\"Home\", \"Main Menu\");\n\n  \/\/-- Add a settings menu to the settings page \n  spaManager.addMenu(\"Settings\", \"Settings Menu\"); <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">addMenuItem(const char* pageName, const char* menuName, const char* itemName, std::function callback)<\/h3>\n\n\n\n<p>Adds a menu item with a callback function to a specific menu on a web page.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pageName: The name of the page containing the menu.<\/li>\n\n\n\n<li>menuName: The name of the menu.<\/li>\n\n\n\n<li>itemName: The name of the menu item.<\/li>\n\n\n\n<li>callback: The function to call when the menu item is selected.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/\/-- Define a callback function \n  void turnOnLED() \n  {\n    digitalWrite(LED_PIN, HIGH); \n    Serial.println(\"LED turned ON\"); \n  }\n\n  \/\/-- Add a menu item that calls the function when clicked\n  spaManager.addMenuItem(\"Home\", \"Main Menu\", \"Turn ON LED\", turnOnLED); <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">addMenuItem(const char* pageName, const char* menuName, const char* itemName, const char* url)<\/h3>\n\n\n\n<p>Adds a menu item with a URL to a specific menu on a web page.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pageName: The name of the page containing the menu.<\/li>\n\n\n\n<li>menuName: The name of the menu.<\/li>\n\n\n\n<li>itemName: The name of the menu item.<\/li>\n\n\n\n<li>url: The URL to navigate to when the menu item is selected.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/\/-- Add a menu item that navigates to the settings page\n  spaManager.addMenuItem(\"Home\", \"Main Menu\", \"Settings\", \"\/settings\");\n\n  \/\/-- Add a menu item that links to an external website\n  spaManager.addMenuItem(\"Home\", \"Main Menu\", \"Documentation\", \"https:\/\/example.com\/docs\"); <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">addMenuItem(const char* pageName, const char* menuName, const char* itemName, std::function&lt;void(const char<em>)&gt; callback, const char<\/em> param)<\/h3>\n\n\n\n<p>Adds a menu item with a parameterized callback function to a specific menu on a web page.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pageName: The name of the page containing the menu.<\/li>\n\n\n\n<li>menuName: The name of the menu.<\/li>\n\n\n\n<li>itemName: The name of the menu item.<\/li>\n\n\n\n<li>callback: The function to call when the menu item is selected, receiving a const char* parameter.<\/li>\n\n\n\n<li>param: The string value to pass to the callback function when invoked.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  void handleMenuAction(const char* action) \n  { \n    if (strcmp(action, \"on\") == 0) \n    { \n      digitalWrite(LED_PIN, HIGH); \n      spaManager.setMessage(\"LED turned ON\", 6); \n    } \n    else if (strcmp(action, \"off\") == 0) \n    { \n      digitalWrite(LED_PIN, LOW);\n      spaManager.setMessage(\"LED turned OFF\", 3); \n    } \n  }\n\n  \/\/-- In setup or where menus are configured: \n  spaManager.addMenuItem(\"Home\", \"Main Menu\", \"Turn ON LED\", handleMenuAction, \"on\");\n  spaManager.addMenuItem(\"Home\", \"Main Menu\", \"Turn OFF LED\", handleMenuAction, \"off\"); <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">addMenuItemPopup(const char* pageName, const char* menuName, const char* menuItem, const char* popupMenu, std::function&amp;)&gt; callback = nullptr)<\/h3>\n\n\n\n<p>Adds a menu item that opens a popup menu when selected.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pageName: The name of the page containing the menu.<\/li>\n\n\n\n<li>menuName: The name of the menu.<\/li>\n\n\n\n<li>menuItem: The name of the menu item that will open the popup.<\/li>\n\n\n\n<li>popupMenu: The HTML content of the popup menu.<\/li>\n\n\n\n<li>callback: Optional callback function that receives a map of input values from the popup.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/\/-- Define a callback function to handle form submission \n  void handleWifiSettings(const std::map&lt;std::string, std::string>&amp; values) \n  {\n    \/\/-- Get values from the map \n    std::string ssid = values.at(\"ssid\"); \n    std::string password = values.at(\"password\"); \n    \/\/-- Use the values to connect to WiFi\n    WiFi.begin(ssid.c_str(), password.c_str()); \n    \/\/-- Show a message\n    spaManager.setMessage(\"Connecting to WiFi...\", 10); \n  }\n\n  \/\/-- Add a menu item that opens a popup for WiFi settings \n  const char* wifiPopupHtml = R\"( \n    &lt;div class=\"popup-content\"> \n    &lt;h3>WiFi Settings&lt;\/h3> \n    &lt;input name=\"ssid\" placeholder=\"WiFi SSID\"> \n    &lt;input name=\"password\" type=\"password\" placeholder=\"WiFi Password\"> \n    &lt;button type=\"submit\">Connect&lt;\/button> \n    &lt;\/div> \n    )\";\n\n  spaManager.addMenuItemPopup(\"Settings\", \"Settings Menu\", \"WiFi Settings\", wifiPopupHtml, handleWifiSettings); <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">enableMenuItem(const char* pageName, const char* menuName, const char* itemName)<\/h3>\n\n\n\n<p>Enables a specific menu item on a web page.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pageName: The name of the page containing the menu.<\/li>\n\n\n\n<li>menuName: The name of the menu.<\/li>\n\n\n\n<li>itemName: The name of the menu item to enable.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/\/-- Enable the \"Reset Device\" menu item\n  spaManager.enableMenuItem(\"Settings\", \"Settings Menu\", \"Reset Device\");\n\n  \/\/-- You might enable items based on conditions \n  if (isWifiConnected) \n  {\n    spaManager.enableMenuItem(\"Home\", \"Main Menu\", \"Cloud Upload\"); \n  } <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">disableMenuItem(const char* pageName, const char* menuName, const char* itemName)<\/h3>\n\n\n\n<p>Disables a specific menu item on a web page.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pageName: The name of the page containing the menu.<\/li>\n\n\n\n<li>menuName: The name of the menu.<\/li>\n\n\n\n<li>itemName: The name of the menu item to disable.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/\/-- Disable the \"Reset Device\" menu item\n  spaManager.disableMenuItem(\"Settings\", \"Settings Menu\", \"Reset Device\");\n\n  \/\/-- You might disable items based on conditions \n  if (!isWifiConnected) \n  {\n    spaManager.disableMenuItem(\"Home\", \"Main Menu\", \"Cloud Upload\"); \n  } <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">setMessage(const char* message, int duration)<\/h3>\n\n\n\n<p>Sets a message to be displayed for a specified duration.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>message: The message to display.<\/li>\n\n\n\n<li>duration: The duration in seconds to display the message (0 is infinite).<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/\/-- Show a temporary message for 3 seconde\n  spaManager.setMessage(\"Settings saved successfully!\", 3);\n\n  \/\/-- Show a message when a sensor is triggered \n  if (motionDetected) \n  {\n    spaManager.setMessage(\"Motion detected in living room\", 5); \n  } <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">setErrorMessage(const char* message, int duration)<\/h3>\n\n\n\n<p>Sets an error message to be displayed for a specified duration.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>message: The error message to display.<\/li>\n\n\n\n<li>duration: The duration in seconds to display the error message (0 is infinite).<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/\/-- Show an error message for 5 seconds\n  spaManager.setErrorMessage(\"Failed to connect to WiFi!\", 5);\n\n  \/\/-- Show an error when a sensor reading fails \n  if (isnan(temperature)) \n  {\n    spaManager.setErrorMessage(\"Temperature sensor error\", 3); \n  } <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">callJsFunction(const char* functionName)<\/h3>\n\n\n\n<p>Calls a JavaScript function in the browser by its name.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>functionName: The name of the JavaScript function to call.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/\/-- Call a simple JavaScript function\n  spaManager.callJsFunction(\"refreshData\");\n\n  \/\/-- Call a JavaScript function with parameters \n  char jsCommand[64]; \n  float sensorValue = 23.5; \n  sprintf(jsCommand, \"updateChart(%f)\", sensorValue);\n  spaManager.callJsFunction(jsCommand); <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">setPlaceholder(const char* pageName, const char* placeholder, T value)<\/h3>\n\n\n\n<p>Sets a placeholder value in the HTML content of a web page.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pageName: The name of the page containing the placeholder.<\/li>\n\n\n\n<li>placeholder: The ID of the element to update.<\/li>\n\n\n\n<li>value: The value to set (can be int, float, double, or const char*).<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/\/-- Set temperature placeholder with a float value float\n  temperature = 23.5; \n  spaManager.setPlaceholder(\"Home\", \"temp\", temperature);\n\n  \/\/-- Set humidity placeholder with an integer value \n  int humidity = 45;\n  spaManager.setPlaceholder(\"Home\", \"humidity\", humidity);\n\n  \/\/-- Set status placeholder with a string value\n  spaManager.setPlaceholder(\"Home\", \"status\", \"Online\"); <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">getPlaceholder(const char* pageName, const char* placeholder)<\/h3>\n\n\n\n<p>Gets the value of a placeholder in the HTML content of a web page.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pageName: The name of the page containing the placeholder.<\/li>\n\n\n\n<li>placeholder: The ID of the element to get the value from.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/\/-- Get the current value of the temperature placeholder\n  PlaceholderValue temp = spaManager.getPlaceholder(\"Home\", \"temp\");\n\n  \/\/-- Use the value in different formats int tempInt = temp.asInt(); float\n  tempFloat = temp.asFloat(); \n  const char* tempStr = temp.c_str();\n\n  Serial.print(\"Current temperature: \"); \n  Serial.println(tempStr); <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">enableID(const char* pageName, const char* id)<\/h3>\n\n\n\n<p>Enables an HTML element with the specified ID on a web page by setting its<br>display style to &#8220;block&#8221;.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pageName: The name of the page containing the element.<\/li>\n\n\n\n<li>id: The ID of the HTML element to enable.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/\/-- Enable a button when WiFi is not connected \n  if (WiFi.status() == WL_CONNECTED) \n  { \n    spaManager.disableID(\"Home\", \"connectButton\");\n  } \n  else \n  { \n    spaManager.enableID(\"Home\", \"connectButton\"); \n  }\n\n  \/\/-- Enable a form when the device is ready \n  spaManager.enableID(\"Settings\", \"wifiForm\"); <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">disableID(const char* pageName, const char* id)<\/h3>\n\n\n\n<p>Disables an HTML element with the specified ID on a web page by setting its<br>display style to &#8220;none&#8221;.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>pageName: The name of the page containing the element.<\/li>\n\n\n\n<li>id: The ID of the HTML element to disable.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/\/-- Disable a button when WiFi is disconnected \n  if (WiFi.status() != WL_CONNECTED) \n  { \n    spaManager.disableID(\"Home\", \"uploadButton\");\n  } \n  else \n  { \n    spaManager.enableID(\"Home\", \"uploadButton\"); \n  }\n\n  \/\/-- Disable a form during processing \n  spaManager.disableID(\"Settings\", \"wifiForm\"); <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">pageIsLoaded(std::function callback)<\/h3>\n\n\n\n<p>Sets a callback function to be called when the web page is fully loaded.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>callback: The function to call when the page is loaded.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  void onPageLoaded() \n  { \n    \/\/-- This function will be called when the page is loaded \n    spaManager.setMessage(\"Page loaded successfully!\", 3); \n    \/\/-- Initialize the page \n    spaManager.callJsFunction(\"initializeDashboard\"); \n    \/\/-- Update placeholders with current values \n    spaManager.setPlaceholder(\"Dashboard\", \"temp\", currentTemperature); \n    spaManager.setPlaceholder(\"Dashboard\", \"humidity\", currentHumidity); \n  }\n\n  void setup() \n  { \n    \/\/-- Set up the display manager \n    spaManager.begin(\"\/SYS\"); \n    \/\/-- Register the page loaded callback \n    spaManager.pageIsLoaded(onPageLoaded); \n    \/\/-- Include JavaScript and CSS files \n    spaManager.includeJsFile(\"\/scripts\/charts.js\");\n    spaManager.includeCssFile(\"\/styles\/custom.css\"); \n    \/\/-- Add pages and other setup\n    \/\/ ... \n  } <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">getSystemFilePath() const<\/h3>\n\n\n\n<p>Returns the path to the system files directory.<\/p>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  \/\/-- Get the system files path \n  std::string sysPath = spaManager.getSystemFilePath();\n\n  \/\/-- Use the path to construct file paths \n  std::string cssPath = sysPath + \"\/custom.css\"; \n  Serial.println((\"CSS file path: \" + cssPath).c_str()); <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">includeCssFile(const std::string &amp;cssFile)<\/h3>\n\n\n\n<p>Includes a CSS file in the web page.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>cssFile: The path to the CSS file to include.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  void onPageLoaded() \n  { \n    \/\/-- initialize fields or what not \n  }\n\n  void setup() \n  { \n    \/\/-- Set up the display manager \n    spaManager.begin(\"\/SYS\"); \n    \/\/-- Register the page loaded callback \n    spaManager.pageIsLoaded(onPageLoaded);\n\n    \/\/-- Include CSS files \n    spaManager.includeCssFile(\"\/styles\/main.css\");\n    spaManager.includeCssFile(\"\/styles\/dashboard.css\");\n\n    \/\/-- Conditionally include theme CSS \n    if (darkModeEnabled) \n    {\n      spaManager.includeCssFile(\"\/styles\/dark-theme.css\"); \n    } \n    else \n    {\n      spaManager.includeCssFile(\"\/styles\/light-theme.css\"); \n    }\n  } <\/pre>\n\n\n\n<hr class=\"wp-block-separator has-text-color has-blue-color has-alpha-channel-opacity has-blue-background-color has-background is-style-wide\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">includeJsFile(const std::string &amp;scriptFile)<\/h3>\n\n\n\n<p>Includes a JavaScript file in the web page.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>scriptFile: The path to the JavaScript file to include.<\/li>\n<\/ul>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">  void onPageLoaded() \n  { \n  \/\/-- Initialize the dashboard after including scripts \n  spaManager.callJsFunction(\"initDashboard\"); \n  }\n\n  void setup() \n  { \n    \/\/-- Set up the display manager \n    spaManager.begin(\"\/SYS\"); \n    \/\/-- Register the page loaded callback \n    spaManager.pageIsLoaded(onPageLoaded);\n\n    \/\/-- Include JavaScript files \/\/-- these files are loaded by the SPAmanager as\n    \/\/-- soon as the html page is fully loaded\n    spaManager.includeJsFile(\"\/scripts\/utils.js\");\n    spaManager.includeJsFile(\"\/scripts\/charts.js\");\n    spaManager.includeJsFile(\"\/scripts\/sensors.js\");\n\n  }<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Single Page Application [ ] Dit is de tweede post over bibliotheken die ik heb geschreven om het bouwen van een applicatie voor esp32 bordjes te vereenvoudigen. Deel 1: NetworkingDeel 2: Single Page Application (GUI)Deel 3: Filesystem manager Deze post &hellip; <a href=\"https:\/\/willem.aandewiel.nl\/index.php\/2025\/03\/17\/bibliotheken-om-het-leven-makkelijker-te-maken-part-2\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[144,2,3,157,6,143,20],"tags":[180,181],"class_list":["post-8302","post","type-post","status-publish","format-standard","hentry","category-aandewiel","category-arduino","category-computer","category-esp32","category-esp8266","category-firmware","category-wifi","tag-bibliotheek","tag-library"],"views":1205,"_links":{"self":[{"href":"https:\/\/willem.aandewiel.nl\/index.php\/wp-json\/wp\/v2\/posts\/8302","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/willem.aandewiel.nl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/willem.aandewiel.nl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/willem.aandewiel.nl\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/willem.aandewiel.nl\/index.php\/wp-json\/wp\/v2\/comments?post=8302"}],"version-history":[{"count":25,"href":"https:\/\/willem.aandewiel.nl\/index.php\/wp-json\/wp\/v2\/posts\/8302\/revisions"}],"predecessor-version":[{"id":8402,"href":"https:\/\/willem.aandewiel.nl\/index.php\/wp-json\/wp\/v2\/posts\/8302\/revisions\/8402"}],"wp:attachment":[{"href":"https:\/\/willem.aandewiel.nl\/index.php\/wp-json\/wp\/v2\/media?parent=8302"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/willem.aandewiel.nl\/index.php\/wp-json\/wp\/v2\/categories?post=8302"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/willem.aandewiel.nl\/index.php\/wp-json\/wp\/v2\/tags?post=8302"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}