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 // Emit <!DOCTYPE html> when the root element is <html>
2072 if (el->getType() == HtmlEl) {
2073 const std::string &tag = static_cast<const HtmlElement*>(el)->getTagname();
2074 if (tag == "html") {
2075 output.append("<!DOCTYPE html>");
2076 if (formated)
2077 output.append("\n");
2078 }
2079 }
2080
2081 auto isContainer = [](const std::string &tagname) {
2082 for(size_t i=0; i<ContainerTypes.size(); ++i){
2083 if(tagname==ContainerTypes[i])
2084 return true;
2085 }
2086 return false;
2087 };
2088
2089 std::stack<const libhtmlpp::Element*> cpylist;
2090
2091 int lvl=0;
2092
2093 bool virgin=true;
2094
2095 PRINTNEXTEL:
2096
2097 if(formated){
2098 for(int i=0; i<lvl; ++i){
2099 output.append(" ");
2100 }
2101 }
2102
2103 switch(el->getType()){
2104 case HtmlEl:{
2105 output.append("<");
2106 output.append(static_cast<const HtmlElement*>(el)->getTagname());
2107 for (HtmlElement::Attributes* curattr = static_cast<const HtmlElement*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2108 output.append(" ");
2109 std::copy(
2110 curattr->_Key.begin(),
2111 curattr->_Key.end(),
2112 std::back_inserter(output)
2113 );
2114 if(!curattr->_Value.empty()){
2115 output.append("=\"");
2116 std::copy(
2117 curattr->_Value.begin(),
2118 curattr->_Value.end(),
2119 std::back_inserter(output)
2120 );
2121 output.append("\"");
2122 }
2123 }
2124
2125 output.append(">");
2126
2127 if( !formated && virgin ){
2128 output.append("\r\n ");
2129 virgin=false;
2130 }
2131
2132 if (static_cast<const HtmlElement*>(el)->_childElement) {
2133 if(formated)
2134 output.append("\r\n");
2135 cpylist.push(el);
2136 el=static_cast<const HtmlElement*>(el)->_childElement.get();
2137 ++lvl;
2138 goto PRINTNEXTEL;
2139 }
2140
2141 //Container must be always terminated fuck html5
2142 if(isContainer(static_cast<const HtmlElement*>(el)->getTagname())){
2143 output.append("</");
2144 std::copy(
2145 static_cast<const HtmlElement*>(el)->_TagName.begin(),
2146 static_cast<const HtmlElement*>(el)->_TagName.end(),
2147 std::back_inserter(output)
2148 );
2149 output.append(">");
2150 }
2151
2152 if(formated)
2153 output.append("\r\n");
2154
2155 if (el->_nextElement) {
2156 el=el->_nextElement.get();
2157 goto PRINTNEXTEL;
2158 }
2159 }break;
2160
2161 case TextEl :{
2162 std::copy(
2163 static_cast<const TextElement*>(el)->_Text.begin(),
2164 static_cast<const TextElement*>(el)->_Text.end(),
2165 std::back_inserter(output)
2166 );
2167 if(formated)
2168 output.append("\r\n");
2169
2170 if (el->_nextElement) {
2171 el=el->_nextElement.get();
2172 goto PRINTNEXTEL;
2173 }
2174 }break;
2175 case CommentEl: {
2176 output.append("<!--");
2177 std::copy(
2178 static_cast<const CommentElement*>(el)->_Comment.begin(),
2179 static_cast<const CommentElement*>(el)->_Comment.end(),
2180 std::back_inserter(output)
2181 );
2182 output.append("-->");
2183 if(formated)
2184 output.append("\r\n");
2185
2186 if (el->_nextElement) {
2187 el=el->_nextElement.get();
2188 goto PRINTNEXTEL;
2189 }
2190 }break;
2191 case ScriptEL:{
2192 output.append("<");
2193 output.append(static_cast<const ScriptElement*>(el)->getTagname());
2194 for (ScriptElement::Attributes* curattr = static_cast<const ScriptElement*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2195 output.append(" ");
2196 std::copy(
2197 curattr->_Key.begin(),
2198 curattr->_Key.end(),
2199 std::back_inserter(output)
2200 );
2201 if(!curattr->_Value.empty()){
2202 output.append("=\"");
2203 std::copy(
2204 curattr->_Value.begin(),
2205 curattr->_Value.end(),
2206 std::back_inserter(output)
2207 );
2208 output.append("\"");
2209 }
2210 }
2211
2212 output.append(">");
2213 if(formated){
2214 output.append("\r\n");
2215 for(int i=0; i<lvl+1; ++i){
2216 output.append(" ");
2217 }
2218 }
2219 std::copy(
2220 static_cast<const ScriptElement*>(el)->_Script.begin(),
2221 static_cast<const ScriptElement*>(el)->_Script.end(),
2222 std::back_inserter(output)
2223 );
2224 if(formated){
2225 output.append("\r\n");
2226 for(int i=0; i<lvl; ++i){
2227 output.append(" ");
2228 }
2229 }
2230 output.append("</");
2231 output.append(static_cast<const ScriptElement*>(el)->getTagname());
2232 output.append(">");
2233 if(formated)
2234 output.append("\r\n");
2235
2236 if (el->_nextElement) {
2237 el=el->_nextElement.get();
2238 goto PRINTNEXTEL;
2239 }
2240 }break;
2241 case SvgEL:{
2242 output.append("<");
2243 output.append(static_cast<const ScriptElement*>(el)->getTagname());
2244 for (SvgElement::Attributes* curattr = static_cast<const SvgElement*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2245 output.append(" ");
2246 std::copy(
2247 curattr->_Key.begin(),
2248 curattr->_Key.end(),
2249 std::back_inserter(output)
2250 );
2251 if(!curattr->_Value.empty()){
2252 output.append("=\"");
2253 std::copy(
2254 curattr->_Value.begin(),
2255 curattr->_Value.end(),
2256 std::back_inserter(output)
2257 );
2258 output.append("\"");
2259 }
2260 }
2261 output.append(">");
2262
2263 if(formated){
2264 output.append("\r\n");
2265 for(int i=0; i<lvl+1; ++i){
2266 output.append(" ");
2267 }
2268 }
2269 std::copy(
2270 static_cast<const SvgElement*>(el)->_Svg.begin(),
2271 static_cast<const SvgElement*>(el)->_Svg.end(),
2272 std::back_inserter(output)
2273 );
2274 if(formated){
2275 output.append("\r\n");
2276 for(int i=0; i<lvl; ++i){
2277 output.append(" ");
2278 }
2279 }
2280 output.append("</");
2281 output.append(static_cast<const SvgElement*>(el)->getTagname());
2282 output.append(">");
2283 if(formated)
2284 output.append("\r\n");
2285
2286 if (el->_nextElement) {
2287 el=el->_nextElement.get();
2288 goto PRINTNEXTEL;
2289 }
2290 }break;
2291 case TextAreaEL:{
2292 output.append("<");
2293 output.append(static_cast<const TextArea*>(el)->getTagname());
2294 for (TextArea::Attributes* curattr = static_cast<const TextArea*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2295 output.append(" ");
2296 std::copy(
2297 curattr->_Key.begin(),
2298 curattr->_Key.end(),
2299 std::back_inserter(output)
2300 );
2301 if(!curattr->_Value.empty()){
2302 output.append("=\"");
2303 std::copy(
2304 curattr->_Value.begin(),
2305 curattr->_Value.end(),
2306 std::back_inserter(output)
2307 );
2308 output.append("\"");
2309 }
2310 }
2311 output.append(">");
2312 std::copy(
2313 static_cast<const TextArea*>(el)->_Text.begin(),
2314 static_cast<const TextArea*>(el)->_Text.end(),
2315 std::back_inserter(output)
2316 );
2317 output.append("</");
2318 output.append(static_cast<const TextArea*>(el)->getTagname());
2319 output.append(">");
2320 if(formated)
2321 output.append("\r\n");
2322
2323 if (el->_nextElement) {
2324 el=el->_nextElement.get();
2325 goto PRINTNEXTEL;
2326 }
2327 }break;
2328 default:
2329 HTMLException excp;
2330 excp[HTMLException::Error] << "Unkown Elementtype";
2331 throw excp;
2332 break;
2333 }
2334
2335 while(!cpylist.empty()){
2336 el=cpylist.top();
2337
2338 --lvl;
2339
2340 if(formated){
2341 for(int i=0; i<lvl; ++i){
2342 output.append(" ");
2343 }
2344 }
2345
2346 output.append("</");
2347 std::copy(
2348 static_cast<const HtmlElement*>(el)->_TagName.begin(),
2349 static_cast<const HtmlElement*>(el)->_TagName.end(),
2350 std::back_inserter(output)
2351 );
2352 output.append(">");
2353
2354 if(formated)
2355 output.append("\r\n");
2356
2357 cpylist.pop();
2358 if (el->_nextElement) {
2359 el=el->_nextElement.get();
2360 goto PRINTNEXTEL;
2361 }
2362 }
2363}
2364
2366 std::stack <Element*> childs;
2367 const Element *curel=this;
2368 SEARCHBYID:
2369 if(curel->getType()==HtmlEl || curel->getType()== ScriptEL || curel->getType()==SvgEL){
2370 if(((HtmlElement*)curel)->_childElement){
2371 childs.push(((HtmlElement*)curel)->_childElement.get());
2372 }
2373 std::string idname=((HtmlElement*)curel)->getAtributte("id");
2374 if(idname.length()==id.length() && std::equal(id.begin(),id.end(),idname.begin()) ){
2375 return (HtmlElement*)curel;
2376 }
2377 }
2378
2379 if(curel->nextElement()){
2380 curel=curel->nextElement();
2381 goto SEARCHBYID;
2382 }
2383
2384 if(!childs.empty()){
2385 curel=childs.top();
2386 childs.pop();
2387 goto SEARCHBYID;
2388 }
2389 return nullptr;
2390}
2391
2393 std::stack <Element*> childs;
2394 const Element *curel=this;
2395 SEARCHBYTAG:
2396 if(curel->getType()==HtmlEl || curel->getType()== ScriptEL || curel->getType()==SvgEL){
2397 if(((HtmlElement*)curel)->_childElement){
2398 childs.push(((HtmlElement*)curel)->_childElement.get());
2399 }
2400 const std::string tname=((HtmlElement*)curel)->getTagname();
2401 if(!tname.empty() && std::equal(tag.begin(),tag.end(),tname.begin())){
2402 return (HtmlElement*)curel;
2403 }
2404 }
2405
2406 if(curel->nextElement()){
2407 curel=curel->nextElement();
2408 goto SEARCHBYTAG;
2409 }
2410
2411 if(!childs.empty()){
2412 curel=childs.top();
2413 childs.pop();
2414 goto SEARCHBYTAG;
2415 }
2416 return nullptr;
2417}
2418
2420 return _childElement.get();
2421}
2422
2424 return std::string(_Key.begin(), _Key.end());
2425}
2426
2428 return std::string(_Value.begin(), _Value.end());
2429}
2430
2434
2436 return _firstAttr.get();
2437}
2438
2439void libhtmlpp::HtmlElement::setAttribute(const std::string &name, const std::string &value) {
2440 Attributes* cattr = nullptr;
2441
2442 const char forbidden[] = {'\"'};
2443
2444 auto checkForbidden = [forbidden](const std::string &input){
2445 for(size_t i = 0; i<input.length(); ++i){
2446 for(size_t ii=0; ii<sizeof(forbidden[ii]); ii++){
2447 if(input[i]==forbidden[ii]){
2448 return true;
2449 }
2450 }
2451 }
2452 return false;
2453 };
2454
2455 if(checkForbidden(name) || checkForbidden(value)){
2456 HTMLException e;
2457 e[HTMLException::Error] << "setAttribute " << name.c_str() << "forbidden sign is used !";
2458 throw e;
2459 }
2460
2461 for (cattr= _firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()) {
2462 if(name.size() == cattr->_Key.size() && std::equal(name.begin(),name.end(),cattr->_Key.begin())){
2463 cattr->_Value.clear();
2464 std::copy(value.begin(),value.end(),std::back_inserter(cattr->_Value));
2465 return;
2466 }
2467 }
2468 if (_lastAttr){
2469 _lastAttr->_nextAttr = std::make_unique<Attributes>();
2470 _lastAttr = _lastAttr->_nextAttr.get();
2471 }else {
2472 _firstAttr = std::make_unique<Attributes>();
2473 _lastAttr = _firstAttr.get();
2474 }
2475
2476 cattr = _lastAttr;
2477 std::copy(name.begin(),name.end(),std::back_inserter(cattr->_Key) );
2478 std::copy(value.begin(),value.end(),std::back_inserter(cattr->_Value));
2479}
2480
2481void libhtmlpp::HtmlElement::setIntAttribute(const std::string& name, int value) {
2482 char buf[255];
2483 snprintf(buf,255,"%d",value);
2484 setAttribute(name,buf);
2485}
2486
2487const std::string libhtmlpp::HtmlElement::getAtributte(const std::string& name) const {
2488 for (Attributes* curattr = _firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2489
2490 if (curattr->_Key.size() != name.length() ) {
2491 continue;
2492 }
2493
2494 if (std::equal(name.begin(), name.end(), curattr->_Key.begin())) {
2495 return std::string(curattr->_Value.begin(), curattr->_Value.end());
2496 }
2497 }
2498 return "";
2499}
2500
2501int libhtmlpp::HtmlElement::getIntAtributte(const std::string& name) const
2502{
2503 return atoi(getAtributte(name).c_str());
2504}
2505
2507 _nextAttr=nullptr;
2508}
2509
2511 auto cur = std::move(_nextAttr);
2512 while (cur) {
2513 cur = std::move(cur->_nextAttr);
2514 }
2515}
2516
2520
2521
2523 _firstRow=nullptr;
2524 _lastRow=nullptr;
2525 _count = 0;
2526}
2527
2530
2532 std::unique_ptr<Row> newRow = std::make_unique<Row>(row);
2533
2534 if (!_firstRow) {
2535 _firstRow = std::move(newRow);
2536 _lastRow = _firstRow.get();
2537 } else {
2538 _lastRow->_nextRow = std::move(newRow);
2539 _lastRow = _lastRow->_nextRow.get();
2540 }
2541
2542 ++_count;
2543 return *_lastRow;
2544}
2545
2547 if(!_firstRow || _count<pos){
2549 exp[HTMLException::Error] << "HtmlTable: Row at this position won't exists !";
2550 throw exp;
2551 }
2552 size_t cpos=0;
2553 Row *curel=nullptr;
2554 for(curel=_firstRow.get(); curel; curel=curel->_nextRow.get()){
2555 if(cpos==pos)
2556 return *curel;
2557 ++cpos;
2558 }
2559 return *curel;
2560}
2561
2563 element->setTagname("table");
2564 for(Row *crow=_firstRow.get(); crow; crow=crow->_nextRow.get()){
2565 HtmlElement hrow("tr");
2566 for(Column *ccol=crow->_firstColumn.get(); ccol; ccol=ccol->_nextColumn.get() ){
2567 HtmlElement hcol("td");
2568 TextElement cellContent(ccol->Data.c_str());
2569 hcol.appendChild(cellContent);
2570 hrow.appendChild(hcol);
2571 }
2572 element->appendChild(&hrow);
2573 }
2574}
2575
2576
2579
2581 va_list args;
2582 va_start(args,count);
2583
2584 for (int i = 0; i < count; ++i) {
2585 _header << va_arg(args, const char*);
2586 }
2587
2588}
2589
2591 if (pos >= _count)
2592 return;
2593 if (pos == 0) {
2594 _firstRow = std::move(_firstRow->_nextRow);
2595 _lastRow = (pos == (_count - 1)) ? nullptr : _firstRow.get();
2596 } else {
2597 Row *prev = &(*this)[pos - 1];
2598 if (prev->_nextRow) {
2599 prev->_nextRow = std::move(prev->_nextRow->_nextRow);
2600
2601 if (prev->_nextRow.get() == nullptr) {
2602 _lastRow = prev;
2603 }
2604 }
2605 }
2606
2607 --_count;
2608 if (_count == 0) {
2609 _firstRow = nullptr;
2610 _lastRow = nullptr;
2611 }
2612}
2613
2615 _nextColumn=nullptr;
2616}
2617
2619 _nextColumn=nullptr;
2620 Data = col.Data;
2621}
2622
2624 _nextColumn=nullptr;
2625 Data=data;
2626}
2627
2629 _nextColumn=nullptr;
2630 Data=data.str();
2631}
2632
2635
2637 _nextColumn = std::move(col._nextColumn);
2638 Data = std::move(col.Data);
2639}
2640
2642 _firstColumn=nullptr;
2643 _lastColumn=nullptr;
2644 _nextRow=nullptr;
2645 _count=0;
2646}
2647
2650
2652 _firstColumn=nullptr;
2653 _lastColumn=nullptr;
2654 _nextRow=nullptr;
2655 _count=0;
2656
2657 for(Column *curel=row._firstColumn.get(); curel; curel=curel->_nextColumn.get()){
2658 *this << HtmlString(curel->Data);
2659 }
2660}
2661
2663 std::unique_ptr<Column> ptr = std::make_unique<Column>(std::move(col));
2664
2665 if(_firstColumn){
2666 _lastColumn->_nextColumn=std::move(ptr);
2667 _lastColumn=_lastColumn->_nextColumn.get();
2668 }else{
2669 _firstColumn= std::move(ptr);
2670 _lastColumn=_firstColumn.get();
2671 }
2672 ++_count;
2673 return *this;
2674}
2675
2677 std::unique_ptr<Column> ptr = std::make_unique<Column>(col);
2678
2679 if(_firstColumn){
2680 _lastColumn->_nextColumn=std::move(ptr);
2681 _lastColumn=_lastColumn->_nextColumn.get();
2682 }else{
2683 _firstColumn= std::move(ptr);
2684 _lastColumn=_firstColumn.get();
2685 }
2686 ++_count;
2687 return *this;
2688}
2689
2691 Column col(value);
2692 *this << col;
2693 return *this;
2694}
2695
2697 Column col(value);
2698 *this << col;
2699 return *this;
2700}
2701
2702
2704 HtmlString buf;
2705 buf << value;
2706 *this << buf;
2707 return *this;
2708}
2709
2711 char buf[255];
2712 snprintf(buf,255,"%d",value);
2713 return *this << buf;
2714}
2715
2717 if(!_firstColumn || _count<pos){
2719 exp[HTMLException::Error] << "HtmlTable: Column at this position won't exists !";
2720 throw exp;
2721 }
2722 size_t cpos=0;
2723 Column *curel=nullptr;
2724 for(curel=_firstColumn.get(); curel; curel=curel->_nextColumn.get()){
2725 if(cpos==pos)
2726 return *curel;
2727 ++cpos;
2728 }
2729 return *curel;
2730}
2731
2733 Column *dcol=&(*this)[pos];
2734 try{
2735 Column *prev=&(*this)[pos-1];
2736 prev->_nextColumn=std::move(dcol->_nextColumn);
2737 }catch(...){}
2738 --_count;
2739}
2740
2742 _firstColumn.reset();
2743 _lastColumn = nullptr;
2744 _count = 0;
2745}
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:2517
const Attributes * firstAttribute() const
Definition html.cpp:2435
HtmlElement * getElementbyTag(const std::string &tag) const
Definition html.cpp:2392
const std::string getAtributte(const std::string &name) const
Definition html.cpp:2487
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:2501
void setTagname(const std::string &name)
Definition html.cpp:739
void setAttribute(const std::string &name, const std::string &value)
Definition html.cpp:2439
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:2419
const std::string getTagname() const
Definition html.cpp:744
HtmlElement * getElementbyID(const std::string &id) const
Definition html.cpp:2365
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:2481
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:2662
Column & operator[](size_t pos)
Definition html.cpp:2716
void delColumn(size_t pos)
Definition html.cpp:2732
Row & operator[](size_t pos)
Definition html.cpp:2546
Row & operator<<(const Row &row)
Definition html.cpp:2531
void deleteRow(size_t pos)
Definition html.cpp:2590
void parse(HtmlElement *element)
Definition html.cpp:2577
void insert(HtmlElement *element)
Definition html.cpp:2562
void setHeader(int count,...)
Definition html.cpp:2580
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:2427
Attributes * nextAttribute() const
Definition html.cpp:2431
const std::string getKey() const
Definition html.cpp:2423