51#define HTMLTAG_OPEN '<'
52#define HTMLTAG_TERMINATE '/'
53#define HTMLTAG_CLOSE '>'
54#define HTMLTAG_COMMENT '!'
180 auto cur = std::move(
nextel);
182 cur = std::move(cur->nextel);
204 if (!_Data.empty() && _Data.back() ==
'\0') {
208 for(
auto i =
str.begin(); i!=
str.end(); ++i){
214 if (_Data.empty() || _Data.back() !=
'\0') {
215 _Data.push_back(
'\0');
223 std::copy(
str._Data.begin(),
str._Data.end(),std::insert_iterator<std::vector<char>>(_Data,_Data.begin()));
230 if (!_Data.empty() && _Data.back() ==
'\0') {
233 _Data.push_back(src);
235 _Data.push_back(
'\0');
242 if (!_Data.empty() && _Data.back() ==
'\0') {
246 for(
auto i = src.begin(); i!=src.end(); ++i){
252 if (_Data.empty() || _Data.back() !=
'\0') {
253 _Data.push_back(
'\0');
258 std::copy(hstring._Data.begin(),hstring._Data.end(),std::back_inserter(_Data));
271 return _Data.empty();
293 std::copy(src._Data.begin(),src._Data.end(),std::insert_iterator<std::vector<char>>(_Data,_Data.begin()));
298 return _Data.at(pos);
313 std::copy(src._Data.begin(),src._Data.end(),std::back_inserter(_Data));
319 snprintf(buf, 255,
"%d", src);
326 snprintf(buf, 255,
"%zu", src);
359 if (!_Data.empty() && _Data.back() ==
'\0')
360 return _Data.size() - 1;
366 return std::string(_Data.begin(), _Data.begin() +
static_cast<long>(size()));
385void libhtmlpp::HtmlString::_buildtreenode(
388 std::unique_ptr<Element> &html)
399 const DocElements *outer_end;
400 Element *outer_prev_el;
402 std::stack<Frame> stack;
404 DocElements *start = firstel;
405 const DocElements *end = lastel;
407 Element *prev_el_in_tree =
nullptr;
409 auto checkContainer = [&](
const std::string &tag) {
417 auto skip_empty = [](DocElements *cur,
const DocElements *stop) -> DocElements* {
418 while (cur && cur != stop && (!cur->element)) {
425 auto find_terminator = [&skip_empty, checkContainer](DocElements *open,
const DocElements *bound) -> DocElements* {
426 if (!open || !open->element || open->terminator ||
427 open->element->getType() !=
HtmlEl)
return nullptr;
429 const std::string &tag =
static_cast<HtmlElement*
>(open->element.get())->getTagname();
431 DocElements *cur = open->nextel.get();
433 while (cur && cur != bound) {
434 cur = skip_empty(cur, bound);
435 if (!cur || cur == bound)
break;
437 if (cur->element && cur->element->getType() ==
HtmlEl) {
438 const std::string &curtag =
static_cast<HtmlElement*
>(cur->element.get())->getTagname();
440 if (cur->terminator) {
441 if (nest == 0)
return cur;
448 cur = cur->nextel.get();
452 if (checkContainer(tag)) {
461 start = skip_empty(start, end);
464 if (start && start != end && start->terminator) {
465 start = start->nextel.get();
470 if (!start || start == end) {
474 html = std::move(firstel->
element);
480 Frame fr = stack.top(); stack.pop();
481 HtmlElement *opener_el =
static_cast<HtmlElement*
>(fr.open->element.get());
484 Element* last_child_in_chain =
nullptr;
486 DocElements* cur = fr.open->nextel.get();
488 while (cur && cur != fr.close && (!cur->element || cur->terminator)) {
489 cur = cur->nextel.get();
493 while (cur && cur != fr.close) {
494 if (cur->element && !cur->terminator) {
495 if (!opener_el->_childElement) {
496 opener_el->_childElement = std::move(cur->element);
497 last_child_in_chain = opener_el->_childElement.get();
499 last_child_in_chain->_nextElement = std::move(cur->element);
501 last_child_in_chain->_nextElement->_prevElement = last_child_in_chain;
502 last_child_in_chain = last_child_in_chain->_nextElement.get();
505 cur = cur->nextel.get();
507 while (cur && cur != fr.close && (!cur->element || cur->terminator)) {
508 cur = cur->nextel.get();
514 prev_el_in_tree = opener_el;
517 if (fr.outer_prev_el) {
518 prev_el_in_tree->_prevElement = fr.outer_prev_el;
519 fr.outer_prev_el->_nextElement = std::move(fr.open->element);
520 prev_el_in_tree = fr.outer_prev_el->_nextElement.get();
524 prev_el_in_tree = opener_el;
525 start = (fr.close ? fr.close->nextel.get() :
nullptr);
532 if (start->element && !start->terminator && start->element->getType() ==
HtmlEl) {
533 if (DocElements *close = find_terminator(start, end)) {
535 stack.push(Frame{start, close, end, prev_el_in_tree});
538 prev_el_in_tree =
nullptr;
539 start = start->nextel.get();
546 if (start->element && !start->terminator) {
547 Element *current_el = start->element.get();
549 if (prev_el_in_tree) {
550 current_el->_prevElement = prev_el_in_tree;
551 prev_el_in_tree->_nextElement = std::move(start->element);
552 prev_el_in_tree = prev_el_in_tree->_nextElement.get();
554 prev_el_in_tree = current_el;
559 start = start->nextel.get();
568void libhtmlpp::HtmlString::_buildTree() {
569 DocElements* lastEl =
nullptr;
570 std::unique_ptr<DocElements> firstEl =
nullptr;
572 auto is_ws = [](
unsigned char ch) ->
bool {
574 return ch ==
' ' || ch ==
'\t' || ch ==
'\n' || ch ==
'\r';
577 auto ascii_tolower = [](
unsigned char c) ->
unsigned char {
578 return (c >=
'A' && c <=
'Z') ?
static_cast<unsigned char>(c + 32) : c;
581 auto starts_with_ci = [&](
const char* s,
const char* e,
const char* k) ->
bool {
582 const size_t klen = std::char_traits<char>::length(k);
583 if (
static_cast<size_t>(e - s) < klen)
return false;
584 for (
size_t i = 0; i < klen; ++i) {
585 unsigned char a =
static_cast<unsigned char>(s[i]);
586 unsigned char b =
static_cast<unsigned char>(k[i]);
587 if (ascii_tolower(a) != ascii_tolower(b))
return false;
592 auto add_element_node = [&](DocElements** last) {
594 firstEl = std::make_unique<DocElements>();
595 *last = firstEl.get();
597 (*last)->nextel = std::make_unique<DocElements>();
598 (*last)->nextel->prevel = (*last);
599 *last = (*last)->nextel.get();
603 const char*
const base = _Data.data();
604 const size_t n = _Data.size();
605 const char*
const end = base + n;
607 const char* p = base;
610 const char* wsStart = p;
611 while (p < end && is_ws(
static_cast<unsigned char>(*p))) ++p;
622 if (p > wsStart && firstEl) {
623 add_element_node(&lastEl);
624 auto ws = std::make_unique<TextElement>();
625 static_cast<TextElement*
>(ws.get())->_Text.push_back(
' ');
626 lastEl->element = std::move(ws);
627 lastEl->terminator =
false;
630 const char*
const remain_end = end;
631 const size_t remain =
static_cast<size_t>(remain_end - p);
634 const unsigned char c1 = ascii_tolower(
static_cast<unsigned char>(p[1]));
638 if (starts_with_ci(p, end,
"<!--")) {
639 add_element_node(&lastEl);
640 size_t i =
static_cast<size_t>(p - base);
645 if (starts_with_ci(p, end,
"<!doctype")) {
646 const char* close_tag = p;
647 while (close_tag < end && *close_tag !=
'>') {
650 if (close_tag < end) {
657 }
else if (c1 ==
's') {
658 if (starts_with_ci(p, end,
"<script")) {
659 add_element_node(&lastEl);
660 size_t i =
static_cast<size_t>(p - base);
665 if (starts_with_ci(p, end,
"<svg")) {
666 add_element_node(&lastEl);
667 size_t i =
static_cast<size_t>(p - base);
672 if (starts_with_ci(p, end,
"<style")) {
681 add_element_node(&lastEl);
682 size_t i =
static_cast<size_t>(p - base);
687 }
else if (c1 ==
't') {
688 if (starts_with_ci(p, end,
"<textarea")) {
689 add_element_node(&lastEl);
690 size_t i =
static_cast<size_t>(p - base);
699 add_element_node(&lastEl);
700 size_t i =
static_cast<size_t>(p - base);
711 add_element_node(&lastEl);
712 size_t i =
static_cast<size_t>(wsStart - base);
718 _buildtreenode(firstEl.get(),
nullptr, _rootEl);
724 if(_rootEl) HtmlElement::_assignParentPointers(_rootEl.get(),
nullptr);
740 size_t ilen=input.length();
741 for(
size_t i=0; i<ilen; ++i){
752 output.push_back(input[i]);
757 size_t ilen=input.length();
758 for(
size_t i=0; i<ilen; ++i){
782 std::copy(tagname.begin(),tagname.end(),std::back_inserter(_TagName));
808 std::copy(name.begin(),name.begin()+name.length(),std::back_inserter(_TagName));
814 return std::string(_TagName.begin(),_TagName.end());
817void libhtmlpp::HtmlElement::_assignParentPointers(
Element* node,
Element* parent){
837 remove(_childElement.get());
842 _childElement=std::make_unique<HtmlElement>();
845 _childElement=std::make_unique<TextElement>();
848 _childElement=std::make_unique<CommentElement>();
851 _childElement=std::make_unique<ScriptElement>();
854 _childElement=std::make_unique<SvgElement>();
861 _copy(_childElement.get(),el);
865 _childElement->_parentElement =
this;
878 for(
Element *curel=_childElement.get(); curel; curel=curel->nextElement()){
924 if( _TagName.size() != hel->_TagName.size())
926 if(std::equal(_TagName.begin(),_TagName.end(),hel->_TagName.begin()))
932 if(_TagName.size() != hel._TagName.size())
934 if(std::equal(_TagName.begin(),_TagName.end(),hel._TagName.begin()))
973 std::unique_ptr<Element> elsNext(el->
_nextElement.release());
977 if(elsNext) elsNext->_prevElement =
nullptr;
978 static_cast<HtmlElement*
>(parent)->_childElement = std::move(elsNext);
979 }
else if(prevSibling){
1001 auto is_space = [](
unsigned char c) ->
bool {
1002 return c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r';
1004 auto tolower_ascii = [](
unsigned char c) ->
unsigned char {
1005 return (c >=
'A' && c <=
'Z') ?
static_cast<unsigned char>(c + 32) : c;
1008 size_t i = 0, n = in.size();
1010 if (i < n && in[i] ==
'<') ++i;
1011 while (i < n && is_space(
static_cast<unsigned char>(in[i]))) ++i;
1013 bool end_tag =
false;
1014 if (i < n && in[i] ==
'/') {
1017 while (i < n && is_space(
static_cast<unsigned char>(in[i]))) ++i;
1021 while (r > i && is_space(
static_cast<unsigned char>(in[r - 1]))) --r;
1022 if (r > i && in[r - 1] ==
'>') --r;
1023 while (r > i && is_space(
static_cast<unsigned char>(in[r - 1]))) --r;
1030 const size_t name_start = i;
1032 unsigned char c =
static_cast<unsigned char>(in[i]);
1033 if (is_space(c) || c ==
'/' || c ==
'>')
break;
1036 const size_t name_end = i;
1037 if (name_start == name_end) {
1042 _TagName.assign(in.begin() + name_start, in.begin() + name_end);
1043 for (
char& ch : _TagName) ch =
static_cast<char>(tolower_ascii(
static_cast<unsigned char>(ch)));
1050 while (i < r && is_space(
static_cast<unsigned char>(in[i]))) ++i;
1055 while (i < r && is_space(
static_cast<unsigned char>(in[i]))) ++i;
1059 const size_t kstart = i;
1061 unsigned char c =
static_cast<unsigned char>(in[i]);
1062 if (is_space(c) || c ==
'=' || c ==
'/' || c ==
'>')
break;
1065 const size_t kend = i;
1066 if (kstart == kend) {
1071 std::string key(in.begin() + kstart, in.begin() + kend);
1072 if (!preserveAttrCase) {
1073 for (
char& ch : key) ch =
static_cast<char>(tolower_ascii(
static_cast<unsigned char>(ch)));
1076 while (i < r && is_space(
static_cast<unsigned char>(in[i]))) ++i;
1080 if (i < r && in[i] ==
'=') {
1082 while (i < r && is_space(
static_cast<unsigned char>(in[i]))) ++i;
1084 if (i < r && (in[i] ==
'"' || in[i] ==
'\'')) {
1085 char quote = in[i++];
1086 const size_t vstart = i;
1087 while (i < r && in[i] != quote) ++i;
1088 const size_t vend = i;
1089 val.assign(in.begin() + vstart, in.begin() + vend);
1090 if (i < r && in[i] == quote) ++i;
1092 const size_t vstart = i;
1094 unsigned char c =
static_cast<unsigned char>(in[i]);
1095 if (is_space(c) || c ==
'/' || c ==
'>')
break;
1098 const size_t vend = i;
1099 val.assign(in.begin() + vstart, in.begin() + vend);
1105 setAttribute(key, val);
1111 const std::vector<char>& in,
1112 std::unique_ptr<libhtmlpp::Element>& el,
1116 el = std::make_unique<HtmlElement>();
1117 termination =
false;
1124 while (i < in.size() && std::isspace(
static_cast<unsigned char>(in[i]))) ++i;
1139 bool foundClose =
false;
1141 while (close < in.size()) {
1144 if (c == quote) quote = 0;
1150 }
else if (c ==
'"' || c ==
'\'') {
1157 while (k < close && std::isspace(
static_cast<unsigned char>(in[k]))) ++k;
1159 std::vector<char> tel;
1164 while (k < close && std::isspace(
static_cast<unsigned char>(in[k]))) ++k;
1167 tel.insert(tel.end(), in.begin() + k, in.begin() + close);
1169 reinterpret_cast<HtmlElement*
>(el.get())->_serialelize(tel);
1171 return foundClose ? close + 1 : close;
1175 const std::vector<char>& in,
1176 std::unique_ptr<Element>& el,
1180 termination =
false;
1181 el = std::make_unique<HtmlElement>(
"style");
1189 auto iequals = [](
char a,
char b) {
1190 return std::tolower(
static_cast<unsigned char>(a)) ==
1191 std::tolower(
static_cast<unsigned char>(b));
1193 auto match_ci = [&](
size_t pos,
const char* k) ->
bool {
1194 for (
size_t j = 0; k[j]; ++j) {
1195 if (pos + j >= in.size() || !iequals(in[pos + j], k[j])) {
1203 while (i < in.size() && std::isspace(
static_cast<unsigned char>(in[i]))) ++i;
1205 const char* tag_keyword =
"style";
1206 size_t keyword_len = std::char_traits<char>::length(tag_keyword);
1208 if (i + keyword_len >= in.size() || !match_ci(i, tag_keyword)) {
1212 if (i < in.size()) ++i;
1222 std::vector<char> raw_tag_data(in.begin() + start, in.begin() + i + 1);
1223 self->_serialelize(raw_tag_data);
1232 size_t content_begin = i;
1237 for (; i < in.size(); ++i) {
1239 if (i > content_begin) {
1240 std::string cssText(in.begin() + content_begin, in.begin() + i);
1242 self->appendChild(&textEl);
1245 size_t closing_tag_end_pos = i + keyword_len + 2;
1246 while (closing_tag_end_pos < in.size() && in[closing_tag_end_pos] !=
HTMLTAG_CLOSE) {
1247 ++closing_tag_end_pos;
1250 if (closing_tag_end_pos < in.size() && in[closing_tag_end_pos] ==
HTMLTAG_CLOSE) {
1251 return closing_tag_end_pos + 1;
1253 return closing_tag_end_pos;
1259 if (in.size() > content_begin) {
1260 std::string cssText(in.begin() + content_begin, in.end());
1262 self->appendChild(&textEl);
1286 cpyel(
const cpyel &src){
1298 std::stack<cpyel> cpylist;
1307 cattr->_Key.begin(),
1310 cattr->_Value.begin(),
1344 cpylist.push(childel);
1349 if(!cattr->_Value.empty()){
1352 cattr->_Key.begin(),
1355 cattr->_Value.begin(),
1367 if(!cattr->_Value.empty()){
1370 cattr->_Key.begin(),
1373 cattr->_Value.begin(),
1378 ((
libhtmlpp::SvgElement*)dest)->setAttribute(std::string(cattr->_Key.begin(),cattr->_Key.end()),
"");
1385 if(!cattr->_Value.empty()){
1388 cattr->_Key.begin(),
1391 cattr->_Value.begin(),
1396 ((
libhtmlpp::TextArea*)dest)->setAttribute(std::string(cattr->_Key.begin(),cattr->_Key.end()),
"");
1420 dest->
_nextElement= std::make_unique<CommentElement>();
1442 if(!cpylist.empty()){
1443 cpyel childel(cpylist.top());
1445 dest=childel.destin;
1463 libhtmlpp::HtmlElement::_assignParentPointers(destHel->
_childElement.get(), originalDest);
1470 std::unique_ptr<Element> nel;
1473 nel->_nextElement= std::make_unique<HtmlElement>();
1476 nel->_nextElement= std::make_unique<TextElement>();
1479 nel->_nextElement= std::make_unique<CommentElement>();
1482 nel->_nextElement= std::make_unique<ScriptElement>();
1485 nel->_nextElement= std::make_unique<SvgElement>();
1488 nel->_nextElement= std::make_unique<TextArea>();
1495 _copy(nel.get(),el);
1496 std::unique_ptr<Element> prev=std::move(_prevElement->_nextElement);
1497 _prevElement->_nextElement=std::move(nel);
1498 nel->_nextElement=std::move(prev);
1502 Element *nexel=
nullptr,*prev=
nullptr;
1506 _nextElement= std::make_unique<HtmlElement>();
1509 _nextElement= std::make_unique<TextElement>();
1512 _nextElement= std::make_unique<CommentElement>();
1515 _nextElement= std::make_unique<ScriptElement>();
1518 _nextElement= std::make_unique<SvgElement>();
1521 _nextElement= std::make_unique<TextArea>();
1529 _copy(_nextElement.get(),el);
1531 nexel=_nextElement.get();
1553 return _nextElement.get();
1557 return _prevElement;
1561 return _parentElement;
1565 _prevElement=
nullptr;
1566 _nextElement=
nullptr;
1570 _prevElement=
nullptr;
1571 _nextElement=
nullptr;
1576 auto cur = std::move(_nextElement);
1578 cur = std::move(cur->_nextElement);
1598 std::unique_ptr<Element> curelsNext(curel->
_nextElement.release());
1640 std::copy(txt.begin(),txt.end(),std::back_inserter(_Text));
1644 return std::string(_Text.begin(),_Text.end());
1652 const std::vector<char>& in,
1653 std::unique_ptr<libhtmlpp::Element>& el,
1657 termination =
false;
1659 std::vector<char> buf;
1661 bool last_was_space =
false;
1671 while (i < in.size()) {
1677 if (c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n') {
1678 if (!last_was_space) {
1680 last_was_space =
true;
1687 last_was_space =
false;
1692 auto text = std::make_unique<TextElement>();
1693 (
static_cast<TextElement*
>(text.get()))->_Text.insert(
1694 (
static_cast<TextElement*
>(text.get()))->_Text.end(),
1695 buf.begin(), buf.end()
1697 el = std::move(text);
1726 std::copy(txt.begin(),txt.end(),
1727 std::insert_iterator<std::vector<char>>(_Comment,_Comment.begin()));
1731 return std::string(_Comment.begin(),_Comment.end());
1739 const std::vector<char>& in,
1740 std::unique_ptr<Element>& el,
1744 termination =
false;
1747 if (i + 3 >= in.size())
return i;
1749 if (!(in[i] ==
'<' && in[i+1] ==
'!' && in[i+2] ==
'-' && in[i+3] ==
'-')) {
1753 el = std::make_unique<CommentElement>();
1756 const size_t content_begin = i;
1758 while (i + 2 < in.size()) {
1759 if (in[i] ==
'-' && in[i+1] ==
'-' && in[i+2] ==
'>') {
1766 if (i > content_begin) {
1767 self->
_Comment.insert(self->_Comment.end(),
1768 in.begin() + content_begin,
1772 if (i + 2 < in.size()) {
1785 _copy(
this,&scriptsrc);
1803 _Script.assign(script.begin(),script.end());
1807 return std::string(_Script.begin(),_Script.end());
1815 const std::vector<char>& in,
1816 std::unique_ptr<Element>& el,
1821 el = std::make_unique<ScriptElement>();
1825 if (i >= in.size() || in[i] !=
'<') {
1831 auto iequals = [](
char a,
char b) {
1832 return std::tolower(
static_cast<unsigned char>(a)) ==
1833 std::tolower(
static_cast<unsigned char>(b));
1837 auto match_ci = [&](
size_t pos,
const char* k) ->
bool {
1838 for (
size_t j = 0; k[j]; ++j) {
1839 if (pos + j >= in.size() || !iequals(in[pos + j], k[j])) {
1850 while (i < in.size() && std::isspace(
static_cast<unsigned char>(in[i]))) {
1854 const char* tag_keyword =
"script";
1855 size_t keyword_len = std::char_traits<char>::length(tag_keyword);
1857 if (i + keyword_len >= in.size() || !match_ci(i, tag_keyword)) {
1860 while (i < in.size() && in[i] !=
'>') {
1863 if (i < in.size()) {
1873 while (i < in.size() && in[i] !=
'>') {
1878 if (i > start && in[i] ==
'>') {
1880 std::vector<char> raw_tag_data(in.begin() + start, in.begin() + i + 1);
1881 self->_serialelize(raw_tag_data);
1884 if (i >= in.size() || in[i] !=
'>') {
1890 size_t content_begin = i;
1893 for (; i < in.size(); ++i) {
1895 if (in[i] ==
'<' && match_ci(i,
"</script")) {
1896 size_t content_end = i;
1899 if (content_end > content_begin) {
1901 self->_Script.insert(self->_Script.end(),
1902 in.begin() + content_begin,
1903 in.begin() + content_end);
1907 size_t closing_tag_end_pos = i + keyword_len + 2;
1910 while (closing_tag_end_pos < in.size() && in[closing_tag_end_pos] !=
'>') {
1911 ++closing_tag_end_pos;
1914 if (closing_tag_end_pos < in.size() && in[closing_tag_end_pos] ==
'>') {
1915 i = closing_tag_end_pos + 1;
1920 return closing_tag_end_pos;
1926 if (in.size() > content_begin) {
1928 self->_Script.insert(self->_Script.end(),
1929 in.begin() + content_begin,
1942 _copy(
this,&svgsrc);
1960 std::copy(script.begin(),script.end(),
1961 std::insert_iterator<std::vector<char>>(_Svg,_Svg.begin()));
1973 std::unique_ptr<libhtmlpp::Element>& el,
1977 const size_t startel = start;
1978 termination =
false;
1980 const auto begin = in.begin();
1981 if (start >= in.size()) {
1985 auto it_close_angle = std::find(begin + start, in.end(),
HTMLTAG_CLOSE);
1986 if (it_close_angle == in.end()) {
1991 el = std::make_unique<SvgElement>();
1992 auto* svgEl =
static_cast<SvgElement*
>(el.get());
1995 std::vector<char> tel;
1996 tel.assign(begin + startel, it_close_angle);
2000 auto it_content_begin = it_close_angle;
2001 if (it_content_begin != in.end()) ++it_content_begin;
2003 static constexpr char kEndTag[] =
"</svg>";
2004 auto ci_eq = [](
char a,
char b) {
2005 auto lower = [](
unsigned char c) ->
unsigned char {
2006 return (c >=
'A' && c <=
'Z') ?
static_cast<unsigned char>(c + 32) : c;
2008 return lower(
static_cast<unsigned char>(a)) == lower(
static_cast<unsigned char>(b));
2010 auto it_end = std::search(it_content_begin, in.end(),
2011 std::begin(kEndTag), std::end(kEndTag) - 1 , ci_eq);
2012 if (it_end == in.end()) {
2017 svgEl->_Svg.insert(svgEl->_Svg.end(), it_content_begin, it_end);
2019 const size_t consumed =
static_cast<size_t>((it_end - begin) + (std::size(kEndTag) - 1));
2029 _copy(
this,&textsrc);
2047 std::copy(text.begin(),text.end(),
2048 std::insert_iterator<std::vector<char>>(_Text,_Text.begin()));
2060 std::unique_ptr<libhtmlpp::Element>& el,
2064 termination =
false;
2066 const auto begin = in.begin();
2067 const auto end = in.end();
2069 if (start >= in.size()) {
2075 auto is_ws = [](
unsigned char c) {
2076 return c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r';
2078 auto tolower_ascii = [](
unsigned char c) ->
unsigned char {
2079 return (c >=
'A' && c <=
'Z') ?
static_cast<unsigned char>(c + 32) : c;
2081 auto ieq_prefix = [&](std::vector<char>::const_iterator it,
2082 std::vector<char>::const_iterator it_end,
2083 const char* lit) ->
bool
2085 for (; *lit; ++lit, ++it) {
2086 if (it == it_end)
return false;
2087 if (tolower_ascii(
static_cast<unsigned char>(*it)) !=
2088 tolower_ascii(
static_cast<unsigned char>(*lit))) {
2101 el = std::make_unique<TextArea>();
2102 auto* ta =
static_cast<TextArea*
>(el.get());
2104 std::vector<char> tel;
2105 tel.assign(begin + start, it_gt);
2110 if (it != end) ++it;
2111 const auto content_begin = it;
2114 auto lt = std::find(it, end,
'<');
2120 if (ieq_prefix(lt, end,
"</textarea")) {
2121 auto after_head = lt + std::strlen(
"</textarea");
2122 while (after_head != end && is_ws(
static_cast<unsigned char>(*after_head))) {
2125 if (after_head == end) {
2129 if (*after_head ==
'>') {
2130 ta->_Text.insert(ta->_Text.end(), content_begin, lt);
2131 const size_t consumed =
static_cast<size_t>((after_head - begin) + 1);
2160 std::ifstream fs(path);
2167 fs.seekg(std::ios::end);
2169 data.reserve(fs.tellg());
2171 fs.seekg(std::ios::beg);
2173 data.assign((std::istreambuf_iterator<char>(fs)), std::istreambuf_iterator<char>());
2222 }
catch(std::exception &e){
2238void libhtmlpp::HtmlPage::_CheckHeader(
const HtmlString &page){
2239 const char type[] = {
'!',
'D',
'O',
'C',
'T',
'Y',
'P',
'E' };
2255 }
while ( page[i]==
'<' || page[i]==
'!' || page[i] ==
' ');
2257 if (page.
size() < 8) {
2268 if (tolower(page[i+1]) != tolower(type[i])) {
2278 }
while (page[i] ==
' ');
2280 const char doctype[] = {
'H',
'T',
'M',
'L' };
2283 const char typevalue4[] = {
'P',
'U',
'B',
'L',
'I',
'C'};
2286 if ((i + tpvl) > page.
size()) {
2294 while ( ii < tpvl) {
2295 if (tolower(page[i++]) != tolower(doctype[ii++])) {
2306 }
while (page[i] ==
' ');
2310 if(i +tpvl4 <page.
size()){
2312 if (tolower(page[i++]) != tolower(typevalue4[ii++])) {
2334 const std::string &tag =
static_cast<const HtmlElement*
>(el)->getTagname();
2335 if (tag ==
"html") {
2336 output.
append(
"<!DOCTYPE html>");
2342 auto isContainer = [](
const std::string &tagname) {
2350 std::stack<const libhtmlpp::Element*> cpylist;
2357 for(
int i=0; i<lvl; ++i){
2369 curattr->_Key.begin(),
2370 curattr->_Key.end(),
2371 std::back_inserter(output)
2381 curattr->_Value.begin(),
2382 curattr->_Value.end(),
2383 std::back_inserter(output)
2390 if (
static_cast<const HtmlElement*
>(el)->_childElement) {
2394 el=
static_cast<const HtmlElement*
>(el)->_childElement.get();
2400 if(isContainer(
static_cast<const HtmlElement*
>(el)->getTagname())){
2403 static_cast<const HtmlElement*
>(el)->_TagName.begin(),
2404 static_cast<const HtmlElement*
>(el)->_TagName.end(),
2405 std::back_inserter(output)
2421 static_cast<const TextElement*
>(el)->_Text.begin(),
2423 std::back_inserter(output)
2438 std::back_inserter(output)
2455 curattr->_Key.begin(),
2456 curattr->_Key.end(),
2457 std::back_inserter(output)
2467 curattr->_Value.begin(),
2468 curattr->_Value.end(),
2469 std::back_inserter(output)
2477 for(
int i=0; i<lvl+1; ++i){
2484 std::back_inserter(output)
2488 for(
int i=0; i<lvl; ++i){
2509 curattr->_Key.begin(),
2510 curattr->_Key.end(),
2511 std::back_inserter(output)
2521 curattr->_Value.begin(),
2522 curattr->_Value.end(),
2523 std::back_inserter(output)
2531 for(
int i=0; i<lvl+1; ++i){
2536 static_cast<const SvgElement*
>(el)->_Svg.begin(),
2538 std::back_inserter(output)
2542 for(
int i=0; i<lvl; ++i){
2560 for (
TextArea::Attributes* curattr =
static_cast<const TextArea*
>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2563 curattr->_Key.begin(),
2564 curattr->_Key.end(),
2565 std::back_inserter(output)
2575 curattr->_Value.begin(),
2576 curattr->_Value.end(),
2577 std::back_inserter(output)
2583 static_cast<const TextArea*
>(el)->_Text.begin(),
2585 std::back_inserter(output)
2605 while(!cpylist.empty()){
2611 for(
int i=0; i<lvl; ++i){
2618 static_cast<const HtmlElement*
>(el)->_TagName.begin(),
2619 static_cast<const HtmlElement*
>(el)->_TagName.end(),
2620 std::back_inserter(output)
2636 std::stack <Element*> childs;
2641 childs.push(((
HtmlElement*)curel)->_childElement.get());
2643 std::string idname=((
HtmlElement*)curel)->getAtributte(
"id");
2644 if(idname.length()==
id.length() && std::equal(
id.begin(),
id.end(),idname.begin()) ){
2654 if(!childs.empty()){
2663 std::stack <Element*> childs;
2668 childs.push(((
HtmlElement*)curel)->_childElement.get());
2670 const std::string tname=((
HtmlElement*)curel)->getTagname();
2671 if(!tname.empty() && std::equal(tag.begin(),tag.end(),tname.begin())){
2681 if(!childs.empty()){
2690 return _childElement.get();
2694 return std::string(_Key.begin(), _Key.end());
2698 return std::string(_Value.begin(), _Value.end());
2702 return _nextAttr.get();
2706 return _firstAttr.get();
2712 const char forbidden[] = {
'\"'};
2714 auto checkForbidden = [forbidden](
const std::string &input){
2715 for(
size_t i = 0; i<input.length(); ++i){
2716 for(
size_t ii=0; ii<
sizeof(forbidden[ii]); ii++){
2717 if(input[i]==forbidden[ii]){
2725 if(checkForbidden(name) || checkForbidden(value)){
2731 for (cattr= _firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()) {
2732 if(name.size() == cattr->_Key.size() && std::equal(name.begin(),name.end(),cattr->_Key.begin())){
2733 cattr->_Value.clear();
2734 std::copy(value.begin(),value.end(),std::back_inserter(cattr->_Value));
2739 _lastAttr->_nextAttr = std::make_unique<Attributes>();
2740 _lastAttr = _lastAttr->_nextAttr.get();
2742 _firstAttr = std::make_unique<Attributes>();
2743 _lastAttr = _firstAttr.get();
2747 std::copy(name.begin(),name.end(),std::back_inserter(cattr->_Key) );
2748 std::copy(value.begin(),value.end(),std::back_inserter(cattr->_Value));
2753 snprintf(buf,255,
"%d",value);
2754 setAttribute(name,buf);
2758 for (
Attributes* curattr = _firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2760 if (curattr->_Key.size() != name.length() ) {
2764 if (std::equal(name.begin(), name.end(), curattr->_Key.begin())) {
2765 return std::string(curattr->_Value.begin(), curattr->_Value.end());
2773 return atoi(getAtributte(name).c_str());
2781 auto cur = std::move(_nextAttr);
2783 cur = std::move(cur->_nextAttr);
2802 std::unique_ptr<Row> newRow = std::make_unique<Row>(row);
2805 _firstRow = std::move(newRow);
2806 _lastRow = _firstRow.get();
2808 _lastRow->_nextRow = std::move(newRow);
2809 _lastRow = _lastRow->_nextRow.get();
2817 if(!_firstRow || _count<pos){
2824 for(curel=_firstRow.get(); curel; curel=curel->_nextRow.get()){
2834 for(
Row *crow=_firstRow.get(); crow; crow=crow->_nextRow.get()){
2836 for(
Column *ccol=crow->_firstColumn.get(); ccol; ccol=ccol->_nextColumn.get() ){
2852 va_start(args,count);
2854 for (
int i = 0; i < count; ++i) {
2855 _header << va_arg(args,
const char*);
2864 _firstRow = std::move(_firstRow->_nextRow);
2865 _lastRow = (pos == (_count - 1)) ? nullptr : _firstRow.get();
2867 Row *prev = &(*this)[pos - 1];
2868 if (prev->_nextRow) {
2869 prev->_nextRow = std::move(prev->_nextRow->_nextRow);
2871 if (prev->_nextRow.get() ==
nullptr) {
2879 _firstRow =
nullptr;
2885 _nextColumn=
nullptr;
2889 _nextColumn=
nullptr;
2894 _nextColumn=
nullptr;
2899 _nextColumn=
nullptr;
2907 _nextColumn = std::move(col._nextColumn);
2908 Data = std::move(col.Data);
2912 _firstColumn=
nullptr;
2913 _lastColumn=
nullptr;
2922 _firstColumn=
nullptr;
2923 _lastColumn=
nullptr;
2927 for(
Column *curel=row._firstColumn.get(); curel; curel=curel->_nextColumn.get()){
2933 std::unique_ptr<Column> ptr = std::make_unique<Column>(std::move(col));
2936 _lastColumn->_nextColumn=std::move(ptr);
2937 _lastColumn=_lastColumn->_nextColumn.get();
2939 _firstColumn= std::move(ptr);
2940 _lastColumn=_firstColumn.get();
2947 std::unique_ptr<Column> ptr = std::make_unique<Column>(col);
2950 _lastColumn->_nextColumn=std::move(ptr);
2951 _lastColumn=_lastColumn->_nextColumn.get();
2953 _firstColumn= std::move(ptr);
2954 _lastColumn=_firstColumn.get();
2982 snprintf(buf,255,
"%d",value);
2983 return *
this << buf;
2987 if(!_firstColumn || _count<pos){
2994 for(curel=_firstColumn.get(); curel; curel=curel->_nextColumn.get()){
3003 Column *dcol=&(*this)[pos];
3005 Column *prev=&(*this)[pos-1];
3006 prev->_nextColumn=std::move(dcol->_nextColumn);
3012 _firstColumn.reset();
3013 _lastColumn =
nullptr;
class DocElements * prevel
std::unique_ptr< Element > element
std::unique_ptr< DocElements > nextel
Abstract base class for all nodes in the HTML tree.
virtual void remove(Element *el)
void insertAfter(Element *el)
void insertBefore(Element *el)
Element & operator=(const Element &hel)
std::unique_ptr< Element > _nextElement
virtual int getType() const =0
Element * prevElement() const
Element * parentElement() const
Element * nextElement() const
const char * what() const noexcept override
const Attributes * firstAttribute() const
HtmlElement * getElementbyTag(const std::string &tag) const
const std::string getAtributte(const std::string &name) const
void _serialelize(const std::vector< char > &in, bool preserveAttrCase=false)
Extracts tag name and attributes from a token vector into an HtmlElement.
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
static size_t parseStyleElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Parses a <style> tag the same "raw text until literal </style>" way ScriptElement::parseElement parse...
void appendChild(const Element *el)
int getIntAtributte(const std::string &name) const
void setTagname(const std::string &name)
void setAttribute(const std::string &name, const std::string &value)
bool operator==(const HtmlElement *hel)
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
void insertChild(const Element *el)
Element * firstChild() const
const std::string getTagname() const
HtmlElement * getElementbyID(const std::string &id) const
HtmlElement & operator=(const HtmlElement &hel)
std::unique_ptr< Element > _childElement
void setIntAttribute(const std::string &name, int value)
void loadString(libhtmlpp::HtmlElement &html, const std::string &src)
Parses an HTML source string and copies the result into html.
void saveFile(libhtmlpp::HtmlElement &html, const std::string &path)
Serializes an HtmlElement subtree and writes it to a file.
void loadFile(libhtmlpp::HtmlElement &html, const std::string &path)
Loads an HTML file from disk into a given HtmlElement root.
const std::vector< char > & data() const
void append(const std::string &src)
HtmlString & operator<<(const char *src)
HtmlString & operator+=(const std::string &src)
char operator[](size_t pos) const
void push_back(const char src)
void insert(size_t pos, char src)
const char * c_str() const
libhtmlpp::Element & parse()
Parses the current buffer into a DOM-like tree and returns the root element.
HtmlString & operator=(const std::string &src)
const std::string str() const
Row & operator<<(Column &&col)
Column & operator[](size_t pos)
void delColumn(size_t pos)
Row & operator[](size_t pos)
Row & operator<<(const Row &row)
void deleteRow(size_t pos)
void parse(HtmlElement *element)
void insert(HtmlElement *element)
void setHeader(int count,...)
Element representing a <script> tag and its text content.
void setScript(const std::string &txt)
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
ScriptElement & operator=(const Element &hel)
std::vector< char > _Script
const std::string getScript()
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Element representing an embedded <svg> tag and its attributes/content.
SvgElement & operator=(const Element &hel)
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
const std::vector< char > getSvg()
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
void setSvg(const std::string &svg)
Element representing an embedded <textarea> tag and its attributes/content.
std::vector< char > _Text
TextArea & operator=(const Element &hel)
const std::vector< char > getText()
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
void setText(const std::string &text)
Leaf node representing plain text content of an HTML document.
TextElement & operator=(const Element &hel)
std::vector< char > _Text
void setText(const std::string &txt)
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
const std::string getText()
#define HTMLTAG_TERMINATE
std::ostream & operator<<(std::ostream &os, const libhtmlpp::HtmlString &p)
Streams an HtmlString to an output stream using its underlying string.
void loadString(libhtmlpp::HtmlElement &html, const libhtmlpp::HtmlString *node)
Public declarations for libhtmlpp HTML element types and utilities.
Core namespace for the libhtmlpp HTML parsing and printing library.
void print(const Element &element, HtmlString &output, bool formated=false)
Serializes an element (and its subtree) into an HtmlString.
void HtmlEncode(const std::string &input, std::string &output)
Encodes special HTML characters in a string and writes into std::string.
const std::array< std::string_view, 100 > ContainerTypes
void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
const char * HtmlSigns[][2]
void HtmlDecode(const std::string &input, HtmlString &output)
Decodes special HTML characters in a string and appends to an HtmlString.
const std::string getValue() const
Attributes * nextAttribute() const
const std::string getKey() const