libhtmlpp 1.0.0
Loading...
Searching...
No Matches
html.cpp
Go to the documentation of this file.
1
9/*******************************************************************************
10 * Copyright (c) 2021, Jan Koester jan.koester@gmx.net
11 * All rights reserved.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions are met:
15 * Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * Neither the name of the <organization> nor the
21 * names of its contributors may be used to endorse or promote products
22 * derived from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
28 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *******************************************************************************/
35
36#include <iostream>
37#include <cstdarg>
38#include <compare>
39#include <string_view>
40#include <array>
41#include <algorithm>
42#include <fstream>
43
44#include "utils.h"
45#include "html.h"
46#include "config.h"
47#include "encode.h"
48#include <assert.h>
49
50
51#define HTMLTAG_OPEN '<'
52#define HTMLTAG_TERMINATE '/'
53#define HTMLTAG_CLOSE '>'
54#define HTMLTAG_COMMENT '!'
60namespace libhtmlpp {
61
62 const std::array<std::string_view,100> ContainerTypes{{
63 "a",
64 "abbr",
65 "address",
66 "article",
67 "aside",
68 "audio",
69 "b",
70 "bdi",
71 "bdo",
72 "blockquote",
73 "body",
74 "button",
75 "canvas",
76 "caption",
77 "cite",
78 "code",
79 "colgroup",
80 "data",
81 "datalist",
82 "dd",
83 "del",
84 "details",
85 "dfn",
86 "dialog",
87 "div",
88 "dl",
89 "dt",
90 "em",
91 "fieldset",
92 "figcaption",
93 "figure",
94 "footer",
95 "form",
96 "frame",
97 "frameset",
98 "h1",
99 "h2",
100 "h3",
101 "h4",
102 "h5",
103 "h6",
104 "head",
105 "header",
106 "hgroup",
107 "html",
108 "i",
109 "iframe",
110 "ins",
111 "kbd",
112 "label",
113 "legend",
114 "li",
115 "main",
116 "map",
117 "mark",
118 "menu",
119 "meter",
120 "nav",
121 "noscript",
122 "object",
123 "ol",
124 "optgroup",
125 "option",
126 "output",
127 "p",
128 "picture",
129 "pre",
130 "progress",
131 "q",
132 "rp",
133 "rt",
134 "ruby",
135 "s",
136 "samp",
137 "search",
138 "section",
139 "select",
140 "small",
141 "span",
142 "strong",
143 "style",
144 "sub",
145 "summary",
146 "sup",
147 "svg",
148 "table",
149 "tbody",
150 "td",
151 "template",
152 "textarea",
153 "tfoot",
154 "th",
155 "thead",
156 "time",
157 "title",
158 "tr",
159 "u",
160 "ul",
161 "var",
162 "video"
163 }};
164
166 public:
167 std::unique_ptr<Element> element;
169 std::unique_ptr<DocElements> nextel;
171
173 nextel = nullptr;
174 prevel = nullptr;
175 element = nullptr;
176 terminator = false;
177 }
178
180 auto cur = std::move(nextel);
181 while (cur) {
182 cur = std::move(cur->nextel);
183 }
184 }
185 };
186};
187
189 _Data.push_back(str);
190}
191
193 if(str.empty())
194 return;
195
196 if (!_Data.empty() && _Data.back() == '\0') {
197 _Data.pop_back();
198 }
199
200 for(auto i = str.begin(); i!=str.end(); ++i){
201 if(*i=='\0')
202 break;
203 _Data.push_back(*i);
204 }
205
206 if (_Data.empty() || _Data.back() != '\0') {
207 _Data.push_back('\0');
208 }
209}
210
213
215 std::copy(str._Data.begin(),str._Data.end(),std::insert_iterator<std::vector<char>>(_Data,_Data.begin()));
216}
217
219 if(!src)
220 return;
221
222 if (!_Data.empty() && _Data.back() == '\0') {
223 _Data.pop_back();
224 }
225 _Data.push_back(src);
226
227 _Data.push_back('\0');
228}
229
230void libhtmlpp::HtmlString::append(const std::string& src) {
231 if(src.empty())
232 return;
233
234 if (!_Data.empty() && _Data.back() == '\0') {
235 _Data.pop_back();
236 }
237
238 for(auto i = src.begin(); i!=src.end(); ++i){
239 if(*i=='\0')
240 break;
241 _Data.push_back(*i);
242 }
243
244 if (_Data.empty() || _Data.back() != '\0') {
245 _Data.push_back('\0');
246 }
247}
248
250 std::copy(hstring._Data.begin(),hstring._Data.end(),std::back_inserter(_Data));
251}
252
253void libhtmlpp::HtmlString::insert(size_t pos, char src){
254 _Data.at(pos)=src;
255}
256
258 _rootEl.reset();
259 _Data.clear();
260}
261
263 return _Data.empty();
264}
265
266
268 append(src);
269 return *this;
270}
271
273 append(hstring);
274 return *this;
275}
276
278 clear();
279 append(src);
280 return *this;
281}
282
284 clear();
285 std::copy(src._Data.begin(),src._Data.end(),std::insert_iterator<std::vector<char>>(_Data,_Data.begin()));
286 return *this;
287}
288
290 return _Data.at(pos);
291}
292
294 append(src);
295 return *this;
296}
297
299 append(src);
300 return *this;
301}
302
304 if(src._Data.data())
305 std::copy(src._Data.begin(),src._Data.end(),std::back_inserter(_Data));
306 return *this;
307}
308
310 char buf[255];
311 snprintf(buf, 255, "%d", src);
312 append(buf);
313 return *this;
314}
315
317 char buf[255];
318 snprintf(buf, 255, "%zu", src);
319 append(buf);
320 return *this;
321}
322
324 push_back(src);
325 return *this;
326}
327
329 return _Data.data();
330}
331
333 return _Data.empty() ? 0 : (_Data.size() - 1);
334}
335
337 return _Data.size();
338}
339
340const std::string libhtmlpp::HtmlString::str() const{
341 if(_Data.data())
342 return std::string(_Data.begin(),_Data.end());
343 return "";
344}
345
347 return _Data.data();
348}
349
350const std::vector<char>& libhtmlpp::HtmlString::data() const{
351 return _Data;
352}
353
354
356 HTMLException excp;
357 _buildTree();
358 return *_rootEl;
359}
360
361void libhtmlpp::HtmlString::_buildtreenode(
362 DocElements *firstel,
364 std::unique_ptr<Element> &html)
365{
366 if (!firstel) {
367 HTMLException excp;
368 excp[HTMLException::Error] << "No start Element!";
369 throw excp;
370 }
371
372 struct Frame {
373 DocElements *open; // Opener-DocElement (Start-Tag)
374 DocElements *close; // passender Terminator-DocElement (End-Tag)
375 const DocElements *outer_end; // Grenze der aktuellen Ebene
376 Element *outer_prev_el; // letztes bereits eingebautes Geschwister der äußeren Ebene
377 };
378 std::stack<Frame> stack;
379
380 DocElements *start = firstel;
381 const DocElements *end = lastel; // nullptr bedeutet: bis Ketten-Ende
382
383 Element *prev_el_in_tree = nullptr; // zuletzt in den Baum eingebautes Element
384
385 auto checkContainer = [&](const std::string &tag) {
386 for (size_t i = 0; i < ContainerTypes.size(); ++i) {
387 if (tag == ContainerTypes[i]) return true;
388 }
389 return false;
390 };
391
392 // Leere DocElements überspringen (z. B. Kommentare/Text, die nicht als element abgebildet sind)
393 auto skip_empty = [](DocElements *cur, const DocElements *stop) -> DocElements* {
394 while (cur && cur != stop && (!cur->element)) {
395 cur = cur->nextel.get();
396 }
397 return cur;
398 };
399
400 // Finde zum gegebenen Start-Tag dessen passenden Terminator in [open->nextel, bound).
401 auto find_terminator = [&skip_empty, checkContainer](DocElements *open, const DocElements *bound) -> DocElements* {
402 if (!open || !open->element || open->terminator ||
403 open->element->getType() != HtmlEl) return nullptr;
404
405 const std::string &tag = static_cast<HtmlElement*>(open->element.get())->getTagname();
406 int nest = 0;
407 DocElements *cur = open->nextel.get();
408
409 while (cur && cur != bound) {
410 cur = skip_empty(cur, bound);
411 if (!cur || cur == bound) break;
412
413 if (cur->element && cur->element->getType() == HtmlEl) {
414 const std::string &curtag = static_cast<HtmlElement*>(cur->element.get())->getTagname();
415 if (curtag == tag) {
416 if (cur->terminator) {
417 if (nest == 0) return cur; // passendes End-Tag gefunden
418 --nest;
419 } else {
420 ++nest;
421 }
422 }
423 }
424 cur = cur->nextel.get();
425 }
426
427 // Wenn ein Container nicht geschlossen wurde: Warnung ignorieren oder als einfaches Tag behandeln
428 if (checkContainer(tag)) {
429 // Exception entfernt für nachsichtigeres Parsing
430 // return nullptr;
431 }
432 return nullptr;
433 };
434
435 for (;;) {
436 // bis zum nächsten sinnvollen DocElement laufen
437 start = skip_empty(start, end);
438
439 // Terminator-Knoten als eigenständige Nodes überspringen
440 if (start && start != end && start->terminator) {
441 start = start->nextel.get();
442 continue;
443 }
444
445 // Ende der aktuellen Ebene erreicht?
446 if (!start || start == end) {
447 if (stack.empty()) {
448 // Ganz oben: Root setzen (falls vorhanden)
449 if (firstel->element) {
450 html = std::move(firstel->element);
451 }
452 return;
453 }
454
455 // Frame schließen: Kinderbereich [open->nextel, close) einsammeln
456 Frame fr = stack.top(); stack.pop();
457 HtmlElement *opener_el = static_cast<HtmlElement*>(fr.open->element.get());
458
459 // Alle Kinder zwischen open und close verketten
460 Element* last_child_in_chain = nullptr;
461 {
462 DocElements* cur = fr.open->nextel.get();
463 // bis zum ersten brauchbaren Kind
464 while (cur && cur != fr.close && (!cur->element || cur->terminator)) {
465 cur = cur->nextel.get();
466 }
467
468 // alle nicht-leeren, nicht-Terminierer bis vor close anbinden
469 while (cur && cur != fr.close) {
470 if (cur->element && !cur->terminator) {
471 if (!opener_el->_childElement) {
472 opener_el->_childElement = std::move(cur->element);
473 last_child_in_chain = opener_el->_childElement.get();
474 } else {
475 last_child_in_chain->_nextElement = std::move(cur->element);
476 // _prev (falls genutzt) setzen
477 last_child_in_chain->_nextElement->_prevElement = last_child_in_chain;
478 last_child_in_chain = last_child_in_chain->_nextElement.get();
479 }
480 }
481 cur = cur->nextel.get();
482 // leere/terminator Knoten überspringen
483 while (cur && cur != fr.close && (!cur->element || cur->terminator)) {
484 cur = cur->nextel.get();
485 }
486 }
487 }
488
489 // dieses Container-Element ist nun das "aktuelle" in der äußeren Ebene
490 prev_el_in_tree = opener_el;
491
492 // Wenn es bereits ein vorheriges Geschwister in der äußeren Ebene gibt: verketten
493 if (fr.outer_prev_el) {
494 prev_el_in_tree->_prevElement = fr.outer_prev_el;
495 fr.outer_prev_el->_nextElement = std::move(fr.open->element);
496 prev_el_in_tree = fr.outer_prev_el->_nextElement.get();
497 }
498
499 // für die äußere Ebene fortsetzen
500 prev_el_in_tree = opener_el;
501 start = (fr.close ? fr.close->nextel.get() : nullptr);
502 end = fr.outer_end;
503
504 continue;
505 }
506
507 // Start-Tag eines HTML-Elements: passenden Terminator suchen → in den Stack tauchen
508 if (start->element && !start->terminator && start->element->getType() == HtmlEl) {
509 if (DocElements *close = find_terminator(start, end)) {
510 // neuen Rahmen für diesen Container aufmachen
511 stack.push(Frame{start, close, end, prev_el_in_tree});
512
513 // wir wechseln in die innere Ebene: prev zurücksetzen
514 prev_el_in_tree = nullptr;
515 start = start->nextel.get();
516 end = close;
517 continue;
518 }
519 }
520
521 // "normales" Element (kein Container mit eigenem Terminatorbereich):
522 if (start->element && !start->terminator) {
523 Element *current_el = start->element.get();
524
525 if (prev_el_in_tree) {
526 current_el->_prevElement = prev_el_in_tree;
527 prev_el_in_tree->_nextElement = std::move(start->element);
528 prev_el_in_tree = prev_el_in_tree->_nextElement.get();
529 } else {
530 prev_el_in_tree = current_el;
531 }
532 }
533
534 // weiter zum nächsten DocElement
535 start = start->nextel.get();
536 }
537}
538
544void libhtmlpp::HtmlString::_buildTree() {
545 DocElements* lastEl = nullptr;
546 std::unique_ptr<DocElements> firstEl = nullptr;
547
548 auto is_ws = [](unsigned char ch) -> bool {
549 // space, \t, \n, \r
550 return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
551 };
552
553 auto ascii_tolower = [](unsigned char c) -> unsigned char {
554 return (c >= 'A' && c <= 'Z') ? static_cast<unsigned char>(c + 32) : c;
555 };
556
557 auto starts_with_ci = [&](const char* s, const char* e, const char* k) -> bool {
558 const size_t klen = std::char_traits<char>::length(k);
559 if (static_cast<size_t>(e - s) < klen) return false;
560 for (size_t i = 0; i < klen; ++i) {
561 unsigned char a = static_cast<unsigned char>(s[i]);
562 unsigned char b = static_cast<unsigned char>(k[i]);
563 if (ascii_tolower(a) != ascii_tolower(b)) return false;
564 }
565 return true;
566 };
567
568 auto add_element_node = [&](DocElements** last) {
569 if (!firstEl) {
570 firstEl = std::make_unique<DocElements>();
571 *last = firstEl.get();
572 } else {
573 (*last)->nextel = std::make_unique<DocElements>();
574 (*last)->nextel->prevel = (*last);
575 *last = (*last)->nextel.get();
576 }
577 };
578
579 const char* const base = _Data.data();
580 const size_t n = _Data.size();
581 const char* const end = base + n;
582
583 const char* p = base;
584
585 while (p < end) {
586 while (p < end && is_ws(static_cast<unsigned char>(*p))) ++p;
587 if (p >= end) break;
588
589 if (*p == HTMLTAG_OPEN) { // '<'
590 const char* const remain_end = end;
591 const size_t remain = static_cast<size_t>(remain_end - p);
592
593 if (remain >= 2) {
594 const unsigned char c1 = ascii_tolower(static_cast<unsigned char>(p[1]));
595
596 if (p[1] == '!') {
597 // <!-- ... -->
598 if (starts_with_ci(p, end, "<!--")) {
599 add_element_node(&lastEl);
600 size_t i = static_cast<size_t>(p - base);
601 i = CommentElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
602 p = base + i;
603 continue;
604 }
605 if (starts_with_ci(p, end, "<!doctype")) {
606 const char* close_tag = p;
607 while (close_tag < end && *close_tag != '>') {
608 ++close_tag;
609 }
610 if (close_tag < end) {
611 p = close_tag + 1;
612 } else {
613 p = end;
614 }
615 continue;
616 }
617 } else if (c1 == 's') {
618 if (starts_with_ci(p, end, "<script")) {
619 add_element_node(&lastEl);
620 size_t i = static_cast<size_t>(p - base);
621 i = ScriptElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
622 p = base + i;
623 continue;
624 }
625 if (starts_with_ci(p, end, "<svg")) {
626 add_element_node(&lastEl);
627 size_t i = static_cast<size_t>(p - base);
628 i = SvgElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
629 p = base + i;
630 continue;
631 }
632 } else if (c1 == 't') {
633 if (starts_with_ci(p, end, "<textarea")) {
634 add_element_node(&lastEl);
635 size_t i = static_cast<size_t>(p - base);
636 i = TextArea::parseElement(_Data, lastEl->element, i, lastEl->terminator);
637 p = base + i;
638 continue;
639 }
640 }
641 }
642
643 {
644 add_element_node(&lastEl);
645 size_t i = static_cast<size_t>(p - base);
646 i = HtmlElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
647 p = base + i;
648 }
649 } else {
650 add_element_node(&lastEl);
651 size_t i = static_cast<size_t>(p - base);
652 i = TextElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
653 p = base + i;
654 }
655 }
656
657 _buildtreenode(firstEl.get(), nullptr, _rootEl);
658}
659
667std::ostream& operator<<(std::ostream& os, const libhtmlpp::HtmlString& p) {
668 os << p.str();
669 return os;
670}
671
672void libhtmlpp::HtmlEncode(const std::string &input, std::string &output){
673 size_t ilen=input.length();
674 for(size_t i=0; i<ilen; ++i){
675 size_t ii=0;
676 bool changed=false;
677 while(HtmlSigns[ii][0]){
678 if(input[i]==HtmlSigns[ii][0][0]){
679 output+=HtmlSigns[ii][1];
680 changed=true;
681 }
682 ++ii;
683 }
684 if(!changed)
685 output.push_back(input[i]);
686 }
687}
688
689void libhtmlpp::HtmlDecode(const std::string &input,std::string &output){
690 size_t ilen=input.length();
691 for(size_t i=0; i<ilen; ++i){
692 size_t ii=0;
693 bool changed=false;
694 while(HtmlSigns[ii][0]){
695 if(input.compare(i,strlen(HtmlSigns[ii][1]),HtmlSigns[ii][1]) == 0){
696 output += HtmlSigns[ii][0];
697 changed=true;
698 }
699 ++ii;
700 }
701 if(!changed)
702 output += input[i];
703 }
704}
705
706void libhtmlpp::HtmlDecode(const std::string &input,HtmlString &output){
707 std::string tmp;
708 HtmlDecode(input,tmp);
709 output << tmp;
710 output.parse();
711}
712
713libhtmlpp::HtmlElement::HtmlElement(const std::string &tagname) : HtmlElement(){
714 _TagName.clear();
715 std::copy(tagname.begin(),tagname.end(),std::back_inserter(_TagName));
716}
717
719 _childElement=nullptr;
720 _firstAttr=nullptr;
721 _lastAttr=nullptr;
722}
723
727
731
734
736 return -1;
737}
738
739void libhtmlpp::HtmlElement::setTagname(const std::string &name){
740 _TagName.clear();
741 std::copy(name.begin(),name.begin()+name.length(),std::back_inserter(_TagName));
742}
743
744const std::string libhtmlpp::HtmlElement::getTagname() const{
745 if(_TagName.empty())
746 return "";
747 return std::string(_TagName.begin(),_TagName.end());
748}
749
751 if(_childElement){
752 remove(_childElement.get());
753 }
754
755 switch(el->getType()){
756 case HtmlEl:
757 _childElement=std::make_unique<HtmlElement>();
758 break;
759 case TextEl:
760 _childElement=std::make_unique<TextElement>();
761 break;
762 case CommentEl:
763 _childElement=std::make_unique<CommentElement>();
764 break;
765 case ScriptEL:
766 _childElement=std::make_unique<ScriptElement>();
767 break;
768 case SvgEL:
769 _childElement=std::make_unique<SvgElement>();
770 break;
771 default:
772 HTMLException ex;
773 ex[HTMLException::Critical] << "appendChild: Unknown html element found: "<< el->getType() << " !";
774 throw ex;
775 }
776 _copy(_childElement.get(),el);
777}
778
780 insertChild(&el);
781}
782
784 if(!el)
785 return;
786 if(_childElement){
787 Element *prev=nullptr;
788
789 for(Element *curel=_childElement.get(); curel; curel=curel->nextElement()){
790 prev=curel;
791 }
792
793 switch(el->getType()){
794 case HtmlEl:
795 prev->_nextElement=std::make_unique<HtmlElement>();
796 break;
797 case TextEl:
798 prev->_nextElement=std::make_unique<TextElement>();
799 break;
800 case CommentEl:
801 prev->_nextElement=std::make_unique<CommentElement>();
802 break;
803 case ScriptEL:
804 prev->_nextElement=std::make_unique<ScriptElement>();
805 break;
806 case SvgEL:
807 prev->_nextElement=std::make_unique<SvgElement>();
808 break;
809 default:
810 HTMLException ex;
811 ex[HTMLException::Critical] << "appendChild: Unknown html element found: "<< el->getType() << " !";
812 throw ex;
813 }
814
815 _copy(prev->_nextElement.get(),el);
816
817 if(prev->_nextElement){
818 prev->_nextElement->_prevElement=prev;
819 }
820 }else{
821 insertChild(el);
822 }
823}
824
826 appendChild(&el);
827}
828
829
831 if(!hel)
832 return false;
833 if( _TagName.size() != hel->_TagName.size())
834 return false;
835 if(std::equal(_TagName.begin(),_TagName.end(),hel->_TagName.begin()))
836 return true;
837 return false;
838}
839
841 if(_TagName.size() != hel._TagName.size())
842 return false;
843 if(std::equal(_TagName.begin(),_TagName.end(),hel._TagName.begin()))
844 return true;
845 return false;
846}
847
849 _copy(this,&hel);
850 return *this;
851}
852
857
859 Element *cur=this;
860
861 std::stack<Element*> parents;
862
863 DELETEELEMENT:
864 while(cur){
865 if(cur==el){
866 if(cur->_prevElement)
867 cur->_prevElement->_nextElement=std::move(cur->_nextElement);
868 if(cur->_nextElement)
869 cur->_nextElement->_prevElement=cur->_prevElement;
870 cur->_nextElement=nullptr;
871 cur->_prevElement=nullptr;
872 cur=nullptr;
873 }
874
875 if(cur->getType()==HtmlEl){
876 if(((HtmlElement*)cur)->_childElement){
877 parents.push(cur);
878 cur=((HtmlElement*)cur)->_childElement.get();
879 }
880 }
881
882 Element *next=cur->_nextElement.get();
883 cur=next;
884 }
885
886 if(!parents.empty()){
887 cur=parents.top()->nextElement();
888 parents.pop();
889 if(cur!=el)
890 goto DELETEELEMENT;
891 }
892
893
894}
895
903void libhtmlpp::HtmlElement::_serialelize(const std::vector<char>& in) {
904 _TagName.clear();
905
906 auto is_space = [](unsigned char c) -> bool {
907 return c == ' ' || c == '\t' || c == '\n' || c == '\r';
908 };
909 auto tolower_ascii = [](unsigned char c) -> unsigned char {
910 return (c >= 'A' && c <= 'Z') ? static_cast<unsigned char>(c + 32) : c;
911 };
912
913 size_t i = 0, n = in.size();
914
915 if (i < n && in[i] == '<') ++i;
916 while (i < n && is_space(static_cast<unsigned char>(in[i]))) ++i;
917
918 bool end_tag = false;
919 if (i < n && in[i] == '/') {
920 end_tag = true;
921 ++i;
922 while (i < n && is_space(static_cast<unsigned char>(in[i]))) ++i;
923 }
924
925 size_t r = n;
926 while (r > i && is_space(static_cast<unsigned char>(in[r - 1]))) --r;
927 if (r > i && in[r - 1] == '>') --r; // ignore '>' if present
928 while (r > i && is_space(static_cast<unsigned char>(in[r - 1]))) --r;
929
930 if (i >= r) {
931 HTMLException excp;
932 throw excp[HTMLException::Critical] << "no tag in element found!";
933 }
934
935 const size_t name_start = i;
936 while (i < r) {
937 unsigned char c = static_cast<unsigned char>(in[i]);
938 if (is_space(c) || c == '/' || c == '>') break;
939 ++i;
940 }
941 const size_t name_end = i;
942 if (name_start == name_end) {
943 HTMLException excp;
944 throw excp[HTMLException::Critical] << "no tag in element found!";
945 }
946
947 _TagName.assign(in.begin() + name_start, in.begin() + name_end);
948 for (char& ch : _TagName) ch = static_cast<char>(tolower_ascii(static_cast<unsigned char>(ch)));
949
950 if (end_tag) {
951 return;
952 }
953
954 while (i < r) {
955 while (i < r && is_space(static_cast<unsigned char>(in[i]))) ++i;
956 if (i >= r) break;
957
958 if (in[i] == '/') {
959 ++i;
960 while (i < r && is_space(static_cast<unsigned char>(in[i]))) ++i;
961 break;
962 }
963
964 const size_t kstart = i;
965 while (i < r) {
966 unsigned char c = static_cast<unsigned char>(in[i]);
967 if (is_space(c) || c == '=' || c == '/' || c == '>') break;
968 ++i;
969 }
970 const size_t kend = i;
971 if (kstart == kend) {
972 ++i;
973 continue;
974 }
975
976 std::string key(in.begin() + kstart, in.begin() + kend);
977 for (char& ch : key) ch = static_cast<char>(tolower_ascii(static_cast<unsigned char>(ch)));
978
979 while (i < r && is_space(static_cast<unsigned char>(in[i]))) ++i;
980
981 std::string val;
982
983 if (i < r && in[i] == '=') {
984 ++i;
985 while (i < r && is_space(static_cast<unsigned char>(in[i]))) ++i;
986
987 if (i < r && (in[i] == '"' || in[i] == '\'')) {
988 char quote = in[i++];
989 const size_t vstart = i;
990 while (i < r && in[i] != quote) ++i;
991 const size_t vend = i;
992 val.assign(in.begin() + vstart, in.begin() + vend);
993 if (i < r && in[i] == quote) ++i;
994 } else {
995 const size_t vstart = i;
996 while (i < r) {
997 unsigned char c = static_cast<unsigned char>(in[i]);
998 if (is_space(c) || c == '/' || c == '>') break;
999 ++i;
1000 }
1001 const size_t vend = i;
1002 val.assign(in.begin() + vstart, in.begin() + vend);
1003 }
1004 } else {
1005 val.clear();
1006 }
1007
1008 setAttribute(key, val);
1009 }
1010}
1011
1012
1014 const std::vector<char>& in,
1015 std::unique_ptr<libhtmlpp::Element>& el,
1016 size_t start,
1017 bool& termination
1018){
1019 el = std::make_unique<HtmlElement>();
1020 termination = false;
1021
1022 size_t i = start;
1023 if (i >= in.size() || in[i] != HTMLTAG_OPEN) return i;
1024
1025 ++i;
1026
1027 while (i < in.size() && std::isspace(static_cast<unsigned char>(in[i]))) ++i;
1028
1029
1030
1031 size_t close = i;
1032 while (close < in.size() && in[close] != HTMLTAG_CLOSE) { // '>'
1033 ++close;
1034 }
1035
1036 size_t k = i;
1037 while (k < close && std::isspace(static_cast<unsigned char>(in[k]))) ++k;
1038
1039 std::vector<char> tel;
1040
1041 if (k < close && in[k] == HTMLTAG_TERMINATE) { // '/'
1042 termination = true;
1043 ++k;
1044 while (k < close && std::isspace(static_cast<unsigned char>(in[k]))) ++k;
1045 }
1046
1047 tel.insert(tel.end(), in.begin() + k, in.begin() + close);
1048
1049 reinterpret_cast<HtmlElement*>(el.get())->_serialelize(tel);
1050
1051 return ++close;
1052}
1053
1054namespace libhtmlpp {
1055
1057 if(!dest || !src)
1058 return;
1059
1060 const libhtmlpp::Element* prev=nullptr;
1061
1062 struct cpyel {
1063 cpyel(){
1064 destin = nullptr;
1065 source = nullptr;
1066 };
1067
1068 cpyel(const cpyel &src){
1069 destin=src.destin;
1070 source=src.source;
1071 };
1072
1073 ~cpyel(){
1074 }
1075
1076 libhtmlpp::Element *destin;
1077 libhtmlpp::Element *source;
1078 };
1079
1080 std::stack<cpyel> cpylist;
1081
1082 NEWEL:
1083
1084 if(src->getType()==HtmlEl && dest->getType()==HtmlEl){
1085 ((libhtmlpp::HtmlElement*)dest)->_TagName=(((libhtmlpp::HtmlElement*)src)->_TagName);
1086 for(libhtmlpp::HtmlElement::Attributes *cattr=((libhtmlpp::HtmlElement*)src)->_firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()){
1087 ((libhtmlpp::HtmlElement*)dest)->setAttribute(
1088 std::string(
1089 cattr->_Key.begin(),
1090 cattr->_Key.end()
1091 ),std::string(
1092 cattr->_Value.begin(),
1093 cattr->_Value.end()
1094 )
1095 );
1096 }
1097
1098 if(((libhtmlpp::HtmlElement*)src)->_childElement){
1099 switch(((libhtmlpp::HtmlElement*)src)->_childElement->getType()){
1100 case HtmlEl:
1101 ((libhtmlpp::HtmlElement*)dest)->_childElement=std::make_unique<HtmlElement>();
1102 break;
1103 case TextEl:
1104 ((libhtmlpp::HtmlElement*)dest)->_childElement =std::make_unique<TextElement>();
1105 break;
1106 case CommentEl:
1107 ((libhtmlpp::HtmlElement*)dest)->_childElement = std::make_unique<CommentElement>();
1108 break;
1109 case ScriptEL:
1110 ((libhtmlpp::HtmlElement*)dest)->_childElement = std::make_unique<ScriptElement>();
1111 break;
1112 case SvgEL:
1113 ((libhtmlpp::HtmlElement*)dest)->_childElement = std::make_unique<SvgElement>();
1114 break;
1115 case TextAreaEL:
1116 ((libhtmlpp::HtmlElement*)dest)->_childElement = std::make_unique<TextArea>();
1117 break;
1118 default:
1119 HTMLException ex;
1120 ex[HTMLException::Critical] << "_copy: Unknown html element found !";
1121 throw ex;
1122 }
1123 cpyel childel;
1124 childel.destin=((libhtmlpp::HtmlElement*)dest)->_childElement.get();
1125 childel.source=((libhtmlpp::HtmlElement*)src)->_childElement.get();
1126 cpylist.push(childel);
1127 }
1128 }else if(src->getType()==libhtmlpp::ScriptEL && dest->getType()== libhtmlpp::ScriptEL){
1129 ((libhtmlpp::ScriptElement*)dest)->_TagName=(((libhtmlpp::ScriptElement*)src)->_TagName);
1130 for(libhtmlpp::ScriptElement::Attributes *cattr=((libhtmlpp::ScriptElement*)src)->_firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()){
1131 if(!cattr->_Value.empty()){
1132 ((libhtmlpp::ScriptElement*)dest)->setAttribute(
1133 std::string(
1134 cattr->_Key.begin(),
1135 cattr->_Key.end()
1136 ),std::string(
1137 cattr->_Value.begin(),
1138 cattr->_Value.end()
1139 )
1140 );
1141 }else{
1142 ((libhtmlpp::ScriptElement*)dest)->setAttribute(std::string(cattr->_Key.begin(),cattr->_Key.end()),"");
1143 }
1144 }
1145 ((ScriptElement*)dest)->_Script=(((ScriptElement*)src)->_Script);
1146 }else if(src->getType()==libhtmlpp::SvgEL && dest->getType()== libhtmlpp::SvgEL){
1147 ((libhtmlpp::SvgElement*)dest)->_TagName=(((libhtmlpp::SvgElement*)src)->_TagName);
1148 for(libhtmlpp::SvgElement::Attributes *cattr=((libhtmlpp::SvgElement*)src)->_firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()){
1149 if(!cattr->_Value.empty()){
1150 ((libhtmlpp::SvgElement*)dest)->setAttribute(
1151 std::string(
1152 cattr->_Key.begin(),
1153 cattr->_Key.end()
1154 ),std::string(
1155 cattr->_Value.begin(),
1156 cattr->_Value.end()
1157 )
1158 );
1159 }else{
1160 ((libhtmlpp::SvgElement*)dest)->setAttribute(std::string(cattr->_Key.begin(),cattr->_Key.end()),"");
1161 }
1162 }
1163 ((SvgElement*)dest)->_Svg=(((SvgElement*)src)->_Svg);
1164 }else if(src->getType()==libhtmlpp::TextAreaEL&& dest->getType()== libhtmlpp::TextAreaEL){
1165 ((libhtmlpp::TextArea*)dest)->_TagName=(((libhtmlpp::TextArea*)src)->_TagName);
1166 for(libhtmlpp::TextArea::Attributes *cattr=((libhtmlpp::TextArea*)src)->_firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()){
1167 if(!cattr->_Value.empty()){
1168 ((libhtmlpp::TextArea*)dest)->setAttribute(
1169 std::string(
1170 cattr->_Key.begin(),
1171 cattr->_Key.end()
1172 ),std::string(
1173 cattr->_Value.begin(),
1174 cattr->_Value.end()
1175 )
1176 );
1177 }else{
1178 ((libhtmlpp::TextArea*)dest)->setAttribute(std::string(cattr->_Key.begin(),cattr->_Key.end()),"");
1179 }
1180 }
1181 ((TextArea*)dest)->_Text=(((TextArea*)src)->_Text);
1182 }else if(src->getType()==libhtmlpp::TextEl && dest->getType()== libhtmlpp::TextEl){
1183 ((TextElement*)dest)->_Text=(((TextElement*)src)->_Text);
1184 }else if(src->getType()==libhtmlpp::CommentEl && dest->getType()== libhtmlpp::CommentEl){
1185 ((CommentElement*)dest)->_Comment=(((CommentElement*)src)->_Comment);
1186 }
1187
1188 if(prev)
1189 dest->_prevElement=(Element*)prev;
1190
1191 Element* next=src->nextElement();
1192
1193 if(next){
1194 switch(next->getType()){
1195 case HtmlEl:
1196 dest->_nextElement= std::make_unique<HtmlElement>();
1197 break;
1198 case TextEl:
1199 dest->_nextElement= std::make_unique<TextElement>();
1200 break;
1201 case CommentEl:
1202 dest->_nextElement= std::make_unique<CommentElement>();
1203 break;
1204 case ScriptEL:
1205 dest->_nextElement= std::make_unique<ScriptElement>();
1206 break;
1207 case SvgEL:
1208 dest->_nextElement= std::make_unique<SvgElement>();
1209 break;
1210 case TextAreaEL:
1211 dest->_nextElement= std::make_unique<TextArea>();
1212 break;
1213 default:
1214 HTMLException ex;
1215 ex[HTMLException::Critical] << "_copy: Unknown next html element found !";
1216 throw ex;
1217 }
1218 prev=dest;
1219 src=next;
1220 dest=dest->_nextElement.get();
1221 goto NEWEL;
1222 }
1223
1224 if(!cpylist.empty()){
1225 cpyel childel(cpylist.top());
1226 prev=nullptr;
1227 dest=childel.destin;
1228 src=childel.source;
1229 cpylist.pop();
1230 goto NEWEL;
1231 }
1232 return;
1233 }
1234};
1235
1237 std::unique_ptr<Element> nel;
1238 switch(el->getType()){
1239 case HtmlEl:
1240 nel->_nextElement= std::make_unique<HtmlElement>();
1241 break;
1242 case TextEl:
1243 nel->_nextElement= std::make_unique<TextElement>();
1244 break;
1245 case CommentEl:
1246 nel->_nextElement= std::make_unique<CommentElement>();
1247 break;
1248 case ScriptEL:
1249 nel->_nextElement= std::make_unique<ScriptElement>();
1250 break;
1251 case SvgEL:
1252 nel->_nextElement= std::make_unique<SvgElement>();
1253 break;
1254 case TextAreaEL:
1255 nel->_nextElement= std::make_unique<TextArea>();
1256 break;
1257 default:
1258 HTMLException ex;
1259 ex[HTMLException::Critical] << "_copy: Unknown next html element found !";
1260 throw ex;
1261 }
1262 _copy(nel.get(),el);
1263 std::unique_ptr<Element> prev=std::move(_prevElement->_nextElement);
1264 _prevElement->_nextElement=std::move(nel);
1265 nel->_nextElement=std::move(prev);
1266}
1267
1269 Element *nexel=nullptr,*prev=nullptr;
1270
1271 switch(el->getType()){
1272 case HtmlEl:
1273 _nextElement= std::make_unique<HtmlElement>();
1274 break;
1275 case TextEl:
1276 _nextElement= std::make_unique<TextElement>();
1277 break;
1278 case CommentEl:
1279 _nextElement= std::make_unique<CommentElement>();
1280 break;
1281 case ScriptEL:
1282 _nextElement= std::make_unique<ScriptElement>();
1283 break;
1284 case SvgEL:
1285 _nextElement= std::make_unique<SvgElement>();
1286 break;
1287 case TextAreaEL:
1288 _nextElement= std::make_unique<TextArea>();
1289 break;
1290 default:
1291 HTMLException ex;
1292 ex[HTMLException::Critical] << "_copy: Unknown next html element found !";
1293 throw ex;
1294 }
1295
1296 _copy(_nextElement.get(),el);
1297
1298 nexel=_nextElement.get();
1299
1300 while(nexel){
1301 prev=nexel;
1302 nexel=nexel->nextElement();
1303 }
1304
1305 nexel=prev;
1306
1307}
1308
1310 _copy(this,&hel);
1311 return *this;
1312}
1313
1315 _copy(this,hel);
1316 return *this;
1317}
1318
1320 return _nextElement.get();
1321}
1322
1324 return _prevElement;
1325}
1326
1328 _prevElement=nullptr;
1329 _nextElement=nullptr;
1330}
1331
1333 _prevElement=nullptr;
1334 _nextElement=nullptr;
1335 _copy(this,&el);
1336}
1337
1339 auto cur = std::move(_nextElement);
1340 while (cur) {
1341 cur = std::move(cur->_nextElement);
1342 }
1343};
1344
1346 Element *curel=this;
1347
1348 while(curel){
1349 Element *next=curel->_nextElement.get();
1350
1351 if(curel==el){
1352 if(curel->_prevElement)
1353 curel->_prevElement->_nextElement=std::move(curel->_nextElement);
1354
1355 if(curel->_nextElement)
1356 curel->_nextElement->_prevElement=curel->_prevElement;
1357 curel->_prevElement=nullptr;
1358 curel->_nextElement=nullptr;
1359 }
1360
1361 curel=next;
1362 }
1363}
1364
1365
1368
1370 setText(txt);
1371}
1372
1374 _copy(this,&texel);
1375}
1376
1379
1380
1382 _copy(this,&hel);
1383 return *this;
1384}
1385
1387 _copy(this,hel);
1388 return *this;
1389}
1390
1391void libhtmlpp::TextElement::setText(const std::string& txt){
1392 std::copy(txt.begin(),txt.end(),std::back_inserter(_Text));
1393}
1394
1396 return std::string(_Text.begin(),_Text.end());
1397}
1398
1402
1404 const std::vector<char>& in,
1405 std::unique_ptr<libhtmlpp::Element>& el,
1406 size_t start,
1407 bool &termination
1408){
1409 termination = false;
1410
1411 std::vector<char> buf;
1412 buf.reserve(64);
1413 bool seen_nonws = false;
1414 bool last_was_space = false;
1415
1416 size_t i = start;
1417 while (i < in.size()) {
1418 char c = in[i];
1419 if (c == HTMLTAG_OPEN) {
1420 break;
1421 }
1422
1423 if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
1424 if (seen_nonws) {
1425 if (!last_was_space) {
1426 buf.push_back(' ');
1427 last_was_space = true;
1428 }
1429 }
1430 ++i;
1431 continue;
1432 }
1433
1434 buf.push_back(c);
1435 seen_nonws = true;
1436 last_was_space = false;
1437 ++i;
1438 }
1439
1440 if (!buf.empty()) {
1441 auto text = std::make_unique<TextElement>();
1442 (static_cast<TextElement*>(text.get()))->_Text.insert(
1443 (static_cast<TextElement*>(text.get()))->_Text.end(),
1444 buf.begin(), buf.end()
1445 );
1446 el = std::move(text);
1447 }
1448
1449 return i;
1450}
1451
1452
1455
1457 _copy(this,&comel);
1458}
1459
1462
1463
1465 _copy(this,&hel);
1466 return *this;
1467}
1468
1470 _copy(this,hel);
1471 return *this;
1472}
1473
1474void libhtmlpp::CommentElement::setComment(const std::string& txt){
1475 std::copy(txt.begin(),txt.end(),
1476 std::insert_iterator<std::vector<char>>(_Comment,_Comment.begin()));
1477}
1478
1480 return std::string(_Comment.begin(),_Comment.end());
1481}
1482
1486
1488 const std::vector<char>& in,
1489 std::unique_ptr<Element>& el,
1490 size_t start,
1491 bool& termination
1492){
1493 termination = false;
1494
1495 size_t i = start;
1496 if (i + 3 >= in.size()) return i;
1497
1498 if (!(in[i] == '<' && in[i+1] == '!' && in[i+2] == '-' && in[i+3] == '-')) {
1499 return i;
1500 }
1501
1502 el = std::make_unique<CommentElement>();
1503
1504 i += 4;
1505 const size_t content_begin = i;
1506
1507 while (i + 2 < in.size()) {
1508 if (in[i] == '-' && in[i+1] == '-' && in[i+2] == '>') {
1509 break;
1510 }
1511 ++i;
1512 }
1513
1514 auto* self = static_cast<CommentElement*>(el.get());
1515 if (i > content_begin) {
1516 self->_Comment.insert(self->_Comment.end(),
1517 in.begin() + content_begin,
1518 in.begin() + i);
1519 }
1520
1521 if (i + 2 < in.size()) {
1522 i += 3;
1523 } else {
1524 i = in.size();
1525 }
1526 return i;
1527}
1528
1529
1532
1534 _copy(this,&scriptsrc);
1535}
1536
1539
1540
1542 _copy(this,&hel);
1543 return *this;
1544}
1545
1547 _copy(this,hel);
1548 return *this;
1549}
1550
1551void libhtmlpp::ScriptElement::setScript(const std::string& script){
1552 std::copy(script.begin(),script.end(),std::back_inserter(_Script));
1553}
1554
1556 return std::string(_Script.begin(),_Script.end());
1557}
1558
1562
1564 const std::vector<char>& in,
1565 std::unique_ptr<Element>& el,
1566 size_t start,
1567 bool& termination
1568){
1569termination = false;
1570 el = std::make_unique<ScriptElement>();
1571 auto* self = static_cast<ScriptElement*>(el.get());
1572
1573 size_t i = start;
1574 if (i >= in.size() || in[i] != '<') {
1575 // If it doesn't start with '<', we can't parse a tag here.
1576 return start;
1577 }
1578
1579 // Helper to perform case-insensitive comparison
1580 auto iequals = [](char a, char b) {
1581 return std::tolower(static_cast<unsigned char>(a)) ==
1582 std::tolower(static_cast<unsigned char>(b));
1583 };
1584
1585 // Helper to perform case-insensitive match for a keyword starting at 'pos'
1586 auto match_ci = [&](size_t pos, const char* k) -> bool {
1587 for (size_t j = 0; k[j]; ++j) {
1588 if (pos + j >= in.size() || !iequals(in[pos + j], k[j])) {
1589 return false;
1590 }
1591 }
1592 return true;
1593 };
1594
1595 // --- 1. Validate Opening Tag Name (<script) ---
1596 ++i; // Consume '<'
1597
1598 // Skip leading whitespace after '<'
1599 while (i < in.size() && std::isspace(static_cast<unsigned char>(in[i]))) {
1600 ++i;
1601 }
1602
1603 const char* tag_keyword = "script";
1604 size_t keyword_len = std::char_traits<char>::length(tag_keyword);
1605
1606 if (i + keyword_len >= in.size() || !match_ci(i, tag_keyword)) {
1607 // Tag name doesn't match "script"
1608 // Skip till next '>' and return the position after it.
1609 while (i < in.size() && in[i] != '>') {
1610 ++i;
1611 }
1612 if (i < in.size()) {
1613 ++i; // Consume '>'
1614 }
1615 return i;
1616 }
1617 i += keyword_len; // Consume "script"
1618
1619 // --- 2. Extract Opening Tag and Attributes ---
1620 // Find the closing '>' of the opening tag.
1621 size_t tag_end = i;
1622 while (i < in.size() && in[i] != '>') {
1623 ++i;
1624 }
1625
1626 // Capture the raw opening tag data (including '<script' and attributes) for serialization.
1627 if (i > start && in[i] == '>') {
1628 // Copy data from '<' (start) up to and including '>' (i)
1629 std::vector<char> raw_tag_data(in.begin() + start, in.begin() + i + 1);
1630 self->_serialelize(raw_tag_data);
1631 }
1632
1633 if (i >= in.size() || in[i] != '>') {
1634 // The tag was never closed (e.g., '<script src="..." EOF')
1635 return i;
1636 }
1637
1638 ++i; // Consume '>' and move to content start
1639 size_t content_begin = i;
1640
1641 // --- 3. Extract Script Content (CDATA-like section) ---
1642 for (; i < in.size(); ++i) {
1643 // Look for the start of the closing tag sequence: </script
1644 if (in[i] == '<' && match_ci(i, "</script")) {
1645 size_t content_end = i;
1646
1647 // 3a. Extract content preceding the closing tag
1648 if (content_end > content_begin) {
1649 // FIX: Use std::vector::insert instead of non-existent append
1650 self->_Script.insert(self->_Script.end(),
1651 in.begin() + content_begin,
1652 in.begin() + content_end);
1653 }
1654
1655 // 3b. Find the end of the closing tag: </script>
1656 size_t closing_tag_end_pos = i + keyword_len + 2; // +2 for '</'
1657
1658 // Skip any characters/whitespace between </script and the final '>'
1659 while (closing_tag_end_pos < in.size() && in[closing_tag_end_pos] != '>') {
1660 ++closing_tag_end_pos;
1661 }
1662
1663 if (closing_tag_end_pos < in.size() && in[closing_tag_end_pos] == '>') {
1664 i = closing_tag_end_pos + 1; // Position after '>'
1665 return i;
1666 }
1667
1668 // If we found "</script" but not the final ">", return the last processed position.
1669 return closing_tag_end_pos;
1670 }
1671 }
1672
1673 // --- 4. End of Input Reached ---
1674 // If the input ends without a closing </script> tag, capture the remaining content.
1675 if (in.size() > content_begin) {
1676 // FIX: Use std::vector::insert instead of non-existent append
1677 self->_Script.insert(self->_Script.end(),
1678 in.begin() + content_begin,
1679 in.end());
1680 }
1681
1682 return i;
1683}
1684
1685
1686
1689
1691 _copy(this,&svgsrc);
1692}
1693
1696
1697
1699 _copy(this,&hel);
1700 return *this;
1701}
1702
1704 _copy(this,hel);
1705 return *this;
1706}
1707
1708void libhtmlpp::SvgElement::setSvg(const std::string& script){
1709 std::copy(script.begin(),script.end(),
1710 std::insert_iterator<std::vector<char>>(_Svg,_Svg.begin()));
1711}
1712
1713const std::vector<char>libhtmlpp::SvgElement::getSvg(){
1714 return _Svg;
1715}
1716
1720
1721size_t libhtmlpp::SvgElement::parseElement(const std::vector<char>& in,
1722 std::unique_ptr<libhtmlpp::Element>& el,
1723 size_t start,
1724 bool& termination)
1725{
1726 const size_t startel = start;
1727 termination = false;
1728
1729 const auto begin = in.begin();
1730 if (start >= in.size()) {
1731 HTMLException excp;
1732 throw excp[HTMLException::Error] << "Parsing error: start offset beyond buffer.";
1733 }
1734 auto it_close_angle = std::find(begin + start, in.end(), HTMLTAG_CLOSE);
1735 if (it_close_angle == in.end()) {
1736 HTMLException excp;
1737 throw excp[HTMLException::Error] << "Parsing error: Missing '>' for <svg> open tag.";
1738 }
1739
1740 el = std::make_unique<SvgElement>();
1741 auto* svgEl = static_cast<SvgElement*>(el.get());
1742
1743 {
1744 std::vector<char> tel;
1745 tel.assign(begin + startel, it_close_angle);
1746 svgEl->_serialelize(tel);
1747 }
1748
1749 auto it_content_begin = it_close_angle;
1750 if (it_content_begin != in.end()) ++it_content_begin; // safe increment
1751
1752 static constexpr char kEndTag[] = "</svg>";
1753 auto it_end = std::search(it_content_begin, in.end(),
1754 std::begin(kEndTag), std::end(kEndTag) - 1 /* no '\0' */);
1755 if (it_end == in.end()) {
1756 HTMLException excp;
1757 throw excp[HTMLException::Error] << "Parsing error: Missing </svg> closing tag.";
1758 }
1759
1760 svgEl->_Svg.insert(svgEl->_Svg.end(), it_content_begin, it_end);
1761
1762 const size_t consumed = static_cast<size_t>((it_end - begin) + (std::size(kEndTag) - 1));
1763 return consumed;
1764}
1765
1766
1767
1770
1772 _copy(this,&textsrc);
1773}
1774
1777
1778
1780 _copy(this,&hel);
1781 return *this;
1782}
1783
1785 _copy(this,hel);
1786 return *this;
1787}
1788
1789void libhtmlpp::TextArea::setText(const std::string& text){
1790 std::copy(text.begin(),text.end(),
1791 std::insert_iterator<std::vector<char>>(_Text,_Text.begin()));
1792}
1793
1794const std::vector<char>libhtmlpp::TextArea::getText(){
1795 return _Text;
1796}
1797
1801
1802size_t libhtmlpp::TextArea::parseElement(const std::vector<char>& in,
1803 std::unique_ptr<libhtmlpp::Element>& el,
1804 size_t start,
1805 bool& termination)
1806{
1807 termination = false;
1808
1809 const auto begin = in.begin();
1810 const auto end = in.end();
1811
1812 if (start >= in.size()) {
1813 HTMLException excp;
1814 throw excp[HTMLException::Error] << "Parsing error: start offset beyond buffer.";
1815 }
1816
1817 // ---- helpers -----------------------------------------------------------
1818 auto is_ws = [](unsigned char c) {
1819 return c == ' ' || c == '\t' || c == '\n' || c == '\r';
1820 };
1821 auto tolower_ascii = [](unsigned char c) -> unsigned char {
1822 return (c >= 'A' && c <= 'Z') ? static_cast<unsigned char>(c + 32) : c;
1823 };
1824 auto ieq_prefix = [&](std::vector<char>::const_iterator it,
1825 std::vector<char>::const_iterator it_end,
1826 const char* lit) -> bool
1827 {
1828 for (; *lit; ++lit, ++it) {
1829 if (it == it_end) return false;
1830 if (tolower_ascii(static_cast<unsigned char>(*it)) !=
1831 tolower_ascii(static_cast<unsigned char>(*lit))) {
1832 return false;
1833 }
1834 }
1835 return true;
1836 };
1837
1838 auto it_gt = std::find(begin + start, end, HTMLTAG_CLOSE);
1839 if (it_gt == end) {
1840 HTMLException excp;
1841 throw excp[HTMLException::Error] << "Parsing error: Unclosed <textarea> tag.";
1842 }
1843
1844 el = std::make_unique<TextArea>();
1845 auto* ta = static_cast<TextArea*>(el.get());
1846 {
1847 std::vector<char> tel;
1848 tel.assign(begin + start, it_gt); // opening tag without '>'
1849 ta->_serialelize(tel);
1850 }
1851
1852 auto it = it_gt;
1853 if (it != end) ++it;
1854 const auto content_begin = it;
1855
1856 for (;;) {
1857 auto lt = std::find(it, end, '<');
1858 if (lt == end) {
1859 HTMLException excp;
1860 throw excp[HTMLException::Error] << "Parsing error: Missing </textarea> closing tag.";
1861 }
1862
1863 if (ieq_prefix(lt, end, "</textarea")) {
1864 auto after_head = lt + std::strlen("</textarea");
1865 while (after_head != end && is_ws(static_cast<unsigned char>(*after_head))) {
1866 ++after_head;
1867 }
1868 if (after_head == end) {
1869 HTMLException excp;
1870 throw excp[HTMLException::Error] << "Parsing error: Unclosed </textarea> end tag.";
1871 }
1872 if (*after_head == '>') {
1873 ta->_Text.insert(ta->_Text.end(), content_begin, lt);
1874 const size_t consumed = static_cast<size_t>((after_head - begin) + 1);
1875 return consumed;
1876 }
1877
1878 it = lt + 1;
1879 continue;
1880 }
1881
1882 it = lt + 1;
1883 }
1884}
1885
1886
1887
1891
1901void libhtmlpp::HtmlPage::loadFile(libhtmlpp::HtmlElement &html,const std::string& path){
1902 std::string data;
1903 std::ifstream fs(path);
1904
1905 if(!fs.is_open()){
1906 HTMLException excp;
1907 throw excp[HTMLException::Critical] << "Can't open file: " << path;
1908 }
1909
1910 fs.seekg(std::ios::end);
1911
1912 data.reserve(fs.tellg());
1913
1914 fs.seekg(std::ios::beg);
1915
1916 data.assign((std::istreambuf_iterator<char>(fs)), std::istreambuf_iterator<char>());
1917
1918 fs.close();
1919
1920 _CheckHeader(data);
1921 loadString(html,data);
1922}
1931 HtmlString buf=src;
1932 Element &el=buf.parse();
1933 _copy(&html,&el);
1934}
1935
1937 HtmlString buf=node;
1938 Element &el=buf.parse();
1939 _copy(&html,&el);
1940}
1941
1943 if(!node){
1945 throw excp[libhtmlpp::HTMLException::Critical] << "loadstring: node can't be null !";
1946 }
1947 libhtmlpp::HtmlString buf=*node;
1948 html=(libhtmlpp::HtmlElement&)buf.parse();
1949}
1957void libhtmlpp::HtmlPage::saveFile(libhtmlpp::HtmlElement &html,const std::string& path){
1958 HtmlString data;
1959 std::ofstream fs;
1960
1961 print(html,data);
1962
1963 try{
1964 fs.open(path);
1965 }catch(std::exception &e){
1966 HTMLException excp;
1967 throw excp[HTMLException::Critical] << e.what();
1968 }
1969
1970 fs << data.str();
1971
1972 fs.close();
1973
1974}
1975
1977 return _Html5;
1978}
1979
1980
1981void libhtmlpp::HtmlPage::_CheckHeader(const HtmlString &page){
1982 const char type[] = { '!','D','O','C','T','Y','P','E' };
1983
1984 int i = 0;
1985
1986 bool start=false;
1987
1988 do{
1989 ++i;
1990 if(page[i]== '<'){
1991 if(start==true){
1992 HTMLException excp;
1993 excp[HTMLException::Critical] << "Wrong Header arborting";
1994 throw excp;
1995 }
1996 start=true;
1997 }
1998 } while ( page[i]== '<' || page[i]== '!' || page[i] == ' ');
1999
2000 if (page.size() < 8) {
2001 HTMLException excp;
2002 excp[HTMLException::Critical] << "No Doctype found arborting";
2003 throw excp;
2004 }
2005
2006 while (i < 8) {
2007 if (page[i+1] != type[i]) {
2008 HTMLException excp;
2009 excp[HTMLException::Critical] << "No Doctype found arborting";
2010 throw excp;
2011 }
2012 ++i;
2013 }
2014
2015 do{
2016 ++i;
2017 }while (page[i] == ' ');
2018
2019 const char doctype[] = { 'H','T','M','L' };
2020 size_t tpvl = 4;
2021
2022 const char typevalue4[] = {'P','U','B','L','I','C'};
2023 size_t tpvl4 = 6;
2024
2025 if ((i + tpvl) > page.size()) {
2026 HTMLException excp;
2027 excp[HTMLException::Critical] << "Document to short broken !";
2028 throw excp;
2029 }
2030
2031 size_t ii=0;
2032
2033 while ( ii < tpvl) {
2034 if (tolower(page[i++]) != tolower(doctype[ii++])) {
2035 HTMLException excp;
2036 excp[HTMLException::Critical] << "Doctype header broken or wrong type";
2037 throw excp;
2038 }
2039 }
2040
2041 ii=0;
2042
2043 do{
2044 ++i;
2045 } while (page[i] == ' ');
2046
2047 bool html4=true;
2048
2049 if(i +tpvl4 <page.size()){
2050 while(ii < tpvl4){
2051 if (tolower(page[i++]) != tolower(typevalue4[ii++])) {
2052 html4=false;
2053 }
2054 }
2055 }
2056
2057 _Html5=!html4;
2058
2059}
2067void libhtmlpp::print(const Element &element, HtmlString &output,bool formated) {
2068
2069 const Element *el=&element;
2070
2071 auto isContainer = [](const std::string &tagname) {
2072 for(size_t i=0; i<ContainerTypes.size(); ++i){
2073 if(tagname==ContainerTypes[i])
2074 return true;
2075 }
2076 return false;
2077 };
2078
2079 std::stack<const libhtmlpp::Element*> cpylist;
2080
2081 int lvl=0;
2082
2083 bool virgin=true;
2084
2085 PRINTNEXTEL:
2086
2087 if(formated){
2088 for(int i=0; i<lvl; ++i){
2089 output.append(" ");
2090 }
2091 }
2092
2093 switch(el->getType()){
2094 case HtmlEl:{
2095 output.append("<");
2096 output.append(static_cast<const HtmlElement*>(el)->getTagname());
2097 for (HtmlElement::Attributes* curattr = static_cast<const HtmlElement*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2098 output.append(" ");
2099 std::copy(
2100 curattr->_Key.begin(),
2101 curattr->_Key.end(),
2102 std::back_inserter(output)
2103 );
2104 if(!curattr->_Value.empty()){
2105 output.append("=\"");
2106 std::copy(
2107 curattr->_Value.begin(),
2108 curattr->_Value.end(),
2109 std::back_inserter(output)
2110 );
2111 output.append("\"");
2112 }
2113 }
2114
2115 output.append(">");
2116
2117 if( !formated && virgin ){
2118 output.append("\r\n ");
2119 virgin=false;
2120 }
2121
2122 if (static_cast<const HtmlElement*>(el)->_childElement) {
2123 if(formated)
2124 output.append("\r\n");
2125 cpylist.push(el);
2126 el=static_cast<const HtmlElement*>(el)->_childElement.get();
2127 ++lvl;
2128 goto PRINTNEXTEL;
2129 }
2130
2131 //Container must be always terminated fuck html5
2132 if(isContainer(static_cast<const HtmlElement*>(el)->getTagname())){
2133 output.append("</");
2134 std::copy(
2135 static_cast<const HtmlElement*>(el)->_TagName.begin(),
2136 static_cast<const HtmlElement*>(el)->_TagName.end(),
2137 std::back_inserter(output)
2138 );
2139 output.append(">");
2140 }
2141
2142 if(formated)
2143 output.append("\r\n");
2144
2145 if (el->_nextElement) {
2146 el=el->_nextElement.get();
2147 goto PRINTNEXTEL;
2148 }
2149 }break;
2150
2151 case TextEl :{
2152 std::copy(
2153 static_cast<const TextElement*>(el)->_Text.begin(),
2154 static_cast<const TextElement*>(el)->_Text.end(),
2155 std::back_inserter(output)
2156 );
2157 if(formated)
2158 output.append("\r\n");
2159
2160 if (el->_nextElement) {
2161 el=el->_nextElement.get();
2162 goto PRINTNEXTEL;
2163 }
2164 }break;
2165 case CommentEl: {
2166 output.append("<!--");
2167 std::copy(
2168 static_cast<const CommentElement*>(el)->_Comment.begin(),
2169 static_cast<const CommentElement*>(el)->_Comment.end(),
2170 std::back_inserter(output)
2171 );
2172 output.append("-->");
2173 if(formated)
2174 output.append("\r\n");
2175
2176 if (el->_nextElement) {
2177 el=el->_nextElement.get();
2178 goto PRINTNEXTEL;
2179 }
2180 }break;
2181 case ScriptEL:{
2182 output.append("<");
2183 output.append(static_cast<const ScriptElement*>(el)->getTagname());
2184 for (ScriptElement::Attributes* curattr = static_cast<const ScriptElement*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2185 output.append(" ");
2186 std::copy(
2187 curattr->_Key.begin(),
2188 curattr->_Key.end(),
2189 std::back_inserter(output)
2190 );
2191 if(!curattr->_Value.empty()){
2192 output.append("=\"");
2193 std::copy(
2194 curattr->_Value.begin(),
2195 curattr->_Value.end(),
2196 std::back_inserter(output)
2197 );
2198 output.append("\"");
2199 }
2200 }
2201
2202 output.append(">");
2203 if(formated){
2204 output.append("\r\n");
2205 for(int i=0; i<lvl+1; ++i){
2206 output.append(" ");
2207 }
2208 }
2209 std::copy(
2210 static_cast<const ScriptElement*>(el)->_Script.begin(),
2211 static_cast<const ScriptElement*>(el)->_Script.end(),
2212 std::back_inserter(output)
2213 );
2214 if(formated){
2215 output.append("\r\n");
2216 for(int i=0; i<lvl; ++i){
2217 output.append(" ");
2218 }
2219 }
2220 output.append("</");
2221 output.append(static_cast<const ScriptElement*>(el)->getTagname());
2222 output.append(">");
2223 if(formated)
2224 output.append("\r\n");
2225
2226 if (el->_nextElement) {
2227 el=el->_nextElement.get();
2228 goto PRINTNEXTEL;
2229 }
2230 }break;
2231 case SvgEL:{
2232 output.append("<");
2233 output.append(static_cast<const ScriptElement*>(el)->getTagname());
2234 for (SvgElement::Attributes* curattr = static_cast<const SvgElement*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2235 output.append(" ");
2236 std::copy(
2237 curattr->_Key.begin(),
2238 curattr->_Key.end(),
2239 std::back_inserter(output)
2240 );
2241 if(!curattr->_Value.empty()){
2242 output.append("=\"");
2243 std::copy(
2244 curattr->_Value.begin(),
2245 curattr->_Value.end(),
2246 std::back_inserter(output)
2247 );
2248 output.append("\"");
2249 }
2250 }
2251 output.append(">");
2252
2253 if(formated){
2254 output.append("\r\n");
2255 for(int i=0; i<lvl+1; ++i){
2256 output.append(" ");
2257 }
2258 }
2259 std::copy(
2260 static_cast<const SvgElement*>(el)->_Svg.begin(),
2261 static_cast<const SvgElement*>(el)->_Svg.end(),
2262 std::back_inserter(output)
2263 );
2264 if(formated){
2265 output.append("\r\n");
2266 for(int i=0; i<lvl; ++i){
2267 output.append(" ");
2268 }
2269 }
2270 output.append("</");
2271 output.append(static_cast<const SvgElement*>(el)->getTagname());
2272 output.append(">");
2273 if(formated)
2274 output.append("\r\n");
2275
2276 if (el->_nextElement) {
2277 el=el->_nextElement.get();
2278 goto PRINTNEXTEL;
2279 }
2280 }break;
2281 case TextAreaEL:{
2282 output.append("<");
2283 output.append(static_cast<const TextArea*>(el)->getTagname());
2284 for (TextArea::Attributes* curattr = static_cast<const TextArea*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2285 output.append(" ");
2286 std::copy(
2287 curattr->_Key.begin(),
2288 curattr->_Key.end(),
2289 std::back_inserter(output)
2290 );
2291 if(!curattr->_Value.empty()){
2292 output.append("=\"");
2293 std::copy(
2294 curattr->_Value.begin(),
2295 curattr->_Value.end(),
2296 std::back_inserter(output)
2297 );
2298 output.append("\"");
2299 }
2300 }
2301 output.append(">");
2302 std::copy(
2303 static_cast<const TextArea*>(el)->_Text.begin(),
2304 static_cast<const TextArea*>(el)->_Text.end(),
2305 std::back_inserter(output)
2306 );
2307 output.append("</");
2308 output.append(static_cast<const TextArea*>(el)->getTagname());
2309 output.append(">");
2310 if(formated)
2311 output.append("\r\n");
2312
2313 if (el->_nextElement) {
2314 el=el->_nextElement.get();
2315 goto PRINTNEXTEL;
2316 }
2317 }break;
2318 default:
2319 HTMLException excp;
2320 excp[HTMLException::Error] << "Unkown Elementtype";
2321 throw excp;
2322 break;
2323 }
2324
2325 while(!cpylist.empty()){
2326 el=cpylist.top();
2327
2328 --lvl;
2329
2330 if(formated){
2331 for(int i=0; i<lvl; ++i){
2332 output.append(" ");
2333 }
2334 }
2335
2336 output.append("</");
2337 std::copy(
2338 static_cast<const HtmlElement*>(el)->_TagName.begin(),
2339 static_cast<const HtmlElement*>(el)->_TagName.end(),
2340 std::back_inserter(output)
2341 );
2342 output.append(">");
2343
2344 if(formated)
2345 output.append("\r\n");
2346
2347 cpylist.pop();
2348 if (el->_nextElement) {
2349 el=el->_nextElement.get();
2350 goto PRINTNEXTEL;
2351 }
2352 }
2353}
2354
2356 std::stack <Element*> childs;
2357 const Element *curel=this;
2358 SEARCHBYID:
2359 if(curel->getType()==HtmlEl || curel->getType()== ScriptEL || curel->getType()==SvgEL){
2360 if(((HtmlElement*)curel)->_childElement){
2361 childs.push(((HtmlElement*)curel)->_childElement.get());
2362 }
2363 std::string idname=((HtmlElement*)curel)->getAtributte("id");
2364 if(idname.length()==id.length() && std::equal(id.begin(),id.end(),idname.begin()) ){
2365 return (HtmlElement*)curel;
2366 }
2367 }
2368
2369 if(curel->nextElement()){
2370 curel=curel->nextElement();
2371 goto SEARCHBYID;
2372 }
2373
2374 if(!childs.empty()){
2375 curel=childs.top();
2376 childs.pop();
2377 goto SEARCHBYID;
2378 }
2379 return nullptr;
2380}
2381
2383 std::stack <Element*> childs;
2384 const Element *curel=this;
2385 SEARCHBYTAG:
2386 if(curel->getType()==HtmlEl || curel->getType()== ScriptEL || curel->getType()==SvgEL){
2387 if(((HtmlElement*)curel)->_childElement){
2388 childs.push(((HtmlElement*)curel)->_childElement.get());
2389 }
2390 const std::string tname=((HtmlElement*)curel)->getTagname();
2391 if(!tname.empty() && std::equal(tag.begin(),tag.end(),tname.begin())){
2392 return (HtmlElement*)curel;
2393 }
2394 }
2395
2396 if(curel->nextElement()){
2397 curel=curel->nextElement();
2398 goto SEARCHBYTAG;
2399 }
2400
2401 if(!childs.empty()){
2402 curel=childs.top();
2403 childs.pop();
2404 goto SEARCHBYTAG;
2405 }
2406 return nullptr;
2407}
2408
2410 return _childElement.get();
2411}
2412
2414 return std::string(_Key.begin(), _Key.end());
2415}
2416
2418 return std::string(_Value.begin(), _Value.end());
2419}
2420
2424
2426 return _firstAttr.get();
2427}
2428
2429void libhtmlpp::HtmlElement::setAttribute(const std::string &name, const std::string &value) {
2430 Attributes* cattr = nullptr;
2431
2432 const char forbidden[] = {'\"'};
2433
2434 auto checkForbidden = [forbidden](const std::string &input){
2435 for(size_t i = 0; i<input.length(); ++i){
2436 for(size_t ii=0; ii<sizeof(forbidden[ii]); ii++){
2437 if(input[i]==forbidden[ii]){
2438 return true;
2439 }
2440 }
2441 }
2442 return false;
2443 };
2444
2445 if(checkForbidden(name) || checkForbidden(value)){
2446 HTMLException e;
2447 e[HTMLException::Error] << "setAttribute " << name.c_str() << "forbidden sign is used !";
2448 throw e;
2449 }
2450
2451 for (cattr= _firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()) {
2452 if(name.size() == cattr->_Key.size() && std::equal(name.begin(),name.end(),cattr->_Key.begin())){
2453 cattr->_Value.clear();
2454 std::copy(value.begin(),value.end(),std::back_inserter(cattr->_Value));
2455 return;
2456 }
2457 }
2458 if (_lastAttr){
2459 _lastAttr->_nextAttr = std::make_unique<Attributes>();
2460 _lastAttr = _lastAttr->_nextAttr.get();
2461 }else {
2462 _firstAttr = std::make_unique<Attributes>();
2463 _lastAttr = _firstAttr.get();
2464 }
2465
2466 cattr = _lastAttr;
2467 std::copy(name.begin(),name.end(),std::back_inserter(cattr->_Key) );
2468 std::copy(value.begin(),value.end(),std::back_inserter(cattr->_Value));
2469}
2470
2471void libhtmlpp::HtmlElement::setIntAttribute(const std::string& name, int value) {
2472 char buf[255];
2473 snprintf(buf,255,"%d",value);
2474 setAttribute(name,buf);
2475}
2476
2477const std::string libhtmlpp::HtmlElement::getAtributte(const std::string& name) const {
2478 for (Attributes* curattr = _firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2479
2480 if (curattr->_Key.size() != name.length() ) {
2481 continue;
2482 }
2483
2484 if (std::equal(name.begin(), name.end(), curattr->_Key.begin())) {
2485 return std::string(curattr->_Value.begin(), curattr->_Value.end());
2486 }
2487 }
2488 return "";
2489}
2490
2491int libhtmlpp::HtmlElement::getIntAtributte(const std::string& name) const
2492{
2493 return atoi(getAtributte(name).c_str());
2494}
2495
2497 _nextAttr=nullptr;
2498}
2499
2501 auto cur = std::move(_nextAttr);
2502 while (cur) {
2503 cur = std::move(cur->_nextAttr);
2504 }
2505}
2506
2510
2511
2513 _firstRow=nullptr;
2514 _lastRow=nullptr;
2515 _count = 0;
2516}
2517
2520
2522 std::unique_ptr<Row> newRow = std::make_unique<Row>(row);
2523
2524 if (!_firstRow) {
2525 _firstRow = std::move(newRow);
2526 _lastRow = _firstRow.get();
2527 } else {
2528 _lastRow->_nextRow = std::move(newRow);
2529 _lastRow = _lastRow->_nextRow.get();
2530 }
2531
2532 ++_count;
2533 return *_lastRow;
2534}
2535
2537 if(!_firstRow || _count<pos){
2539 exp[HTMLException::Error] << "HtmlTable: Row at this position won't exists !";
2540 throw exp;
2541 }
2542 size_t cpos=0;
2543 Row *curel=nullptr;
2544 for(curel=_firstRow.get(); curel; curel=curel->_nextRow.get()){
2545 if(cpos==pos)
2546 return *curel;
2547 ++cpos;
2548 }
2549 return *curel;
2550}
2551
2553 element->setTagname("table");
2554 for(Row *crow=_firstRow.get(); crow; crow=crow->_nextRow.get()){
2555 HtmlElement hrow("tr");
2556 for(Column *ccol=crow->_firstColumn.get(); ccol; ccol=ccol->_nextColumn.get() ){
2557 HtmlElement hcol("td");
2558 TextElement cellContent(ccol->Data.c_str());
2559 hcol.appendChild(cellContent);
2560 hrow.appendChild(hcol);
2561 }
2562 element->appendChild(&hrow);
2563 }
2564}
2565
2566
2569
2571 va_list args;
2572 va_start(args,count);
2573
2574 for (int i = 0; i < count; ++i) {
2575 _header << va_arg(args, const char*);
2576 }
2577
2578}
2579
2581 if (pos >= _count)
2582 return;
2583 if (pos == 0) {
2584 _firstRow = std::move(_firstRow->_nextRow);
2585 _lastRow = (pos == (_count - 1)) ? nullptr : _firstRow.get();
2586 } else {
2587 Row *prev = &(*this)[pos - 1];
2588 if (prev->_nextRow) {
2589 prev->_nextRow = std::move(prev->_nextRow->_nextRow);
2590
2591 if (prev->_nextRow.get() == nullptr) {
2592 _lastRow = prev;
2593 }
2594 }
2595 }
2596
2597 --_count;
2598 if (_count == 0) {
2599 _firstRow = nullptr;
2600 _lastRow = nullptr;
2601 }
2602}
2603
2605 _nextColumn=nullptr;
2606}
2607
2609 _nextColumn=nullptr;
2610 Data = col.Data;
2611}
2612
2614 _nextColumn=nullptr;
2615 Data=data;
2616}
2617
2619 _nextColumn=nullptr;
2620 Data=data.str();
2621}
2622
2625
2627 _nextColumn = std::move(col._nextColumn);
2628 Data = std::move(col.Data);
2629}
2630
2632 _firstColumn=nullptr;
2633 _lastColumn=nullptr;
2634 _nextRow=nullptr;
2635 _count=0;
2636}
2637
2640
2642 _firstColumn=nullptr;
2643 _lastColumn=nullptr;
2644 _nextRow=nullptr;
2645 _count=0;
2646
2647 for(Column *curel=row._firstColumn.get(); curel; curel=curel->_nextColumn.get()){
2648 *this << HtmlString(curel->Data);
2649 }
2650}
2651
2653 std::unique_ptr<Column> ptr = std::make_unique<Column>(std::move(col));
2654
2655 if(_firstColumn){
2656 _lastColumn->_nextColumn=std::move(ptr);
2657 _lastColumn=_lastColumn->_nextColumn.get();
2658 }else{
2659 _firstColumn= std::move(ptr);
2660 _lastColumn=_firstColumn.get();
2661 }
2662 ++_count;
2663 return *this;
2664}
2665
2667 std::unique_ptr<Column> ptr = std::make_unique<Column>(col);
2668
2669 if(_firstColumn){
2670 _lastColumn->_nextColumn=std::move(ptr);
2671 _lastColumn=_lastColumn->_nextColumn.get();
2672 }else{
2673 _firstColumn= std::move(ptr);
2674 _lastColumn=_firstColumn.get();
2675 }
2676 ++_count;
2677 return *this;
2678}
2679
2681 Column col(value);
2682 *this << col;
2683 return *this;
2684}
2685
2687 Column col(value);
2688 *this << col;
2689 return *this;
2690}
2691
2692
2694 HtmlString buf;
2695 buf << value;
2696 *this << buf;
2697 return *this;
2698}
2699
2701 char buf[255];
2702 snprintf(buf,255,"%d",value);
2703 return *this << buf;
2704}
2705
2707 if(!_firstColumn || _count<pos){
2709 exp[HTMLException::Error] << "HtmlTable: Column at this position won't exists !";
2710 throw exp;
2711 }
2712 size_t cpos=0;
2713 Column *curel=nullptr;
2714 for(curel=_firstColumn.get(); curel; curel=curel->_nextColumn.get()){
2715 if(cpos==pos)
2716 return *curel;
2717 ++cpos;
2718 }
2719 return *curel;
2720}
2721
2723 Column *dcol=&(*this)[pos];
2724 try{
2725 Column *prev=&(*this)[pos-1];
2726 prev->_nextColumn=std::move(dcol->_nextColumn);
2727 }catch(...){}
2728 --_count;
2729}
2730
2732 _firstColumn.reset();
2733 _lastColumn = nullptr;
2734 _count = 0;
2735}
Leaf node representing an HTML comment ().
Definition html.h:216
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1487
CommentElement & operator=(const Element &hel)
Definition html.cpp:1464
std::vector< char > _Comment
Definition html.h:232
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1056
void setComment(const std::string &txt)
Definition html.cpp:1474
const std::string getComment()
Definition html.cpp:1479
class DocElements * prevel
Definition html.cpp:170
std::unique_ptr< Element > element
Definition html.cpp:167
std::unique_ptr< DocElements > nextel
Definition html.cpp:169
Abstract base class for all nodes in the HTML tree.
Definition html.h:76
virtual void remove(Element *el)
Definition html.cpp:1345
Element * _prevElement
Definition html.h:99
void insertAfter(Element *el)
Definition html.cpp:1268
virtual ~Element()
Definition html.cpp:1338
void insertBefore(Element *el)
Definition html.cpp:1236
Element & operator=(const Element &hel)
Definition html.cpp:1309
std::unique_ptr< Element > _nextElement
Definition html.h:98
virtual int getType() const =0
Definition html.cpp:735
Element * prevElement() const
Definition html.cpp:1323
Element * nextElement() const
Definition html.cpp:1319
int getType() const
Definition html.cpp:2507
const Attributes * firstAttribute() const
Definition html.cpp:2425
HtmlElement * getElementbyTag(const std::string &tag) const
Definition html.cpp:2382
const std::string getAtributte(const std::string &name) const
Definition html.cpp:2477
void _serialelize(const std::vector< char > &in)
Extracts tag name and attributes from a token vector into an HtmlElement.
Definition html.cpp:903
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1013
void appendChild(const Element *el)
Definition html.cpp:783
int getIntAtributte(const std::string &name) const
Definition html.cpp:2491
void setTagname(const std::string &name)
Definition html.cpp:739
void setAttribute(const std::string &name, const std::string &value)
Definition html.cpp:2429
void remove(Element *el)
Definition html.cpp:858
bool operator==(const HtmlElement *hel)
Definition html.cpp:830
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1056
void insertChild(const Element *el)
Definition html.cpp:750
Element * firstChild() const
Definition html.cpp:2409
const std::string getTagname() const
Definition html.cpp:744
HtmlElement * getElementbyID(const std::string &id) const
Definition html.cpp:2355
HtmlElement & operator=(const HtmlElement &hel)
Definition html.cpp:848
std::unique_ptr< Element > _childElement
Definition html.h:169
void setIntAttribute(const std::string &name, int value)
Definition html.cpp:2471
void loadString(libhtmlpp::HtmlElement &html, const std::string &src)
Parses an HTML source string and copies the result into html.
Definition html.cpp:1930
void saveFile(libhtmlpp::HtmlElement &html, const std::string &path)
Serializes an HtmlElement subtree and writes it to a file.
Definition html.cpp:1957
void loadFile(libhtmlpp::HtmlElement &html, const std::string &path)
Loads an HTML file from disk into a given HtmlElement root.
Definition html.cpp:1901
const std::vector< char > & data() const
Definition html.cpp:350
void append(const std::string &src)
Definition html.cpp:230
HtmlString & operator<<(const char *src)
Definition html.cpp:293
HtmlString & operator+=(const std::string &src)
Definition html.cpp:267
size_t size() const
Definition html.cpp:336
char operator[](size_t pos) const
Definition html.cpp:289
void push_back(const char src)
Definition html.cpp:218
void insert(size_t pos, char src)
Definition html.cpp:253
const char * c_str() const
Definition html.cpp:346
size_t length() const
Definition html.cpp:332
libhtmlpp::Element & parse()
Parses the current buffer into a DOM-like tree and returns the root element.
Definition html.cpp:355
const char * operator*()
Definition html.cpp:328
HtmlString & operator=(const std::string &src)
Definition html.cpp:277
const std::string str() const
Definition html.cpp:340
Row & operator<<(Column &&col)
Definition html.cpp:2652
Column & operator[](size_t pos)
Definition html.cpp:2706
void delColumn(size_t pos)
Definition html.cpp:2722
Row & operator[](size_t pos)
Definition html.cpp:2536
Row & operator<<(const Row &row)
Definition html.cpp:2521
void deleteRow(size_t pos)
Definition html.cpp:2580
void parse(HtmlElement *element)
Definition html.cpp:2567
void insert(HtmlElement *element)
Definition html.cpp:2552
void setHeader(int count,...)
Definition html.cpp:2570
Element representing a <script> tag and its text content.
Definition html.h:241
void setScript(const std::string &txt)
Definition html.cpp:1551
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1563
ScriptElement & operator=(const Element &hel)
Definition html.cpp:1541
std::vector< char > _Script
Definition html.h:265
const std::string getScript()
Definition html.cpp:1555
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1056
Element representing an embedded <svg> tag and its attributes/content.
Definition html.h:274
int getType() const
Definition html.cpp:1717
SvgElement & operator=(const Element &hel)
Definition html.cpp:1698
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1721
const std::vector< char > getSvg()
Definition html.cpp:1713
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1056
std::vector< char > _Svg
Definition html.h:298
void setSvg(const std::string &svg)
Definition html.cpp:1708
Element representing an embedded <textarea> tag and its attributes/content.
Definition html.h:308
std::vector< char > _Text
Definition html.h:332
TextArea & operator=(const Element &hel)
Definition html.cpp:1779
const std::vector< char > getText()
Definition html.cpp:1794
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1056
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1802
int getType() const
Definition html.cpp:1798
void setText(const std::string &text)
Definition html.cpp:1789
Leaf node representing plain text content of an HTML document.
Definition html.h:189
TextElement & operator=(const Element &hel)
Definition html.cpp:1381
std::vector< char > _Text
Definition html.h:207
void setText(const std::string &txt)
Definition html.cpp:1391
int getType() const
Definition html.cpp:1399
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1056
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1403
const std::string getText()
Definition html.cpp:1395
#define HTMLTAG_TERMINATE
Definition html.cpp:52
#define HTMLTAG_CLOSE
Definition html.cpp:53
#define HTMLTAG_OPEN
Definition html.cpp:51
std::ostream & operator<<(std::ostream &os, const libhtmlpp::HtmlString &p)
Streams an HtmlString to an output stream using its underlying string.
Definition html.cpp:667
void loadString(libhtmlpp::HtmlElement &html, const libhtmlpp::HtmlString *node)
Definition html.cpp:1942
Public declarations for libhtmlpp HTML element types and utilities.
Core namespace for the libhtmlpp HTML parsing and printing library.
Definition css.h:34
@ TextEl
Definition html.h:65
@ ScriptEL
Definition html.h:68
@ HtmlEl
Definition html.h:66
@ TextAreaEL
Definition html.h:70
@ SvgEL
Definition html.h:69
@ CommentEl
Definition html.h:67
void print(const Element &element, HtmlString &output, bool formated=false)
Serializes an element (and its subtree) into an HtmlString.
Definition html.cpp:2067
void HtmlEncode(const std::string &input, std::string &output)
Encodes special HTML characters in a string and writes into std::string.
Definition html.cpp:672
const std::array< std::string_view, 100 > ContainerTypes
Definition html.cpp:62
void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1056
const char * HtmlSigns[][2]
Definition encode.h:31
void HtmlDecode(const std::string &input, HtmlString &output)
Decodes special HTML characters in a string and appends to an HtmlString.
Definition html.cpp:706
const std::string getValue() const
Definition html.cpp:2417
Attributes * nextAttribute() const
Definition html.cpp:2421
const std::string getKey() const
Definition html.cpp:2413