RSeries astromech firmware
WifiWebServer.h
Go to the documentation of this file.
1 #ifndef WifiWebServer_h
2 #define WifiWebServer_h
3 
4 #include "ReelTwo.h"
5 #include "wifi/WifiAccess.h"
6 #include <WiFiClient.h>
7 #include <FS.h>
8 
9 #include "core/FormatString.h"
10 #include "core/MallocString.h"
12 
13 class WValue
14 {
15 public:
16  virtual bool getQuoteValue() { return false; }
17  virtual String get() = 0;
18  virtual void set(String val) = 0;
19 };
20 
21 class WAction
22 {
23 public:
24  WAction(void (*action)()) :
25  fAction(action)
26  {
27  }
28 
29  void perform()
30  {
31  if (fAction != nullptr)
32  fAction();
33  }
34 
35 protected:
36  void (*fAction)();
37 };
38 
39 class WBoolean : public WValue
40 {
41 public:
42  WBoolean(bool (*getValue)(), void (*setValue)(bool)) :
43  fGetValue(getValue),
44  fSetValue(setValue)
45  {
46  }
47 
48  virtual String get() override
49  {
50  return (fGetValue != NULL) ? (fGetValue() ? "true" : "false") : "";
51  }
52 
53  virtual void set(String val) override
54  {
55  if (fGetValue != nullptr)
56  fSetValue(val.equalsIgnoreCase("true"));
57  }
58 
59 protected:
60  bool (*fGetValue)();
61  void (*fSetValue)(bool);
62 };
63 
64 class WInteger : public WValue
65 {
66 public:
67  WInteger(int (*getValue)(), void (*setValue)(int)) :
68  fGetValue(getValue),
69  fSetValue(setValue)
70  {
71  }
72 
73  virtual String get() override
74  {
75  if (fGetValue != nullptr)
76  return String(fGetValue());
77  return "";
78  }
79 
80  virtual void set(String val) override
81  {
82  if (fSetValue != nullptr)
83  fSetValue(val.toInt());
84  }
85 
86 protected:
87  int (*fGetValue)();
88  void (*fSetValue)(int);
89 };
90 
91 class WString : public WValue
92 {
93 public:
94  WString(String (*getValue)(), void (*setValue)(String)) :
95  fGetValue(getValue),
96  fSetValue(setValue)
97  {
98  }
99 
100  virtual bool getQuoteValue() override
101  {
102  return true;
103  }
104 
105  virtual String get() override
106  {
107  if (fGetValue != nullptr)
108  return fGetValue();
109  return "";
110  }
111 
112  virtual void set(String val) override
113  {
114  if (fSetValue != nullptr)
115  fSetValue(val);
116  }
117 
118 protected:
119  String (*fGetValue)();
120  void (*fSetValue)(String);
121 };
122 
123 class WDynamic
124 {
125 public:
126  virtual void emitCSS(Print& out) const {}
127 
128  virtual void emitBody(Print& out) const = 0;
129 
130  virtual void emitScript(Print& out) const {}
131 };
132 
133 class WElement
134 {
135 public:
136  WElement(WValue* value = nullptr) :
137  fValue(value)
138  {
139  }
140 
141  WElement(WAction* action) :
142  fAction(action)
143  {
144  }
145 
146  inline bool needsReload() const { return fReload; }
147  inline String getID() const { return fID; }
148  inline void appendCSS(String str) { fCSS = fCSS + str; }
149  inline void appendBody(String str) { fBody = fBody + str; }
150  inline void appendScript(String str) { fScript = fScript + str; }
151 
152  void appendCSSf(const char* fmt, ...)
153  {
154  char* str = NULL;
155  va_list ap;
156  va_start(ap, fmt);
157  int r = FormatString(&str, fmt, ap);
158  va_end(ap);
159  if (r != -1)
160  {
161  appendCSS(MallocString(str));
162  }
163  }
164 
165  void appendBodyf(const char* fmt, ...)
166  {
167  char* str = NULL;
168  va_list ap;
169  va_start(ap, fmt);
170  int r = FormatString(&str, fmt, ap);
171  va_end(ap);
172  if (r != -1)
173  {
174  appendBody(MallocString(str));
175  }
176  }
177 
178  void appendScriptf(const char* fmt, ...)
179  {
180  char* str = NULL;
181  va_list ap;
182  va_start(ap, fmt);
183  int r = FormatString(&str, fmt, ap);
184  va_end(ap);
185  if (r != -1)
186  {
188  }
189  }
190 
191  inline void emitCSS(Print& out) const
192  {
193  if (fEnabled != nullptr && !fEnabled())
194  return;
195  if (fDynamic)
196  fDynamic->emitCSS(out);
197  else
198  out.println(fCSS);
199  }
200 
201  inline void emitBody(Print& out) const
202  {
203  if (fEnabled != nullptr && !fEnabled())
204  return;
205  if (fDynamic)
206  fDynamic->emitBody(out);
207  else
208  out.println(fBody);
209  }
210 
211  inline void emitValue(Print& out) const
212  {
213  if (fEnabled != nullptr && !fEnabled())
214  return;
215  if (fValue != nullptr)
216  {
217  if (fValue->getQuoteValue())
218  out.println("var "+String(fID)+"_val_ = '"+fValue->get()+"';\n");
219  else
220  out.println("var "+String(fID)+"_val_ = "+fValue->get()+";\n");
221  }
222  }
223 
224  inline void emitScript(Print& out) const
225  {
226  if (fEnabled != nullptr && !fEnabled())
227  return;
228  if (fDynamic)
229  fDynamic->emitScript(out);
230  else
231  out.println(fScript);
232  }
233 
234  String getValue() const
235  {
236  if (fEnabled != nullptr && !fEnabled())
237  return "";
238  return (fValue != nullptr) ? fValue->get() : "";
239  }
240 
241  void setValue(String val) const
242  {
243  if (fEnabled != nullptr && !fEnabled())
244  return;
245  if (fValue != nullptr)
246  fValue->set(val);
247  else if (fAction != nullptr)
248  fAction->perform();
249  }
250 
251 protected:
252  String fID = "";
253  String fCSS = "";
254  String fBody = "";
255  String fScript = "";
256  WValue* fValue = nullptr;
257  WAction* fAction = nullptr;
258  bool fReload = false;
259  const WDynamic* fDynamic = nullptr;
260  bool (*fEnabled)() = nullptr;
261 
263  {
264  static bool sAlign = true;
265  return sAlign;
266  }
267 };
268 
269 class WDynamicElement : public WElement
270 {
271 public:
272  WDynamicElement(const WDynamic& dynamicRef) :
273  WElement()
274  {
275  fDynamic = &dynamicRef;
276  }
277 
278  WDynamicElement(String id, const WDynamic& dynamicRef, WValue* value = nullptr) :
279  WElement(value)
280  {
281  fID = id;
282  fDynamic = &dynamicRef;
283  }
284 };
285 
287 {
288 public:
289  WDynamicElementInt(String id, const WDynamic& dynamicRef, int (*getValue)(), void (*setValue)(int)) :
291  {
292  fID = id;
293  fDynamic = &dynamicRef;
294  fReload = true;
295  }
296 };
297 
298 class WVerticalAlign : public WElement
299 {
300 public:
302  {
303  verticalAlignment() = true;
304  }
305 };
306 
308 {
309 public:
311  {
312  verticalAlignment() = false;
313  }
314 };
315 
316 class WStyle : public WElement
317 {
318 public:
319  WStyle(String style)
320  {
321  appendCSS(style);
322  }
323 };
324 
325 class WSlider : public WElement
326 {
327 public:
328  WSlider(String title, String id, int min, int max, int (*getValue)(), void (*setValue)(int)) :
330  {
331  fID = id;
332  appendCSSf(".%s_css { width: 300px; }", id.c_str());
333  if (verticalAlignment())
334  appendBodyf("<p>%s: <span id='%s_val'></span></p>\n", title.c_str(), id.c_str());
335  else
336  appendBodyf("<span id='%s_val'></span>\n", id.c_str());
337  appendBodyf("<input type='range' min='%d' max='%d' class='%s_css' id='%s_slider' onchange='updateValue_%s(this.value)'/>\n", min, max, id.c_str(), id.c_str(), id.c_str());
338 
339  appendScriptf("var %s = document.getElementById('%s_slider');\n", id.c_str(), id.c_str());
340  appendScriptf("%s.value = %s_val_;\n", id.c_str(), id.c_str());
341  appendScriptf("var %s_priv = document.getElementById('%s_val'); %s_priv.innerHTML = %s.value;\n", id.c_str(), id.c_str(), id.c_str(), id.c_str());
342  appendScriptf("%s.oninput = function() { %s.value = this.value; %s_priv.innerHTML = this.value; }\n", id.c_str(), id.c_str(), id.c_str());
343  appendScriptf("function updateValue_%s(pos) {fetchNoload('%s', pos); {Connection: close};}\n", id.c_str(), id.c_str());
344  }
345 };
346 
347 class WCheckbox : public WElement
348 {
349 public:
350  WCheckbox(String title, String id, bool (*getValue)(), void (*setValue)(bool), bool (*enabled)() = nullptr) :
352  {
353  fID = id;
354  fEnabled = enabled;
355  appendCSSf(".%s_css { width: 300px; }", id.c_str());
356  if (verticalAlignment())
357  appendBodyf("<p><span id='%s_val'></span></p>\n", id.c_str());
358  else
359  appendBodyf("<span id='%s_val'></span>\n", id.c_str());
360  appendBodyf("<input type='checkbox' id='%s_cbox' onchange='updateValue_%s(this.checked)'/>\n", id.c_str(), id.c_str());
361  appendBodyf("<label class='%s_css' for='%s_cbox'>%s</label>\n", id.c_str(), id.c_str(), title.c_str());
362  appendScriptf("var %s = document.getElementById('%s_cbox');\n", id.c_str(), id.c_str());
363  appendScriptf("%s.checked = %s_val_;\n", id.c_str(), id.c_str());
364  appendScriptf("function updateValue_%s(pos) {fetchNoload('%s', pos); {Connection: close};}\n", id.c_str(), id.c_str());
365  }
366 };
367 
368 class WCheckboxReload : public WElement
369 {
370 public:
371  WCheckboxReload(String title, String id, bool (*getValue)(), void (*setValue)(bool), bool (*enabled)() = nullptr) :
373  {
374  fID = id;
375  fEnabled = enabled;
376  fReload = true;
377  appendCSSf(".%s_css { width: 300px; }", id.c_str());
378  if (verticalAlignment())
379  appendBodyf("<p><span id='%s_val'></span></p>\n", id.c_str());
380  else
381  appendBodyf("<span id='%s_val'></span>\n", id.c_str());
382  appendBodyf("<input type='checkbox' id='%s_cbox' onchange='updateValue_%s(this.checked)'/>\n", id.c_str(), id.c_str());
383  appendBodyf("<label class='%s_css' for='%s_cbox'>%s</label>\n", id.c_str(), id.c_str(), title.c_str());
384  appendScriptf("var %s = document.getElementById('%s_cbox');\n", id.c_str(), id.c_str());
385  appendScriptf("%s.checked = %s_val_;\n", id.c_str(), id.c_str());
386  appendScriptf("function updateValue_%s(pos) {fetchLoad('%s', pos); {Connection: close};}\n", id.c_str(), id.c_str());
387  }
388 };
389 
390 class WButton : public WElement
391 {
392 public:
393  WButton(String title, String id, void (*pressed)()) :
394  WElement(new WAction(pressed))
395  {
396  fID = id;
397  appendCSSf(".%s_css { width: 300px; }", id.c_str());
398  if (verticalAlignment())
399  appendBodyf("<p><span id='%s_val'></span></p>\n", id.c_str());
400  appendBodyf("<input type='button' id='%s_btn' value='%s' onclick='pressed_%s()'/>\n", id.c_str(), title.c_str(), id.c_str());
401  appendScriptf("function pressed_%s() {fetchNoload('%s', true); {Connection: close};}\n", id.c_str(), id.c_str());
402  }
403 
404  WButton(String title, String id, String href)
405  {
406  fID = id;
407  appendCSSf(".%s_css { width: 300px; }", id.c_str());
408  if (verticalAlignment())
409  appendBodyf("<p><span id='%s_val'></span></p>\n", id.c_str());
410  appendBodyf("<input type='button' id='%s_btn' value='%s' onclick='window.location.href=\"%s\"'/>\n", id.c_str(), title.c_str(), href.c_str());
411  }
412 
413  WButton(String title, String id, String href, void (*pressed)()) :
414  WElement(new WAction(pressed))
415  {
416  fID = id;
417  fReload = true;
418  appendCSSf(".%s_css { width: 300px; }", id.c_str());
419  if (verticalAlignment())
420  appendBodyf("<p><span id='%s_val'></span></p>\n", id.c_str());
421  appendBodyf("<input type='button' id='%s_btn' value='%s' onclick='pressed_%s()'/>\n", id.c_str(), title.c_str(), id.c_str());
422  appendScriptf("function pressed_%s() {window.location.href='\"%s?%s=true&\"'; {Connection: close};}\n", id.c_str(), href.c_str(), id.c_str());
423  }
424 
425  WButton(String title, String id, bool reload, void (*pressed)()) :
426  WElement(new WAction(pressed))
427  {
428  fID = id;
429  fReload = true;
430  appendCSSf(".%s_css { width: 300px; }", id.c_str());
431  if (verticalAlignment())
432  appendBodyf("<p><span id='%s_val'></span></p>\n", id.c_str());
433  appendBodyf("<input type='button' id='%s_btn' value='%s' onclick='pressed_%s()'/>\n", id.c_str(), title.c_str(), id.c_str());
434  appendScriptf("function pressed_%s() {fetchLoad('%s', true); {Connection: close};}\n", id.c_str(), id.c_str());
435  }
436 };
437 
438 class WButtonReload : public WButton
439 {
440 public:
441  WButtonReload(String title, String id, void (*pressed)()) :
442  WButton(title, id, true, pressed)
443  {
444  }
445 };
446 
447 class WLabel : public WElement
448 {
449 public:
450  WLabel(String text, String id, bool (*enabled)() = nullptr)
451  {
452  fID = id;
453  fEnabled = enabled;
454  appendCSSf(".%s_css { width: 300px; }", id.c_str());
455  appendBodyf("<p><span id='%s_val'></span></p>\n", id.c_str());
456  appendBodyf("<label class='%s_css'>%s</label>\n", id.c_str(), text.c_str());
457  }
458 };
459 
460 class WTextField : public WElement
461 {
462 public:
463  WTextField(String title, String id, String (*getValue)(), void (*setValue)(String), bool (*enabled)() = nullptr) :
465  {
466  fID = id;
467  fEnabled = enabled;
468  appendCSSf(".%s_css { width: 300px; }", id.c_str());
469  appendBodyf("<p><span id='%s_val'></span></p>\n", id.c_str());
470  appendBodyf("<label class='%s_css' for='%s_fld'>%s</label>\n", id.c_str(), id.c_str(), title.c_str());
471  appendBodyf("<input type='text' id='%s_fld' onchange='updateValue_%s(this.value)'/>\n", id.c_str(), id.c_str());
472  appendScriptf("var %s = document.getElementById('%s_fld');\n", id.c_str(), id.c_str());
473  appendScriptf("%s.value = %s_val_;\n", id.c_str(), id.c_str());
474  appendScriptf("function updateValue_%s(pos) {fetchNoload('%s', pos); {Connection: close};}\n", id.c_str(), id.c_str());
475  }
476 };
477 
479 {
480 public:
481  WTextFieldInteger(String title, String id, String (*getValue)(), void (*setValue)(String), bool (*enabled)() = nullptr) :
483  {
484  fID = id;
485  fEnabled = enabled;
486  appendCSSf(".%s_css { width: 300px; }", id.c_str());
487  appendBodyf("<p><span id='%s_val'></span></p>\n", id.c_str());
488  appendBodyf("<label class='%s_css' for='%s_fld'>%s</label>\n", id.c_str(), id.c_str(), title.c_str());
489  appendBodyf("<input type='text' id='%s_fld' onchange='updateValue_%s(this.value)'/>\n", id.c_str(), id.c_str());
490  appendScriptf("var %s = document.getElementById('%s_fld');\n", id.c_str(), id.c_str());
491  appendScriptf("setInputFilter(%s, function(value) {", id.c_str());
492  appendScriptf(" return /^[0-9]*$/.test(value);");
493  appendScriptf("});");
494  appendScriptf("%s.value = %s_val_;\n", id.c_str(), id.c_str());
495  appendScriptf("function updateValue_%s(pos) {fetchNoload('%s', pos); {Connection: close};}\n", id.c_str(), id.c_str());
496  }
497 };
498 
500 {
501 public:
502  WTextFieldIntegerRange(String title, String id, int minValue, int maxValue, String (*getValue)(), void (*setValue)(String), bool (*enabled)() = nullptr) :
504  {
505  fID = id;
506  fEnabled = enabled;
507  appendCSSf(".%s_css { width: 300px; }", id.c_str());
508  appendBodyf("<p><span id='%s_val'></span></p>\n", id.c_str());
509  appendBodyf("<label class='%s_css' for='%s_fld'>%s</label>\n", id.c_str(), id.c_str(), title.c_str());
510  appendBodyf("<input type='text' id='%s_fld' onkeypress='limitKeypress(event,this.value,%d)' onchange='updateValue_%s(this.value)'/>\n",
511  id.c_str(), int(log(maxValue) * M_LOG10E + 1), id.c_str());
512  appendScriptf("var %s = document.getElementById('%s_fld');\n", id.c_str(), id.c_str());
513  appendScriptf("setInputFilter(%s, function(value) {", id.c_str());
514  appendScriptf(" return /^[0-9]*$/.test(value);");
515  appendScriptf("});");
516  appendScriptf("%s.value = %s_val_;\n", id.c_str(), id.c_str());
517  appendScriptf("function updateValue_%s(pos) {if(pos<%d) {", id.c_str(), minValue);
518  appendScriptf(" alert('Minimum allowed value is: '+%d);", minValue);
519  appendScriptf(" %s.value = %d", id.c_str(), minValue);
520  appendScriptf("} else if (pos > %d) {", maxValue);
521  appendScriptf(" alert('Maximum allowed value is: '+%d);", maxValue);
522  appendScriptf(" %s.value = %d", id.c_str(), maxValue);
523  appendScriptf("} else {");
524  appendScriptf(" fetchNoload('%s', pos);\n", id.c_str());
525  appendScriptf("}");
526  appendScriptf("{Connection: close};}\n");
527  }
528 };
529  // appendScript(" return /^\\d*\\.?\\d*$/.test(value);");
530 
531 class WSelect : public WElement
532 {
533 public:
534  WSelect(String title, String id, String options[], unsigned numOptions, int (*getValue)(), void (*setValue)(int), bool (*enabled)() = nullptr) :
536  {
537  fID = id;
538  fEnabled = enabled;
539  appendCSSf(".%s_css { width: 300px; }\n", id.c_str());
540  appendBodyf("<p><span id='%s_val'></span></p>\n", id.c_str());
541  appendBodyf("<label class='%s_css' for='%s_fld'>%s</label>\n", id.c_str(), id.c_str(), title.c_str());
542  appendBodyf("<select id='%s_fld' onchange='updateValue_%s(this.value)'>\n", id.c_str(), id.c_str());
543  for (unsigned i = 0; i < numOptions; i++)
544  {
545  appendBodyf("<option value='%d'>%s</option>\n", i, options[i].c_str());
546  }
547  appendBodyf("</select>\n");
548  appendScriptf("var %s = document.getElementById('%s_fld');\n", id.c_str(), id.c_str());
549  appendScriptf("%s.value = %s_val_;\n", id.c_str(), id.c_str());
550  appendScriptf("function updateValue_%s(pos) {fetchNoload('%s', pos); {Connection: close};}\n", id.c_str(), id.c_str());
551  }
552 
553  WSelect(String title, String id, String options[], String values[], unsigned numOptions, int (*getValue)(), void (*setValue)(int), bool (*enabled)() = nullptr) :
555  {
556  fID = id;
557  fEnabled = enabled;
558  appendCSSf(".%s_css { width: 300px; }\n", id.c_str());
559  appendBodyf("<p><span id='%s_val'></span></p>\n", id.c_str());
560  appendBodyf("<label class='%s_css' for='%s_fld'>%s</label>\n", id.c_str(), id.c_str(), title.c_str());
561  appendBodyf("<select id='%s_fld' onchange='updateValue_%s(this.value)'>\n", id.c_str(), id.c_str());
562  for (unsigned i = 0; i < numOptions; i++)
563  {
564  // appendBodyf("<option value='%s'>%s</option>\n", values[i].c_str(), options[i].c_str());
565  }
566  appendBodyf("</select>\n");
567  appendScriptf("var %s = document.getElementById('%s_fld');\n", id.c_str(), id.c_str());
568  appendScriptf("%s.value = %s_val_;\n", id.c_str(), id.c_str());
569  appendScriptf("function updateValue_%s(pos) {fetchNoload('%s', pos); {Connection: close};}\n", id.c_str(), id.c_str());
570  }
571 };
572 
573 class WPassword : public WElement
574 {
575 public:
576  WPassword(String title, String id, String (*getValue)(), void (*setValue)(String)) :
578  {
579  fID = id;
580  appendCSSf(".%s_css { width: 300px; }", id.c_str());
581  if (verticalAlignment())
582  appendBodyf("<p><span id='%s_val'></span></p>\n", id.c_str());
583  appendBodyf("<label class='%s_css' for='%s_fld'>%s</label>\n", id.c_str(), id.c_str(), title.c_str());
584  appendBodyf("<input type='password' id='%s_fld' onchange='updateValue_%s(this.value)'/>\n", id.c_str(), id.c_str());
585  appendScriptf("var %s = document.getElementById('%s_fld');\n", id.c_str(), id.c_str());
586  appendScriptf("%s.value = %s_val_;\n", id.c_str(), id.c_str());
587  appendScriptf("function updateValue_%s(pos) {fetchNoload('%s', pos); {Connection: close};}\n", id.c_str(), id.c_str());
588  }
589 };
590 
591 class WFileInput : public WElement
592 {
593 public:
594  WFileInput(String title, String id, String (*getValue)(), void (*setValue)(String)) :
596  {
597  fID = id;
598  appendCSSf(".%s_css { width: 300px; }", id.c_str());
599  if (verticalAlignment())
600  appendBodyf("<p><span id='%s_val'></span></p>\n", id.c_str());
601  appendBodyf("<label class='%s_css' for='%s_file'>%s</label>\n", id.c_str(), id.c_str(), title.c_str());
602  appendBodyf("<input type='file' id='%s_file' onchange='updateValue_%s(this)'/>\n", id.c_str(), id.c_str());
603  appendScriptf("var %s = document.getElementById('%s_file');\n", id.c_str(), id.c_str());
604  appendScriptf("function updateValue_%s(pos) {);}\n", id.c_str());
605  }
606 };
607 
608 class WFirmwareFile : public WElement
609 {
610 public:
611  WFirmwareFile(String title, String id)
612  {
613  fID = id;
614  appendCSSf(".%s_css { width: 300px; }", id.c_str());
615  if (verticalAlignment())
616  appendBodyf("<p><span id='%s_val'></span></p>\n", id.c_str());
617  appendBodyf("<label class='%s_css' for='%s_file'>%s</label>\n", id.c_str(), id.c_str(), title.c_str());
618  appendBodyf("<input type='file' id='%s_file' accept='.bin' onchange='updateValue_%s(this)'/>\n", id.c_str(), id.c_str());
619  appendScriptf("var %s = document.getElementById('%s_file');\n", id.c_str(), id.c_str());
620  appendScriptf("function updateValue_%s(pos) { document.getElementById('%s_upload').disabled = false; }\n", id.c_str(), id.c_str());
621  }
622 };
623 
624 class WFirmwareUpload : public WElement
625 {
626 public:
627  WFirmwareUpload(String title, String id)
628  {
629  fID = id;
630  appendCSS("."+String(id)+"_css { width: 300px; }");
631  appendCSS("#"+String(id)+"_prg,#"+String(id)+"_prgbar{background-color:#f1f1f1;border-radius:10px}");
632  appendCSS("#"+String(id)+"_bar{background-color:#3498db;width:0%;height:10px}");
633  if (verticalAlignment())
634  appendBody("<p><span id='"+String(id)+"_val'></span></p>\n");
635  appendBody("<input type='button' id='"+String(id)+"_upload' value='"+title+"' onclick='upload_"+String(id)+"()'/>\n");
636  appendBody("<br><br>\n");
637  appendBody("<div id='"+String(id)+"_prg'></div>\n");
638  appendBody("<br><div id='"+String(id)+"_prgbar'><div id='"+String(id)+"_bar'></div></div><br></form>\n");
639  appendScript("var "+String(id)+"_upload = document.getElementById('"+String(id)+"_upload');\n");
640  appendScript("var "+String(id)+"_prg = document.getElementById('"+String(id)+"_prg');\n");
641  appendScript("var "+String(id)+"_prgbar = document.getElementById('"+String(id)+"_prgbar');\n");
642  appendScript("var "+String(id)+"_bar = document.getElementById('"+String(id)+"_bar');\n");
643  appendScript(String(id)+"_upload.disabled = true;\n");
644  appendScript("function upload_"+String(id)+"() {\n");
645  appendScript("const xhr = new XMLHttpRequest();\n");
646  appendScript("xhr.upload.onprogress = (evt) => {\n");
647  appendScript(" if (evt.lengthComputable) {\n");
648  appendScript(" var per = evt.loaded / evt.total;\n");
649  appendScript(" "+String(id)+"_prg.innerHTML = 'progress: ' + Math.round(per*100) + '%';\n");
650  appendScript(" "+String(id)+"_bar.style.width = Math.round(per*100) + '%';\n");
651  appendScript(" }\n");
652  appendScript("};\n");
653  appendScript("xhr.upload.onerror = () => {\n");
654  appendScript(" "+String(id)+"_prg.innerHTML = 'Upload failed!';\n");
655  appendScript(" "+String(id)+"_bar.style.width = '0%';\n");
656  appendScript("};\n");
657  appendScript("xhr.upload.abort = () => {\n");
658  appendScript(" "+String(id)+"_prg.innerHTML = 'Upload cancelled';\n");
659  appendScript(" "+String(id)+"_bar.style.width = '0%';\n");
660  appendScript("};\n");
661  appendScript("xhr.upload.onload = () => {\n");
662  appendScript(" "+String(id)+"_prg.innerHTML = 'Upload complete. Please wait .';\n");
663  appendScript(" "+String(id)+"_bar.style.width = '0%';\n");
664  appendScript(" xhr.counter = 0;\n");
665  appendScript(" setInterval(function() {\n");
666  appendScript(" if (xhr.counter++ >= 8) window.location.href='/';\n");
667  appendScript(" "+String(id)+"_prg.innerHTML = "+String(id)+"_prg.innerHTML + '.';\n");
668  appendScript(" }, 2000);\n");
669  appendScript("};\n");
670  appendScript("xhr.open('POST', 'upload/firmware', true);\n");
671  appendScript("xhr.send("+String(id)+".files[0]);\n");
672  appendScript("}\n");
673  }
674 };
675 
676 struct WMenuData
677 {
678  const char* title;
679  const char* href;
680 };
681 
682 class WVerticalMenu : public WElement
683 {
684 public:
685  WVerticalMenu(String id, const WMenuData* menuData, unsigned menuCount, unsigned active = 0)
686  {
687  appendCSSf(".%s_vertical_menu { width: 300px; margin-left: auto; margin-right: auto; }\n", id.c_str());
688  appendCSSf(".%s_vertical_menu a { background-color: #eee; color: black; display: block; padding: 12px; text-decoration: none; }\n", id.c_str());
689  appendCSSf(".%s_vertical_menu a:hover { background-color: #ccc; }\n", id.c_str());
690  appendCSSf(".%s_vertical_menu a:active { background-color: #4CAF50; color: white; }\n", id.c_str());
691  appendBodyf("<div class='%s_vertical_menu'>\n", id.c_str());
692  for (unsigned i = 0; i < menuCount; i++)
693  {
694  if (i == active)
695  appendBodyf("<a href='%s' class='active'>%s</a>\n", menuData[i].href, menuData[i].title);
696  else
697  appendBodyf("<a href='%s'>%s</a>\n", menuData[i].href, menuData[i].title);
698  }
699  appendBodyf("</div>\n");
700  }
701 };
702 
703 class W1 : public WElement
704 {
705 public:
706  W1(String title)
707  {
708  appendBody("<h1>"+String(title)+"</h1>");
709  }
710 };
711 
712 class WHR : public WElement
713 {
714 public:
715  WHR()
716  {
717  appendBody("<hr>");
718  }
719 };
720 
721 class WHRef : public WElement
722 {
723 public:
724  WHRef(String link, String text)
725  {
726  appendBodyf("<a href=\"%s\">%s</a>", link.c_str(), text.c_str());
727  }
728 };
729 
730 class WImage : public WElement
731 {
732 public:
733  WImage(String alt, String data)
734  {
735  appendBody("<p><img src='data:image/png;base64, ");
736  appendBody(data);
737  appendBody("' alt='"+String(alt)+"'></p>");
738  }
739 };
740 
741 class WSVG : public WElement
742 {
743 public:
744  WSVG(String data)
745  {
746  appendBody("<p>");
747  appendBody(data);
748  appendBody("</p>");
749  }
750 };
751 
752 class WHTML : public WElement
753 {
754 public:
755  WHTML(String data)
756  {
757  appendBody(data);
758  }
759 };
760 
761 class WJavaScript : public WElement
762 {
763 public:
764  WJavaScript(String data)
765  {
766  appendScript(data);
767  }
768 };
769 
770 class WTableRow : public WElement
771 {
772 public:
774  {
775  appendBody("<tr>");
776  }
777 };
778 
779 class WTableCol : public WElement
780 {
781 public:
783  {
784  appendBody("<td>");
785  verticalAlignment() = false;
786  }
787 
788  WTableCol(String styleClass)
789  {
790  appendBody("<td class=\"");
791  appendBody(styleClass);
792  appendBody("\">");
793  verticalAlignment() = false;
794  }
795 };
796 
797 class WTableColEnd : public WElement
798 {
799 public:
801  {
802  appendBody("</td>");
803  verticalAlignment() = true;
804  }
805 };
806 
807 class WTableRowEnd : public WElement
808 {
809 public:
811  {
812  appendBody("</tr>");
813  }
814 };
815 
816 class WTableLabel : public WElement
817 {
818 public:
819  WTableLabel(String text, String id, bool (*enabled)() = nullptr)
820  {
821  fID = id;
822  fEnabled = enabled;
823  appendBodyf("<label class='%s_css'>%s</label>\n", id.c_str(), text.c_str());
824  }
825 };
826 
827 class WTableTextField : public WElement
828 {
829 public:
830  WTableTextField(String id, String (*getValue)(), void (*setValue)(String), bool (*enabled)() = nullptr) :
832  {
833  fID = id;
834  fEnabled = enabled;
835  appendBodyf("<input type='text' id='%s_fld' onchange='updateValue_%s(this.value)'/>\n", id.c_str(), id.c_str());
836  appendScriptf("var %s = document.getElementById('%s_fld');\n", id.c_str(), id.c_str());
837  appendScriptf("%s.value = %s_val_;\n", id.c_str(), id.c_str());
838  appendScriptf("function updateValue_%s(pos) {fetchNoload('%s', pos); {Connection: close};}\n", id.c_str(), id.c_str());
839  }
840 };
841 
842 #ifndef HTTP_UPLOAD_BUFLEN
843 #define HTTP_UPLOAD_BUFLEN 1436
844 #endif
845 
847 {
852 };
853 
855 {
856 public:
858  String filename;
859  String name;
860  String type;
861  String queryString;
862  size_t fileSize; // file size
863  size_t receivedSize; // received size
864  size_t currentSize; // size of data currently in buf
866 };
867 
868 class WPage
869 {
870 public:
871  WPage(String url, const WElement contents[], unsigned numElements, String title = "", String lang = "en") :
872  fURL(url),
873  fTitleOrPath(title),
874  fLanguageOrMimeType(lang),
875  fNumElements(numElements),
876  fContents(contents)
877  {
878  }
879 
880  WPage(String url, fs::FS* fs, String path, const WElement contents[], unsigned numElements) :
881  fURL(url),
882  fTitleOrPath(path),
883  fLanguageOrMimeType("text/html"),
884  fFS(fs),
885  fNumElements(numElements),
886  fContents(contents)
887  {
888  }
889 
890  WPage(String url, fs::FS* fs, String mimeType) :
891  fURL(url),
892  fTitleOrPath(url),
893  fLanguageOrMimeType(mimeType),
894  fFS(fs),
895  fNumElements(0),
896  fContents(nullptr)
897  {
898  }
899 
900  inline const String& getURL() const
901  {
902  return fURL;
903  }
904 
905  inline bool isGet() const
906  {
907  return (fCompleteProc == nullptr && fUploaderProc == nullptr);
908  }
909 
910  void handleGetRequest(Print& out, String &header) const
911  {
912  bool needsReload = true;
913  String prefix = "GET "+fURL+"?";
914  if (header.startsWith(prefix) || fAPIProc != nullptr)
915  {
916  if (!isGet())
917  return;
918 
919  if (fAPIProc)
920  {
921  int end = header.indexOf(' ', prefix.length());
922  fAPIProc(out, header.substring(prefix.length(), end));
923  return;
924  }
925  int skiplen = 2;
926  int pos1 = header.indexOf("&?");
927  if (pos1 == -1)
928  {
929  skiplen = 1;
930  pos1 = header.indexOf('?');
931  }
932  int pos2 = header.indexOf('=',pos1);
933  int pos3 = header.indexOf('&',pos2);
934  if (pos1 != -1 && pos2 != -1 && pos3 != -1)
935  {
936  String var = header.substring(pos1+skiplen, pos2);
937  String val = header.substring(pos2+1, pos3);
938  DEBUG_PRINT("SET "); DEBUG_PRINT(var); DEBUG_PRINT(" = "); DEBUG_PRINTLN(val);
939  for (unsigned i = 0; i < fNumElements; i++)
940  {
941  if (var == fContents[i].getID())
942  {
943  fContents[i].setValue(val);
944  needsReload = fContents[i].needsReload();
945  break;
946  }
947  }
948  }
949  }
950  if (needsReload)
951  {
952  if (fFS != nullptr)
953  {
954  bool compressed = false;
955  fs::File file = openFileOrCompressed(fTitleOrPath, compressed);
956  if (file)
957  {
958  ::printf("FILE: %s (compressed=%d)\n", fTitleOrPath.c_str(), compressed);
959  if (compressed && header.indexOf("Accept-Encoding: gzip") == -1)
960  {
961  ::printf("Client needs to support compression\n");
962  if (fLanguageOrMimeType == "text/html")
963  {
964  out.println("HTTP/1.0 200 OK");
965  out.print("Content-type:"); out.println(fLanguageOrMimeType);
966  out.println("Connection: close");
967  out.println();
968  out.println("Compression required");
969  }
970  else
971  {
972  out.println("HTTP/1.0 404 Not Found");
973  out.print("Content-type:"); out.println(fLanguageOrMimeType);
974  out.println("Content-type:text/html");
975  out.println("Connection: close");
976  out.println();
977  }
978  }
979  else
980  {
981  size_t fileSize = file.size();
982  out.println("HTTP/1.0 200 OK");
983  out.print("Content-type:"); out.println(fLanguageOrMimeType);
984  out.print("Content-Length:"); out.println(fileSize);
985  out.println("Cache-Control: private, max-age=2592000");
986  if (compressed)
987  out.println("Content-Encoding: gzip");
988  out.println("Connection: close");
989  out.println();
990  char* buffer = (char*)malloc(1024);
991  while (file.available())
992  {
993  size_t bytesRead = file.readBytes(buffer, 1024);
994  out.write(buffer, bytesRead);
995  }
996  free(buffer);
997  }
998  }
999  else
1000  {
1001  DEBUG_PRINTLN("FILE NOT FOUND: "+String(fTitleOrPath));
1002  out.println("HTTP/1.0 404 Not Found");
1003  out.print("Content-type:"); out.println(fLanguageOrMimeType);
1004  out.println("Content-type:text/html");
1005  out.println("Connection: close");
1006  out.println();
1007  }
1008  }
1009  else
1010  {
1011  out.println("HTTP/1.0 200 OK");
1012  out.println("Content-type:text/html");
1013  out.println("Connection: close");
1014  out.println();
1015  out.print(R"RAW(<!DOCTYPE html><html lang=")RAW");
1016  out.print(fLanguageOrMimeType);
1017  out.print(
1018  R"RAW("><head><meta charset="UTF-8"><meta name="viewport", content="width=device-width, initial-scale=1">
1019  <link rel="icon" href="data:,"><title>)RAW");
1020  out.print(fTitleOrPath);
1021  out.print(
1022  R"RAW(</title>
1023  <style>body { text-align: center; font-family: "Trebuchet MS", Arial; margin-left:auto; margin-right:auto;}
1024  )RAW");
1025  for (unsigned i = 0; i < fNumElements; i++)
1026  fContents[i].emitCSS(out);
1027  out.print(
1028  R"RAW(
1029  </style>
1030  </head><body>
1031  )RAW");
1032  for (unsigned i = 0; i < fNumElements; i++)
1033  fContents[i].emitBody(out);
1034  out.print(
1035  R"RAW(
1036  <script>
1037  function fetchNoload(key,val) {
1038  var baseurl = window.location.protocol+'//'+window.location.host+location.pathname;
1039  fetch(baseurl+'?'+key+'='+val+'&');
1040  }
1041  function fetchLoad(key,val) {
1042  var baseurl = window.location.protocol+'//'+window.location.host+location.pathname;
1043  window.location.href=baseurl+'?'+key+'='+val+'&';
1044  }
1045  function limitKeypress(event, value, maxLength) {
1046  if (value != undefined && value.toString().length >= maxLength) {
1047  event.preventDefault();
1048  }
1049  }
1050  function setInputFilter(textbox, inputFilter) {
1051  ["input", "keydown", "keyup", "mousedown", "mouseup", "select", "contextmenu", "drop"].forEach(function(event) {
1052  textbox.addEventListener(event, function() {
1053  if (inputFilter(this.value)) {
1054  this.oldValue = this.value;
1055  this.oldSelectionStart = this.selectionStart;
1056  this.oldSelectionEnd = this.selectionEnd;
1057  } else if (this.hasOwnProperty("oldValue")) {
1058  this.value = this.oldValue;
1059  this.setSelectionRange(this.oldSelectionStart, this.oldSelectionEnd);
1060  } else {
1061  this.value = "";
1062  }
1063  });
1064  });
1065  }
1066  )RAW");
1067  for (unsigned i = 0; i < fNumElements; i++)
1068  fContents[i].emitValue(out);
1069  for (unsigned i = 0; i < fNumElements; i++)
1070  fContents[i].emitScript(out);
1071  out.print(
1072  R"RAW(
1073  {Connection: close};
1074  </script>
1075  </body></html>
1076  )RAW");
1077  }
1078  }
1079  else
1080  {
1081  out.println("HTTP/1.0 200 OK");
1082  out.println("Content-type:text/html");
1083  out.println("Connection: close");
1084  out.println();
1085  }
1086  }
1087 
1088  inline void callComplete(Client& client) const
1089  {
1090  if (fCompleteProc != nullptr)
1091  fCompleteProc(client);
1092  }
1093 
1094  inline void callUploader(WUploader &uploader) const
1095  {
1096  if (fUploaderProc != nullptr)
1097  fUploaderProc(uploader);
1098  }
1099 
1100 protected:
1101  inline fs::File openFileOrCompressed(String fileName, bool &compressed) const
1102  {
1103  // SPIFFS open always returns true
1104  fs::File file = fFS->open(fileName + ".gz", FILE_READ, false);
1105  if (file && !file.isDirectory())
1106  {
1107  compressed = true;
1108  return file;
1109  }
1110  compressed = false;
1111  return fFS->open(fileName);
1112  }
1113 
1114  String fURL;
1115  String fTitleOrPath;
1116  String fLanguageOrMimeType;
1117  fs::FS* fFS;
1118  uint8_t fFlags = 0;
1119  unsigned fNumElements;
1120  const WElement* fContents;
1121  void (*fCompleteProc)(Client& client) = nullptr;
1122  void (*fUploaderProc)(WUploader &uploader) = nullptr;
1123  void (*fAPIProc)(Print& out, String queryString) = nullptr;
1124 };
1125 
1126 class WAPI : public WPage
1127 {
1128 public:
1129  WAPI(String url, void (*apiProc)(Print& out, String queryString)) :
1130  WPage(url, nullptr, 0)
1131  {
1132  fAPIProc = apiProc;
1133  }
1134 };
1135 
1136 class WUpload : public WPage
1137 {
1138 public:
1139  WUpload(String url, void (*completeProc)(Client& client), void (*uploaderProc)(WUploader &uploader)) :
1140  WPage(url, nullptr, 0)
1141  {
1142  fCompleteProc = completeProc;
1143  fUploaderProc = uploaderProc;
1144  }
1145 };
1146 
1170 template<unsigned maxClients = 10, unsigned numPages = 0>
1171 class WifiWebServer : public WiFiServer, public WifiAccess::Notify
1172 {
1173 public:
1174  WiFiClient fClients[maxClients];
1175 
1182  WifiWebServer(const WPage pages[], WifiAccess &wifiAccess, uint16_t port = 80) :
1183  WiFiServer(port),
1184  fHeader(""),
1185  fRequest(""),
1186  fPages(pages)
1187  {
1188  wifiAccess.addNotify(this);
1189  }
1190 
1191  void setConnect(void (*callback)())
1192  {
1193  fConnectedCallback = callback;
1194  }
1195 
1196  void setActivity(void (*callback)())
1197  {
1198  fActivityCallback = callback;
1199  }
1200 
1201  bool enabled()
1202  {
1203  return fEnabled;
1204  }
1205 
1206  virtual void wifiConnected(WifiAccess& access) override
1207  {
1208  DEBUG_PRINTLN("WifiWebServer.wifiConnected");
1209  if (!fStarted)
1210  {
1211  begin();
1212  fStarted = true;
1213  }
1214  }
1215 
1216  virtual void wifiDisconnected(WifiAccess& access) override
1217  {
1218  DEBUG_PRINTLN("WifiWebServer.wifiDisconnected");
1219  for (unsigned i = 0; i < maxClients; i++)
1220  {
1221  if (fClients[i])
1222  {
1223  fClients[i].stop();
1224  }
1225  }
1226  }
1227 
1231  void handle()
1232  {
1233  if (!fEnabled || !fStarted)
1234  return;
1235  //check if there are any new clients
1236  if (hasClient())
1237  {
1238  unsigned i;
1239  for (i = 0; i < maxClients; i++)
1240  {
1241  //find free/disconnected spot
1242  if (!fClients[i] || !fClients[i].connected())
1243  {
1244  if (fActivityCallback != nullptr)
1245  fActivityCallback();
1246  if (fClients[i])
1247  fClients[i].stop();
1248  fClients[i] = available();
1249  fClients[i].setNoDelay(true);
1250  if (!fClients[i])
1251  DEBUG_PRINTLN("available broken");
1252  DEBUG_PRINT("New client: ");
1253  DEBUG_PRINT(i); DEBUG_PRINT(' ');
1254  DEBUG_PRINTLN(fClients[i].remoteIP());
1255  fRequest.reserve(4096);
1256  fHeader.reserve(4096);
1257  if (fConnectedCallback)
1258  fConnectedCallback();
1259  break;
1260  }
1261  }
1262  if (i >= maxClients)
1263  {
1264  //no free/disconnected spot so reject
1265  Serial.println("NO CLIENTS AVAILABLE");
1266  available().stop();
1267  }
1268  }
1269  //check clients for data
1270  for (unsigned i = 0; i < maxClients; i++)
1271  {
1272  if (fClients[i] && fClients[i].connected())
1273  {
1274  //get data from the telnet client and push it to the UART
1275  if (fUploader != nullptr)
1276  {
1277  if (fUploader->receivedSize + fUploader->currentSize == fUploader->fileSize)
1278  {
1279  if (fUploader->currentSize)
1280  {
1281  fUploader->status = UPLOAD_FILE_WRITE;
1282  fUploaderPage->callUploader(*fUploader);
1283  fUploader->receivedSize += fUploader->currentSize;
1284  fUploader->currentSize = 0;
1285  }
1286  fUploader->status = UPLOAD_FILE_END;
1287  fUploaderPage->callUploader(*fUploader);
1288  fUploaderPage->callComplete(fClients[i]);
1289  delete fUploader;
1290  fUploaderPage = nullptr;
1291  fUploader = nullptr;
1292 
1293  // The HTTP response ends with another blank line
1294  // Break out of the while loop
1295  fHeader = "";
1296  fClients[i].stop();
1297  }
1298  }
1299  if (fClients[i].available() && fActivityCallback != nullptr)
1300  fActivityCallback();
1301  while (fClients[i].available())
1302  {
1303  char c = fClients[i].read();
1304  // Serial.write(c);
1305  if (fUploader != nullptr)
1306  {
1307  if (fUploader->currentSize == HTTP_UPLOAD_BUFLEN)
1308  {
1309  fUploader->status = UPLOAD_FILE_WRITE;
1310  fUploaderPage->callUploader(*fUploader);
1311  fUploader->receivedSize += fUploader->currentSize;
1312  fUploader->currentSize = 0;
1313  }
1314  fUploader->buf[fUploader->currentSize++] = c;
1315  fUploader->buf[fUploader->currentSize] = 0;
1316  continue;
1317  }
1318  fHeader.concat(c);
1319  if (c == '\n')
1320  {
1321  // if the byte is a newline character
1322  // if the current line is blank, you got two newline characters in a row.
1323  // that's the end of the client HTTP request, so send a response:
1324  if (fRequest.length() == 0)
1325  {
1326  if (fHeader.startsWith("POST /"))
1327  {
1328  fUploaderPage = getPost();
1329  if (fUploaderPage == nullptr)
1330  {
1331  fClients[i].println("HTTP/1.0 404 Not Found");
1332  fClients[i].println("Content-type:text/html");
1333  fClients[i].println("Connection: close");
1334  fClients[i].println();
1335  break;
1336  }
1337  else
1338  {
1339  int offs = fHeader.indexOf("\nContent-Length: ");
1340  unsigned contentLength = 0;
1341  if (offs > 0)
1342  {
1343  int pos1 = fHeader.indexOf('\n', offs+1);
1344  int pos2 = fHeader.lastIndexOf(' ', pos1);
1345  if (pos1 != 0 && pos2 != 0)
1346  {
1347  contentLength = fHeader.substring(pos1+1, pos2).toInt();
1348  }
1349  }
1350  fUploader = new WUploader;
1351  fUploader->status = UPLOAD_FILE_START;
1352  fUploader->filename = "filename.txt";
1353  fUploader->name = "name.txt";
1354  fUploader->type = "type.txt";
1355  fUploader->fileSize = contentLength;
1356  fUploader->receivedSize = 0;
1357  fUploader->currentSize = 0;
1358 
1359  String prefix = "POST "+fUploaderPage->getURL()+"?";
1360  if (fHeader.startsWith(prefix))
1361  {
1362  int end = fHeader.indexOf(' ', prefix.length());
1363  fUploader->queryString = fHeader.substring(prefix.length(), end);
1364  }
1365  else
1366  {
1367  fUploader->queryString = "";
1368  }
1369  fUploaderPage->callUploader(*fUploader);
1370  }
1371  }
1372  else if (fHeader.startsWith("GET /robots.txt"))
1373  {
1374  Stream& out = fClients[i];
1375  //PSRamBufferedPrintStream out(fClients[i]);
1376  // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
1377  // and a content-type so the client knows what's coming, then a blank line:
1378  out.println("HTTP/1.0 200 OK");
1379  out.println("Content-type:text/html");
1380  out.println("Connection: close");
1381  out.println();
1382 
1383  // The HTTP response ends with another blank line
1384  out.println();
1385  out.flush();
1386  // Break out of the while loop
1387  fHeader = "";
1388  fClients[i].stop();
1389  break;
1390  }
1391  else if (fHeader.startsWith("GET /"))
1392  {
1393  // Stream& out = fClients[i];
1395  // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
1396  // and a content-type so the client knows what's coming, then a blank line:
1397  handleGetRequest(out);
1398 
1399  // The HTTP response ends with another blank line
1400  out.println();
1401  out.flush();
1402  // Break out of the while loop
1403  fHeader = "";
1404  fClients[i].stop();
1405  break;
1406  }
1407  }
1408  else
1409  {
1410  fRequest = "";
1411  }
1412  }
1413  else if (c != '\r')
1414  {
1415  fRequest.concat(c);
1416  }
1417  }
1418 
1419  }
1420  else
1421  {
1422  if (fClients[i])
1423  {
1424  fClients[i].stop();
1425  }
1426  }
1427  }
1428  }
1429 
1430 private:
1431  bool fEnabled = true;
1432  bool fStarted = false;
1433  String fHeader;
1434  String fRequest;
1435  bool fHandlePost;
1436  const WPage* fPages;
1437  const WPage* fUploaderPage = nullptr;
1438  WUploader* fUploader = nullptr;
1439  void (*fConnectedCallback)() = nullptr;
1440  void (*fWiFiActiveCallback)(bool ap) = nullptr;
1441  void (*fActivityCallback)() = nullptr;
1442 
1443  const WPage* getPage() const
1444  {
1445  String url = "/";
1446  if (fHeader.startsWith("GET /"))
1447  {
1448  int pos1 = fHeader.indexOf('\n');
1449  int pos2 = fHeader.lastIndexOf(' ', pos1);
1450  if (pos1 != -1 && pos2 != -1)
1451  {
1452  url = fHeader.substring(4, pos2);
1453  if ((pos1 = url.indexOf('?')) != -1)
1454  {
1455  url = url.substring(0, pos1);
1456  }
1457  }
1458  }
1459  for (unsigned i = 0; i < numPages; i++)
1460  {
1461  if (fPages[i].getURL() == url && fPages[i].isGet())
1462  {
1463  return &fPages[i];
1464  }
1465  }
1466  return nullptr;
1467  }
1468 
1469  const WPage* getPost() const
1470  {
1471  String url = "/";
1472  if (fHeader.startsWith("POST /"))
1473  {
1474  int pos1 = fHeader.indexOf('\n');
1475  int pos2 = fHeader.lastIndexOf(' ', pos1);
1476  if (pos1 != -1 && pos2 != -1)
1477  {
1478  url = fHeader.substring(5, pos2);
1479  if ((pos1 = url.indexOf('?')) != -1)
1480  {
1481  url = url.substring(0, pos1);
1482  }
1483  }
1484  }
1485  for (unsigned i = 0; i < numPages; i++)
1486  {
1487  if (fPages[i].getURL() == url && !fPages[i].isGet())
1488  {
1489  return &fPages[i];
1490  }
1491  }
1492  return nullptr;
1493  }
1494 
1495  void handleGetRequest(Print &out)
1496  {
1497  const WPage* page = getPage();
1498  if (page != nullptr)
1499  {
1500  page->handleGetRequest(out, fHeader);
1501  }
1502  else
1503  {
1504  out.println("HTTP/1.0 404 NOT FOUND");
1505  out.println("Connection: close");
1506  out.println();
1507  }
1508  }
1509 };
1510 
1511 
1512 #endif
WMenuData
Definition: WifiWebServer.h:676
WSVG
Definition: WifiWebServer.h:741
WifiWebServer::enabled
bool enabled()
Definition: WifiWebServer.h:1201
WElement::getID
String getID() const
Definition: WifiWebServer.h:147
WifiWebServer::setActivity
void setActivity(void(*callback)())
Definition: WifiWebServer.h:1196
WPage::WPage
WPage(String url, const WElement contents[], unsigned numElements, String title="", String lang="en")
Definition: WifiWebServer.h:871
WPage::isGet
bool isGet() const
Definition: WifiWebServer.h:905
WTextFieldIntegerRange
Definition: WifiWebServer.h:499
WFirmwareUpload
Definition: WifiWebServer.h:624
WCheckbox::WCheckbox
WCheckbox(String title, String id, bool(*getValue)(), void(*setValue)(bool), bool(*enabled)()=nullptr)
Definition: WifiWebServer.h:350
UPLOAD_FILE_ABORTED
@ UPLOAD_FILE_ABORTED
Definition: WifiWebServer.h:851
WValue::get
virtual String get()=0
WInteger::WInteger
WInteger(int(*getValue)(), void(*setValue)(int))
Definition: WifiWebServer.h:67
WDynamicElement
Definition: WifiWebServer.h:269
WTextFieldInteger::WTextFieldInteger
WTextFieldInteger(String title, String id, String(*getValue)(), void(*setValue)(String), bool(*enabled)()=nullptr)
Definition: WifiWebServer.h:481
WElement::fValue
WValue * fValue
Definition: WifiWebServer.h:256
WElement::fReload
bool fReload
Definition: WifiWebServer.h:258
W1
Definition: WifiWebServer.h:703
WElement::appendCSS
void appendCSS(String str)
Definition: WifiWebServer.h:148
WButton
Definition: WifiWebServer.h:390
WUpload
Definition: WifiWebServer.h:1136
WUploader::currentSize
size_t currentSize
Definition: WifiWebServer.h:864
WifiAccess
Definition: WifiAccess.h:25
WElement::appendScriptf
void appendScriptf(const char *fmt,...)
Definition: WifiWebServer.h:178
WElement::emitBody
void emitBody(Print &out) const
Definition: WifiWebServer.h:201
DEBUG_PRINT
#define DEBUG_PRINT(s)
Definition: ReelTwo.h:189
WButtonReload::WButtonReload
WButtonReload(String title, String id, void(*pressed)())
Definition: WifiWebServer.h:441
UPLOAD_FILE_WRITE
@ UPLOAD_FILE_WRITE
Definition: WifiWebServer.h:849
WButtonReload
Definition: WifiWebServer.h:438
WVerticalAlign
Definition: WifiWebServer.h:298
ReelTwo.h
WDynamic::emitBody
virtual void emitBody(Print &out) const =0
WLabel::WLabel
WLabel(String text, String id, bool(*enabled)()=nullptr)
Definition: WifiWebServer.h:450
WTableColEnd::WTableColEnd
WTableColEnd()
Definition: WifiWebServer.h:800
WElement::appendCSSf
void appendCSSf(const char *fmt,...)
Definition: WifiWebServer.h:152
WBoolean::set
virtual void set(String val) override
Definition: WifiWebServer.h:53
WValue
Definition: WifiWebServer.h:13
WUploader::fileSize
size_t fileSize
Definition: WifiWebServer.h:862
WElement::emitCSS
void emitCSS(Print &out) const
Definition: WifiWebServer.h:191
WBoolean::fSetValue
void(* fSetValue)(bool)
Definition: WifiWebServer.h:61
WInteger::fSetValue
void(* fSetValue)(int)
Definition: WifiWebServer.h:88
WSelect::WSelect
WSelect(String title, String id, String options[], unsigned numOptions, int(*getValue)(), void(*setValue)(int), bool(*enabled)()=nullptr)
Definition: WifiWebServer.h:534
WifiAccess::Notify
Definition: WifiAccess.h:166
WString::fGetValue
String(* fGetValue)()
Definition: WifiWebServer.h:119
WString::getQuoteValue
virtual bool getQuoteValue() override
Definition: WifiWebServer.h:100
WAction::perform
void perform()
Definition: WifiWebServer.h:29
WifiWebServer::WifiWebServer
WifiWebServer(const WPage pages[], WifiAccess &wifiAccess, uint16_t port=80)
Constructor.
Definition: WifiWebServer.h:1182
WFirmwareFile::WFirmwareFile
WFirmwareFile(String title, String id)
Definition: WifiWebServer.h:611
WPage::callComplete
void callComplete(Client &client) const
Definition: WifiWebServer.h:1048
WString
Definition: WifiWebServer.h:91
WTextFieldIntegerRange::WTextFieldIntegerRange
WTextFieldIntegerRange(String title, String id, int minValue, int maxValue, String(*getValue)(), void(*setValue)(String), bool(*enabled)()=nullptr)
Definition: WifiWebServer.h:502
WElement::setValue
void setValue(String val) const
Definition: WifiWebServer.h:241
WStyle
Definition: WifiWebServer.h:316
WPassword::WPassword
WPassword(String title, String id, String(*getValue)(), void(*setValue)(String))
Definition: WifiWebServer.h:576
WTextField::WTextField
WTextField(String title, String id, String(*getValue)(), void(*setValue)(String), bool(*enabled)()=nullptr)
Definition: WifiWebServer.h:463
WPage::fFS
fs::FS * fFS
Definition: WifiWebServer.h:1077
WCheckbox
Definition: WifiWebServer.h:347
WPage::fTitleOrPath
String fTitleOrPath
Definition: WifiWebServer.h:1075
WInteger::fGetValue
int(* fGetValue)()
Definition: WifiWebServer.h:87
WBoolean::fGetValue
bool(* fGetValue)()
Definition: WifiWebServer.h:60
WPage::fAPIProc
void(* fAPIProc)(Print &out, String queryString)
Definition: WifiWebServer.h:1083
DEBUG_PRINTLN
#define DEBUG_PRINTLN(s)
Definition: ReelTwo.h:188
WifiWebServer::wifiConnected
virtual void wifiConnected(WifiAccess &access) override
Definition: WifiWebServer.h:1206
WElement::fEnabled
bool(* fEnabled)()
Definition: WifiWebServer.h:260
WFirmwareUpload::WFirmwareUpload
WFirmwareUpload(String title, String id)
Definition: WifiWebServer.h:627
UPLOAD_FILE_START
@ UPLOAD_FILE_START
Definition: WifiWebServer.h:848
WInteger
Definition: WifiWebServer.h:64
WString::set
virtual void set(String val) override
Definition: WifiWebServer.h:112
WPage::handleGetRequest
void handleGetRequest(Print &out, String &header) const
Definition: WifiWebServer.h:910
WTableTextField::WTableTextField
WTableTextField(String id, String(*getValue)(), void(*setValue)(String), bool(*enabled)()=nullptr)
Definition: WifiWebServer.h:830
WLabel
Definition: WifiWebServer.h:447
WTableRow::WTableRow
WTableRow()
Definition: WifiWebServer.h:773
RamBufferedPrintStream::flush
void flush()
Definition: PSRamBufferedPrintStream.h:17
WSlider
Definition: WifiWebServer.h:325
WPage::fUploaderProc
void(* fUploaderProc)(WUploader &uploader)
Definition: WifiWebServer.h:1082
WVerticalMenu
Definition: WifiWebServer.h:682
UPLOAD_FILE_END
@ UPLOAD_FILE_END
Definition: WifiWebServer.h:850
WHorizontalAlign
Definition: WifiWebServer.h:307
WTextFieldInteger
Definition: WifiWebServer.h:478
WCheckboxReload
Definition: WifiWebServer.h:368
WPage
Definition: WifiWebServer.h:868
W1::W1
W1(String title)
Definition: WifiWebServer.h:706
WValue::getQuoteValue
virtual bool getQuoteValue()
Definition: WifiWebServer.h:16
WUpload::WUpload
WUpload(String url, void(*completeProc)(Client &client), void(*uploaderProc)(WUploader &uploader))
Definition: WifiWebServer.h:1139
WHRef::WHRef
WHRef(String link, String text)
Definition: WifiWebServer.h:724
WDynamicElementInt::WDynamicElementInt
WDynamicElementInt(String id, const WDynamic &dynamicRef, int(*getValue)(), void(*setValue)(int))
Definition: WifiWebServer.h:289
WVerticalAlign::WVerticalAlign
WVerticalAlign()
Definition: WifiWebServer.h:301
WTableLabel::WTableLabel
WTableLabel(String text, String id, bool(*enabled)()=nullptr)
Definition: WifiWebServer.h:819
WPage::fURL
String fURL
Definition: WifiWebServer.h:1074
WElement::needsReload
bool needsReload() const
Definition: WifiWebServer.h:146
WString::get
virtual String get() override
Definition: WifiWebServer.h:105
WMenuData::title
const char * title
Definition: WifiWebServer.h:678
WifiWebServer::fClients
WiFiClient fClients[maxClients]
Definition: WifiWebServer.h:1174
WSVG::WSVG
WSVG(String data)
Definition: WifiWebServer.h:744
WElement::fDynamic
const WDynamic * fDynamic
Definition: WifiWebServer.h:259
WElement::appendBodyf
void appendBodyf(const char *fmt,...)
Definition: WifiWebServer.h:165
WTableCol::WTableCol
WTableCol(String styleClass)
Definition: WifiWebServer.h:788
WSlider::WSlider
WSlider(String title, String id, int min, int max, int(*getValue)(), void(*setValue)(int))
Definition: WifiWebServer.h:328
WElement::WElement
WElement(WAction *action)
Definition: WifiWebServer.h:141
HTTP_UPLOAD_BUFLEN
#define HTTP_UPLOAD_BUFLEN
Definition: WifiWebServer.h:843
WUploader::buf
uint8_t buf[HTTP_UPLOAD_BUFLEN+1]
Definition: WifiWebServer.h:865
WElement::verticalAlignment
bool & verticalAlignment()
Definition: WifiWebServer.h:262
WString::WString
WString(String(*getValue)(), void(*setValue)(String))
Definition: WifiWebServer.h:94
WUploadStatus
WUploadStatus
Definition: WifiWebServer.h:846
WHTML
Definition: WifiWebServer.h:752
PSRamBufferedPrintStream.h
WTableCol::WTableCol
WTableCol()
Definition: WifiWebServer.h:782
WDynamicElementInt
Definition: WifiWebServer.h:286
MallocString.h
WTableRowEnd::WTableRowEnd
WTableRowEnd()
Definition: WifiWebServer.h:810
RamBufferedPrintStream
Definition: PSRamBufferedPrintStream.h:6
WifiWebServer::handle
void handle()
Dispatch any received i2c event to CommandEvent.
Definition: WifiWebServer.h:1231
WInteger::set
virtual void set(String val) override
Definition: WifiWebServer.h:80
WString::fSetValue
void(* fSetValue)(String)
Definition: WifiWebServer.h:120
WHorizontalAlign::WHorizontalAlign
WHorizontalAlign()
Definition: WifiWebServer.h:310
WVerticalMenu::WVerticalMenu
WVerticalMenu(String id, const WMenuData *menuData, unsigned menuCount, unsigned active=0)
Definition: WifiWebServer.h:685
WPage::WPage
WPage(String url, fs::FS *fs, String mimeType)
Definition: WifiWebServer.h:890
WDynamic::emitCSS
virtual void emitCSS(Print &out) const
Definition: WifiWebServer.h:126
WValue::set
virtual void set(String val)=0
WTableRow
Definition: WifiWebServer.h:770
WPage::fCompleteProc
void(* fCompleteProc)(Client &client)
Definition: WifiWebServer.h:1081
FormatString.h
WUploader::receivedSize
size_t receivedSize
Definition: WifiWebServer.h:863
WPage::getURL
const String & getURL() const
Definition: WifiWebServer.h:900
WImage::WImage
WImage(String alt, String data)
Definition: WifiWebServer.h:733
WAction
Definition: WifiWebServer.h:21
WAction::WAction
WAction(void(*action)())
Definition: WifiWebServer.h:24
WUploader
Definition: WifiWebServer.h:854
WAPI
Definition: WifiWebServer.h:1126
WTableCol
Definition: WifiWebServer.h:779
WFirmwareFile
Definition: WifiWebServer.h:608
WUploader::filename
String filename
Definition: WifiWebServer.h:858
WElement::fBody
String fBody
Definition: WifiWebServer.h:254
WDynamicElement::WDynamicElement
WDynamicElement(const WDynamic &dynamicRef)
Definition: WifiWebServer.h:272
WifiAccess::addNotify
void addNotify(WifiAccess::Notify *client)
Definition: WifiAccess.h:173
WHTML::WHTML
WHTML(String data)
Definition: WifiWebServer.h:755
WBoolean::WBoolean
WBoolean(bool(*getValue)(), void(*setValue)(bool))
Definition: WifiWebServer.h:42
WElement::appendScript
void appendScript(String str)
Definition: WifiWebServer.h:150
WJavaScript::WJavaScript
WJavaScript(String data)
Definition: WifiWebServer.h:764
WPage::fFlags
uint8_t fFlags
Definition: WifiWebServer.h:1078
WElement::fCSS
String fCSS
Definition: WifiWebServer.h:253
WPage::fContents
const WElement * fContents
Definition: WifiWebServer.h:1080
WElement
Definition: WifiWebServer.h:133
WifiAccess.h
WifiWebServer::wifiDisconnected
virtual void wifiDisconnected(WifiAccess &access) override
Definition: WifiWebServer.h:1216
WUploader::status
WUploadStatus status
Definition: WifiWebServer.h:857
WStyle::WStyle
WStyle(String style)
Definition: WifiWebServer.h:319
WImage
Definition: WifiWebServer.h:730
WPassword
Definition: WifiWebServer.h:573
WElement::emitValue
void emitValue(Print &out) const
Definition: WifiWebServer.h:211
WFileInput::WFileInput
WFileInput(String title, String id, String(*getValue)(), void(*setValue)(String))
Definition: WifiWebServer.h:594
WTableColEnd
Definition: WifiWebServer.h:797
WElement::fID
String fID
Definition: WifiWebServer.h:252
WUploader::queryString
String queryString
Definition: WifiWebServer.h:861
FormatString
int FormatString(char **acBuf, const char *szFmt, va_list tArg)
Definition: FormatString.h:817
MallocString
Definition: MallocString.h:8
WButton::WButton
WButton(String title, String id, String href)
Definition: WifiWebServer.h:404
WElement::appendBody
void appendBody(String str)
Definition: WifiWebServer.h:149
WPage::WPage
WPage(String url, fs::FS *fs, String path, const WElement contents[], unsigned numElements)
Definition: WifiWebServer.h:880
WAction::fAction
void(* fAction)()
Definition: WifiWebServer.h:36
WBoolean::get
virtual String get() override
Definition: WifiWebServer.h:48
WPage::callUploader
void callUploader(WUploader &uploader) const
Definition: WifiWebServer.h:1054
WElement::fAction
WAction * fAction
Definition: WifiWebServer.h:257
WHR::WHR
WHR()
Definition: WifiWebServer.h:715
WDynamic::emitScript
virtual void emitScript(Print &out) const
Definition: WifiWebServer.h:130
WUploader::name
String name
Definition: WifiWebServer.h:859
WTableLabel
Definition: WifiWebServer.h:816
WHRef
Definition: WifiWebServer.h:721
WElement::getValue
String getValue() const
Definition: WifiWebServer.h:234
WPage::fNumElements
unsigned fNumElements
Definition: WifiWebServer.h:1079
WDynamicElement::WDynamicElement
WDynamicElement(String id, const WDynamic &dynamicRef, WValue *value=nullptr)
Definition: WifiWebServer.h:278
WHR
Definition: WifiWebServer.h:712
WElement::WElement
WElement(WValue *value=nullptr)
Definition: WifiWebServer.h:136
WCheckboxReload::WCheckboxReload
WCheckboxReload(String title, String id, bool(*getValue)(), void(*setValue)(bool), bool(*enabled)()=nullptr)
Definition: WifiWebServer.h:371
WButton::WButton
WButton(String title, String id, String href, void(*pressed)())
Definition: WifiWebServer.h:413
WDynamic
Definition: WifiWebServer.h:123
WButton::WButton
WButton(String title, String id, void(*pressed)())
Definition: WifiWebServer.h:393
WTextField
Definition: WifiWebServer.h:460
WSelect
Definition: WifiWebServer.h:531
WTableRowEnd
Definition: WifiWebServer.h:807
WMenuData::href
const char * href
Definition: WifiWebServer.h:679
WElement::emitScript
void emitScript(Print &out) const
Definition: WifiWebServer.h:224
WAPI::WAPI
WAPI(String url, void(*apiProc)(Print &out, String queryString))
Definition: WifiWebServer.h:1129
WPage::openFileOrCompressed
fs::File openFileOrCompressed(String fileName, bool &compressed) const
Definition: WifiWebServer.h:1061
WPage::fLanguageOrMimeType
String fLanguageOrMimeType
Definition: WifiWebServer.h:1076
WFileInput
Definition: WifiWebServer.h:591
WTableTextField
Definition: WifiWebServer.h:827
WUploader::type
String type
Definition: WifiWebServer.h:860
WifiWebServer::setConnect
void setConnect(void(*callback)())
Definition: WifiWebServer.h:1191
WSelect::WSelect
WSelect(String title, String id, String options[], String values[], unsigned numOptions, int(*getValue)(), void(*setValue)(int), bool(*enabled)()=nullptr)
Definition: WifiWebServer.h:553
WInteger::get
virtual String get() override
Definition: WifiWebServer.h:73
WifiWebServer
Simple WiFi web server.
Definition: WifiWebServer.h:1171
WElement::fScript
String fScript
Definition: WifiWebServer.h:255
WJavaScript
Definition: WifiWebServer.h:761
WBoolean
Definition: WifiWebServer.h:39
WButton::WButton
WButton(String title, String id, bool reload, void(*pressed)())
Definition: WifiWebServer.h:425