Skip to content

Commit 89237da

Browse files
committed
Add convenience macro around printContentF
Makes it easier to read, provides some basic error protection, and will make it easier to auto-wrap inside F(), where appropriate.
1 parent 3a49c88 commit 89237da

6 files changed

+184
-72
lines changed

EmbAJAX.cpp

+41-44
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
#include "EmbAJAX.h"
2626

27-
#include <cstdarg> // For va_args in printContentF.
27+
#include <cstdarg> // For va_args in _printContentF.
2828

2929
#define ITOA_BUFLEN 8
3030

@@ -77,7 +77,7 @@ void EmbAJAXOutputDriverBase::_printContent(const char* value) {
7777
}
7878
}
7979

80-
void EmbAJAXOutputDriverBase::printContentF(const char* fmt, ...) {
80+
void EmbAJAXOutputDriverBase::_printContentF(const char* fmt, ...) {
8181
va_list args;
8282
va_start(args, fmt);
8383
const char *pos = fmt;
@@ -104,24 +104,23 @@ void EmbAJAXOutputDriverBase::printContentF(const char* fmt, ...) {
104104
}
105105

106106
void EmbAJAXOutputDriverBase::printAttribute(const char* name, const char* value) {
107-
printContentF(" " PLAIN_STRING_ARG "=" HTML_QUOTED_STRING_ARG, name, value);
107+
_printContentF(" " PLAIN_STRING_ARG "=" HTML_QUOTED_STRING_ARG, name, value);
108108
}
109109

110110
void EmbAJAXOutputDriverBase::printAttribute(const char* name, const int32_t value) {
111-
printContentF(" " PLAIN_STRING_ARG "=" INTEGER_VALUE_ARG, name, value);
111+
_printContentF(" " PLAIN_STRING_ARG "=" INTEGER_VALUE_ARG, name, value);
112112
}
113113

114114
//////////////////////// EmbAJAXConnectionIndicator ///////////////////////
115115

116116
void EmbAJAXConnectionIndicator::print() const {
117-
_driver->printContentF("<div class=\"EmbAJAXStatus\"><span>" PLAIN_STRING_ARG "</span><span>" PLAIN_STRING_ARG "</span><script>\n"
117+
_driver->printFormatted("<div class=\"EmbAJAXStatus\"><span>", PLAIN_STRING(_content_ok), "</span><span>", PLAIN_STRING(_content_fail), "</span><script>\n"
118118
"window.ardujaxsh = { 'div': document.scripts[document.scripts.length-1].parentNode,\n"
119119
"'good': 0,\n"
120120
"'tid': null,\n"
121121
"'toggle': function(on) { this.div.children[on].style.display = 'none'; this.div.children[1-on].style.display = 'inline'; this.good = on; },\n"
122122
"'in': function() { clearTimeout(this.tid); this.tid = window.setTimeout(this.toggle.bind(this, 0), 5000); if(!this.good) {this.toggle(1);} }\n"
123-
"};\nwindow.ardujaxsh.in();\n</script></div>",
124-
_content_ok, _content_fail);
123+
"};\nwindow.ardujaxsh.in();\n</script></div>");
125124
}
126125

127126
////////////////////////////// EmbAJAXElement /////////////////////////////
@@ -136,15 +135,15 @@ EmbAJAXElement::EmbAJAXElement(const char* id) : EmbAJAXBase() {
136135
bool EmbAJAXElement::sendUpdates(uint16_t since, bool first) {
137136
if (!changed(since)) return false;
138137
if (!first) _driver->printContent(",\n");
139-
_driver->printContentF("{\n\"id\": " JS_QUOTED_STRING_ARG ",\n\"changes\": [", _id);
138+
_driver->printFormatted("{\n\"id\": ", JS_QUOTED_STRING(_id), ",\n\"changes\": [");
140139
uint8_t i = 0;
141140
while (true) {
142141
const char* pid = valueProperty(i);
143142
const char* pval = value(i);
144143
if (!pid || !pval) break;
145144

146145
if (i != 0) _driver->printContent(",");
147-
_driver->printContentF("[" JS_QUOTED_STRING_ARG ", ", pid);
146+
_driver->printFormatted("[", JS_QUOTED_STRING(pid), ", ");
148147
_driver->printFiltered(pval, EmbAJAXOutputDriverBase::JSQuoted, valueNeedsEscaping(i));
149148
_driver->printContent("]");
150149

@@ -172,10 +171,9 @@ bool EmbAJAXElement::changed(uint16_t since) {
172171
}
173172

174173
void EmbAJAXElement::printTextInput(size_t SIZE, const char* _value) const {
175-
_driver->printContentF("<input type=\"text\" id=" HTML_QUOTED_STRING_ARG " maxLength=" INTEGER_VALUE_ARG " size=" INTEGER_VALUE_ARG
176-
" value=" HTML_QUOTED_STRING_ARG " onInput=\"doRequest(this.id, this.value);\"/>",
177-
_id, SIZE-1, min(max((size_t) SIZE, (size_t) 11), (size_t) 41) - 1, // Arbitray limit for rendered width of text fields: 10..40 chars
178-
_value);
174+
_driver->printFormatted("<input type=\"text\" id=", HTML_QUOTED_STRING(_id), " maxLength=", INTEGER_VALUE(SIZE-1),
175+
" size=", INTEGER_VALUE(min(max((size_t) SIZE, (size_t) 11), (size_t) 41) - 1), // Arbitray limit for rendered width of text fields: 10..40 chars
176+
" value=", HTML_QUOTED_STRING(_value), " onInput=\"doRequest(this.id, this.value);\"/>");
179177
}
180178

181179
//////////////////////// EmbAJAXContainer ////////////////////////////////////
@@ -209,7 +207,7 @@ EmbAJAXElement* EmbAJAXBase::findChild(EmbAJAXBase** _children, size_t NUM, cons
209207
//////////////////////// EmbAJAXMutableSpan /////////////////////////////
210208

211209
void EmbAJAXMutableSpan::print() const {
212-
_driver->printContentF("<span id=" HTML_QUOTED_STRING_ARG ">", _id);
210+
_driver->printFormatted("<span id=", HTML_QUOTED_STRING(_id), ">");
213211
if (_value) _driver->printFiltered(_value, EmbAJAXOutputDriverBase::NotQuoted, valueNeedsEscaping());
214212
_driver->printContent("</span>\n");
215213
}
@@ -246,8 +244,8 @@ EmbAJAXSlider::EmbAJAXSlider(const char* id, int16_t min, int16_t max, int16_t i
246244
}
247245

248246
void EmbAJAXSlider::print() const {
249-
_driver->printContentF("<input type=\"range\" id=" HTML_QUOTED_STRING_ARG " min=" INTEGER_VALUE_ARG " max=" INTEGER_VALUE_ARG " value=" INTEGER_VALUE_ARG
250-
" oninput=\"doRequest(this.id, this.value);\" onchange=\"oninput();\"/>", _id, _min, _max, _value);
247+
_driver->printFormatted("<input type=\"range\" id=", HTML_QUOTED_STRING(_id), " min=", INTEGER_VALUE(_min), " max=", INTEGER_VALUE(_max), " value=", INTEGER_VALUE(_value),
248+
" oninput=\"doRequest(this.id, this.value);\" onchange=\"oninput();\"/>");
251249
}
252250

253251
const char* EmbAJAXSlider::value(uint8_t which) const {
@@ -280,8 +278,8 @@ EmbAJAXColorPicker::EmbAJAXColorPicker(const char* id, uint8_t r, uint8_t g, uin
280278
}
281279

282280
void EmbAJAXColorPicker::print() const {
283-
_driver->printContentF("<input type=\"color\" id=" HTML_QUOTED_STRING_ARG " value=" HTML_QUOTED_STRING_ARG
284-
" oninput=\"doRequest(this.id, this.value);\" onchange=\"oninput();\"/>", _id, value());
281+
_driver->printFormatted("<input type=\"color\" id=", HTML_QUOTED_STRING(_id), " value=", HTML_QUOTED_STRING(value()),
282+
" oninput=\"doRequest(this.id, this.value);\" onchange=\"oninput();\"/>");
285283
}
286284

287285
// helper to make sure we get exactly two hex digits for any input
@@ -353,8 +351,8 @@ EmbAJAXPushButton::EmbAJAXPushButton(const char* id, const char* label, void (*c
353351
}
354352

355353
void EmbAJAXPushButton::print() const {
356-
_driver->printContentF("<button type=\"button\" id=" HTML_QUOTED_STRING_ARG
357-
" onClick=\"doRequest(this.id, 'p', 2);\">", _id); // 2 -> not mergeable -> so we can count individual presses, even if they happen fast
354+
_driver->printFormatted("<button type=\"button\" id=", HTML_QUOTED_STRING(_id),
355+
" onClick=\"doRequest(this.id, 'p', 2);\">"); // 2 -> not mergeable -> so we can count individual presses, even if they happen fast
358356
_driver->printFiltered(_label, EmbAJAXOutputDriverBase::NotQuoted, valueNeedsEscaping());
359357
_driver->printContent("</button>");
360358
}
@@ -392,14 +390,14 @@ EmbAJAXMomentaryButton::EmbAJAXMomentaryButton(const char* id, const char* label
392390
}
393391

394392
void EmbAJAXMomentaryButton::print() const {
395-
_driver->printContentF("<button type=\"button\" id=" HTML_QUOTED_STRING_ARG ">", _id);
393+
_driver->printFormatted("<button type=\"button\" id=", HTML_QUOTED_STRING(_id), ">");
396394
_driver->printFiltered(_label, EmbAJAXOutputDriverBase::NotQuoted, valueNeedsEscaping());
397-
_driver->printContentF("</button>"
395+
_driver->printFormatted("</button>"
398396
"<script>\n"
399-
"{let btn=document.getElementById(" JS_QUOTED_STRING_ARG ");\n"
400-
"btn.onmousedown = btn.ontouchstart = function() { clearInterval(this.pinger); this.pinger=setInterval(function() {doRequest(this.id, 'p');}.bind(this)," INTEGER_VALUE_ARG "); doRequest(this.id, 'p'); return false; };\n"
397+
"{let btn=document.getElementById(", JS_QUOTED_STRING(_id), ");\n"
398+
"btn.onmousedown = btn.ontouchstart = function() { clearInterval(this.pinger); this.pinger=setInterval(function() {doRequest(this.id, 'p');}.bind(this),", INTEGER_VALUE((int) (_timeout / 1.5)), "); doRequest(this.id, 'p'); return false; };\n"
401399
"btn.onmouseup = btn.ontouchend = btn.onmouseleave = function() { clearInterval(this.pinger); doRequest(this.id, 'r'); return false;};}\n"
402-
"</script>", _id, (int) (_timeout / 1.5));
400+
"</script>");
403401
}
404402

405403
EmbAJAXMomentaryButton::Status EmbAJAXMomentaryButton::status() const {
@@ -429,16 +427,15 @@ EmbAJAXCheckButton::EmbAJAXCheckButton(const char* id, const char* label, bool c
429427
}
430428

431429
void EmbAJAXCheckButton::print() const {
432-
_driver->printContentF("<span class=" HTML_QUOTED_STRING_ARG ">" // <input> and <label> inside a common span to support hiding, better.
433-
// Also, assign a class to the surrounding span to ease styling via CSS.
434-
"<input id=" HTML_QUOTED_STRING_ARG " type=" HTML_QUOTED_STRING_ARG
435-
" value=\"t\" onChange=\"doRequest(this.id, this.checked ? 't' : 'f');\"",
436-
radiogroup ? "radio" : "checkbox", _id, radiogroup ? "radio" : "checkbox");
430+
_driver->printFormatted("<span class=", HTML_QUOTED_STRING(radiogroup ? "radio" : "checkbox"), ">" // <input> and <label> inside a common span to support hiding, better.
431+
// Also, assign a class to the surrounding span to ease styling via CSS.
432+
"<input id=", HTML_QUOTED_STRING(_id), " type=", HTML_QUOTED_STRING(radiogroup ? "radio" : "checkbox"),
433+
" value=\"t\" onChange=\"doRequest(this.id, this.checked ? 't' : 'f');\"");
437434
if (radiogroup) _driver->printAttribute("name", radiogroup->_name);
438435
if (_checked) _driver->printContent(" checked=\"true\"");
439436
// Note: Internal <span> element for more flexbility in styling the control
440-
_driver->printContentF("/><label for=" HTML_QUOTED_STRING_ARG ">" PLAIN_STRING_ARG // NOTE: Not escaping _label, so user can insert HTML.
441-
"</label></span>", _id, _label);
437+
_driver->printFormatted("/><label for=", HTML_QUOTED_STRING(_id), ">", PLAIN_STRING(_label), // NOTE: Not escaping _label, so user can insert HTML.
438+
"</label></span>");
442439
}
443440

444441
const char* EmbAJAXCheckButton::value(uint8_t which) const {
@@ -469,9 +466,9 @@ void EmbAJAXCheckButton::setChecked(bool checked) {
469466
//////////////////////// EmbAJAXOptionSelect(Base) ///////////////
470467

471468
void EmbAJAXOptionSelectBase::print(const char* const* _labels, uint8_t NUM) const {
472-
_driver->printContentF("<select id=" HTML_QUOTED_STRING_ARG " onChange=\"doRequest(this.id, this.value)\">\n", _id);
469+
_driver->printFormatted("<select id=", HTML_QUOTED_STRING(_id), " onChange=\"doRequest(this.id, this.value)\">\n");
473470
for(uint8_t i = 0; i < NUM; ++i) {
474-
_driver->printContentF("<option value=" INTEGER_VALUE_ARG ">" HTML_QUOTED_STRING_ARG "</option>\n", i, _labels[i]);
471+
_driver->printFormatted("<option value=", INTEGER_VALUE(i), ">", HTML_QUOTED_STRING(_labels[i]), "</option>\n");
475472
}
476473
_driver->printContent("</select>");
477474
}
@@ -506,7 +503,7 @@ void EmbAJAXBase::printPage(EmbAJAXBase** _children, size_t NUM, const char* _ti
506503
time_t start = millis();
507504
#endif
508505
_driver->printHeader(true);
509-
_driver->printContentF("<!DOCTYPE html>\n<HTML><HEAD><TITLE>" PLAIN_STRING_ARG "</TITLE>\n<SCRIPT>\n"
506+
_driver->printFormatted("<!DOCTYPE html>\n<HTML><HEAD><TITLE>", PLAIN_STRING(_title), "</TITLE>\n<SCRIPT>\n"
510507

511508
"var serverrevision = 0;\n"
512509
"var request_queue = [];\n" // requests waiting to be sent
@@ -525,7 +522,7 @@ void EmbAJAXBase::printPage(EmbAJAXBase** _children, size_t NUM, const char* _ti
525522
"var prev_request = 0;\n"
526523
"function sendQueued() {\n"
527524
" var now = new Date().getTime();\n"
528-
" if (num_waiting > 0 || (now - prev_request < " INTEGER_VALUE_ARG ")) return;\n"
525+
" if (num_waiting > 0 || (now - prev_request < ", INTEGER_VALUE(_min_interval), ")) return;\n"
529526
" var e = request_queue.shift();\n"
530527
" if (!e && (now - prev_request < 1000)) return;\n"
531528
" if (!e) e = {id: '', value: ''};\n" //Nothing in queue, but last request more than 1000 ms ago? Send a ping to query for updates
@@ -545,7 +542,7 @@ void EmbAJAXBase::printPage(EmbAJAXBase** _children, size_t NUM, const char* _ti
545542
" req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');\n"
546543
" req.send('id=' + e.id + '&value=' + encodeURIComponent(e.value) + '&revision=' + serverrevision);\n"
547544
"}\n"
548-
"window.setInterval(sendQueued, " INTEGER_VALUE_ARG ");\n"
545+
"window.setInterval(sendQueued, ", INTEGER_VALUE(_min_interval/2+1), ");\n"
549546

550547
"function doUpdates(response) {\n"
551548
" serverrevision = response.revision;\n"
@@ -567,18 +564,18 @@ void EmbAJAXBase::printPage(EmbAJAXBase** _children, size_t NUM, const char* _ti
567564
" }\n"
568565
"}\n"
569566

570-
"</SCRIPT>\n" PLAIN_STRING_ARG
571-
"</HEAD>\n<BODY><FORM autocomplete=\"off\" onSubmit=\"return false;\">\n" // NOTE: The nasty thing about autocomplete is that it does not trigger
572-
// onChange() functions, but also the "restore latest settings after client
573-
// reload" is questionable in our use-case.
574-
, _title, _min_interval, _min_interval/2+1, _header_add);
567+
"</SCRIPT>\n", PLAIN_STRING(_header_add),
568+
"</HEAD>\n<BODY><FORM autocomplete=\"off\" onSubmit=\"return false;\">\n");
569+
// NOTE: The nasty thing about autocomplete is that it does not trigger onChange() functions, but also the
570+
// "restore latest settings after client reload" is questionable in our use-case.
575571

576572
printChildren(_children, NUM);
577573

578574
_driver->printContent("\n</FORM></BODY></HTML>\n");
579575
#if EMBAJAX_DEBUG > 2
576+
auto diff = millis() - start;
580577
Serial.print("Page rendered in ");
581-
Serial.print(millis() - start);
578+
Serial.print(diff);
582579
Serial.println("ms");
583580
#endif
584581
}
@@ -628,7 +625,7 @@ void EmbAJAXBase::handleRequest(EmbAJAXBase** _children, size_t NUM, void (*chan
628625

629626
// then relay value changes that have occured in the server (possibly in response to those sent)
630627
_driver->printHeader(false);
631-
_driver->printContentF("{\"revision\": " INTEGER_VALUE_ARG ",\n\"updates\": [\n", _driver->revision());
628+
_driver->printFormatted("{\"revision\": ", INTEGER_VALUE(_driver->revision()), ",\n\"updates\": [\n");
632629
sendUpdates(_children, NUM, client_revision, true);
633630
_driver->printContent("\n]}\n");
634631

0 commit comments

Comments
 (0)