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 // append()/push_back()/etc. maintain a trailing '\0' sentinel in _Data so
338 // c_str() stays valid between calls (pop it, write new content, push it
339 // back) — that byte is a private bookkeeping detail, not real content,
340 // so it must not be counted here. Without this, every consumer that
341 // trusts size()/str() to describe the actual string (e.g. building a
342 // larger buffer by concatenating multiple HtmlString results) ends up
343 // with a genuine embedded NUL baked into otherwise-clean text, which
344 // then silently truncates any later strlen()-based use of that buffer.
345 if (!_Data.empty() && _Data.back() == '\0')
346 return _Data.size() - 1;
347 return _Data.size();
348}
349
350const std::string libhtmlpp::HtmlString::str() const{
351 if(_Data.data())
352 return std::string(_Data.begin(), _Data.begin() + static_cast<long>(size()));
353 return "";
354}
355
357 return _Data.data();
358}
359
360const std::vector<char>& libhtmlpp::HtmlString::data() const{
361 return _Data;
362}
363
364
366 HTMLException excp;
367 _buildTree();
368 return *_rootEl;
369}
370
371void libhtmlpp::HtmlString::_buildtreenode(
372 DocElements *firstel,
374 std::unique_ptr<Element> &html)
375{
376 if (!firstel) {
377 HTMLException excp;
378 excp[HTMLException::Error] << "No start Element!";
379 throw excp;
380 }
381
382 struct Frame {
383 DocElements *open; // Opener-DocElement (Start-Tag)
384 DocElements *close; // passender Terminator-DocElement (End-Tag)
385 const DocElements *outer_end; // Grenze der aktuellen Ebene
386 Element *outer_prev_el; // letztes bereits eingebautes Geschwister der äußeren Ebene
387 };
388 std::stack<Frame> stack;
389
390 DocElements *start = firstel;
391 const DocElements *end = lastel; // nullptr bedeutet: bis Ketten-Ende
392
393 Element *prev_el_in_tree = nullptr; // zuletzt in den Baum eingebautes Element
394
395 auto checkContainer = [&](const std::string &tag) {
396 for (size_t i = 0; i < ContainerTypes.size(); ++i) {
397 if (tag == ContainerTypes[i]) return true;
398 }
399 return false;
400 };
401
402 // Leere DocElements überspringen (z. B. Kommentare/Text, die nicht als element abgebildet sind)
403 auto skip_empty = [](DocElements *cur, const DocElements *stop) -> DocElements* {
404 while (cur && cur != stop && (!cur->element)) {
405 cur = cur->nextel.get();
406 }
407 return cur;
408 };
409
410 // Finde zum gegebenen Start-Tag dessen passenden Terminator in [open->nextel, bound).
411 auto find_terminator = [&skip_empty, checkContainer](DocElements *open, const DocElements *bound) -> DocElements* {
412 if (!open || !open->element || open->terminator ||
413 open->element->getType() != HtmlEl) return nullptr;
414
415 const std::string &tag = static_cast<HtmlElement*>(open->element.get())->getTagname();
416 int nest = 0;
417 DocElements *cur = open->nextel.get();
418
419 while (cur && cur != bound) {
420 cur = skip_empty(cur, bound);
421 if (!cur || cur == bound) break;
422
423 if (cur->element && cur->element->getType() == HtmlEl) {
424 const std::string &curtag = static_cast<HtmlElement*>(cur->element.get())->getTagname();
425 if (curtag == tag) {
426 if (cur->terminator) {
427 if (nest == 0) return cur; // passendes End-Tag gefunden
428 --nest;
429 } else {
430 ++nest;
431 }
432 }
433 }
434 cur = cur->nextel.get();
435 }
436
437 // Wenn ein Container nicht geschlossen wurde: Warnung ignorieren oder als einfaches Tag behandeln
438 if (checkContainer(tag)) {
439 // Exception entfernt für nachsichtigeres Parsing
440 // return nullptr;
441 }
442 return nullptr;
443 };
444
445 for (;;) {
446 // bis zum nächsten sinnvollen DocElement laufen
447 start = skip_empty(start, end);
448
449 // Terminator-Knoten als eigenständige Nodes überspringen
450 if (start && start != end && start->terminator) {
451 start = start->nextel.get();
452 continue;
453 }
454
455 // Ende der aktuellen Ebene erreicht?
456 if (!start || start == end) {
457 if (stack.empty()) {
458 // Ganz oben: Root setzen (falls vorhanden)
459 if (firstel->element) {
460 html = std::move(firstel->element);
461 }
462 return;
463 }
464
465 // Frame schließen: Kinderbereich [open->nextel, close) einsammeln
466 Frame fr = stack.top(); stack.pop();
467 HtmlElement *opener_el = static_cast<HtmlElement*>(fr.open->element.get());
468
469 // Alle Kinder zwischen open und close verketten
470 Element* last_child_in_chain = nullptr;
471 {
472 DocElements* cur = fr.open->nextel.get();
473 // bis zum ersten brauchbaren Kind
474 while (cur && cur != fr.close && (!cur->element || cur->terminator)) {
475 cur = cur->nextel.get();
476 }
477
478 // alle nicht-leeren, nicht-Terminierer bis vor close anbinden
479 while (cur && cur != fr.close) {
480 if (cur->element && !cur->terminator) {
481 if (!opener_el->_childElement) {
482 opener_el->_childElement = std::move(cur->element);
483 last_child_in_chain = opener_el->_childElement.get();
484 } else {
485 last_child_in_chain->_nextElement = std::move(cur->element);
486 // _prev (falls genutzt) setzen
487 last_child_in_chain->_nextElement->_prevElement = last_child_in_chain;
488 last_child_in_chain = last_child_in_chain->_nextElement.get();
489 }
490 }
491 cur = cur->nextel.get();
492 // leere/terminator Knoten überspringen
493 while (cur && cur != fr.close && (!cur->element || cur->terminator)) {
494 cur = cur->nextel.get();
495 }
496 }
497 }
498
499 // dieses Container-Element ist nun das "aktuelle" in der äußeren Ebene
500 prev_el_in_tree = opener_el;
501
502 // Wenn es bereits ein vorheriges Geschwister in der äußeren Ebene gibt: verketten
503 if (fr.outer_prev_el) {
504 prev_el_in_tree->_prevElement = fr.outer_prev_el;
505 fr.outer_prev_el->_nextElement = std::move(fr.open->element);
506 prev_el_in_tree = fr.outer_prev_el->_nextElement.get();
507 }
508
509 // für die äußere Ebene fortsetzen
510 prev_el_in_tree = opener_el;
511 start = (fr.close ? fr.close->nextel.get() : nullptr);
512 end = fr.outer_end;
513
514 continue;
515 }
516
517 // Start-Tag eines HTML-Elements: passenden Terminator suchen → in den Stack tauchen
518 if (start->element && !start->terminator && start->element->getType() == HtmlEl) {
519 if (DocElements *close = find_terminator(start, end)) {
520 // neuen Rahmen für diesen Container aufmachen
521 stack.push(Frame{start, close, end, prev_el_in_tree});
522
523 // wir wechseln in die innere Ebene: prev zurücksetzen
524 prev_el_in_tree = nullptr;
525 start = start->nextel.get();
526 end = close;
527 continue;
528 }
529 }
530
531 // "normales" Element (kein Container mit eigenem Terminatorbereich):
532 if (start->element && !start->terminator) {
533 Element *current_el = start->element.get();
534
535 if (prev_el_in_tree) {
536 current_el->_prevElement = prev_el_in_tree;
537 prev_el_in_tree->_nextElement = std::move(start->element);
538 prev_el_in_tree = prev_el_in_tree->_nextElement.get();
539 } else {
540 prev_el_in_tree = current_el;
541 }
542 }
543
544 // weiter zum nächsten DocElement
545 start = start->nextel.get();
546 }
547}
548
554void libhtmlpp::HtmlString::_buildTree() {
555 DocElements* lastEl = nullptr;
556 std::unique_ptr<DocElements> firstEl = nullptr;
557
558 auto is_ws = [](unsigned char ch) -> bool {
559 // space, \t, \n, \r
560 return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
561 };
562
563 auto ascii_tolower = [](unsigned char c) -> unsigned char {
564 return (c >= 'A' && c <= 'Z') ? static_cast<unsigned char>(c + 32) : c;
565 };
566
567 auto starts_with_ci = [&](const char* s, const char* e, const char* k) -> bool {
568 const size_t klen = std::char_traits<char>::length(k);
569 if (static_cast<size_t>(e - s) < klen) return false;
570 for (size_t i = 0; i < klen; ++i) {
571 unsigned char a = static_cast<unsigned char>(s[i]);
572 unsigned char b = static_cast<unsigned char>(k[i]);
573 if (ascii_tolower(a) != ascii_tolower(b)) return false;
574 }
575 return true;
576 };
577
578 auto add_element_node = [&](DocElements** last) {
579 if (!firstEl) {
580 firstEl = std::make_unique<DocElements>();
581 *last = firstEl.get();
582 } else {
583 (*last)->nextel = std::make_unique<DocElements>();
584 (*last)->nextel->prevel = (*last);
585 *last = (*last)->nextel.get();
586 }
587 };
588
589 const char* const base = _Data.data();
590 const size_t n = _Data.size();
591 const char* const end = base + n;
592
593 const char* p = base;
594
595 while (p < end) {
596 while (p < end && is_ws(static_cast<unsigned char>(*p))) ++p;
597 if (p >= end) break;
598
599 if (*p == HTMLTAG_OPEN) { // '<'
600 const char* const remain_end = end;
601 const size_t remain = static_cast<size_t>(remain_end - p);
602
603 if (remain >= 2) {
604 const unsigned char c1 = ascii_tolower(static_cast<unsigned char>(p[1]));
605
606 if (p[1] == '!') {
607 // <!-- ... -->
608 if (starts_with_ci(p, end, "<!--")) {
609 add_element_node(&lastEl);
610 size_t i = static_cast<size_t>(p - base);
611 i = CommentElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
612 p = base + i;
613 continue;
614 }
615 if (starts_with_ci(p, end, "<!doctype")) {
616 const char* close_tag = p;
617 while (close_tag < end && *close_tag != '>') {
618 ++close_tag;
619 }
620 if (close_tag < end) {
621 p = close_tag + 1;
622 } else {
623 p = end;
624 }
625 continue;
626 }
627 } else if (c1 == 's') {
628 if (starts_with_ci(p, end, "<script")) {
629 add_element_node(&lastEl);
630 size_t i = static_cast<size_t>(p - base);
631 i = ScriptElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
632 p = base + i;
633 continue;
634 }
635 if (starts_with_ci(p, end, "<svg")) {
636 add_element_node(&lastEl);
637 size_t i = static_cast<size_t>(p - base);
638 i = SvgElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
639 p = base + i;
640 continue;
641 }
642 } else if (c1 == 't') {
643 if (starts_with_ci(p, end, "<textarea")) {
644 add_element_node(&lastEl);
645 size_t i = static_cast<size_t>(p - base);
646 i = TextArea::parseElement(_Data, lastEl->element, i, lastEl->terminator);
647 p = base + i;
648 continue;
649 }
650 }
651 }
652
653 {
654 add_element_node(&lastEl);
655 size_t i = static_cast<size_t>(p - base);
656 i = HtmlElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
657 p = base + i;
658 }
659 } else {
660 add_element_node(&lastEl);
661 size_t i = static_cast<size_t>(p - base);
662 i = TextElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
663 p = base + i;
664 }
665 }
666
667 _buildtreenode(firstEl.get(), nullptr, _rootEl);
668}
669
677std::ostream& operator<<(std::ostream& os, const libhtmlpp::HtmlString& p) {
678 os << p.str();
679 return os;
680}
681
682void libhtmlpp::HtmlEncode(const std::string &input, std::string &output){
683 size_t ilen=input.length();
684 for(size_t i=0; i<ilen; ++i){
685 size_t ii=0;
686 bool changed=false;
687 while(HtmlSigns[ii][0]){
688 if(input[i]==HtmlSigns[ii][0][0]){
689 output+=HtmlSigns[ii][1];
690 changed=true;
691 }
692 ++ii;
693 }
694 if(!changed)
695 output.push_back(input[i]);
696 }
697}
698
699void libhtmlpp::HtmlDecode(const std::string &input,std::string &output){
700 size_t ilen=input.length();
701 for(size_t i=0; i<ilen; ++i){
702 size_t ii=0;
703 bool changed=false;
704 while(HtmlSigns[ii][0]){
705 if(input.compare(i,strlen(HtmlSigns[ii][1]),HtmlSigns[ii][1]) == 0){
706 output += HtmlSigns[ii][0];
707 changed=true;
708 }
709 ++ii;
710 }
711 if(!changed)
712 output += input[i];
713 }
714}
715
716void libhtmlpp::HtmlDecode(const std::string &input,HtmlString &output){
717 std::string tmp;
718 HtmlDecode(input,tmp);
719 output << tmp;
720 output.parse();
721}
722
723libhtmlpp::HtmlElement::HtmlElement(const std::string &tagname) : HtmlElement(){
724 _TagName.clear();
725 std::copy(tagname.begin(),tagname.end(),std::back_inserter(_TagName));
726}
727
729 _childElement=nullptr;
730 _firstAttr=nullptr;
731 _lastAttr=nullptr;
732}
733
737
741
744
746 return -1;
747}
748
749void libhtmlpp::HtmlElement::setTagname(const std::string &name){
750 _TagName.clear();
751 std::copy(name.begin(),name.begin()+name.length(),std::back_inserter(_TagName));
752}
753
754const std::string libhtmlpp::HtmlElement::getTagname() const{
755 if(_TagName.empty())
756 return "";
757 return std::string(_TagName.begin(),_TagName.end());
758}
759
761 if(_childElement){
762 remove(_childElement.get());
763 }
764
765 switch(el->getType()){
766 case HtmlEl:
767 _childElement=std::make_unique<HtmlElement>();
768 break;
769 case TextEl:
770 _childElement=std::make_unique<TextElement>();
771 break;
772 case CommentEl:
773 _childElement=std::make_unique<CommentElement>();
774 break;
775 case ScriptEL:
776 _childElement=std::make_unique<ScriptElement>();
777 break;
778 case SvgEL:
779 _childElement=std::make_unique<SvgElement>();
780 break;
781 default:
782 HTMLException ex;
783 ex[HTMLException::Critical] << "appendChild: Unknown html element found: "<< el->getType() << " !";
784 throw ex;
785 }
786 _copy(_childElement.get(),el);
787}
788
790 insertChild(&el);
791}
792
794 if(!el)
795 return;
796 if(_childElement){
797 Element *prev=nullptr;
798
799 for(Element *curel=_childElement.get(); curel; curel=curel->nextElement()){
800 prev=curel;
801 }
802
803 switch(el->getType()){
804 case HtmlEl:
805 prev->_nextElement=std::make_unique<HtmlElement>();
806 break;
807 case TextEl:
808 prev->_nextElement=std::make_unique<TextElement>();
809 break;
810 case CommentEl:
811 prev->_nextElement=std::make_unique<CommentElement>();
812 break;
813 case ScriptEL:
814 prev->_nextElement=std::make_unique<ScriptElement>();
815 break;
816 case SvgEL:
817 prev->_nextElement=std::make_unique<SvgElement>();
818 break;
819 default:
820 HTMLException ex;
821 ex[HTMLException::Critical] << "appendChild: Unknown html element found: "<< el->getType() << " !";
822 throw ex;
823 }
824
825 _copy(prev->_nextElement.get(),el);
826
827 if(prev->_nextElement){
828 prev->_nextElement->_prevElement=prev;
829 }
830 }else{
831 insertChild(el);
832 }
833}
834
836 appendChild(&el);
837}
838
839
841 if(!hel)
842 return false;
843 if( _TagName.size() != hel->_TagName.size())
844 return false;
845 if(std::equal(_TagName.begin(),_TagName.end(),hel->_TagName.begin()))
846 return true;
847 return false;
848}
849
851 if(_TagName.size() != hel._TagName.size())
852 return false;
853 if(std::equal(_TagName.begin(),_TagName.end(),hel._TagName.begin()))
854 return true;
855 return false;
856}
857
859 _copy(this,&hel);
860 return *this;
861}
862
867
869 Element *cur=this;
870
871 std::stack<Element*> parents;
872
873 DELETEELEMENT:
874 while(cur){
875 if(cur==el){
876 if(cur->_prevElement)
877 cur->_prevElement->_nextElement=std::move(cur->_nextElement);
878 if(cur->_nextElement)
879 cur->_nextElement->_prevElement=cur->_prevElement;
880 cur->_nextElement=nullptr;
881 cur->_prevElement=nullptr;
882 cur=nullptr;
883 }
884
885 if(cur->getType()==HtmlEl){
886 if(((HtmlElement*)cur)->_childElement){
887 parents.push(cur);
888 cur=((HtmlElement*)cur)->_childElement.get();
889 }
890 }
891
892 Element *next=cur->_nextElement.get();
893 cur=next;
894 }
895
896 if(!parents.empty()){
897 cur=parents.top()->nextElement();
898 parents.pop();
899 if(cur!=el)
900 goto DELETEELEMENT;
901 }
902
903
904}
905
913void libhtmlpp::HtmlElement::_serialelize(const std::vector<char>& in, bool preserveAttrCase) {
914 _TagName.clear();
915
916 auto is_space = [](unsigned char c) -> bool {
917 return c == ' ' || c == '\t' || c == '\n' || c == '\r';
918 };
919 auto tolower_ascii = [](unsigned char c) -> unsigned char {
920 return (c >= 'A' && c <= 'Z') ? static_cast<unsigned char>(c + 32) : c;
921 };
922
923 size_t i = 0, n = in.size();
924
925 if (i < n && in[i] == '<') ++i;
926 while (i < n && is_space(static_cast<unsigned char>(in[i]))) ++i;
927
928 bool end_tag = false;
929 if (i < n && in[i] == '/') {
930 end_tag = true;
931 ++i;
932 while (i < n && is_space(static_cast<unsigned char>(in[i]))) ++i;
933 }
934
935 size_t r = n;
936 while (r > i && is_space(static_cast<unsigned char>(in[r - 1]))) --r;
937 if (r > i && in[r - 1] == '>') --r; // ignore '>' if present
938 while (r > i && is_space(static_cast<unsigned char>(in[r - 1]))) --r;
939
940 if (i >= r) {
941 HTMLException excp;
942 throw excp[HTMLException::Critical] << "no tag in element found!";
943 }
944
945 const size_t name_start = i;
946 while (i < r) {
947 unsigned char c = static_cast<unsigned char>(in[i]);
948 if (is_space(c) || c == '/' || c == '>') break;
949 ++i;
950 }
951 const size_t name_end = i;
952 if (name_start == name_end) {
953 HTMLException excp;
954 throw excp[HTMLException::Critical] << "no tag in element found!";
955 }
956
957 _TagName.assign(in.begin() + name_start, in.begin() + name_end);
958 for (char& ch : _TagName) ch = static_cast<char>(tolower_ascii(static_cast<unsigned char>(ch)));
959
960 if (end_tag) {
961 return;
962 }
963
964 while (i < r) {
965 while (i < r && is_space(static_cast<unsigned char>(in[i]))) ++i;
966 if (i >= r) break;
967
968 if (in[i] == '/') {
969 ++i;
970 while (i < r && is_space(static_cast<unsigned char>(in[i]))) ++i;
971 break;
972 }
973
974 const size_t kstart = i;
975 while (i < r) {
976 unsigned char c = static_cast<unsigned char>(in[i]);
977 if (is_space(c) || c == '=' || c == '/' || c == '>') break;
978 ++i;
979 }
980 const size_t kend = i;
981 if (kstart == kend) {
982 ++i;
983 continue;
984 }
985
986 std::string key(in.begin() + kstart, in.begin() + kend);
987 if (!preserveAttrCase) {
988 for (char& ch : key) ch = static_cast<char>(tolower_ascii(static_cast<unsigned char>(ch)));
989 }
990
991 while (i < r && is_space(static_cast<unsigned char>(in[i]))) ++i;
992
993 std::string val;
994
995 if (i < r && in[i] == '=') {
996 ++i;
997 while (i < r && is_space(static_cast<unsigned char>(in[i]))) ++i;
998
999 if (i < r && (in[i] == '"' || in[i] == '\'')) {
1000 char quote = in[i++];
1001 const size_t vstart = i;
1002 while (i < r && in[i] != quote) ++i;
1003 const size_t vend = i;
1004 val.assign(in.begin() + vstart, in.begin() + vend);
1005 if (i < r && in[i] == quote) ++i;
1006 } else {
1007 const size_t vstart = i;
1008 while (i < r) {
1009 unsigned char c = static_cast<unsigned char>(in[i]);
1010 if (is_space(c) || c == '/' || c == '>') break;
1011 ++i;
1012 }
1013 const size_t vend = i;
1014 val.assign(in.begin() + vstart, in.begin() + vend);
1015 }
1016 } else {
1017 val.clear();
1018 }
1019
1020 setAttribute(key, val);
1021 }
1022}
1023
1024
1026 const std::vector<char>& in,
1027 std::unique_ptr<libhtmlpp::Element>& el,
1028 size_t start,
1029 bool& termination
1030){
1031 el = std::make_unique<HtmlElement>();
1032 termination = false;
1033
1034 size_t i = start;
1035 if (i >= in.size() || in[i] != HTMLTAG_OPEN) return i;
1036
1037 ++i;
1038
1039 while (i < in.size() && std::isspace(static_cast<unsigned char>(in[i]))) ++i;
1040
1041
1042
1043 size_t close = i;
1044 while (close < in.size() && in[close] != HTMLTAG_CLOSE) { // '>'
1045 ++close;
1046 }
1047
1048 size_t k = i;
1049 while (k < close && std::isspace(static_cast<unsigned char>(in[k]))) ++k;
1050
1051 std::vector<char> tel;
1052
1053 if (k < close && in[k] == HTMLTAG_TERMINATE) { // '/'
1054 termination = true;
1055 ++k;
1056 while (k < close && std::isspace(static_cast<unsigned char>(in[k]))) ++k;
1057 }
1058
1059 tel.insert(tel.end(), in.begin() + k, in.begin() + close);
1060
1061 reinterpret_cast<HtmlElement*>(el.get())->_serialelize(tel);
1062
1063 return ++close;
1064}
1065
1066namespace libhtmlpp {
1067
1069 if(!dest || !src)
1070 return;
1071
1072 const libhtmlpp::Element* prev=nullptr;
1073
1074 struct cpyel {
1075 cpyel(){
1076 destin = nullptr;
1077 source = nullptr;
1078 };
1079
1080 cpyel(const cpyel &src){
1081 destin=src.destin;
1082 source=src.source;
1083 };
1084
1085 ~cpyel(){
1086 }
1087
1088 libhtmlpp::Element *destin;
1089 libhtmlpp::Element *source;
1090 };
1091
1092 std::stack<cpyel> cpylist;
1093
1094 NEWEL:
1095
1096 if(src->getType()==HtmlEl && dest->getType()==HtmlEl){
1097 ((libhtmlpp::HtmlElement*)dest)->_TagName=(((libhtmlpp::HtmlElement*)src)->_TagName);
1098 for(libhtmlpp::HtmlElement::Attributes *cattr=((libhtmlpp::HtmlElement*)src)->_firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()){
1099 ((libhtmlpp::HtmlElement*)dest)->setAttribute(
1100 std::string(
1101 cattr->_Key.begin(),
1102 cattr->_Key.end()
1103 ),std::string(
1104 cattr->_Value.begin(),
1105 cattr->_Value.end()
1106 )
1107 );
1108 }
1109
1110 if(((libhtmlpp::HtmlElement*)src)->_childElement){
1111 switch(((libhtmlpp::HtmlElement*)src)->_childElement->getType()){
1112 case HtmlEl:
1113 ((libhtmlpp::HtmlElement*)dest)->_childElement=std::make_unique<HtmlElement>();
1114 break;
1115 case TextEl:
1116 ((libhtmlpp::HtmlElement*)dest)->_childElement =std::make_unique<TextElement>();
1117 break;
1118 case CommentEl:
1119 ((libhtmlpp::HtmlElement*)dest)->_childElement = std::make_unique<CommentElement>();
1120 break;
1121 case ScriptEL:
1122 ((libhtmlpp::HtmlElement*)dest)->_childElement = std::make_unique<ScriptElement>();
1123 break;
1124 case SvgEL:
1125 ((libhtmlpp::HtmlElement*)dest)->_childElement = std::make_unique<SvgElement>();
1126 break;
1127 case TextAreaEL:
1128 ((libhtmlpp::HtmlElement*)dest)->_childElement = std::make_unique<TextArea>();
1129 break;
1130 default:
1131 HTMLException ex;
1132 ex[HTMLException::Critical] << "_copy: Unknown html element found !";
1133 throw ex;
1134 }
1135 cpyel childel;
1136 childel.destin=((libhtmlpp::HtmlElement*)dest)->_childElement.get();
1137 childel.source=((libhtmlpp::HtmlElement*)src)->_childElement.get();
1138 cpylist.push(childel);
1139 }
1140 }else if(src->getType()==libhtmlpp::ScriptEL && dest->getType()== libhtmlpp::ScriptEL){
1141 ((libhtmlpp::ScriptElement*)dest)->_TagName=(((libhtmlpp::ScriptElement*)src)->_TagName);
1142 for(libhtmlpp::ScriptElement::Attributes *cattr=((libhtmlpp::ScriptElement*)src)->_firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()){
1143 if(!cattr->_Value.empty()){
1144 ((libhtmlpp::ScriptElement*)dest)->setAttribute(
1145 std::string(
1146 cattr->_Key.begin(),
1147 cattr->_Key.end()
1148 ),std::string(
1149 cattr->_Value.begin(),
1150 cattr->_Value.end()
1151 )
1152 );
1153 }else{
1154 ((libhtmlpp::ScriptElement*)dest)->setAttribute(std::string(cattr->_Key.begin(),cattr->_Key.end()),"");
1155 }
1156 }
1157 ((ScriptElement*)dest)->_Script=(((ScriptElement*)src)->_Script);
1158 }else if(src->getType()==libhtmlpp::SvgEL && dest->getType()== libhtmlpp::SvgEL){
1159 ((libhtmlpp::SvgElement*)dest)->_TagName=(((libhtmlpp::SvgElement*)src)->_TagName);
1160 for(libhtmlpp::SvgElement::Attributes *cattr=((libhtmlpp::SvgElement*)src)->_firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()){
1161 if(!cattr->_Value.empty()){
1162 ((libhtmlpp::SvgElement*)dest)->setAttribute(
1163 std::string(
1164 cattr->_Key.begin(),
1165 cattr->_Key.end()
1166 ),std::string(
1167 cattr->_Value.begin(),
1168 cattr->_Value.end()
1169 )
1170 );
1171 }else{
1172 ((libhtmlpp::SvgElement*)dest)->setAttribute(std::string(cattr->_Key.begin(),cattr->_Key.end()),"");
1173 }
1174 }
1175 ((SvgElement*)dest)->_Svg=(((SvgElement*)src)->_Svg);
1176 }else if(src->getType()==libhtmlpp::TextAreaEL&& dest->getType()== libhtmlpp::TextAreaEL){
1177 ((libhtmlpp::TextArea*)dest)->_TagName=(((libhtmlpp::TextArea*)src)->_TagName);
1178 for(libhtmlpp::TextArea::Attributes *cattr=((libhtmlpp::TextArea*)src)->_firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()){
1179 if(!cattr->_Value.empty()){
1180 ((libhtmlpp::TextArea*)dest)->setAttribute(
1181 std::string(
1182 cattr->_Key.begin(),
1183 cattr->_Key.end()
1184 ),std::string(
1185 cattr->_Value.begin(),
1186 cattr->_Value.end()
1187 )
1188 );
1189 }else{
1190 ((libhtmlpp::TextArea*)dest)->setAttribute(std::string(cattr->_Key.begin(),cattr->_Key.end()),"");
1191 }
1192 }
1193 ((TextArea*)dest)->_Text=(((TextArea*)src)->_Text);
1194 }else if(src->getType()==libhtmlpp::TextEl && dest->getType()== libhtmlpp::TextEl){
1195 ((TextElement*)dest)->_Text=(((TextElement*)src)->_Text);
1196 }else if(src->getType()==libhtmlpp::CommentEl && dest->getType()== libhtmlpp::CommentEl){
1197 ((CommentElement*)dest)->_Comment=(((CommentElement*)src)->_Comment);
1198 }
1199
1200 if(prev)
1201 dest->_prevElement=(Element*)prev;
1202
1203 Element* next=src->nextElement();
1204
1205 if(next){
1206 switch(next->getType()){
1207 case HtmlEl:
1208 dest->_nextElement= std::make_unique<HtmlElement>();
1209 break;
1210 case TextEl:
1211 dest->_nextElement= std::make_unique<TextElement>();
1212 break;
1213 case CommentEl:
1214 dest->_nextElement= std::make_unique<CommentElement>();
1215 break;
1216 case ScriptEL:
1217 dest->_nextElement= std::make_unique<ScriptElement>();
1218 break;
1219 case SvgEL:
1220 dest->_nextElement= std::make_unique<SvgElement>();
1221 break;
1222 case TextAreaEL:
1223 dest->_nextElement= std::make_unique<TextArea>();
1224 break;
1225 default:
1226 HTMLException ex;
1227 ex[HTMLException::Critical] << "_copy: Unknown next html element found !";
1228 throw ex;
1229 }
1230 prev=dest;
1231 src=next;
1232 dest=dest->_nextElement.get();
1233 goto NEWEL;
1234 }
1235
1236 if(!cpylist.empty()){
1237 cpyel childel(cpylist.top());
1238 prev=nullptr;
1239 dest=childel.destin;
1240 src=childel.source;
1241 cpylist.pop();
1242 goto NEWEL;
1243 }
1244 return;
1245 }
1246};
1247
1249 std::unique_ptr<Element> nel;
1250 switch(el->getType()){
1251 case HtmlEl:
1252 nel->_nextElement= std::make_unique<HtmlElement>();
1253 break;
1254 case TextEl:
1255 nel->_nextElement= std::make_unique<TextElement>();
1256 break;
1257 case CommentEl:
1258 nel->_nextElement= std::make_unique<CommentElement>();
1259 break;
1260 case ScriptEL:
1261 nel->_nextElement= std::make_unique<ScriptElement>();
1262 break;
1263 case SvgEL:
1264 nel->_nextElement= std::make_unique<SvgElement>();
1265 break;
1266 case TextAreaEL:
1267 nel->_nextElement= std::make_unique<TextArea>();
1268 break;
1269 default:
1270 HTMLException ex;
1271 ex[HTMLException::Critical] << "_copy: Unknown next html element found !";
1272 throw ex;
1273 }
1274 _copy(nel.get(),el);
1275 std::unique_ptr<Element> prev=std::move(_prevElement->_nextElement);
1276 _prevElement->_nextElement=std::move(nel);
1277 nel->_nextElement=std::move(prev);
1278}
1279
1281 Element *nexel=nullptr,*prev=nullptr;
1282
1283 switch(el->getType()){
1284 case HtmlEl:
1285 _nextElement= std::make_unique<HtmlElement>();
1286 break;
1287 case TextEl:
1288 _nextElement= std::make_unique<TextElement>();
1289 break;
1290 case CommentEl:
1291 _nextElement= std::make_unique<CommentElement>();
1292 break;
1293 case ScriptEL:
1294 _nextElement= std::make_unique<ScriptElement>();
1295 break;
1296 case SvgEL:
1297 _nextElement= std::make_unique<SvgElement>();
1298 break;
1299 case TextAreaEL:
1300 _nextElement= std::make_unique<TextArea>();
1301 break;
1302 default:
1303 HTMLException ex;
1304 ex[HTMLException::Critical] << "_copy: Unknown next html element found !";
1305 throw ex;
1306 }
1307
1308 _copy(_nextElement.get(),el);
1309
1310 nexel=_nextElement.get();
1311
1312 while(nexel){
1313 prev=nexel;
1314 nexel=nexel->nextElement();
1315 }
1316
1317 nexel=prev;
1318
1319}
1320
1322 _copy(this,&hel);
1323 return *this;
1324}
1325
1327 _copy(this,hel);
1328 return *this;
1329}
1330
1332 return _nextElement.get();
1333}
1334
1336 return _prevElement;
1337}
1338
1340 _prevElement=nullptr;
1341 _nextElement=nullptr;
1342}
1343
1345 _prevElement=nullptr;
1346 _nextElement=nullptr;
1347 _copy(this,&el);
1348}
1349
1351 auto cur = std::move(_nextElement);
1352 while (cur) {
1353 cur = std::move(cur->_nextElement);
1354 }
1355};
1356
1358 Element *curel=this;
1359
1360 while(curel){
1361 Element *next=curel->_nextElement.get();
1362
1363 if(curel==el){
1364 if(curel->_prevElement)
1365 curel->_prevElement->_nextElement=std::move(curel->_nextElement);
1366
1367 if(curel->_nextElement)
1368 curel->_nextElement->_prevElement=curel->_prevElement;
1369 curel->_prevElement=nullptr;
1370 curel->_nextElement=nullptr;
1371 }
1372
1373 curel=next;
1374 }
1375}
1376
1377
1380
1382 setText(txt);
1383}
1384
1386 _copy(this,&texel);
1387}
1388
1391
1392
1394 _copy(this,&hel);
1395 return *this;
1396}
1397
1399 _copy(this,hel);
1400 return *this;
1401}
1402
1403void libhtmlpp::TextElement::setText(const std::string& txt){
1404 std::copy(txt.begin(),txt.end(),std::back_inserter(_Text));
1405}
1406
1408 return std::string(_Text.begin(),_Text.end());
1409}
1410
1414
1416 const std::vector<char>& in,
1417 std::unique_ptr<libhtmlpp::Element>& el,
1418 size_t start,
1419 bool &termination
1420){
1421 termination = false;
1422
1423 std::vector<char> buf;
1424 buf.reserve(64);
1425 bool seen_nonws = false;
1426 bool last_was_space = false;
1427
1428 size_t i = start;
1429 while (i < in.size()) {
1430 char c = in[i];
1431 if (c == HTMLTAG_OPEN) {
1432 break;
1433 }
1434
1435 if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
1436 if (seen_nonws) {
1437 if (!last_was_space) {
1438 buf.push_back(' ');
1439 last_was_space = true;
1440 }
1441 }
1442 ++i;
1443 continue;
1444 }
1445
1446 buf.push_back(c);
1447 seen_nonws = true;
1448 last_was_space = false;
1449 ++i;
1450 }
1451
1452 if (!buf.empty()) {
1453 auto text = std::make_unique<TextElement>();
1454 (static_cast<TextElement*>(text.get()))->_Text.insert(
1455 (static_cast<TextElement*>(text.get()))->_Text.end(),
1456 buf.begin(), buf.end()
1457 );
1458 el = std::move(text);
1459 }
1460
1461 return i;
1462}
1463
1464
1467
1469 _copy(this,&comel);
1470}
1471
1474
1475
1477 _copy(this,&hel);
1478 return *this;
1479}
1480
1482 _copy(this,hel);
1483 return *this;
1484}
1485
1486void libhtmlpp::CommentElement::setComment(const std::string& txt){
1487 std::copy(txt.begin(),txt.end(),
1488 std::insert_iterator<std::vector<char>>(_Comment,_Comment.begin()));
1489}
1490
1492 return std::string(_Comment.begin(),_Comment.end());
1493}
1494
1498
1500 const std::vector<char>& in,
1501 std::unique_ptr<Element>& el,
1502 size_t start,
1503 bool& termination
1504){
1505 termination = false;
1506
1507 size_t i = start;
1508 if (i + 3 >= in.size()) return i;
1509
1510 if (!(in[i] == '<' && in[i+1] == '!' && in[i+2] == '-' && in[i+3] == '-')) {
1511 return i;
1512 }
1513
1514 el = std::make_unique<CommentElement>();
1515
1516 i += 4;
1517 const size_t content_begin = i;
1518
1519 while (i + 2 < in.size()) {
1520 if (in[i] == '-' && in[i+1] == '-' && in[i+2] == '>') {
1521 break;
1522 }
1523 ++i;
1524 }
1525
1526 auto* self = static_cast<CommentElement*>(el.get());
1527 if (i > content_begin) {
1528 self->_Comment.insert(self->_Comment.end(),
1529 in.begin() + content_begin,
1530 in.begin() + i);
1531 }
1532
1533 if (i + 2 < in.size()) {
1534 i += 3;
1535 } else {
1536 i = in.size();
1537 }
1538 return i;
1539}
1540
1541
1544
1546 _copy(this,&scriptsrc);
1547}
1548
1551
1552
1554 _copy(this,&hel);
1555 return *this;
1556}
1557
1559 _copy(this,hel);
1560 return *this;
1561}
1562
1563void libhtmlpp::ScriptElement::setScript(const std::string& script){
1564 _Script.assign(script.begin(),script.end());
1565}
1566
1568 return std::string(_Script.begin(),_Script.end());
1569}
1570
1574
1576 const std::vector<char>& in,
1577 std::unique_ptr<Element>& el,
1578 size_t start,
1579 bool& termination
1580){
1581termination = false;
1582 el = std::make_unique<ScriptElement>();
1583 auto* self = static_cast<ScriptElement*>(el.get());
1584
1585 size_t i = start;
1586 if (i >= in.size() || in[i] != '<') {
1587 // If it doesn't start with '<', we can't parse a tag here.
1588 return start;
1589 }
1590
1591 // Helper to perform case-insensitive comparison
1592 auto iequals = [](char a, char b) {
1593 return std::tolower(static_cast<unsigned char>(a)) ==
1594 std::tolower(static_cast<unsigned char>(b));
1595 };
1596
1597 // Helper to perform case-insensitive match for a keyword starting at 'pos'
1598 auto match_ci = [&](size_t pos, const char* k) -> bool {
1599 for (size_t j = 0; k[j]; ++j) {
1600 if (pos + j >= in.size() || !iequals(in[pos + j], k[j])) {
1601 return false;
1602 }
1603 }
1604 return true;
1605 };
1606
1607 // --- 1. Validate Opening Tag Name (<script) ---
1608 ++i; // Consume '<'
1609
1610 // Skip leading whitespace after '<'
1611 while (i < in.size() && std::isspace(static_cast<unsigned char>(in[i]))) {
1612 ++i;
1613 }
1614
1615 const char* tag_keyword = "script";
1616 size_t keyword_len = std::char_traits<char>::length(tag_keyword);
1617
1618 if (i + keyword_len >= in.size() || !match_ci(i, tag_keyword)) {
1619 // Tag name doesn't match "script"
1620 // Skip till next '>' and return the position after it.
1621 while (i < in.size() && in[i] != '>') {
1622 ++i;
1623 }
1624 if (i < in.size()) {
1625 ++i; // Consume '>'
1626 }
1627 return i;
1628 }
1629 i += keyword_len; // Consume "script"
1630
1631 // --- 2. Extract Opening Tag and Attributes ---
1632 // Find the closing '>' of the opening tag.
1633 size_t tag_end = i;
1634 while (i < in.size() && in[i] != '>') {
1635 ++i;
1636 }
1637
1638 // Capture the raw opening tag data (including '<script' and attributes) for serialization.
1639 if (i > start && in[i] == '>') {
1640 // Copy data from '<' (start) up to and including '>' (i)
1641 std::vector<char> raw_tag_data(in.begin() + start, in.begin() + i + 1);
1642 self->_serialelize(raw_tag_data);
1643 }
1644
1645 if (i >= in.size() || in[i] != '>') {
1646 // The tag was never closed (e.g., '<script src="..." EOF')
1647 return i;
1648 }
1649
1650 ++i; // Consume '>' and move to content start
1651 size_t content_begin = i;
1652
1653 // --- 3. Extract Script Content (CDATA-like section) ---
1654 for (; i < in.size(); ++i) {
1655 // Look for the start of the closing tag sequence: </script
1656 if (in[i] == '<' && match_ci(i, "</script")) {
1657 size_t content_end = i;
1658
1659 // 3a. Extract content preceding the closing tag
1660 if (content_end > content_begin) {
1661 // FIX: Use std::vector::insert instead of non-existent append
1662 self->_Script.insert(self->_Script.end(),
1663 in.begin() + content_begin,
1664 in.begin() + content_end);
1665 }
1666
1667 // 3b. Find the end of the closing tag: </script>
1668 size_t closing_tag_end_pos = i + keyword_len + 2; // +2 for '</'
1669
1670 // Skip any characters/whitespace between </script and the final '>'
1671 while (closing_tag_end_pos < in.size() && in[closing_tag_end_pos] != '>') {
1672 ++closing_tag_end_pos;
1673 }
1674
1675 if (closing_tag_end_pos < in.size() && in[closing_tag_end_pos] == '>') {
1676 i = closing_tag_end_pos + 1; // Position after '>'
1677 return i;
1678 }
1679
1680 // If we found "</script" but not the final ">", return the last processed position.
1681 return closing_tag_end_pos;
1682 }
1683 }
1684
1685 // --- 4. End of Input Reached ---
1686 // If the input ends without a closing </script> tag, capture the remaining content.
1687 if (in.size() > content_begin) {
1688 // FIX: Use std::vector::insert instead of non-existent append
1689 self->_Script.insert(self->_Script.end(),
1690 in.begin() + content_begin,
1691 in.end());
1692 }
1693
1694 return i;
1695}
1696
1697
1698
1701
1703 _copy(this,&svgsrc);
1704}
1705
1708
1709
1711 _copy(this,&hel);
1712 return *this;
1713}
1714
1716 _copy(this,hel);
1717 return *this;
1718}
1719
1720void libhtmlpp::SvgElement::setSvg(const std::string& script){
1721 std::copy(script.begin(),script.end(),
1722 std::insert_iterator<std::vector<char>>(_Svg,_Svg.begin()));
1723}
1724
1725const std::vector<char>libhtmlpp::SvgElement::getSvg(){
1726 return _Svg;
1727}
1728
1732
1733size_t libhtmlpp::SvgElement::parseElement(const std::vector<char>& in,
1734 std::unique_ptr<libhtmlpp::Element>& el,
1735 size_t start,
1736 bool& termination)
1737{
1738 const size_t startel = start;
1739 termination = false;
1740
1741 const auto begin = in.begin();
1742 if (start >= in.size()) {
1743 HTMLException excp;
1744 throw excp[HTMLException::Error] << "Parsing error: start offset beyond buffer.";
1745 }
1746 auto it_close_angle = std::find(begin + start, in.end(), HTMLTAG_CLOSE);
1747 if (it_close_angle == in.end()) {
1748 HTMLException excp;
1749 throw excp[HTMLException::Error] << "Parsing error: Missing '>' for <svg> open tag.";
1750 }
1751
1752 el = std::make_unique<SvgElement>();
1753 auto* svgEl = static_cast<SvgElement*>(el.get());
1754
1755 {
1756 std::vector<char> tel;
1757 tel.assign(begin + startel, it_close_angle);
1758 svgEl->_serialelize(tel, /*preserveAttrCase=*/true);
1759 }
1760
1761 auto it_content_begin = it_close_angle;
1762 if (it_content_begin != in.end()) ++it_content_begin; // safe increment
1763
1764 static constexpr char kEndTag[] = "</svg>";
1765 auto ci_eq = [](char a, char b) {
1766 auto lower = [](unsigned char c) -> unsigned char {
1767 return (c >= 'A' && c <= 'Z') ? static_cast<unsigned char>(c + 32) : c;
1768 };
1769 return lower(static_cast<unsigned char>(a)) == lower(static_cast<unsigned char>(b));
1770 };
1771 auto it_end = std::search(it_content_begin, in.end(),
1772 std::begin(kEndTag), std::end(kEndTag) - 1 /* no '\0' */, ci_eq);
1773 if (it_end == in.end()) {
1774 HTMLException excp;
1775 throw excp[HTMLException::Error] << "Parsing error: Missing </svg> closing tag.";
1776 }
1777
1778 svgEl->_Svg.insert(svgEl->_Svg.end(), it_content_begin, it_end);
1779
1780 const size_t consumed = static_cast<size_t>((it_end - begin) + (std::size(kEndTag) - 1));
1781 return consumed;
1782}
1783
1784
1785
1788
1790 _copy(this,&textsrc);
1791}
1792
1795
1796
1798 _copy(this,&hel);
1799 return *this;
1800}
1801
1803 _copy(this,hel);
1804 return *this;
1805}
1806
1807void libhtmlpp::TextArea::setText(const std::string& text){
1808 std::copy(text.begin(),text.end(),
1809 std::insert_iterator<std::vector<char>>(_Text,_Text.begin()));
1810}
1811
1812const std::vector<char>libhtmlpp::TextArea::getText(){
1813 return _Text;
1814}
1815
1819
1820size_t libhtmlpp::TextArea::parseElement(const std::vector<char>& in,
1821 std::unique_ptr<libhtmlpp::Element>& el,
1822 size_t start,
1823 bool& termination)
1824{
1825 termination = false;
1826
1827 const auto begin = in.begin();
1828 const auto end = in.end();
1829
1830 if (start >= in.size()) {
1831 HTMLException excp;
1832 throw excp[HTMLException::Error] << "Parsing error: start offset beyond buffer.";
1833 }
1834
1835 // ---- helpers -----------------------------------------------------------
1836 auto is_ws = [](unsigned char c) {
1837 return c == ' ' || c == '\t' || c == '\n' || c == '\r';
1838 };
1839 auto tolower_ascii = [](unsigned char c) -> unsigned char {
1840 return (c >= 'A' && c <= 'Z') ? static_cast<unsigned char>(c + 32) : c;
1841 };
1842 auto ieq_prefix = [&](std::vector<char>::const_iterator it,
1843 std::vector<char>::const_iterator it_end,
1844 const char* lit) -> bool
1845 {
1846 for (; *lit; ++lit, ++it) {
1847 if (it == it_end) return false;
1848 if (tolower_ascii(static_cast<unsigned char>(*it)) !=
1849 tolower_ascii(static_cast<unsigned char>(*lit))) {
1850 return false;
1851 }
1852 }
1853 return true;
1854 };
1855
1856 auto it_gt = std::find(begin + start, end, HTMLTAG_CLOSE);
1857 if (it_gt == end) {
1858 HTMLException excp;
1859 throw excp[HTMLException::Error] << "Parsing error: Unclosed <textarea> tag.";
1860 }
1861
1862 el = std::make_unique<TextArea>();
1863 auto* ta = static_cast<TextArea*>(el.get());
1864 {
1865 std::vector<char> tel;
1866 tel.assign(begin + start, it_gt); // opening tag without '>'
1867 ta->_serialelize(tel);
1868 }
1869
1870 auto it = it_gt;
1871 if (it != end) ++it;
1872 const auto content_begin = it;
1873
1874 for (;;) {
1875 auto lt = std::find(it, end, '<');
1876 if (lt == end) {
1877 HTMLException excp;
1878 throw excp[HTMLException::Error] << "Parsing error: Missing </textarea> closing tag.";
1879 }
1880
1881 if (ieq_prefix(lt, end, "</textarea")) {
1882 auto after_head = lt + std::strlen("</textarea");
1883 while (after_head != end && is_ws(static_cast<unsigned char>(*after_head))) {
1884 ++after_head;
1885 }
1886 if (after_head == end) {
1887 HTMLException excp;
1888 throw excp[HTMLException::Error] << "Parsing error: Unclosed </textarea> end tag.";
1889 }
1890 if (*after_head == '>') {
1891 ta->_Text.insert(ta->_Text.end(), content_begin, lt);
1892 const size_t consumed = static_cast<size_t>((after_head - begin) + 1);
1893 return consumed;
1894 }
1895
1896 it = lt + 1;
1897 continue;
1898 }
1899
1900 it = lt + 1;
1901 }
1902}
1903
1904
1905
1909
1919void libhtmlpp::HtmlPage::loadFile(libhtmlpp::HtmlElement &html,const std::string& path){
1920 std::string data;
1921 std::ifstream fs(path);
1922
1923 if(!fs.is_open()){
1924 HTMLException excp;
1925 throw excp[HTMLException::Critical] << "Can't open file: " << path;
1926 }
1927
1928 fs.seekg(std::ios::end);
1929
1930 data.reserve(fs.tellg());
1931
1932 fs.seekg(std::ios::beg);
1933
1934 data.assign((std::istreambuf_iterator<char>(fs)), std::istreambuf_iterator<char>());
1935
1936 fs.close();
1937
1938 _CheckHeader(data);
1939 loadString(html,data);
1940}
1949 HtmlString buf=src;
1950 Element &el=buf.parse();
1951 _copy(&html,&el);
1952}
1953
1955 HtmlString buf=node;
1956 Element &el=buf.parse();
1957 _copy(&html,&el);
1958}
1959
1961 if(!node){
1963 throw excp[libhtmlpp::HTMLException::Critical] << "loadstring: node can't be null !";
1964 }
1965 libhtmlpp::HtmlString buf=*node;
1966 html=(libhtmlpp::HtmlElement&)buf.parse();
1967}
1975void libhtmlpp::HtmlPage::saveFile(libhtmlpp::HtmlElement &html,const std::string& path){
1976 HtmlString data;
1977 std::ofstream fs;
1978
1979 print(html,data);
1980
1981 try{
1982 fs.open(path);
1983 }catch(std::exception &e){
1984 HTMLException excp;
1985 throw excp[HTMLException::Critical] << e.what();
1986 }
1987
1988 fs << data.str();
1989
1990 fs.close();
1991
1992}
1993
1995 return _Html5;
1996}
1997
1998
1999void libhtmlpp::HtmlPage::_CheckHeader(const HtmlString &page){
2000 const char type[] = { '!','D','O','C','T','Y','P','E' };
2001
2002 int i = 0;
2003
2004 bool start=false;
2005
2006 do{
2007 ++i;
2008 if(page[i]== '<'){
2009 if(start==true){
2010 HTMLException excp;
2011 excp[HTMLException::Critical] << "Wrong Header arborting";
2012 throw excp;
2013 }
2014 start=true;
2015 }
2016 } while ( page[i]== '<' || page[i]== '!' || page[i] == ' ');
2017
2018 if (page.size() < 8) {
2019 HTMLException excp;
2020 excp[HTMLException::Critical] << "No Doctype found arborting";
2021 throw excp;
2022 }
2023
2024 while (i < 8) {
2025 if (page[i+1] != type[i]) {
2026 HTMLException excp;
2027 excp[HTMLException::Critical] << "No Doctype found arborting";
2028 throw excp;
2029 }
2030 ++i;
2031 }
2032
2033 do{
2034 ++i;
2035 }while (page[i] == ' ');
2036
2037 const char doctype[] = { 'H','T','M','L' };
2038 size_t tpvl = 4;
2039
2040 const char typevalue4[] = {'P','U','B','L','I','C'};
2041 size_t tpvl4 = 6;
2042
2043 if ((i + tpvl) > page.size()) {
2044 HTMLException excp;
2045 excp[HTMLException::Critical] << "Document to short broken !";
2046 throw excp;
2047 }
2048
2049 size_t ii=0;
2050
2051 while ( ii < tpvl) {
2052 if (tolower(page[i++]) != tolower(doctype[ii++])) {
2053 HTMLException excp;
2054 excp[HTMLException::Critical] << "Doctype header broken or wrong type";
2055 throw excp;
2056 }
2057 }
2058
2059 ii=0;
2060
2061 do{
2062 ++i;
2063 } while (page[i] == ' ');
2064
2065 bool html4=true;
2066
2067 if(i +tpvl4 <page.size()){
2068 while(ii < tpvl4){
2069 if (tolower(page[i++]) != tolower(typevalue4[ii++])) {
2070 html4=false;
2071 }
2072 }
2073 }
2074
2075 _Html5=!html4;
2076
2077}
2085void libhtmlpp::print(const Element &element, HtmlString &output,bool formated) {
2086
2087 const Element *el=&element;
2088
2089 // Emit <!DOCTYPE html> when the root element is <html>
2090 if (el->getType() == HtmlEl) {
2091 const std::string &tag = static_cast<const HtmlElement*>(el)->getTagname();
2092 if (tag == "html") {
2093 output.append("<!DOCTYPE html>");
2094 if (formated)
2095 output.append("\n");
2096 }
2097 }
2098
2099 auto isContainer = [](const std::string &tagname) {
2100 for(size_t i=0; i<ContainerTypes.size(); ++i){
2101 if(tagname==ContainerTypes[i])
2102 return true;
2103 }
2104 return false;
2105 };
2106
2107 std::stack<const libhtmlpp::Element*> cpylist;
2108
2109 int lvl=0;
2110
2111 bool virgin=true;
2112
2113 PRINTNEXTEL:
2114
2115 if(formated){
2116 for(int i=0; i<lvl; ++i){
2117 output.append(" ");
2118 }
2119 }
2120
2121 switch(el->getType()){
2122 case HtmlEl:{
2123 output.append("<");
2124 output.append(static_cast<const HtmlElement*>(el)->getTagname());
2125 for (HtmlElement::Attributes* curattr = static_cast<const HtmlElement*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2126 output.append(" ");
2127 std::copy(
2128 curattr->_Key.begin(),
2129 curattr->_Key.end(),
2130 std::back_inserter(output)
2131 );
2132 if(!curattr->_Value.empty()){
2133 output.append("=\"");
2134 std::copy(
2135 curattr->_Value.begin(),
2136 curattr->_Value.end(),
2137 std::back_inserter(output)
2138 );
2139 output.append("\"");
2140 }
2141 }
2142
2143 output.append(">");
2144
2145 if( !formated && virgin ){
2146 output.append("\r\n ");
2147 virgin=false;
2148 }
2149
2150 if (static_cast<const HtmlElement*>(el)->_childElement) {
2151 if(formated)
2152 output.append("\r\n");
2153 cpylist.push(el);
2154 el=static_cast<const HtmlElement*>(el)->_childElement.get();
2155 ++lvl;
2156 goto PRINTNEXTEL;
2157 }
2158
2159 //Container must be always terminated fuck html5
2160 if(isContainer(static_cast<const HtmlElement*>(el)->getTagname())){
2161 output.append("</");
2162 std::copy(
2163 static_cast<const HtmlElement*>(el)->_TagName.begin(),
2164 static_cast<const HtmlElement*>(el)->_TagName.end(),
2165 std::back_inserter(output)
2166 );
2167 output.append(">");
2168 }
2169
2170 if(formated)
2171 output.append("\r\n");
2172
2173 if (el->_nextElement) {
2174 el=el->_nextElement.get();
2175 goto PRINTNEXTEL;
2176 }
2177 }break;
2178
2179 case TextEl :{
2180 std::copy(
2181 static_cast<const TextElement*>(el)->_Text.begin(),
2182 static_cast<const TextElement*>(el)->_Text.end(),
2183 std::back_inserter(output)
2184 );
2185 if(formated)
2186 output.append("\r\n");
2187
2188 if (el->_nextElement) {
2189 el=el->_nextElement.get();
2190 goto PRINTNEXTEL;
2191 }
2192 }break;
2193 case CommentEl: {
2194 output.append("<!--");
2195 std::copy(
2196 static_cast<const CommentElement*>(el)->_Comment.begin(),
2197 static_cast<const CommentElement*>(el)->_Comment.end(),
2198 std::back_inserter(output)
2199 );
2200 output.append("-->");
2201 if(formated)
2202 output.append("\r\n");
2203
2204 if (el->_nextElement) {
2205 el=el->_nextElement.get();
2206 goto PRINTNEXTEL;
2207 }
2208 }break;
2209 case ScriptEL:{
2210 output.append("<");
2211 output.append(static_cast<const ScriptElement*>(el)->getTagname());
2212 for (ScriptElement::Attributes* curattr = static_cast<const ScriptElement*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2213 output.append(" ");
2214 std::copy(
2215 curattr->_Key.begin(),
2216 curattr->_Key.end(),
2217 std::back_inserter(output)
2218 );
2219 if(!curattr->_Value.empty()){
2220 output.append("=\"");
2221 std::copy(
2222 curattr->_Value.begin(),
2223 curattr->_Value.end(),
2224 std::back_inserter(output)
2225 );
2226 output.append("\"");
2227 }
2228 }
2229
2230 output.append(">");
2231 if(formated){
2232 output.append("\r\n");
2233 for(int i=0; i<lvl+1; ++i){
2234 output.append(" ");
2235 }
2236 }
2237 std::copy(
2238 static_cast<const ScriptElement*>(el)->_Script.begin(),
2239 static_cast<const ScriptElement*>(el)->_Script.end(),
2240 std::back_inserter(output)
2241 );
2242 if(formated){
2243 output.append("\r\n");
2244 for(int i=0; i<lvl; ++i){
2245 output.append(" ");
2246 }
2247 }
2248 output.append("</");
2249 output.append(static_cast<const ScriptElement*>(el)->getTagname());
2250 output.append(">");
2251 if(formated)
2252 output.append("\r\n");
2253
2254 if (el->_nextElement) {
2255 el=el->_nextElement.get();
2256 goto PRINTNEXTEL;
2257 }
2258 }break;
2259 case SvgEL:{
2260 output.append("<");
2261 output.append(static_cast<const ScriptElement*>(el)->getTagname());
2262 for (SvgElement::Attributes* curattr = static_cast<const SvgElement*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2263 output.append(" ");
2264 std::copy(
2265 curattr->_Key.begin(),
2266 curattr->_Key.end(),
2267 std::back_inserter(output)
2268 );
2269 if(!curattr->_Value.empty()){
2270 output.append("=\"");
2271 std::copy(
2272 curattr->_Value.begin(),
2273 curattr->_Value.end(),
2274 std::back_inserter(output)
2275 );
2276 output.append("\"");
2277 }
2278 }
2279 output.append(">");
2280
2281 if(formated){
2282 output.append("\r\n");
2283 for(int i=0; i<lvl+1; ++i){
2284 output.append(" ");
2285 }
2286 }
2287 std::copy(
2288 static_cast<const SvgElement*>(el)->_Svg.begin(),
2289 static_cast<const SvgElement*>(el)->_Svg.end(),
2290 std::back_inserter(output)
2291 );
2292 if(formated){
2293 output.append("\r\n");
2294 for(int i=0; i<lvl; ++i){
2295 output.append(" ");
2296 }
2297 }
2298 output.append("</");
2299 output.append(static_cast<const SvgElement*>(el)->getTagname());
2300 output.append(">");
2301 if(formated)
2302 output.append("\r\n");
2303
2304 if (el->_nextElement) {
2305 el=el->_nextElement.get();
2306 goto PRINTNEXTEL;
2307 }
2308 }break;
2309 case TextAreaEL:{
2310 output.append("<");
2311 output.append(static_cast<const TextArea*>(el)->getTagname());
2312 for (TextArea::Attributes* curattr = static_cast<const TextArea*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2313 output.append(" ");
2314 std::copy(
2315 curattr->_Key.begin(),
2316 curattr->_Key.end(),
2317 std::back_inserter(output)
2318 );
2319 if(!curattr->_Value.empty()){
2320 output.append("=\"");
2321 std::copy(
2322 curattr->_Value.begin(),
2323 curattr->_Value.end(),
2324 std::back_inserter(output)
2325 );
2326 output.append("\"");
2327 }
2328 }
2329 output.append(">");
2330 std::copy(
2331 static_cast<const TextArea*>(el)->_Text.begin(),
2332 static_cast<const TextArea*>(el)->_Text.end(),
2333 std::back_inserter(output)
2334 );
2335 output.append("</");
2336 output.append(static_cast<const TextArea*>(el)->getTagname());
2337 output.append(">");
2338 if(formated)
2339 output.append("\r\n");
2340
2341 if (el->_nextElement) {
2342 el=el->_nextElement.get();
2343 goto PRINTNEXTEL;
2344 }
2345 }break;
2346 default:
2347 HTMLException excp;
2348 excp[HTMLException::Error] << "Unkown Elementtype";
2349 throw excp;
2350 break;
2351 }
2352
2353 while(!cpylist.empty()){
2354 el=cpylist.top();
2355
2356 --lvl;
2357
2358 if(formated){
2359 for(int i=0; i<lvl; ++i){
2360 output.append(" ");
2361 }
2362 }
2363
2364 output.append("</");
2365 std::copy(
2366 static_cast<const HtmlElement*>(el)->_TagName.begin(),
2367 static_cast<const HtmlElement*>(el)->_TagName.end(),
2368 std::back_inserter(output)
2369 );
2370 output.append(">");
2371
2372 if(formated)
2373 output.append("\r\n");
2374
2375 cpylist.pop();
2376 if (el->_nextElement) {
2377 el=el->_nextElement.get();
2378 goto PRINTNEXTEL;
2379 }
2380 }
2381}
2382
2384 std::stack <Element*> childs;
2385 const Element *curel=this;
2386 SEARCHBYID:
2387 if(curel->getType()==HtmlEl || curel->getType()== ScriptEL || curel->getType()==SvgEL){
2388 if(((HtmlElement*)curel)->_childElement){
2389 childs.push(((HtmlElement*)curel)->_childElement.get());
2390 }
2391 std::string idname=((HtmlElement*)curel)->getAtributte("id");
2392 if(idname.length()==id.length() && std::equal(id.begin(),id.end(),idname.begin()) ){
2393 return (HtmlElement*)curel;
2394 }
2395 }
2396
2397 if(curel->nextElement()){
2398 curel=curel->nextElement();
2399 goto SEARCHBYID;
2400 }
2401
2402 if(!childs.empty()){
2403 curel=childs.top();
2404 childs.pop();
2405 goto SEARCHBYID;
2406 }
2407 return nullptr;
2408}
2409
2411 std::stack <Element*> childs;
2412 const Element *curel=this;
2413 SEARCHBYTAG:
2414 if(curel->getType()==HtmlEl || curel->getType()== ScriptEL || curel->getType()==SvgEL){
2415 if(((HtmlElement*)curel)->_childElement){
2416 childs.push(((HtmlElement*)curel)->_childElement.get());
2417 }
2418 const std::string tname=((HtmlElement*)curel)->getTagname();
2419 if(!tname.empty() && std::equal(tag.begin(),tag.end(),tname.begin())){
2420 return (HtmlElement*)curel;
2421 }
2422 }
2423
2424 if(curel->nextElement()){
2425 curel=curel->nextElement();
2426 goto SEARCHBYTAG;
2427 }
2428
2429 if(!childs.empty()){
2430 curel=childs.top();
2431 childs.pop();
2432 goto SEARCHBYTAG;
2433 }
2434 return nullptr;
2435}
2436
2438 return _childElement.get();
2439}
2440
2442 return std::string(_Key.begin(), _Key.end());
2443}
2444
2446 return std::string(_Value.begin(), _Value.end());
2447}
2448
2452
2454 return _firstAttr.get();
2455}
2456
2457void libhtmlpp::HtmlElement::setAttribute(const std::string &name, const std::string &value) {
2458 Attributes* cattr = nullptr;
2459
2460 const char forbidden[] = {'\"'};
2461
2462 auto checkForbidden = [forbidden](const std::string &input){
2463 for(size_t i = 0; i<input.length(); ++i){
2464 for(size_t ii=0; ii<sizeof(forbidden[ii]); ii++){
2465 if(input[i]==forbidden[ii]){
2466 return true;
2467 }
2468 }
2469 }
2470 return false;
2471 };
2472
2473 if(checkForbidden(name) || checkForbidden(value)){
2474 HTMLException e;
2475 e[HTMLException::Error] << "setAttribute " << name.c_str() << "forbidden sign is used !";
2476 throw e;
2477 }
2478
2479 for (cattr= _firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()) {
2480 if(name.size() == cattr->_Key.size() && std::equal(name.begin(),name.end(),cattr->_Key.begin())){
2481 cattr->_Value.clear();
2482 std::copy(value.begin(),value.end(),std::back_inserter(cattr->_Value));
2483 return;
2484 }
2485 }
2486 if (_lastAttr){
2487 _lastAttr->_nextAttr = std::make_unique<Attributes>();
2488 _lastAttr = _lastAttr->_nextAttr.get();
2489 }else {
2490 _firstAttr = std::make_unique<Attributes>();
2491 _lastAttr = _firstAttr.get();
2492 }
2493
2494 cattr = _lastAttr;
2495 std::copy(name.begin(),name.end(),std::back_inserter(cattr->_Key) );
2496 std::copy(value.begin(),value.end(),std::back_inserter(cattr->_Value));
2497}
2498
2499void libhtmlpp::HtmlElement::setIntAttribute(const std::string& name, int value) {
2500 char buf[255];
2501 snprintf(buf,255,"%d",value);
2502 setAttribute(name,buf);
2503}
2504
2505const std::string libhtmlpp::HtmlElement::getAtributte(const std::string& name) const {
2506 for (Attributes* curattr = _firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2507
2508 if (curattr->_Key.size() != name.length() ) {
2509 continue;
2510 }
2511
2512 if (std::equal(name.begin(), name.end(), curattr->_Key.begin())) {
2513 return std::string(curattr->_Value.begin(), curattr->_Value.end());
2514 }
2515 }
2516 return "";
2517}
2518
2519int libhtmlpp::HtmlElement::getIntAtributte(const std::string& name) const
2520{
2521 return atoi(getAtributte(name).c_str());
2522}
2523
2525 _nextAttr=nullptr;
2526}
2527
2529 auto cur = std::move(_nextAttr);
2530 while (cur) {
2531 cur = std::move(cur->_nextAttr);
2532 }
2533}
2534
2538
2539
2541 _firstRow=nullptr;
2542 _lastRow=nullptr;
2543 _count = 0;
2544}
2545
2548
2550 std::unique_ptr<Row> newRow = std::make_unique<Row>(row);
2551
2552 if (!_firstRow) {
2553 _firstRow = std::move(newRow);
2554 _lastRow = _firstRow.get();
2555 } else {
2556 _lastRow->_nextRow = std::move(newRow);
2557 _lastRow = _lastRow->_nextRow.get();
2558 }
2559
2560 ++_count;
2561 return *_lastRow;
2562}
2563
2565 if(!_firstRow || _count<pos){
2567 exp[HTMLException::Error] << "HtmlTable: Row at this position won't exists !";
2568 throw exp;
2569 }
2570 size_t cpos=0;
2571 Row *curel=nullptr;
2572 for(curel=_firstRow.get(); curel; curel=curel->_nextRow.get()){
2573 if(cpos==pos)
2574 return *curel;
2575 ++cpos;
2576 }
2577 return *curel;
2578}
2579
2581 element->setTagname("table");
2582 for(Row *crow=_firstRow.get(); crow; crow=crow->_nextRow.get()){
2583 HtmlElement hrow("tr");
2584 for(Column *ccol=crow->_firstColumn.get(); ccol; ccol=ccol->_nextColumn.get() ){
2585 HtmlElement hcol("td");
2586 TextElement cellContent(ccol->Data.c_str());
2587 hcol.appendChild(cellContent);
2588 hrow.appendChild(hcol);
2589 }
2590 element->appendChild(&hrow);
2591 }
2592}
2593
2594
2597
2599 va_list args;
2600 va_start(args,count);
2601
2602 for (int i = 0; i < count; ++i) {
2603 _header << va_arg(args, const char*);
2604 }
2605
2606}
2607
2609 if (pos >= _count)
2610 return;
2611 if (pos == 0) {
2612 _firstRow = std::move(_firstRow->_nextRow);
2613 _lastRow = (pos == (_count - 1)) ? nullptr : _firstRow.get();
2614 } else {
2615 Row *prev = &(*this)[pos - 1];
2616 if (prev->_nextRow) {
2617 prev->_nextRow = std::move(prev->_nextRow->_nextRow);
2618
2619 if (prev->_nextRow.get() == nullptr) {
2620 _lastRow = prev;
2621 }
2622 }
2623 }
2624
2625 --_count;
2626 if (_count == 0) {
2627 _firstRow = nullptr;
2628 _lastRow = nullptr;
2629 }
2630}
2631
2633 _nextColumn=nullptr;
2634}
2635
2637 _nextColumn=nullptr;
2638 Data = col.Data;
2639}
2640
2642 _nextColumn=nullptr;
2643 Data=data;
2644}
2645
2647 _nextColumn=nullptr;
2648 Data=data.str();
2649}
2650
2653
2655 _nextColumn = std::move(col._nextColumn);
2656 Data = std::move(col.Data);
2657}
2658
2660 _firstColumn=nullptr;
2661 _lastColumn=nullptr;
2662 _nextRow=nullptr;
2663 _count=0;
2664}
2665
2668
2670 _firstColumn=nullptr;
2671 _lastColumn=nullptr;
2672 _nextRow=nullptr;
2673 _count=0;
2674
2675 for(Column *curel=row._firstColumn.get(); curel; curel=curel->_nextColumn.get()){
2676 *this << HtmlString(curel->Data);
2677 }
2678}
2679
2681 std::unique_ptr<Column> ptr = std::make_unique<Column>(std::move(col));
2682
2683 if(_firstColumn){
2684 _lastColumn->_nextColumn=std::move(ptr);
2685 _lastColumn=_lastColumn->_nextColumn.get();
2686 }else{
2687 _firstColumn= std::move(ptr);
2688 _lastColumn=_firstColumn.get();
2689 }
2690 ++_count;
2691 return *this;
2692}
2693
2695 std::unique_ptr<Column> ptr = std::make_unique<Column>(col);
2696
2697 if(_firstColumn){
2698 _lastColumn->_nextColumn=std::move(ptr);
2699 _lastColumn=_lastColumn->_nextColumn.get();
2700 }else{
2701 _firstColumn= std::move(ptr);
2702 _lastColumn=_firstColumn.get();
2703 }
2704 ++_count;
2705 return *this;
2706}
2707
2709 Column col(value);
2710 *this << col;
2711 return *this;
2712}
2713
2715 Column col(value);
2716 *this << col;
2717 return *this;
2718}
2719
2720
2722 HtmlString buf;
2723 buf << value;
2724 *this << buf;
2725 return *this;
2726}
2727
2729 char buf[255];
2730 snprintf(buf,255,"%d",value);
2731 return *this << buf;
2732}
2733
2735 if(!_firstColumn || _count<pos){
2737 exp[HTMLException::Error] << "HtmlTable: Column at this position won't exists !";
2738 throw exp;
2739 }
2740 size_t cpos=0;
2741 Column *curel=nullptr;
2742 for(curel=_firstColumn.get(); curel; curel=curel->_nextColumn.get()){
2743 if(cpos==pos)
2744 return *curel;
2745 ++cpos;
2746 }
2747 return *curel;
2748}
2749
2751 Column *dcol=&(*this)[pos];
2752 try{
2753 Column *prev=&(*this)[pos-1];
2754 prev->_nextColumn=std::move(dcol->_nextColumn);
2755 }catch(...){}
2756 --_count;
2757}
2758
2760 _firstColumn.reset();
2761 _lastColumn = nullptr;
2762 _count = 0;
2763}
Leaf node representing an HTML comment ().
Definition html.h:219
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1499
CommentElement & operator=(const Element &hel)
Definition html.cpp:1476
std::vector< char > _Comment
Definition html.h:235
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1068
void setComment(const std::string &txt)
Definition html.cpp:1486
const std::string getComment()
Definition html.cpp:1491
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:1357
Element * _prevElement
Definition html.h:99
void insertAfter(Element *el)
Definition html.cpp:1280
virtual ~Element()
Definition html.cpp:1350
void insertBefore(Element *el)
Definition html.cpp:1248
Element & operator=(const Element &hel)
Definition html.cpp:1321
std::unique_ptr< Element > _nextElement
Definition html.h:98
virtual int getType() const =0
Definition html.cpp:745
Element * prevElement() const
Definition html.cpp:1335
Element * nextElement() const
Definition html.cpp:1331
const char * what() const noexcept override
Definition exception.cpp:51
int getType() const
Definition html.cpp:2535
const Attributes * firstAttribute() const
Definition html.cpp:2453
HtmlElement * getElementbyTag(const std::string &tag) const
Definition html.cpp:2410
const std::string getAtributte(const std::string &name) const
Definition html.cpp:2505
void _serialelize(const std::vector< char > &in, bool preserveAttrCase=false)
Extracts tag name and attributes from a token vector into an HtmlElement.
Definition html.cpp:913
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1025
void appendChild(const Element *el)
Definition html.cpp:793
int getIntAtributte(const std::string &name) const
Definition html.cpp:2519
void setTagname(const std::string &name)
Definition html.cpp:749
void setAttribute(const std::string &name, const std::string &value)
Definition html.cpp:2457
void remove(Element *el)
Definition html.cpp:868
bool operator==(const HtmlElement *hel)
Definition html.cpp:840
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1068
void insertChild(const Element *el)
Definition html.cpp:760
Element * firstChild() const
Definition html.cpp:2437
const std::string getTagname() const
Definition html.cpp:754
HtmlElement * getElementbyID(const std::string &id) const
Definition html.cpp:2383
HtmlElement & operator=(const HtmlElement &hel)
Definition html.cpp:858
std::unique_ptr< Element > _childElement
Definition html.h:169
void setIntAttribute(const std::string &name, int value)
Definition html.cpp:2499
void loadString(libhtmlpp::HtmlElement &html, const std::string &src)
Parses an HTML source string and copies the result into html.
Definition html.cpp:1948
void saveFile(libhtmlpp::HtmlElement &html, const std::string &path)
Serializes an HtmlElement subtree and writes it to a file.
Definition html.cpp:1975
void loadFile(libhtmlpp::HtmlElement &html, const std::string &path)
Loads an HTML file from disk into a given HtmlElement root.
Definition html.cpp:1919
const std::vector< char > & data() const
Definition html.cpp:360
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:356
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:365
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:350
Row & operator<<(Column &&col)
Definition html.cpp:2680
Column & operator[](size_t pos)
Definition html.cpp:2734
void delColumn(size_t pos)
Definition html.cpp:2750
Row & operator[](size_t pos)
Definition html.cpp:2564
Row & operator<<(const Row &row)
Definition html.cpp:2549
void deleteRow(size_t pos)
Definition html.cpp:2608
void parse(HtmlElement *element)
Definition html.cpp:2595
void insert(HtmlElement *element)
Definition html.cpp:2580
void setHeader(int count,...)
Definition html.cpp:2598
Element representing a <script> tag and its text content.
Definition html.h:244
void setScript(const std::string &txt)
Definition html.cpp:1563
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1575
ScriptElement & operator=(const Element &hel)
Definition html.cpp:1553
std::vector< char > _Script
Definition html.h:268
const std::string getScript()
Definition html.cpp:1567
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1068
Element representing an embedded <svg> tag and its attributes/content.
Definition html.h:277
int getType() const
Definition html.cpp:1729
SvgElement & operator=(const Element &hel)
Definition html.cpp:1710
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1733
const std::vector< char > getSvg()
Definition html.cpp:1725
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1068
std::vector< char > _Svg
Definition html.h:301
void setSvg(const std::string &svg)
Definition html.cpp:1720
Element representing an embedded <textarea> tag and its attributes/content.
Definition html.h:311
std::vector< char > _Text
Definition html.h:335
TextArea & operator=(const Element &hel)
Definition html.cpp:1797
const std::vector< char > getText()
Definition html.cpp:1812
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1068
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1820
int getType() const
Definition html.cpp:1816
void setText(const std::string &text)
Definition html.cpp:1807
Leaf node representing plain text content of an HTML document.
Definition html.h:192
TextElement & operator=(const Element &hel)
Definition html.cpp:1393
std::vector< char > _Text
Definition html.h:210
void setText(const std::string &txt)
Definition html.cpp:1403
int getType() const
Definition html.cpp:1411
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1068
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1415
const std::string getText()
Definition html.cpp:1407
#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:677
void loadString(libhtmlpp::HtmlElement &html, const libhtmlpp::HtmlString *node)
Definition html.cpp:1960
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:2085
void HtmlEncode(const std::string &input, std::string &output)
Encodes special HTML characters in a string and writes into std::string.
Definition html.cpp:682
const std::array< std::string_view, 100 > ContainerTypes
Definition html.cpp:62
void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1068
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:716
const std::string getValue() const
Definition html.cpp:2445
Attributes * nextAttribute() const
Definition html.cpp:2449
const std::string getKey() const
Definition html.cpp:2441