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 // Route through the member push_back() rather than pushing onto _Data
190 // directly -- that's the one place the trailing '\0' sentinel (see
191 // size()'s doc comment) and the NUL-guard are maintained. Pushing raw
192 // left _Data without a sentinel at all, so c_str() returned a
193 // one-element buffer with no NUL terminator -- undefined behavior for
194 // any caller that treats it as a C string -- and length() (before it
195 // started delegating to size()) silently reported 0 for a real
196 // 1-character string.
197 push_back(str);
198}
199
201 if(str.empty())
202 return;
203
204 if (!_Data.empty() && _Data.back() == '\0') {
205 _Data.pop_back();
206 }
207
208 for(auto i = str.begin(); i!=str.end(); ++i){
209 if(*i=='\0')
210 break;
211 _Data.push_back(*i);
212 }
213
214 if (_Data.empty() || _Data.back() != '\0') {
215 _Data.push_back('\0');
216 }
217}
218
221
223 std::copy(str._Data.begin(),str._Data.end(),std::insert_iterator<std::vector<char>>(_Data,_Data.begin()));
224}
225
227 if(!src)
228 return;
229
230 if (!_Data.empty() && _Data.back() == '\0') {
231 _Data.pop_back();
232 }
233 _Data.push_back(src);
234
235 _Data.push_back('\0');
236}
237
238void libhtmlpp::HtmlString::append(const std::string& src) {
239 if(src.empty())
240 return;
241
242 if (!_Data.empty() && _Data.back() == '\0') {
243 _Data.pop_back();
244 }
245
246 for(auto i = src.begin(); i!=src.end(); ++i){
247 if(*i=='\0')
248 break;
249 _Data.push_back(*i);
250 }
251
252 if (_Data.empty() || _Data.back() != '\0') {
253 _Data.push_back('\0');
254 }
255}
256
258 std::copy(hstring._Data.begin(),hstring._Data.end(),std::back_inserter(_Data));
259}
260
261void libhtmlpp::HtmlString::insert(size_t pos, char src){
262 _Data.at(pos)=src;
263}
264
266 _rootEl.reset();
267 _Data.clear();
268}
269
271 return _Data.empty();
272}
273
274
276 append(src);
277 return *this;
278}
279
281 append(hstring);
282 return *this;
283}
284
286 clear();
287 append(src);
288 return *this;
289}
290
292 clear();
293 std::copy(src._Data.begin(),src._Data.end(),std::insert_iterator<std::vector<char>>(_Data,_Data.begin()));
294 return *this;
295}
296
298 return _Data.at(pos);
299}
300
302 append(src);
303 return *this;
304}
305
307 append(src);
308 return *this;
309}
310
312 if(src._Data.data())
313 std::copy(src._Data.begin(),src._Data.end(),std::back_inserter(_Data));
314 return *this;
315}
316
318 char buf[255];
319 snprintf(buf, 255, "%d", src);
320 append(buf);
321 return *this;
322}
323
325 char buf[255];
326 snprintf(buf, 255, "%zu", src);
327 append(buf);
328 return *this;
329}
330
332 push_back(src);
333 return *this;
334}
335
337 return _Data.data();
338}
339
341 // Pure synonym for size() (matching std::string's own size()/length()
342 // convention) -- delegate instead of duplicating the sentinel-aware
343 // logic a second time, which is what let this drift out of sync with
344 // size() in the first place (this used to unconditionally subtract 1,
345 // silently under-counting by one byte -- and never NUL-guarding --
346 // whenever the trailing '\0' sentinel wasn't actually present).
347 return size();
348}
349
351 // append()/push_back()/etc. maintain a trailing '\0' sentinel in _Data so
352 // c_str() stays valid between calls (pop it, write new content, push it
353 // back) — that byte is a private bookkeeping detail, not real content,
354 // so it must not be counted here. Without this, every consumer that
355 // trusts size()/str() to describe the actual string (e.g. building a
356 // larger buffer by concatenating multiple HtmlString results) ends up
357 // with a genuine embedded NUL baked into otherwise-clean text, which
358 // then silently truncates any later strlen()-based use of that buffer.
359 if (!_Data.empty() && _Data.back() == '\0')
360 return _Data.size() - 1;
361 return _Data.size();
362}
363
364const std::string libhtmlpp::HtmlString::str() const{
365 if(_Data.data())
366 return std::string(_Data.begin(), _Data.begin() + static_cast<long>(size()));
367 return "";
368}
369
371 return _Data.data();
372}
373
374const std::vector<char>& libhtmlpp::HtmlString::data() const{
375 return _Data;
376}
377
378
380 HTMLException excp;
381 _buildTree();
382 return *_rootEl;
383}
384
385void libhtmlpp::HtmlString::_buildtreenode(
386 DocElements *firstel,
388 std::unique_ptr<Element> &html)
389{
390 if (!firstel) {
391 HTMLException excp;
392 excp[HTMLException::Error] << "No start Element!";
393 throw excp;
394 }
395
396 struct Frame {
397 DocElements *open; // Opener-DocElement (Start-Tag)
398 DocElements *close; // passender Terminator-DocElement (End-Tag)
399 const DocElements *outer_end; // Grenze der aktuellen Ebene
400 Element *outer_prev_el; // letztes bereits eingebautes Geschwister der äußeren Ebene
401 };
402 std::stack<Frame> stack;
403
404 DocElements *start = firstel;
405 const DocElements *end = lastel; // nullptr bedeutet: bis Ketten-Ende
406
407 Element *prev_el_in_tree = nullptr; // zuletzt in den Baum eingebautes Element
408
409 auto checkContainer = [&](const std::string &tag) {
410 for (size_t i = 0; i < ContainerTypes.size(); ++i) {
411 if (tag == ContainerTypes[i]) return true;
412 }
413 return false;
414 };
415
416 // Leere DocElements überspringen (z. B. Kommentare/Text, die nicht als element abgebildet sind)
417 auto skip_empty = [](DocElements *cur, const DocElements *stop) -> DocElements* {
418 while (cur && cur != stop && (!cur->element)) {
419 cur = cur->nextel.get();
420 }
421 return cur;
422 };
423
424 // Finde zum gegebenen Start-Tag dessen passenden Terminator in [open->nextel, bound).
425 auto find_terminator = [&skip_empty, checkContainer](DocElements *open, const DocElements *bound) -> DocElements* {
426 if (!open || !open->element || open->terminator ||
427 open->element->getType() != HtmlEl) return nullptr;
428
429 const std::string &tag = static_cast<HtmlElement*>(open->element.get())->getTagname();
430 int nest = 0;
431 DocElements *cur = open->nextel.get();
432
433 while (cur && cur != bound) {
434 cur = skip_empty(cur, bound);
435 if (!cur || cur == bound) break;
436
437 if (cur->element && cur->element->getType() == HtmlEl) {
438 const std::string &curtag = static_cast<HtmlElement*>(cur->element.get())->getTagname();
439 if (curtag == tag) {
440 if (cur->terminator) {
441 if (nest == 0) return cur; // passendes End-Tag gefunden
442 --nest;
443 } else {
444 ++nest;
445 }
446 }
447 }
448 cur = cur->nextel.get();
449 }
450
451 // Wenn ein Container nicht geschlossen wurde: Warnung ignorieren oder als einfaches Tag behandeln
452 if (checkContainer(tag)) {
453 // Exception entfernt für nachsichtigeres Parsing
454 // return nullptr;
455 }
456 return nullptr;
457 };
458
459 for (;;) {
460 // bis zum nächsten sinnvollen DocElement laufen
461 start = skip_empty(start, end);
462
463 // Terminator-Knoten als eigenständige Nodes überspringen
464 if (start && start != end && start->terminator) {
465 start = start->nextel.get();
466 continue;
467 }
468
469 // Ende der aktuellen Ebene erreicht?
470 if (!start || start == end) {
471 if (stack.empty()) {
472 // Ganz oben: Root setzen (falls vorhanden)
473 if (firstel->element) {
474 html = std::move(firstel->element);
475 }
476 return;
477 }
478
479 // Frame schließen: Kinderbereich [open->nextel, close) einsammeln
480 Frame fr = stack.top(); stack.pop();
481 HtmlElement *opener_el = static_cast<HtmlElement*>(fr.open->element.get());
482
483 // Alle Kinder zwischen open und close verketten
484 Element* last_child_in_chain = nullptr;
485 {
486 DocElements* cur = fr.open->nextel.get();
487 // bis zum ersten brauchbaren Kind
488 while (cur && cur != fr.close && (!cur->element || cur->terminator)) {
489 cur = cur->nextel.get();
490 }
491
492 // alle nicht-leeren, nicht-Terminierer bis vor close anbinden
493 while (cur && cur != fr.close) {
494 if (cur->element && !cur->terminator) {
495 if (!opener_el->_childElement) {
496 opener_el->_childElement = std::move(cur->element);
497 last_child_in_chain = opener_el->_childElement.get();
498 } else {
499 last_child_in_chain->_nextElement = std::move(cur->element);
500 // _prev (falls genutzt) setzen
501 last_child_in_chain->_nextElement->_prevElement = last_child_in_chain;
502 last_child_in_chain = last_child_in_chain->_nextElement.get();
503 }
504 }
505 cur = cur->nextel.get();
506 // leere/terminator Knoten überspringen
507 while (cur && cur != fr.close && (!cur->element || cur->terminator)) {
508 cur = cur->nextel.get();
509 }
510 }
511 }
512
513 // dieses Container-Element ist nun das "aktuelle" in der äußeren Ebene
514 prev_el_in_tree = opener_el;
515
516 // Wenn es bereits ein vorheriges Geschwister in der äußeren Ebene gibt: verketten
517 if (fr.outer_prev_el) {
518 prev_el_in_tree->_prevElement = fr.outer_prev_el;
519 fr.outer_prev_el->_nextElement = std::move(fr.open->element);
520 prev_el_in_tree = fr.outer_prev_el->_nextElement.get();
521 }
522
523 // für die äußere Ebene fortsetzen
524 prev_el_in_tree = opener_el;
525 start = (fr.close ? fr.close->nextel.get() : nullptr);
526 end = fr.outer_end;
527
528 continue;
529 }
530
531 // Start-Tag eines HTML-Elements: passenden Terminator suchen → in den Stack tauchen
532 if (start->element && !start->terminator && start->element->getType() == HtmlEl) {
533 if (DocElements *close = find_terminator(start, end)) {
534 // neuen Rahmen für diesen Container aufmachen
535 stack.push(Frame{start, close, end, prev_el_in_tree});
536
537 // wir wechseln in die innere Ebene: prev zurücksetzen
538 prev_el_in_tree = nullptr;
539 start = start->nextel.get();
540 end = close;
541 continue;
542 }
543 }
544
545 // "normales" Element (kein Container mit eigenem Terminatorbereich):
546 if (start->element && !start->terminator) {
547 Element *current_el = start->element.get();
548
549 if (prev_el_in_tree) {
550 current_el->_prevElement = prev_el_in_tree;
551 prev_el_in_tree->_nextElement = std::move(start->element);
552 prev_el_in_tree = prev_el_in_tree->_nextElement.get();
553 } else {
554 prev_el_in_tree = current_el;
555 }
556 }
557
558 // weiter zum nächsten DocElement
559 start = start->nextel.get();
560 }
561}
562
568void libhtmlpp::HtmlString::_buildTree() {
569 DocElements* lastEl = nullptr;
570 std::unique_ptr<DocElements> firstEl = nullptr;
571
572 auto is_ws = [](unsigned char ch) -> bool {
573 // space, \t, \n, \r
574 return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
575 };
576
577 auto ascii_tolower = [](unsigned char c) -> unsigned char {
578 return (c >= 'A' && c <= 'Z') ? static_cast<unsigned char>(c + 32) : c;
579 };
580
581 auto starts_with_ci = [&](const char* s, const char* e, const char* k) -> bool {
582 const size_t klen = std::char_traits<char>::length(k);
583 if (static_cast<size_t>(e - s) < klen) return false;
584 for (size_t i = 0; i < klen; ++i) {
585 unsigned char a = static_cast<unsigned char>(s[i]);
586 unsigned char b = static_cast<unsigned char>(k[i]);
587 if (ascii_tolower(a) != ascii_tolower(b)) return false;
588 }
589 return true;
590 };
591
592 auto add_element_node = [&](DocElements** last) {
593 if (!firstEl) {
594 firstEl = std::make_unique<DocElements>();
595 *last = firstEl.get();
596 } else {
597 (*last)->nextel = std::make_unique<DocElements>();
598 (*last)->nextel->prevel = (*last);
599 *last = (*last)->nextel.get();
600 }
601 };
602
603 const char* const base = _Data.data();
604 const size_t n = _Data.size();
605 const char* const end = base + n;
606
607 const char* p = base;
608
609 while (p < end) {
610 const char* wsStart = p;
611 while (p < end && is_ws(static_cast<unsigned char>(*p))) ++p;
612 if (p >= end) break;
613
614 if (*p == HTMLTAG_OPEN) { // '<'
615 // A whitespace-only run directly between two tags (e.g.
616 // "</span> <a>", "</a> <a>") still separates whatever inline
617 // content sits on either side of it -- collapse it to a single
618 // space instead of silently discarding it, or adjacent inline
619 // elements lose the space that keeps their rendered text apart.
620 // Leading whitespace before the very first tag in the document
621 // is not meaningful the same way, so it's still dropped.
622 if (p > wsStart && firstEl) {
623 add_element_node(&lastEl);
624 auto ws = std::make_unique<TextElement>();
625 static_cast<TextElement*>(ws.get())->_Text.push_back(' ');
626 lastEl->element = std::move(ws);
627 lastEl->terminator = false;
628 }
629
630 const char* const remain_end = end;
631 const size_t remain = static_cast<size_t>(remain_end - p);
632
633 if (remain >= 2) {
634 const unsigned char c1 = ascii_tolower(static_cast<unsigned char>(p[1]));
635
636 if (p[1] == '!') {
637 // <!-- ... -->
638 if (starts_with_ci(p, end, "<!--")) {
639 add_element_node(&lastEl);
640 size_t i = static_cast<size_t>(p - base);
641 i = CommentElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
642 p = base + i;
643 continue;
644 }
645 if (starts_with_ci(p, end, "<!doctype")) {
646 const char* close_tag = p;
647 while (close_tag < end && *close_tag != '>') {
648 ++close_tag;
649 }
650 if (close_tag < end) {
651 p = close_tag + 1;
652 } else {
653 p = end;
654 }
655 continue;
656 }
657 } else if (c1 == 's') {
658 if (starts_with_ci(p, end, "<script")) {
659 add_element_node(&lastEl);
660 size_t i = static_cast<size_t>(p - base);
661 i = ScriptElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
662 p = base + i;
663 continue;
664 }
665 if (starts_with_ci(p, end, "<svg")) {
666 add_element_node(&lastEl);
667 size_t i = static_cast<size_t>(p - base);
668 i = SvgElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
669 p = base + i;
670 continue;
671 }
672 if (starts_with_ci(p, end, "<style")) {
673 // Same raw-text rationale as <script> above: CSS
674 // content can contain '<'/'>' (child-combinator
675 // selectors, stray markup pasted into a comment,
676 // duplicate/unclosed <style> tags from a page
677 // builder concatenating snippets, ...) that the
678 // ordinary tag-tokenizing path below would
679 // misinterpret as nested markup, corrupting
680 // everything parsed after it.
681 add_element_node(&lastEl);
682 size_t i = static_cast<size_t>(p - base);
683 i = HtmlElement::parseStyleElement(_Data, lastEl->element, i, lastEl->terminator);
684 p = base + i;
685 continue;
686 }
687 } else if (c1 == 't') {
688 if (starts_with_ci(p, end, "<textarea")) {
689 add_element_node(&lastEl);
690 size_t i = static_cast<size_t>(p - base);
691 i = TextArea::parseElement(_Data, lastEl->element, i, lastEl->terminator);
692 p = base + i;
693 continue;
694 }
695 }
696 }
697
698 {
699 add_element_node(&lastEl);
700 size_t i = static_cast<size_t>(p - base);
701 i = HtmlElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
702 p = base + i;
703 }
704 } else {
705 // Start from wsStart, not p: any whitespace just skipped above
706 // is genuine leading whitespace of this text run (it wasn't
707 // followed by '<', so it's not an inter-tag separator handled
708 // above), and TextElement::parseElement collapses it to a
709 // single leading space itself -- re-skipping it here would
710 // drop that space entirely.
711 add_element_node(&lastEl);
712 size_t i = static_cast<size_t>(wsStart - base);
713 i = TextElement::parseElement(_Data, lastEl->element, i, lastEl->terminator);
714 p = base + i;
715 }
716 }
717
718 _buildtreenode(firstEl.get(), nullptr, _rootEl);
719 // _buildtreenode's own linking logic is intricate enough (see its own
720 // comments) that threading parent-assignment through it directly risks
721 // a subtle new bug for marginal benefit -- this single pass over the
722 // now-correctly-linked tree is simple to get right by inspection
723 // instead.
724 if(_rootEl) HtmlElement::_assignParentPointers(_rootEl.get(), nullptr);
725}
726
734std::ostream& operator<<(std::ostream& os, const libhtmlpp::HtmlString& p) {
735 os << p.str();
736 return os;
737}
738
739void libhtmlpp::HtmlEncode(const std::string &input, std::string &output){
740 size_t ilen=input.length();
741 for(size_t i=0; i<ilen; ++i){
742 size_t ii=0;
743 bool changed=false;
744 while(HtmlSigns[ii][0]){
745 if(input[i]==HtmlSigns[ii][0][0]){
746 output+=HtmlSigns[ii][1];
747 changed=true;
748 }
749 ++ii;
750 }
751 if(!changed)
752 output.push_back(input[i]);
753 }
754}
755
756void libhtmlpp::HtmlDecode(const std::string &input,std::string &output){
757 size_t ilen=input.length();
758 for(size_t i=0; i<ilen; ++i){
759 size_t ii=0;
760 bool changed=false;
761 while(HtmlSigns[ii][0]){
762 if(input.compare(i,strlen(HtmlSigns[ii][1]),HtmlSigns[ii][1]) == 0){
763 output += HtmlSigns[ii][0];
764 changed=true;
765 }
766 ++ii;
767 }
768 if(!changed)
769 output += input[i];
770 }
771}
772
773void libhtmlpp::HtmlDecode(const std::string &input,HtmlString &output){
774 std::string tmp;
775 HtmlDecode(input,tmp);
776 output << tmp;
777 output.parse();
778}
779
780libhtmlpp::HtmlElement::HtmlElement(const std::string &tagname) : HtmlElement(){
781 _TagName.clear();
782 std::copy(tagname.begin(),tagname.end(),std::back_inserter(_TagName));
783}
784
786 _childElement=nullptr;
787 _firstAttr=nullptr;
788 _lastAttr=nullptr;
789}
790
794
798
801
803 return -1;
804}
805
806void libhtmlpp::HtmlElement::setTagname(const std::string &name){
807 _TagName.clear();
808 std::copy(name.begin(),name.begin()+name.length(),std::back_inserter(_TagName));
809}
810
811const std::string libhtmlpp::HtmlElement::getTagname() const{
812 if(_TagName.empty())
813 return "";
814 return std::string(_TagName.begin(),_TagName.end());
815}
816
817void libhtmlpp::HtmlElement::_assignParentPointers(Element* node, Element* parent){
818 while(node){
819 node->_parentElement = parent;
820 // Only plain HtmlEl nodes use this (base) _childElement for real
821 // children via insertChild/appendChild -- ScriptElement/SvgElement/
822 // TextArea each shadow their own separate _childElement instead
823 // (and don't support insertChild/appendChild at all, both deleted),
824 // so descending through the base pointer for those types would
825 // read the wrong field entirely.
826 if(node->getType()==HtmlEl){
827 HtmlElement *hel = static_cast<HtmlElement*>(node);
828 if(hel->_childElement)
829 _assignParentPointers(hel->_childElement.get(), node);
830 }
831 node = node->_nextElement.get();
832 }
833}
834
836 if(_childElement){
837 remove(_childElement.get());
838 }
839
840 switch(el->getType()){
841 case HtmlEl:
842 _childElement=std::make_unique<HtmlElement>();
843 break;
844 case TextEl:
845 _childElement=std::make_unique<TextElement>();
846 break;
847 case CommentEl:
848 _childElement=std::make_unique<CommentElement>();
849 break;
850 case ScriptEL:
851 _childElement=std::make_unique<ScriptElement>();
852 break;
853 case SvgEL:
854 _childElement=std::make_unique<SvgElement>();
855 break;
856 default:
857 HTMLException ex;
858 ex[HTMLException::Critical] << "appendChild: Unknown html element found: "<< el->getType() << " !";
859 throw ex;
860 }
861 _copy(_childElement.get(),el);
862 // _copy re-parents the copied subtree's own descendants (relative to
863 // _childElement.get()) but deliberately never touches _childElement's
864 // OWN parent -- that's this call site's job.
865 _childElement->_parentElement = this;
866}
867
869 insertChild(&el);
870}
871
873 if(!el)
874 return;
875 if(_childElement){
876 Element *prev=nullptr;
877
878 for(Element *curel=_childElement.get(); curel; curel=curel->nextElement()){
879 prev=curel;
880 }
881
882 switch(el->getType()){
883 case HtmlEl:
884 prev->_nextElement=std::make_unique<HtmlElement>();
885 break;
886 case TextEl:
887 prev->_nextElement=std::make_unique<TextElement>();
888 break;
889 case CommentEl:
890 prev->_nextElement=std::make_unique<CommentElement>();
891 break;
892 case ScriptEL:
893 prev->_nextElement=std::make_unique<ScriptElement>();
894 break;
895 case SvgEL:
896 prev->_nextElement=std::make_unique<SvgElement>();
897 break;
898 default:
899 HTMLException ex;
900 ex[HTMLException::Critical] << "appendChild: Unknown html element found: "<< el->getType() << " !";
901 throw ex;
902 }
903
904 _copy(prev->_nextElement.get(),el);
905
906 if(prev->_nextElement){
907 prev->_nextElement->_prevElement=prev;
908 // Siblings share the same parent -- this, not prev.
909 prev->_nextElement->_parentElement=this;
910 }
911 }else{
912 insertChild(el);
913 }
914}
915
917 appendChild(&el);
918}
919
920
922 if(!hel)
923 return false;
924 if( _TagName.size() != hel->_TagName.size())
925 return false;
926 if(std::equal(_TagName.begin(),_TagName.end(),hel->_TagName.begin()))
927 return true;
928 return false;
929}
930
932 if(_TagName.size() != hel._TagName.size())
933 return false;
934 if(std::equal(_TagName.begin(),_TagName.end(),hel._TagName.begin()))
935 return true;
936 return false;
937}
938
940 _copy(this,&hel);
941 return *this;
942}
943
948
950 // Previously a DFS over the whole subtree via an explicit stack/goto,
951 // searching for whoever owns `el` -- with _parentElement, that lookup
952 // is O(1) instead, and the simpler logic below replaces several bugs
953 // that walk found in that DFS: a null-pointer dereference right after
954 // finding `el` via ordinary sibling advancement (the old code set
955 // `cur=nullptr` on a match and then unconditionally dereferenced it on
956 // the very next line); `el` being silently walked past, never unlinked,
957 // when reached by diving into a `_childElement` (the old code never
958 // re-checked the dived-into node against `el`); and removing a parent's
959 // first child never repointing `parent->_childElement`, which quietly
960 // discarded the rest of that sibling chain.
961 if(!el) return;
962
963 Element *parent = el->_parentElement;
964 Element *prevSibling = el->_prevElement;
965 // Whoever currently owns `el` via a unique_ptr (parent->_childElement,
966 // or a previous sibling's _nextElement) is about to be reassigned away
967 // from `el` below, which destroys `el` immediately as part of that
968 // assignment (reassigning a unique_ptr destroys whatever it used to
969 // own) -- so everything needed from `el` must be captured first.
970 // release() (not move/get()) detaches el's own next-sibling chain
971 // without destroying anything, unlike move-assigning it while it's
972 // still reachable through `el`.
973 std::unique_ptr<Element> elsNext(el->_nextElement.release());
974
975 if(parent && parent->getType()==HtmlEl &&
976 static_cast<HtmlElement*>(parent)->_childElement.get()==el){
977 if(elsNext) elsNext->_prevElement = nullptr;
978 static_cast<HtmlElement*>(parent)->_childElement = std::move(elsNext); // destroys el
979 } else if(prevSibling){
980 if(elsNext) elsNext->_prevElement = prevSibling;
981 prevSibling->_nextElement = std::move(elsNext); // destroys el
982 }
983 // else: el has neither a parent nor a previous sibling (e.g. a
984 // rootless standalone node) -- nothing owns it via a reassignable
985 // unique_ptr, so it's left unlinked-from but not destroyed.
986
987 // el must not be touched below this point in the two branches above --
988 // it no longer exists.
989}
990
998void libhtmlpp::HtmlElement::_serialelize(const std::vector<char>& in, bool preserveAttrCase) {
999 _TagName.clear();
1000
1001 auto is_space = [](unsigned char c) -> bool {
1002 return c == ' ' || c == '\t' || c == '\n' || c == '\r';
1003 };
1004 auto tolower_ascii = [](unsigned char c) -> unsigned char {
1005 return (c >= 'A' && c <= 'Z') ? static_cast<unsigned char>(c + 32) : c;
1006 };
1007
1008 size_t i = 0, n = in.size();
1009
1010 if (i < n && in[i] == '<') ++i;
1011 while (i < n && is_space(static_cast<unsigned char>(in[i]))) ++i;
1012
1013 bool end_tag = false;
1014 if (i < n && in[i] == '/') {
1015 end_tag = true;
1016 ++i;
1017 while (i < n && is_space(static_cast<unsigned char>(in[i]))) ++i;
1018 }
1019
1020 size_t r = n;
1021 while (r > i && is_space(static_cast<unsigned char>(in[r - 1]))) --r;
1022 if (r > i && in[r - 1] == '>') --r; // ignore '>' if present
1023 while (r > i && is_space(static_cast<unsigned char>(in[r - 1]))) --r;
1024
1025 if (i >= r) {
1026 HTMLException excp;
1027 throw excp[HTMLException::Critical] << "no tag in element found!";
1028 }
1029
1030 const size_t name_start = i;
1031 while (i < r) {
1032 unsigned char c = static_cast<unsigned char>(in[i]);
1033 if (is_space(c) || c == '/' || c == '>') break;
1034 ++i;
1035 }
1036 const size_t name_end = i;
1037 if (name_start == name_end) {
1038 HTMLException excp;
1039 throw excp[HTMLException::Critical] << "no tag in element found!";
1040 }
1041
1042 _TagName.assign(in.begin() + name_start, in.begin() + name_end);
1043 for (char& ch : _TagName) ch = static_cast<char>(tolower_ascii(static_cast<unsigned char>(ch)));
1044
1045 if (end_tag) {
1046 return;
1047 }
1048
1049 while (i < r) {
1050 while (i < r && is_space(static_cast<unsigned char>(in[i]))) ++i;
1051 if (i >= r) break;
1052
1053 if (in[i] == '/') {
1054 ++i;
1055 while (i < r && is_space(static_cast<unsigned char>(in[i]))) ++i;
1056 break;
1057 }
1058
1059 const size_t kstart = i;
1060 while (i < r) {
1061 unsigned char c = static_cast<unsigned char>(in[i]);
1062 if (is_space(c) || c == '=' || c == '/' || c == '>') break;
1063 ++i;
1064 }
1065 const size_t kend = i;
1066 if (kstart == kend) {
1067 ++i;
1068 continue;
1069 }
1070
1071 std::string key(in.begin() + kstart, in.begin() + kend);
1072 if (!preserveAttrCase) {
1073 for (char& ch : key) ch = static_cast<char>(tolower_ascii(static_cast<unsigned char>(ch)));
1074 }
1075
1076 while (i < r && is_space(static_cast<unsigned char>(in[i]))) ++i;
1077
1078 std::string val;
1079
1080 if (i < r && in[i] == '=') {
1081 ++i;
1082 while (i < r && is_space(static_cast<unsigned char>(in[i]))) ++i;
1083
1084 if (i < r && (in[i] == '"' || in[i] == '\'')) {
1085 char quote = in[i++];
1086 const size_t vstart = i;
1087 while (i < r && in[i] != quote) ++i;
1088 const size_t vend = i;
1089 val.assign(in.begin() + vstart, in.begin() + vend);
1090 if (i < r && in[i] == quote) ++i;
1091 } else {
1092 const size_t vstart = i;
1093 while (i < r) {
1094 unsigned char c = static_cast<unsigned char>(in[i]);
1095 if (is_space(c) || c == '/' || c == '>') break;
1096 ++i;
1097 }
1098 const size_t vend = i;
1099 val.assign(in.begin() + vstart, in.begin() + vend);
1100 }
1101 } else {
1102 val.clear();
1103 }
1104
1105 setAttribute(key, val);
1106 }
1107}
1108
1109
1111 const std::vector<char>& in,
1112 std::unique_ptr<libhtmlpp::Element>& el,
1113 size_t start,
1114 bool& termination
1115){
1116 el = std::make_unique<HtmlElement>();
1117 termination = false;
1118
1119 size_t i = start;
1120 if (i >= in.size() || in[i] != HTMLTAG_OPEN) return i;
1121
1122 ++i;
1123
1124 while (i < in.size() && std::isspace(static_cast<unsigned char>(in[i]))) ++i;
1125
1126
1127
1128 // Quote-aware scan for this tag's own '>' -- a '<'/'>' inside a quoted
1129 // attribute value (e.g. title="a < b") doesn't end the tag. Critically,
1130 // an UNQUOTED '<' encountered here means this tag itself was never
1131 // terminated (a missing '>', e.g. a malformed "</div" straight into the
1132 // next markup) -- stopping at that '<' instead of scanning past it
1133 // keeps that next tag intact for the caller to (re-)dispatch on its own
1134 // merits (e.g. a following <script> still gets ScriptElement's raw-text
1135 // handling) rather than silently swallowing it as bogus "attribute"
1136 // content of this broken tag, whose own closing '>' would otherwise
1137 // wrongly end up terminating THIS one instead.
1138 size_t close = i;
1139 bool foundClose = false;
1140 char quote = 0;
1141 while (close < in.size()) {
1142 char c = in[close];
1143 if (quote) {
1144 if (c == quote) quote = 0;
1145 } else if (c == HTMLTAG_CLOSE) { // '>'
1146 foundClose = true;
1147 break;
1148 } else if (c == HTMLTAG_OPEN) { // '<'
1149 break;
1150 } else if (c == '"' || c == '\'') {
1151 quote = c;
1152 }
1153 ++close;
1154 }
1155
1156 size_t k = i;
1157 while (k < close && std::isspace(static_cast<unsigned char>(in[k]))) ++k;
1158
1159 std::vector<char> tel;
1160
1161 if (k < close && in[k] == HTMLTAG_TERMINATE) { // '/'
1162 termination = true;
1163 ++k;
1164 while (k < close && std::isspace(static_cast<unsigned char>(in[k]))) ++k;
1165 }
1166
1167 tel.insert(tel.end(), in.begin() + k, in.begin() + close);
1168
1169 reinterpret_cast<HtmlElement*>(el.get())->_serialelize(tel);
1170
1171 return foundClose ? close + 1 : close;
1172}
1173
1175 const std::vector<char>& in,
1176 std::unique_ptr<Element>& el,
1177 size_t start,
1178 bool& termination
1179){
1180 termination = false;
1181 el = std::make_unique<HtmlElement>("style");
1182 auto* self = static_cast<HtmlElement*>(el.get());
1183
1184 size_t i = start;
1185 if (i >= in.size() || in[i] != HTMLTAG_OPEN) {
1186 return start;
1187 }
1188
1189 auto iequals = [](char a, char b) {
1190 return std::tolower(static_cast<unsigned char>(a)) ==
1191 std::tolower(static_cast<unsigned char>(b));
1192 };
1193 auto match_ci = [&](size_t pos, const char* k) -> bool {
1194 for (size_t j = 0; k[j]; ++j) {
1195 if (pos + j >= in.size() || !iequals(in[pos + j], k[j])) {
1196 return false;
1197 }
1198 }
1199 return true;
1200 };
1201
1202 ++i; // consume '<'
1203 while (i < in.size() && std::isspace(static_cast<unsigned char>(in[i]))) ++i;
1204
1205 const char* tag_keyword = "style";
1206 size_t keyword_len = std::char_traits<char>::length(tag_keyword);
1207
1208 if (i + keyword_len >= in.size() || !match_ci(i, tag_keyword)) {
1209 // Not actually a <style -- skip to the next '>' like the other
1210 // dedicated parseElement variants do for their own mismatched case.
1211 while (i < in.size() && in[i] != HTMLTAG_CLOSE) ++i;
1212 if (i < in.size()) ++i;
1213 return i;
1214 }
1215 i += keyword_len; // consume "style"
1216
1217 // Find the opening tag's own '>' (attributes only -- style's opening
1218 // tag is ordinary markup, just its content that needs raw-text care).
1219 while (i < in.size() && in[i] != HTMLTAG_CLOSE) ++i;
1220
1221 if (i > start && i < in.size() && in[i] == HTMLTAG_CLOSE) {
1222 std::vector<char> raw_tag_data(in.begin() + start, in.begin() + i + 1);
1223 self->_serialelize(raw_tag_data);
1224 }
1225
1226 if (i >= in.size() || in[i] != HTMLTAG_CLOSE) {
1227 // Opening tag itself was never closed (e.g. EOF mid-attribute-list).
1228 return i;
1229 }
1230
1231 ++i; // consume '>'
1232 size_t content_begin = i;
1233
1234 // Raw-text scan for the literal closing sequence: </style -- everything
1235 // in between, including any '<'/'>' a CSS selector/comment happens to
1236 // contain, is opaque content, exactly like <script>'s own handling.
1237 for (; i < in.size(); ++i) {
1238 if (in[i] == HTMLTAG_OPEN && match_ci(i, "</style")) {
1239 if (i > content_begin) {
1240 std::string cssText(in.begin() + content_begin, in.begin() + i);
1241 TextElement textEl(cssText);
1242 self->appendChild(&textEl);
1243 }
1244
1245 size_t closing_tag_end_pos = i + keyword_len + 2; // +2 for '</'
1246 while (closing_tag_end_pos < in.size() && in[closing_tag_end_pos] != HTMLTAG_CLOSE) {
1247 ++closing_tag_end_pos;
1248 }
1249
1250 if (closing_tag_end_pos < in.size() && in[closing_tag_end_pos] == HTMLTAG_CLOSE) {
1251 return closing_tag_end_pos + 1;
1252 }
1253 return closing_tag_end_pos;
1254 }
1255 }
1256
1257 // No closing </style> before EOF -- capture whatever's left, same
1258 // best-effort fallback ScriptElement::parseElement uses.
1259 if (in.size() > content_begin) {
1260 std::string cssText(in.begin() + content_begin, in.end());
1261 TextElement textEl(cssText);
1262 self->appendChild(&textEl);
1263 }
1264 return i;
1265}
1266
1267namespace libhtmlpp {
1268
1270 if(!dest || !src)
1271 return;
1272
1273 // `dest` itself is reassigned throughout the copy loop below --
1274 // captured here so the parent-pointer fixup at the end can still
1275 // find the original top-level destination.
1276 libhtmlpp::Element* const originalDest = dest;
1277
1278 const libhtmlpp::Element* prev=nullptr;
1279
1280 struct cpyel {
1281 cpyel(){
1282 destin = nullptr;
1283 source = nullptr;
1284 };
1285
1286 cpyel(const cpyel &src){
1287 destin=src.destin;
1288 source=src.source;
1289 };
1290
1291 ~cpyel(){
1292 }
1293
1294 libhtmlpp::Element *destin;
1295 libhtmlpp::Element *source;
1296 };
1297
1298 std::stack<cpyel> cpylist;
1299
1300 NEWEL:
1301
1302 if(src->getType()==HtmlEl && dest->getType()==HtmlEl){
1303 ((libhtmlpp::HtmlElement*)dest)->_TagName=(((libhtmlpp::HtmlElement*)src)->_TagName);
1304 for(libhtmlpp::HtmlElement::Attributes *cattr=((libhtmlpp::HtmlElement*)src)->_firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()){
1305 ((libhtmlpp::HtmlElement*)dest)->setAttribute(
1306 std::string(
1307 cattr->_Key.begin(),
1308 cattr->_Key.end()
1309 ),std::string(
1310 cattr->_Value.begin(),
1311 cattr->_Value.end()
1312 )
1313 );
1314 }
1315
1316 if(((libhtmlpp::HtmlElement*)src)->_childElement){
1317 switch(((libhtmlpp::HtmlElement*)src)->_childElement->getType()){
1318 case HtmlEl:
1319 ((libhtmlpp::HtmlElement*)dest)->_childElement=std::make_unique<HtmlElement>();
1320 break;
1321 case TextEl:
1322 ((libhtmlpp::HtmlElement*)dest)->_childElement =std::make_unique<TextElement>();
1323 break;
1324 case CommentEl:
1325 ((libhtmlpp::HtmlElement*)dest)->_childElement = std::make_unique<CommentElement>();
1326 break;
1327 case ScriptEL:
1328 ((libhtmlpp::HtmlElement*)dest)->_childElement = std::make_unique<ScriptElement>();
1329 break;
1330 case SvgEL:
1331 ((libhtmlpp::HtmlElement*)dest)->_childElement = std::make_unique<SvgElement>();
1332 break;
1333 case TextAreaEL:
1334 ((libhtmlpp::HtmlElement*)dest)->_childElement = std::make_unique<TextArea>();
1335 break;
1336 default:
1337 HTMLException ex;
1338 ex[HTMLException::Critical] << "_copy: Unknown html element found !";
1339 throw ex;
1340 }
1341 cpyel childel;
1342 childel.destin=((libhtmlpp::HtmlElement*)dest)->_childElement.get();
1343 childel.source=((libhtmlpp::HtmlElement*)src)->_childElement.get();
1344 cpylist.push(childel);
1345 }
1346 }else if(src->getType()==libhtmlpp::ScriptEL && dest->getType()== libhtmlpp::ScriptEL){
1347 ((libhtmlpp::ScriptElement*)dest)->_TagName=(((libhtmlpp::ScriptElement*)src)->_TagName);
1348 for(libhtmlpp::ScriptElement::Attributes *cattr=((libhtmlpp::ScriptElement*)src)->_firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()){
1349 if(!cattr->_Value.empty()){
1350 ((libhtmlpp::ScriptElement*)dest)->setAttribute(
1351 std::string(
1352 cattr->_Key.begin(),
1353 cattr->_Key.end()
1354 ),std::string(
1355 cattr->_Value.begin(),
1356 cattr->_Value.end()
1357 )
1358 );
1359 }else{
1360 ((libhtmlpp::ScriptElement*)dest)->setAttribute(std::string(cattr->_Key.begin(),cattr->_Key.end()),"");
1361 }
1362 }
1363 ((ScriptElement*)dest)->_Script=(((ScriptElement*)src)->_Script);
1364 }else if(src->getType()==libhtmlpp::SvgEL && dest->getType()== libhtmlpp::SvgEL){
1365 ((libhtmlpp::SvgElement*)dest)->_TagName=(((libhtmlpp::SvgElement*)src)->_TagName);
1366 for(libhtmlpp::SvgElement::Attributes *cattr=((libhtmlpp::SvgElement*)src)->_firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()){
1367 if(!cattr->_Value.empty()){
1368 ((libhtmlpp::SvgElement*)dest)->setAttribute(
1369 std::string(
1370 cattr->_Key.begin(),
1371 cattr->_Key.end()
1372 ),std::string(
1373 cattr->_Value.begin(),
1374 cattr->_Value.end()
1375 )
1376 );
1377 }else{
1378 ((libhtmlpp::SvgElement*)dest)->setAttribute(std::string(cattr->_Key.begin(),cattr->_Key.end()),"");
1379 }
1380 }
1381 ((SvgElement*)dest)->_Svg=(((SvgElement*)src)->_Svg);
1382 }else if(src->getType()==libhtmlpp::TextAreaEL&& dest->getType()== libhtmlpp::TextAreaEL){
1383 ((libhtmlpp::TextArea*)dest)->_TagName=(((libhtmlpp::TextArea*)src)->_TagName);
1384 for(libhtmlpp::TextArea::Attributes *cattr=((libhtmlpp::TextArea*)src)->_firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()){
1385 if(!cattr->_Value.empty()){
1386 ((libhtmlpp::TextArea*)dest)->setAttribute(
1387 std::string(
1388 cattr->_Key.begin(),
1389 cattr->_Key.end()
1390 ),std::string(
1391 cattr->_Value.begin(),
1392 cattr->_Value.end()
1393 )
1394 );
1395 }else{
1396 ((libhtmlpp::TextArea*)dest)->setAttribute(std::string(cattr->_Key.begin(),cattr->_Key.end()),"");
1397 }
1398 }
1399 ((TextArea*)dest)->_Text=(((TextArea*)src)->_Text);
1400 }else if(src->getType()==libhtmlpp::TextEl && dest->getType()== libhtmlpp::TextEl){
1401 ((TextElement*)dest)->_Text=(((TextElement*)src)->_Text);
1402 }else if(src->getType()==libhtmlpp::CommentEl && dest->getType()== libhtmlpp::CommentEl){
1403 ((CommentElement*)dest)->_Comment=(((CommentElement*)src)->_Comment);
1404 }
1405
1406 if(prev)
1407 dest->_prevElement=(Element*)prev;
1408
1409 Element* next=src->nextElement();
1410
1411 if(next){
1412 switch(next->getType()){
1413 case HtmlEl:
1414 dest->_nextElement= std::make_unique<HtmlElement>();
1415 break;
1416 case TextEl:
1417 dest->_nextElement= std::make_unique<TextElement>();
1418 break;
1419 case CommentEl:
1420 dest->_nextElement= std::make_unique<CommentElement>();
1421 break;
1422 case ScriptEL:
1423 dest->_nextElement= std::make_unique<ScriptElement>();
1424 break;
1425 case SvgEL:
1426 dest->_nextElement= std::make_unique<SvgElement>();
1427 break;
1428 case TextAreaEL:
1429 dest->_nextElement= std::make_unique<TextArea>();
1430 break;
1431 default:
1432 HTMLException ex;
1433 ex[HTMLException::Critical] << "_copy: Unknown next html element found !";
1434 throw ex;
1435 }
1436 prev=dest;
1437 src=next;
1438 dest=dest->_nextElement.get();
1439 goto NEWEL;
1440 }
1441
1442 if(!cpylist.empty()){
1443 cpyel childel(cpylist.top());
1444 prev=nullptr;
1445 dest=childel.destin;
1446 src=childel.source;
1447 cpylist.pop();
1448 goto NEWEL;
1449 }
1450
1451 // Re-parent the whole copied subtree relative to originalDest --
1452 // never derived from src's original ancestry, since this copy may
1453 // be landing somewhere entirely different (e.g. a fresh standalone
1454 // object, or as a new child elsewhere). originalDest's OWN parent
1455 // is deliberately left untouched: that's always the caller's
1456 // responsibility (insertChild/appendChild set it explicitly right
1457 // after calling _copy; operator=/copy-ctors leave it as whatever
1458 // it already was, since copying INTO an object doesn't change
1459 // where that object itself lives).
1460 if(originalDest->getType()==HtmlEl){
1461 libhtmlpp::HtmlElement *destHel = static_cast<libhtmlpp::HtmlElement*>(originalDest);
1462 if(destHel->_childElement)
1463 libhtmlpp::HtmlElement::_assignParentPointers(destHel->_childElement.get(), originalDest);
1464 }
1465 return;
1466 }
1467};
1468
1470 std::unique_ptr<Element> nel;
1471 switch(el->getType()){
1472 case HtmlEl:
1473 nel->_nextElement= std::make_unique<HtmlElement>();
1474 break;
1475 case TextEl:
1476 nel->_nextElement= std::make_unique<TextElement>();
1477 break;
1478 case CommentEl:
1479 nel->_nextElement= std::make_unique<CommentElement>();
1480 break;
1481 case ScriptEL:
1482 nel->_nextElement= std::make_unique<ScriptElement>();
1483 break;
1484 case SvgEL:
1485 nel->_nextElement= std::make_unique<SvgElement>();
1486 break;
1487 case TextAreaEL:
1488 nel->_nextElement= std::make_unique<TextArea>();
1489 break;
1490 default:
1491 HTMLException ex;
1492 ex[HTMLException::Critical] << "_copy: Unknown next html element found !";
1493 throw ex;
1494 }
1495 _copy(nel.get(),el);
1496 std::unique_ptr<Element> prev=std::move(_prevElement->_nextElement);
1497 _prevElement->_nextElement=std::move(nel);
1498 nel->_nextElement=std::move(prev);
1499}
1500
1502 Element *nexel=nullptr,*prev=nullptr;
1503
1504 switch(el->getType()){
1505 case HtmlEl:
1506 _nextElement= std::make_unique<HtmlElement>();
1507 break;
1508 case TextEl:
1509 _nextElement= std::make_unique<TextElement>();
1510 break;
1511 case CommentEl:
1512 _nextElement= std::make_unique<CommentElement>();
1513 break;
1514 case ScriptEL:
1515 _nextElement= std::make_unique<ScriptElement>();
1516 break;
1517 case SvgEL:
1518 _nextElement= std::make_unique<SvgElement>();
1519 break;
1520 case TextAreaEL:
1521 _nextElement= std::make_unique<TextArea>();
1522 break;
1523 default:
1524 HTMLException ex;
1525 ex[HTMLException::Critical] << "_copy: Unknown next html element found !";
1526 throw ex;
1527 }
1528
1529 _copy(_nextElement.get(),el);
1530
1531 nexel=_nextElement.get();
1532
1533 while(nexel){
1534 prev=nexel;
1535 nexel=nexel->nextElement();
1536 }
1537
1538 nexel=prev;
1539
1540}
1541
1543 _copy(this,&hel);
1544 return *this;
1545}
1546
1548 _copy(this,hel);
1549 return *this;
1550}
1551
1553 return _nextElement.get();
1554}
1555
1557 return _prevElement;
1558}
1559
1561 return _parentElement;
1562}
1563
1565 _prevElement=nullptr;
1566 _nextElement=nullptr;
1567}
1568
1570 _prevElement=nullptr;
1571 _nextElement=nullptr;
1572 _copy(this,&el);
1573}
1574
1576 auto cur = std::move(_nextElement);
1577 while (cur) {
1578 cur = std::move(cur->_nextElement);
1579 }
1580};
1581
1583 Element *curel=this;
1584
1585 while(curel){
1586 Element *next=curel->_nextElement.get();
1587
1588 if(curel==el){
1589 // Whoever owns curel via a unique_ptr (curel->_prevElement's
1590 // own _nextElement) is about to be reassigned away from curel
1591 // below, which destroys curel immediately as part of that
1592 // assignment -- so everything needed from curel (its previous
1593 // sibling, and its own next-sibling chain, detached via
1594 // release() rather than a move that's still reachable through
1595 // curel) must be captured first, and curel must not be
1596 // touched afterward.
1597 Element *prev = curel->_prevElement;
1598 std::unique_ptr<Element> curelsNext(curel->_nextElement.release());
1599 if(next) next->_prevElement=prev;
1600 if(prev)
1601 prev->_nextElement=std::move(curelsNext); // destroys curel
1602 // else: curel has no previous sibling (e.g. `this` itself was
1603 // passed as `el`) -- the base class has no notion of a
1604 // parent's own child-pointer to repoint, so curel is left
1605 // unlinked-from but not destroyed.
1606 return;
1607 }
1608
1609 curel=next;
1610 }
1611}
1612
1613
1616
1618 setText(txt);
1619}
1620
1622 _copy(this,&texel);
1623}
1624
1627
1628
1630 _copy(this,&hel);
1631 return *this;
1632}
1633
1635 _copy(this,hel);
1636 return *this;
1637}
1638
1639void libhtmlpp::TextElement::setText(const std::string& txt){
1640 std::copy(txt.begin(),txt.end(),std::back_inserter(_Text));
1641}
1642
1644 return std::string(_Text.begin(),_Text.end());
1645}
1646
1650
1652 const std::vector<char>& in,
1653 std::unique_ptr<libhtmlpp::Element>& el,
1654 size_t start,
1655 bool &termination
1656){
1657 termination = false;
1658
1659 std::vector<char> buf;
1660 buf.reserve(64);
1661 bool last_was_space = false;
1662
1663 // A whitespace run always collapses to exactly one space, whether it's
1664 // leading, internal, trailing, or the entire run -- never to zero. A
1665 // leading run is just as significant as an internal one: it's what
1666 // separates this text from whatever inline element preceded it (e.g.
1667 // "</span> next word", or two adjacent elements separated only by
1668 // whitespace like "<a>A</a> <a>B</a>"). Dropping it silently glues
1669 // words together in the rendered output.
1670 size_t i = start;
1671 while (i < in.size()) {
1672 char c = in[i];
1673 if (c == HTMLTAG_OPEN) {
1674 break;
1675 }
1676
1677 if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
1678 if (!last_was_space) {
1679 buf.push_back(' ');
1680 last_was_space = true;
1681 }
1682 ++i;
1683 continue;
1684 }
1685
1686 buf.push_back(c);
1687 last_was_space = false;
1688 ++i;
1689 }
1690
1691 if (!buf.empty()) {
1692 auto text = std::make_unique<TextElement>();
1693 (static_cast<TextElement*>(text.get()))->_Text.insert(
1694 (static_cast<TextElement*>(text.get()))->_Text.end(),
1695 buf.begin(), buf.end()
1696 );
1697 el = std::move(text);
1698 }
1699
1700 return i;
1701}
1702
1703
1706
1708 _copy(this,&comel);
1709}
1710
1713
1714
1716 _copy(this,&hel);
1717 return *this;
1718}
1719
1721 _copy(this,hel);
1722 return *this;
1723}
1724
1725void libhtmlpp::CommentElement::setComment(const std::string& txt){
1726 std::copy(txt.begin(),txt.end(),
1727 std::insert_iterator<std::vector<char>>(_Comment,_Comment.begin()));
1728}
1729
1731 return std::string(_Comment.begin(),_Comment.end());
1732}
1733
1737
1739 const std::vector<char>& in,
1740 std::unique_ptr<Element>& el,
1741 size_t start,
1742 bool& termination
1743){
1744 termination = false;
1745
1746 size_t i = start;
1747 if (i + 3 >= in.size()) return i;
1748
1749 if (!(in[i] == '<' && in[i+1] == '!' && in[i+2] == '-' && in[i+3] == '-')) {
1750 return i;
1751 }
1752
1753 el = std::make_unique<CommentElement>();
1754
1755 i += 4;
1756 const size_t content_begin = i;
1757
1758 while (i + 2 < in.size()) {
1759 if (in[i] == '-' && in[i+1] == '-' && in[i+2] == '>') {
1760 break;
1761 }
1762 ++i;
1763 }
1764
1765 auto* self = static_cast<CommentElement*>(el.get());
1766 if (i > content_begin) {
1767 self->_Comment.insert(self->_Comment.end(),
1768 in.begin() + content_begin,
1769 in.begin() + i);
1770 }
1771
1772 if (i + 2 < in.size()) {
1773 i += 3;
1774 } else {
1775 i = in.size();
1776 }
1777 return i;
1778}
1779
1780
1783
1785 _copy(this,&scriptsrc);
1786}
1787
1790
1791
1793 _copy(this,&hel);
1794 return *this;
1795}
1796
1798 _copy(this,hel);
1799 return *this;
1800}
1801
1802void libhtmlpp::ScriptElement::setScript(const std::string& script){
1803 _Script.assign(script.begin(),script.end());
1804}
1805
1807 return std::string(_Script.begin(),_Script.end());
1808}
1809
1813
1815 const std::vector<char>& in,
1816 std::unique_ptr<Element>& el,
1817 size_t start,
1818 bool& termination
1819){
1820termination = false;
1821 el = std::make_unique<ScriptElement>();
1822 auto* self = static_cast<ScriptElement*>(el.get());
1823
1824 size_t i = start;
1825 if (i >= in.size() || in[i] != '<') {
1826 // If it doesn't start with '<', we can't parse a tag here.
1827 return start;
1828 }
1829
1830 // Helper to perform case-insensitive comparison
1831 auto iequals = [](char a, char b) {
1832 return std::tolower(static_cast<unsigned char>(a)) ==
1833 std::tolower(static_cast<unsigned char>(b));
1834 };
1835
1836 // Helper to perform case-insensitive match for a keyword starting at 'pos'
1837 auto match_ci = [&](size_t pos, const char* k) -> bool {
1838 for (size_t j = 0; k[j]; ++j) {
1839 if (pos + j >= in.size() || !iequals(in[pos + j], k[j])) {
1840 return false;
1841 }
1842 }
1843 return true;
1844 };
1845
1846 // --- 1. Validate Opening Tag Name (<script) ---
1847 ++i; // Consume '<'
1848
1849 // Skip leading whitespace after '<'
1850 while (i < in.size() && std::isspace(static_cast<unsigned char>(in[i]))) {
1851 ++i;
1852 }
1853
1854 const char* tag_keyword = "script";
1855 size_t keyword_len = std::char_traits<char>::length(tag_keyword);
1856
1857 if (i + keyword_len >= in.size() || !match_ci(i, tag_keyword)) {
1858 // Tag name doesn't match "script"
1859 // Skip till next '>' and return the position after it.
1860 while (i < in.size() && in[i] != '>') {
1861 ++i;
1862 }
1863 if (i < in.size()) {
1864 ++i; // Consume '>'
1865 }
1866 return i;
1867 }
1868 i += keyword_len; // Consume "script"
1869
1870 // --- 2. Extract Opening Tag and Attributes ---
1871 // Find the closing '>' of the opening tag.
1872 size_t tag_end = i;
1873 while (i < in.size() && in[i] != '>') {
1874 ++i;
1875 }
1876
1877 // Capture the raw opening tag data (including '<script' and attributes) for serialization.
1878 if (i > start && in[i] == '>') {
1879 // Copy data from '<' (start) up to and including '>' (i)
1880 std::vector<char> raw_tag_data(in.begin() + start, in.begin() + i + 1);
1881 self->_serialelize(raw_tag_data);
1882 }
1883
1884 if (i >= in.size() || in[i] != '>') {
1885 // The tag was never closed (e.g., '<script src="..." EOF')
1886 return i;
1887 }
1888
1889 ++i; // Consume '>' and move to content start
1890 size_t content_begin = i;
1891
1892 // --- 3. Extract Script Content (CDATA-like section) ---
1893 for (; i < in.size(); ++i) {
1894 // Look for the start of the closing tag sequence: </script
1895 if (in[i] == '<' && match_ci(i, "</script")) {
1896 size_t content_end = i;
1897
1898 // 3a. Extract content preceding the closing tag
1899 if (content_end > content_begin) {
1900 // FIX: Use std::vector::insert instead of non-existent append
1901 self->_Script.insert(self->_Script.end(),
1902 in.begin() + content_begin,
1903 in.begin() + content_end);
1904 }
1905
1906 // 3b. Find the end of the closing tag: </script>
1907 size_t closing_tag_end_pos = i + keyword_len + 2; // +2 for '</'
1908
1909 // Skip any characters/whitespace between </script and the final '>'
1910 while (closing_tag_end_pos < in.size() && in[closing_tag_end_pos] != '>') {
1911 ++closing_tag_end_pos;
1912 }
1913
1914 if (closing_tag_end_pos < in.size() && in[closing_tag_end_pos] == '>') {
1915 i = closing_tag_end_pos + 1; // Position after '>'
1916 return i;
1917 }
1918
1919 // If we found "</script" but not the final ">", return the last processed position.
1920 return closing_tag_end_pos;
1921 }
1922 }
1923
1924 // --- 4. End of Input Reached ---
1925 // If the input ends without a closing </script> tag, capture the remaining content.
1926 if (in.size() > content_begin) {
1927 // FIX: Use std::vector::insert instead of non-existent append
1928 self->_Script.insert(self->_Script.end(),
1929 in.begin() + content_begin,
1930 in.end());
1931 }
1932
1933 return i;
1934}
1935
1936
1937
1940
1942 _copy(this,&svgsrc);
1943}
1944
1947
1948
1950 _copy(this,&hel);
1951 return *this;
1952}
1953
1955 _copy(this,hel);
1956 return *this;
1957}
1958
1959void libhtmlpp::SvgElement::setSvg(const std::string& script){
1960 std::copy(script.begin(),script.end(),
1961 std::insert_iterator<std::vector<char>>(_Svg,_Svg.begin()));
1962}
1963
1964const std::vector<char>libhtmlpp::SvgElement::getSvg(){
1965 return _Svg;
1966}
1967
1971
1972size_t libhtmlpp::SvgElement::parseElement(const std::vector<char>& in,
1973 std::unique_ptr<libhtmlpp::Element>& el,
1974 size_t start,
1975 bool& termination)
1976{
1977 const size_t startel = start;
1978 termination = false;
1979
1980 const auto begin = in.begin();
1981 if (start >= in.size()) {
1982 HTMLException excp;
1983 throw excp[HTMLException::Error] << "Parsing error: start offset beyond buffer.";
1984 }
1985 auto it_close_angle = std::find(begin + start, in.end(), HTMLTAG_CLOSE);
1986 if (it_close_angle == in.end()) {
1987 HTMLException excp;
1988 throw excp[HTMLException::Error] << "Parsing error: Missing '>' for <svg> open tag.";
1989 }
1990
1991 el = std::make_unique<SvgElement>();
1992 auto* svgEl = static_cast<SvgElement*>(el.get());
1993
1994 {
1995 std::vector<char> tel;
1996 tel.assign(begin + startel, it_close_angle);
1997 svgEl->_serialelize(tel, /*preserveAttrCase=*/true);
1998 }
1999
2000 auto it_content_begin = it_close_angle;
2001 if (it_content_begin != in.end()) ++it_content_begin; // safe increment
2002
2003 static constexpr char kEndTag[] = "</svg>";
2004 auto ci_eq = [](char a, char b) {
2005 auto lower = [](unsigned char c) -> unsigned char {
2006 return (c >= 'A' && c <= 'Z') ? static_cast<unsigned char>(c + 32) : c;
2007 };
2008 return lower(static_cast<unsigned char>(a)) == lower(static_cast<unsigned char>(b));
2009 };
2010 auto it_end = std::search(it_content_begin, in.end(),
2011 std::begin(kEndTag), std::end(kEndTag) - 1 /* no '\0' */, ci_eq);
2012 if (it_end == in.end()) {
2013 HTMLException excp;
2014 throw excp[HTMLException::Error] << "Parsing error: Missing </svg> closing tag.";
2015 }
2016
2017 svgEl->_Svg.insert(svgEl->_Svg.end(), it_content_begin, it_end);
2018
2019 const size_t consumed = static_cast<size_t>((it_end - begin) + (std::size(kEndTag) - 1));
2020 return consumed;
2021}
2022
2023
2024
2027
2029 _copy(this,&textsrc);
2030}
2031
2034
2035
2037 _copy(this,&hel);
2038 return *this;
2039}
2040
2042 _copy(this,hel);
2043 return *this;
2044}
2045
2046void libhtmlpp::TextArea::setText(const std::string& text){
2047 std::copy(text.begin(),text.end(),
2048 std::insert_iterator<std::vector<char>>(_Text,_Text.begin()));
2049}
2050
2051const std::vector<char>libhtmlpp::TextArea::getText(){
2052 return _Text;
2053}
2054
2058
2059size_t libhtmlpp::TextArea::parseElement(const std::vector<char>& in,
2060 std::unique_ptr<libhtmlpp::Element>& el,
2061 size_t start,
2062 bool& termination)
2063{
2064 termination = false;
2065
2066 const auto begin = in.begin();
2067 const auto end = in.end();
2068
2069 if (start >= in.size()) {
2070 HTMLException excp;
2071 throw excp[HTMLException::Error] << "Parsing error: start offset beyond buffer.";
2072 }
2073
2074 // ---- helpers -----------------------------------------------------------
2075 auto is_ws = [](unsigned char c) {
2076 return c == ' ' || c == '\t' || c == '\n' || c == '\r';
2077 };
2078 auto tolower_ascii = [](unsigned char c) -> unsigned char {
2079 return (c >= 'A' && c <= 'Z') ? static_cast<unsigned char>(c + 32) : c;
2080 };
2081 auto ieq_prefix = [&](std::vector<char>::const_iterator it,
2082 std::vector<char>::const_iterator it_end,
2083 const char* lit) -> bool
2084 {
2085 for (; *lit; ++lit, ++it) {
2086 if (it == it_end) return false;
2087 if (tolower_ascii(static_cast<unsigned char>(*it)) !=
2088 tolower_ascii(static_cast<unsigned char>(*lit))) {
2089 return false;
2090 }
2091 }
2092 return true;
2093 };
2094
2095 auto it_gt = std::find(begin + start, end, HTMLTAG_CLOSE);
2096 if (it_gt == end) {
2097 HTMLException excp;
2098 throw excp[HTMLException::Error] << "Parsing error: Unclosed <textarea> tag.";
2099 }
2100
2101 el = std::make_unique<TextArea>();
2102 auto* ta = static_cast<TextArea*>(el.get());
2103 {
2104 std::vector<char> tel;
2105 tel.assign(begin + start, it_gt); // opening tag without '>'
2106 ta->_serialelize(tel);
2107 }
2108
2109 auto it = it_gt;
2110 if (it != end) ++it;
2111 const auto content_begin = it;
2112
2113 for (;;) {
2114 auto lt = std::find(it, end, '<');
2115 if (lt == end) {
2116 HTMLException excp;
2117 throw excp[HTMLException::Error] << "Parsing error: Missing </textarea> closing tag.";
2118 }
2119
2120 if (ieq_prefix(lt, end, "</textarea")) {
2121 auto after_head = lt + std::strlen("</textarea");
2122 while (after_head != end && is_ws(static_cast<unsigned char>(*after_head))) {
2123 ++after_head;
2124 }
2125 if (after_head == end) {
2126 HTMLException excp;
2127 throw excp[HTMLException::Error] << "Parsing error: Unclosed </textarea> end tag.";
2128 }
2129 if (*after_head == '>') {
2130 ta->_Text.insert(ta->_Text.end(), content_begin, lt);
2131 const size_t consumed = static_cast<size_t>((after_head - begin) + 1);
2132 return consumed;
2133 }
2134
2135 it = lt + 1;
2136 continue;
2137 }
2138
2139 it = lt + 1;
2140 }
2141}
2142
2143
2144
2148
2158void libhtmlpp::HtmlPage::loadFile(libhtmlpp::HtmlElement &html,const std::string& path){
2159 std::string data;
2160 std::ifstream fs(path);
2161
2162 if(!fs.is_open()){
2163 HTMLException excp;
2164 throw excp[HTMLException::Critical] << "Can't open file: " << path;
2165 }
2166
2167 fs.seekg(std::ios::end);
2168
2169 data.reserve(fs.tellg());
2170
2171 fs.seekg(std::ios::beg);
2172
2173 data.assign((std::istreambuf_iterator<char>(fs)), std::istreambuf_iterator<char>());
2174
2175 fs.close();
2176
2177 _CheckHeader(data);
2178 loadString(html,data);
2179}
2188 HtmlString buf=src;
2189 Element &el=buf.parse();
2190 _copy(&html,&el);
2191}
2192
2194 HtmlString buf=node;
2195 Element &el=buf.parse();
2196 _copy(&html,&el);
2197}
2198
2200 if(!node){
2202 throw excp[libhtmlpp::HTMLException::Critical] << "loadstring: node can't be null !";
2203 }
2204 libhtmlpp::HtmlString buf=*node;
2205 html=(libhtmlpp::HtmlElement&)buf.parse();
2206}
2214void libhtmlpp::HtmlPage::saveFile(libhtmlpp::HtmlElement &html,const std::string& path){
2215 HtmlString data;
2216 std::ofstream fs;
2217
2218 print(html,data);
2219
2220 try{
2221 fs.open(path);
2222 }catch(std::exception &e){
2223 HTMLException excp;
2224 throw excp[HTMLException::Critical] << e.what();
2225 }
2226
2227 fs << data.str();
2228
2229 fs.close();
2230
2231}
2232
2234 return _Html5;
2235}
2236
2237
2238void libhtmlpp::HtmlPage::_CheckHeader(const HtmlString &page){
2239 const char type[] = { '!','D','O','C','T','Y','P','E' };
2240
2241 int i = 0;
2242
2243 bool start=false;
2244
2245 do{
2246 ++i;
2247 if(page[i]== '<'){
2248 if(start==true){
2249 HTMLException excp;
2250 excp[HTMLException::Critical] << "Wrong Header arborting";
2251 throw excp;
2252 }
2253 start=true;
2254 }
2255 } while ( page[i]== '<' || page[i]== '!' || page[i] == ' ');
2256
2257 if (page.size() < 8) {
2258 HTMLException excp;
2259 excp[HTMLException::Critical] << "No Doctype found arborting";
2260 throw excp;
2261 }
2262
2263 while (i < 8) {
2264 // Case-insensitive, matching the "HTML"/"PUBLIC" checks just below
2265 // and the HTML5 spec itself: "<!doctype html>" is exactly as valid
2266 // as "<!DOCTYPE HTML>", and is in fact the more common convention
2267 // in the wild (most build tools/frameworks emit it lowercase).
2268 if (tolower(page[i+1]) != tolower(type[i])) {
2269 HTMLException excp;
2270 excp[HTMLException::Critical] << "No Doctype found arborting";
2271 throw excp;
2272 }
2273 ++i;
2274 }
2275
2276 do{
2277 ++i;
2278 }while (page[i] == ' ');
2279
2280 const char doctype[] = { 'H','T','M','L' };
2281 size_t tpvl = 4;
2282
2283 const char typevalue4[] = {'P','U','B','L','I','C'};
2284 size_t tpvl4 = 6;
2285
2286 if ((i + tpvl) > page.size()) {
2287 HTMLException excp;
2288 excp[HTMLException::Critical] << "Document to short broken !";
2289 throw excp;
2290 }
2291
2292 size_t ii=0;
2293
2294 while ( ii < tpvl) {
2295 if (tolower(page[i++]) != tolower(doctype[ii++])) {
2296 HTMLException excp;
2297 excp[HTMLException::Critical] << "Doctype header broken or wrong type";
2298 throw excp;
2299 }
2300 }
2301
2302 ii=0;
2303
2304 do{
2305 ++i;
2306 } while (page[i] == ' ');
2307
2308 bool html4=true;
2309
2310 if(i +tpvl4 <page.size()){
2311 while(ii < tpvl4){
2312 if (tolower(page[i++]) != tolower(typevalue4[ii++])) {
2313 html4=false;
2314 }
2315 }
2316 }
2317
2318 _Html5=!html4;
2319
2320}
2328void libhtmlpp::print(const Element &element, HtmlString &output,bool formated) {
2329
2330 const Element *el=&element;
2331
2332 // Emit <!DOCTYPE html> when the root element is <html>
2333 if (el->getType() == HtmlEl) {
2334 const std::string &tag = static_cast<const HtmlElement*>(el)->getTagname();
2335 if (tag == "html") {
2336 output.append("<!DOCTYPE html>");
2337 if (formated)
2338 output.append("\n");
2339 }
2340 }
2341
2342 auto isContainer = [](const std::string &tagname) {
2343 for(size_t i=0; i<ContainerTypes.size(); ++i){
2344 if(tagname==ContainerTypes[i])
2345 return true;
2346 }
2347 return false;
2348 };
2349
2350 std::stack<const libhtmlpp::Element*> cpylist;
2351
2352 int lvl=0;
2353
2354 PRINTNEXTEL:
2355
2356 if(formated){
2357 for(int i=0; i<lvl; ++i){
2358 output.append(" ");
2359 }
2360 }
2361
2362 switch(el->getType()){
2363 case HtmlEl:{
2364 output.append("<");
2365 output.append(static_cast<const HtmlElement*>(el)->getTagname());
2366 for (HtmlElement::Attributes* curattr = static_cast<const HtmlElement*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2367 output.append(" ");
2368 std::copy(
2369 curattr->_Key.begin(),
2370 curattr->_Key.end(),
2371 std::back_inserter(output)
2372 );
2373 // Always emit ="value", even when value is empty -- matches
2374 // the WHATWG HTML fragment serialization algorithm (browsers'
2375 // outerHTML never drops the ="" for e.g. alt="" or a boolean
2376 // attribute like disabled) and keeps round-tripping stable:
2377 // an attribute with an explicit empty value stays
2378 // distinguishable in the output from one with none.
2379 output.append("=\"");
2380 std::copy(
2381 curattr->_Value.begin(),
2382 curattr->_Value.end(),
2383 std::back_inserter(output)
2384 );
2385 output.append("\"");
2386 }
2387
2388 output.append(">");
2389
2390 if (static_cast<const HtmlElement*>(el)->_childElement) {
2391 if(formated)
2392 output.append("\r\n");
2393 cpylist.push(el);
2394 el=static_cast<const HtmlElement*>(el)->_childElement.get();
2395 ++lvl;
2396 goto PRINTNEXTEL;
2397 }
2398
2399 //Container must be always terminated fuck html5
2400 if(isContainer(static_cast<const HtmlElement*>(el)->getTagname())){
2401 output.append("</");
2402 std::copy(
2403 static_cast<const HtmlElement*>(el)->_TagName.begin(),
2404 static_cast<const HtmlElement*>(el)->_TagName.end(),
2405 std::back_inserter(output)
2406 );
2407 output.append(">");
2408 }
2409
2410 if(formated)
2411 output.append("\r\n");
2412
2413 if (el->_nextElement) {
2414 el=el->_nextElement.get();
2415 goto PRINTNEXTEL;
2416 }
2417 }break;
2418
2419 case TextEl :{
2420 std::copy(
2421 static_cast<const TextElement*>(el)->_Text.begin(),
2422 static_cast<const TextElement*>(el)->_Text.end(),
2423 std::back_inserter(output)
2424 );
2425 if(formated)
2426 output.append("\r\n");
2427
2428 if (el->_nextElement) {
2429 el=el->_nextElement.get();
2430 goto PRINTNEXTEL;
2431 }
2432 }break;
2433 case CommentEl: {
2434 output.append("<!--");
2435 std::copy(
2436 static_cast<const CommentElement*>(el)->_Comment.begin(),
2437 static_cast<const CommentElement*>(el)->_Comment.end(),
2438 std::back_inserter(output)
2439 );
2440 output.append("-->");
2441 if(formated)
2442 output.append("\r\n");
2443
2444 if (el->_nextElement) {
2445 el=el->_nextElement.get();
2446 goto PRINTNEXTEL;
2447 }
2448 }break;
2449 case ScriptEL:{
2450 output.append("<");
2451 output.append(static_cast<const ScriptElement*>(el)->getTagname());
2452 for (ScriptElement::Attributes* curattr = static_cast<const ScriptElement*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2453 output.append(" ");
2454 std::copy(
2455 curattr->_Key.begin(),
2456 curattr->_Key.end(),
2457 std::back_inserter(output)
2458 );
2459 // Always emit ="value", even when value is empty -- matches
2460 // the WHATWG HTML fragment serialization algorithm (browsers'
2461 // outerHTML never drops the ="" for e.g. alt="" or a boolean
2462 // attribute like disabled) and keeps round-tripping stable:
2463 // an attribute with an explicit empty value stays
2464 // distinguishable in the output from one with none.
2465 output.append("=\"");
2466 std::copy(
2467 curattr->_Value.begin(),
2468 curattr->_Value.end(),
2469 std::back_inserter(output)
2470 );
2471 output.append("\"");
2472 }
2473
2474 output.append(">");
2475 if(formated){
2476 output.append("\r\n");
2477 for(int i=0; i<lvl+1; ++i){
2478 output.append(" ");
2479 }
2480 }
2481 std::copy(
2482 static_cast<const ScriptElement*>(el)->_Script.begin(),
2483 static_cast<const ScriptElement*>(el)->_Script.end(),
2484 std::back_inserter(output)
2485 );
2486 if(formated){
2487 output.append("\r\n");
2488 for(int i=0; i<lvl; ++i){
2489 output.append(" ");
2490 }
2491 }
2492 output.append("</");
2493 output.append(static_cast<const ScriptElement*>(el)->getTagname());
2494 output.append(">");
2495 if(formated)
2496 output.append("\r\n");
2497
2498 if (el->_nextElement) {
2499 el=el->_nextElement.get();
2500 goto PRINTNEXTEL;
2501 }
2502 }break;
2503 case SvgEL:{
2504 output.append("<");
2505 output.append(static_cast<const ScriptElement*>(el)->getTagname());
2506 for (SvgElement::Attributes* curattr = static_cast<const SvgElement*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2507 output.append(" ");
2508 std::copy(
2509 curattr->_Key.begin(),
2510 curattr->_Key.end(),
2511 std::back_inserter(output)
2512 );
2513 // Always emit ="value", even when value is empty -- matches
2514 // the WHATWG HTML fragment serialization algorithm (browsers'
2515 // outerHTML never drops the ="" for e.g. alt="" or a boolean
2516 // attribute like disabled) and keeps round-tripping stable:
2517 // an attribute with an explicit empty value stays
2518 // distinguishable in the output from one with none.
2519 output.append("=\"");
2520 std::copy(
2521 curattr->_Value.begin(),
2522 curattr->_Value.end(),
2523 std::back_inserter(output)
2524 );
2525 output.append("\"");
2526 }
2527 output.append(">");
2528
2529 if(formated){
2530 output.append("\r\n");
2531 for(int i=0; i<lvl+1; ++i){
2532 output.append(" ");
2533 }
2534 }
2535 std::copy(
2536 static_cast<const SvgElement*>(el)->_Svg.begin(),
2537 static_cast<const SvgElement*>(el)->_Svg.end(),
2538 std::back_inserter(output)
2539 );
2540 if(formated){
2541 output.append("\r\n");
2542 for(int i=0; i<lvl; ++i){
2543 output.append(" ");
2544 }
2545 }
2546 output.append("</");
2547 output.append(static_cast<const SvgElement*>(el)->getTagname());
2548 output.append(">");
2549 if(formated)
2550 output.append("\r\n");
2551
2552 if (el->_nextElement) {
2553 el=el->_nextElement.get();
2554 goto PRINTNEXTEL;
2555 }
2556 }break;
2557 case TextAreaEL:{
2558 output.append("<");
2559 output.append(static_cast<const TextArea*>(el)->getTagname());
2560 for (TextArea::Attributes* curattr = static_cast<const TextArea*>(el)->_firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2561 output.append(" ");
2562 std::copy(
2563 curattr->_Key.begin(),
2564 curattr->_Key.end(),
2565 std::back_inserter(output)
2566 );
2567 // Always emit ="value", even when value is empty -- matches
2568 // the WHATWG HTML fragment serialization algorithm (browsers'
2569 // outerHTML never drops the ="" for e.g. alt="" or a boolean
2570 // attribute like disabled) and keeps round-tripping stable:
2571 // an attribute with an explicit empty value stays
2572 // distinguishable in the output from one with none.
2573 output.append("=\"");
2574 std::copy(
2575 curattr->_Value.begin(),
2576 curattr->_Value.end(),
2577 std::back_inserter(output)
2578 );
2579 output.append("\"");
2580 }
2581 output.append(">");
2582 std::copy(
2583 static_cast<const TextArea*>(el)->_Text.begin(),
2584 static_cast<const TextArea*>(el)->_Text.end(),
2585 std::back_inserter(output)
2586 );
2587 output.append("</");
2588 output.append(static_cast<const TextArea*>(el)->getTagname());
2589 output.append(">");
2590 if(formated)
2591 output.append("\r\n");
2592
2593 if (el->_nextElement) {
2594 el=el->_nextElement.get();
2595 goto PRINTNEXTEL;
2596 }
2597 }break;
2598 default:
2599 HTMLException excp;
2600 excp[HTMLException::Error] << "Unkown Elementtype";
2601 throw excp;
2602 break;
2603 }
2604
2605 while(!cpylist.empty()){
2606 el=cpylist.top();
2607
2608 --lvl;
2609
2610 if(formated){
2611 for(int i=0; i<lvl; ++i){
2612 output.append(" ");
2613 }
2614 }
2615
2616 output.append("</");
2617 std::copy(
2618 static_cast<const HtmlElement*>(el)->_TagName.begin(),
2619 static_cast<const HtmlElement*>(el)->_TagName.end(),
2620 std::back_inserter(output)
2621 );
2622 output.append(">");
2623
2624 if(formated)
2625 output.append("\r\n");
2626
2627 cpylist.pop();
2628 if (el->_nextElement) {
2629 el=el->_nextElement.get();
2630 goto PRINTNEXTEL;
2631 }
2632 }
2633}
2634
2636 std::stack <Element*> childs;
2637 const Element *curel=this;
2638 SEARCHBYID:
2639 if(curel->getType()==HtmlEl || curel->getType()== ScriptEL || curel->getType()==SvgEL){
2640 if(((HtmlElement*)curel)->_childElement){
2641 childs.push(((HtmlElement*)curel)->_childElement.get());
2642 }
2643 std::string idname=((HtmlElement*)curel)->getAtributte("id");
2644 if(idname.length()==id.length() && std::equal(id.begin(),id.end(),idname.begin()) ){
2645 return (HtmlElement*)curel;
2646 }
2647 }
2648
2649 if(curel->nextElement()){
2650 curel=curel->nextElement();
2651 goto SEARCHBYID;
2652 }
2653
2654 if(!childs.empty()){
2655 curel=childs.top();
2656 childs.pop();
2657 goto SEARCHBYID;
2658 }
2659 return nullptr;
2660}
2661
2663 std::stack <Element*> childs;
2664 const Element *curel=this;
2665 SEARCHBYTAG:
2666 if(curel->getType()==HtmlEl || curel->getType()== ScriptEL || curel->getType()==SvgEL){
2667 if(((HtmlElement*)curel)->_childElement){
2668 childs.push(((HtmlElement*)curel)->_childElement.get());
2669 }
2670 const std::string tname=((HtmlElement*)curel)->getTagname();
2671 if(!tname.empty() && std::equal(tag.begin(),tag.end(),tname.begin())){
2672 return (HtmlElement*)curel;
2673 }
2674 }
2675
2676 if(curel->nextElement()){
2677 curel=curel->nextElement();
2678 goto SEARCHBYTAG;
2679 }
2680
2681 if(!childs.empty()){
2682 curel=childs.top();
2683 childs.pop();
2684 goto SEARCHBYTAG;
2685 }
2686 return nullptr;
2687}
2688
2690 return _childElement.get();
2691}
2692
2694 return std::string(_Key.begin(), _Key.end());
2695}
2696
2698 return std::string(_Value.begin(), _Value.end());
2699}
2700
2704
2706 return _firstAttr.get();
2707}
2708
2709void libhtmlpp::HtmlElement::setAttribute(const std::string &name, const std::string &value) {
2710 Attributes* cattr = nullptr;
2711
2712 const char forbidden[] = {'\"'};
2713
2714 auto checkForbidden = [forbidden](const std::string &input){
2715 for(size_t i = 0; i<input.length(); ++i){
2716 for(size_t ii=0; ii<sizeof(forbidden[ii]); ii++){
2717 if(input[i]==forbidden[ii]){
2718 return true;
2719 }
2720 }
2721 }
2722 return false;
2723 };
2724
2725 if(checkForbidden(name) || checkForbidden(value)){
2726 HTMLException e;
2727 e[HTMLException::Error] << "setAttribute " << name.c_str() << "forbidden sign is used !";
2728 throw e;
2729 }
2730
2731 for (cattr= _firstAttr.get(); cattr; cattr=cattr->_nextAttr.get()) {
2732 if(name.size() == cattr->_Key.size() && std::equal(name.begin(),name.end(),cattr->_Key.begin())){
2733 cattr->_Value.clear();
2734 std::copy(value.begin(),value.end(),std::back_inserter(cattr->_Value));
2735 return;
2736 }
2737 }
2738 if (_lastAttr){
2739 _lastAttr->_nextAttr = std::make_unique<Attributes>();
2740 _lastAttr = _lastAttr->_nextAttr.get();
2741 }else {
2742 _firstAttr = std::make_unique<Attributes>();
2743 _lastAttr = _firstAttr.get();
2744 }
2745
2746 cattr = _lastAttr;
2747 std::copy(name.begin(),name.end(),std::back_inserter(cattr->_Key) );
2748 std::copy(value.begin(),value.end(),std::back_inserter(cattr->_Value));
2749}
2750
2751void libhtmlpp::HtmlElement::setIntAttribute(const std::string& name, int value) {
2752 char buf[255];
2753 snprintf(buf,255,"%d",value);
2754 setAttribute(name,buf);
2755}
2756
2757const std::string libhtmlpp::HtmlElement::getAtributte(const std::string& name) const {
2758 for (Attributes* curattr = _firstAttr.get(); curattr; curattr = curattr->_nextAttr.get()) {
2759
2760 if (curattr->_Key.size() != name.length() ) {
2761 continue;
2762 }
2763
2764 if (std::equal(name.begin(), name.end(), curattr->_Key.begin())) {
2765 return std::string(curattr->_Value.begin(), curattr->_Value.end());
2766 }
2767 }
2768 return "";
2769}
2770
2771int libhtmlpp::HtmlElement::getIntAtributte(const std::string& name) const
2772{
2773 return atoi(getAtributte(name).c_str());
2774}
2775
2777 _nextAttr=nullptr;
2778}
2779
2781 auto cur = std::move(_nextAttr);
2782 while (cur) {
2783 cur = std::move(cur->_nextAttr);
2784 }
2785}
2786
2790
2791
2793 _firstRow=nullptr;
2794 _lastRow=nullptr;
2795 _count = 0;
2796}
2797
2800
2802 std::unique_ptr<Row> newRow = std::make_unique<Row>(row);
2803
2804 if (!_firstRow) {
2805 _firstRow = std::move(newRow);
2806 _lastRow = _firstRow.get();
2807 } else {
2808 _lastRow->_nextRow = std::move(newRow);
2809 _lastRow = _lastRow->_nextRow.get();
2810 }
2811
2812 ++_count;
2813 return *_lastRow;
2814}
2815
2817 if(!_firstRow || _count<pos){
2819 exp[HTMLException::Error] << "HtmlTable: Row at this position won't exists !";
2820 throw exp;
2821 }
2822 size_t cpos=0;
2823 Row *curel=nullptr;
2824 for(curel=_firstRow.get(); curel; curel=curel->_nextRow.get()){
2825 if(cpos==pos)
2826 return *curel;
2827 ++cpos;
2828 }
2829 return *curel;
2830}
2831
2833 element->setTagname("table");
2834 for(Row *crow=_firstRow.get(); crow; crow=crow->_nextRow.get()){
2835 HtmlElement hrow("tr");
2836 for(Column *ccol=crow->_firstColumn.get(); ccol; ccol=ccol->_nextColumn.get() ){
2837 HtmlElement hcol("td");
2838 TextElement cellContent(ccol->Data.c_str());
2839 hcol.appendChild(cellContent);
2840 hrow.appendChild(hcol);
2841 }
2842 element->appendChild(&hrow);
2843 }
2844}
2845
2846
2849
2851 va_list args;
2852 va_start(args,count);
2853
2854 for (int i = 0; i < count; ++i) {
2855 _header << va_arg(args, const char*);
2856 }
2857
2858}
2859
2861 if (pos >= _count)
2862 return;
2863 if (pos == 0) {
2864 _firstRow = std::move(_firstRow->_nextRow);
2865 _lastRow = (pos == (_count - 1)) ? nullptr : _firstRow.get();
2866 } else {
2867 Row *prev = &(*this)[pos - 1];
2868 if (prev->_nextRow) {
2869 prev->_nextRow = std::move(prev->_nextRow->_nextRow);
2870
2871 if (prev->_nextRow.get() == nullptr) {
2872 _lastRow = prev;
2873 }
2874 }
2875 }
2876
2877 --_count;
2878 if (_count == 0) {
2879 _firstRow = nullptr;
2880 _lastRow = nullptr;
2881 }
2882}
2883
2885 _nextColumn=nullptr;
2886}
2887
2889 _nextColumn=nullptr;
2890 Data = col.Data;
2891}
2892
2894 _nextColumn=nullptr;
2895 Data=data;
2896}
2897
2899 _nextColumn=nullptr;
2900 Data=data.str();
2901}
2902
2905
2907 _nextColumn = std::move(col._nextColumn);
2908 Data = std::move(col.Data);
2909}
2910
2912 _firstColumn=nullptr;
2913 _lastColumn=nullptr;
2914 _nextRow=nullptr;
2915 _count=0;
2916}
2917
2920
2922 _firstColumn=nullptr;
2923 _lastColumn=nullptr;
2924 _nextRow=nullptr;
2925 _count=0;
2926
2927 for(Column *curel=row._firstColumn.get(); curel; curel=curel->_nextColumn.get()){
2928 *this << HtmlString(curel->Data);
2929 }
2930}
2931
2933 std::unique_ptr<Column> ptr = std::make_unique<Column>(std::move(col));
2934
2935 if(_firstColumn){
2936 _lastColumn->_nextColumn=std::move(ptr);
2937 _lastColumn=_lastColumn->_nextColumn.get();
2938 }else{
2939 _firstColumn= std::move(ptr);
2940 _lastColumn=_firstColumn.get();
2941 }
2942 ++_count;
2943 return *this;
2944}
2945
2947 std::unique_ptr<Column> ptr = std::make_unique<Column>(col);
2948
2949 if(_firstColumn){
2950 _lastColumn->_nextColumn=std::move(ptr);
2951 _lastColumn=_lastColumn->_nextColumn.get();
2952 }else{
2953 _firstColumn= std::move(ptr);
2954 _lastColumn=_firstColumn.get();
2955 }
2956 ++_count;
2957 return *this;
2958}
2959
2961 Column col(value);
2962 *this << col;
2963 return *this;
2964}
2965
2967 Column col(value);
2968 *this << col;
2969 return *this;
2970}
2971
2972
2974 HtmlString buf;
2975 buf << value;
2976 *this << buf;
2977 return *this;
2978}
2979
2981 char buf[255];
2982 snprintf(buf,255,"%d",value);
2983 return *this << buf;
2984}
2985
2987 if(!_firstColumn || _count<pos){
2989 exp[HTMLException::Error] << "HtmlTable: Column at this position won't exists !";
2990 throw exp;
2991 }
2992 size_t cpos=0;
2993 Column *curel=nullptr;
2994 for(curel=_firstColumn.get(); curel; curel=curel->_nextColumn.get()){
2995 if(cpos==pos)
2996 return *curel;
2997 ++cpos;
2998 }
2999 return *curel;
3000}
3001
3003 Column *dcol=&(*this)[pos];
3004 try{
3005 Column *prev=&(*this)[pos-1];
3006 prev->_nextColumn=std::move(dcol->_nextColumn);
3007 }catch(...){}
3008 --_count;
3009}
3010
3012 _firstColumn.reset();
3013 _lastColumn = nullptr;
3014 _count = 0;
3015}
Leaf node representing an HTML comment ().
Definition html.h:241
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1738
CommentElement & operator=(const Element &hel)
Definition html.cpp:1715
std::vector< char > _Comment
Definition html.h:257
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1269
void setComment(const std::string &txt)
Definition html.cpp:1725
const std::string getComment()
Definition html.cpp:1730
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:1582
Element * _prevElement
Definition html.h:100
void insertAfter(Element *el)
Definition html.cpp:1501
virtual ~Element()
Definition html.cpp:1575
Element * _parentElement
Definition html.h:101
void insertBefore(Element *el)
Definition html.cpp:1469
Element & operator=(const Element &hel)
Definition html.cpp:1542
std::unique_ptr< Element > _nextElement
Definition html.h:99
virtual int getType() const =0
Definition html.cpp:802
Element * prevElement() const
Definition html.cpp:1556
Element * parentElement() const
Definition html.cpp:1560
Element * nextElement() const
Definition html.cpp:1552
const char * what() const noexcept override
Definition exception.cpp:51
int getType() const
Definition html.cpp:2787
const Attributes * firstAttribute() const
Definition html.cpp:2705
HtmlElement * getElementbyTag(const std::string &tag) const
Definition html.cpp:2662
const std::string getAtributte(const std::string &name) const
Definition html.cpp:2757
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:998
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1110
static size_t parseStyleElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Parses a <style> tag the same "raw text until literal </style>" way ScriptElement::parseElement parse...
Definition html.cpp:1174
void appendChild(const Element *el)
Definition html.cpp:872
int getIntAtributte(const std::string &name) const
Definition html.cpp:2771
void setTagname(const std::string &name)
Definition html.cpp:806
void setAttribute(const std::string &name, const std::string &value)
Definition html.cpp:2709
void remove(Element *el)
Definition html.cpp:949
bool operator==(const HtmlElement *hel)
Definition html.cpp:921
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1269
void insertChild(const Element *el)
Definition html.cpp:835
Element * firstChild() const
Definition html.cpp:2689
const std::string getTagname() const
Definition html.cpp:811
HtmlElement * getElementbyID(const std::string &id) const
Definition html.cpp:2635
HtmlElement & operator=(const HtmlElement &hel)
Definition html.cpp:939
std::unique_ptr< Element > _childElement
Definition html.h:182
void setIntAttribute(const std::string &name, int value)
Definition html.cpp:2751
void loadString(libhtmlpp::HtmlElement &html, const std::string &src)
Parses an HTML source string and copies the result into html.
Definition html.cpp:2187
void saveFile(libhtmlpp::HtmlElement &html, const std::string &path)
Serializes an HtmlElement subtree and writes it to a file.
Definition html.cpp:2214
void loadFile(libhtmlpp::HtmlElement &html, const std::string &path)
Loads an HTML file from disk into a given HtmlElement root.
Definition html.cpp:2158
const std::vector< char > & data() const
Definition html.cpp:374
void append(const std::string &src)
Definition html.cpp:238
HtmlString & operator<<(const char *src)
Definition html.cpp:301
HtmlString & operator+=(const std::string &src)
Definition html.cpp:275
size_t size() const
Definition html.cpp:350
char operator[](size_t pos) const
Definition html.cpp:297
void push_back(const char src)
Definition html.cpp:226
void insert(size_t pos, char src)
Definition html.cpp:261
const char * c_str() const
Definition html.cpp:370
size_t length() const
Definition html.cpp:340
libhtmlpp::Element & parse()
Parses the current buffer into a DOM-like tree and returns the root element.
Definition html.cpp:379
const char * operator*()
Definition html.cpp:336
HtmlString & operator=(const std::string &src)
Definition html.cpp:285
const std::string str() const
Definition html.cpp:364
Row & operator<<(Column &&col)
Definition html.cpp:2932
Column & operator[](size_t pos)
Definition html.cpp:2986
void delColumn(size_t pos)
Definition html.cpp:3002
Row & operator[](size_t pos)
Definition html.cpp:2816
Row & operator<<(const Row &row)
Definition html.cpp:2801
void deleteRow(size_t pos)
Definition html.cpp:2860
void parse(HtmlElement *element)
Definition html.cpp:2847
void insert(HtmlElement *element)
Definition html.cpp:2832
void setHeader(int count,...)
Definition html.cpp:2850
Element representing a <script> tag and its text content.
Definition html.h:266
void setScript(const std::string &txt)
Definition html.cpp:1802
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1814
ScriptElement & operator=(const Element &hel)
Definition html.cpp:1792
std::vector< char > _Script
Definition html.h:290
const std::string getScript()
Definition html.cpp:1806
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1269
Element representing an embedded <svg> tag and its attributes/content.
Definition html.h:299
int getType() const
Definition html.cpp:1968
SvgElement & operator=(const Element &hel)
Definition html.cpp:1949
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1972
const std::vector< char > getSvg()
Definition html.cpp:1964
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1269
std::vector< char > _Svg
Definition html.h:323
void setSvg(const std::string &svg)
Definition html.cpp:1959
Element representing an embedded <textarea> tag and its attributes/content.
Definition html.h:333
std::vector< char > _Text
Definition html.h:357
TextArea & operator=(const Element &hel)
Definition html.cpp:2036
const std::vector< char > getText()
Definition html.cpp:2051
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1269
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:2059
int getType() const
Definition html.cpp:2055
void setText(const std::string &text)
Definition html.cpp:2046
Leaf node representing plain text content of an HTML document.
Definition html.h:214
TextElement & operator=(const Element &hel)
Definition html.cpp:1629
std::vector< char > _Text
Definition html.h:232
void setText(const std::string &txt)
Definition html.cpp:1639
int getType() const
Definition html.cpp:1647
friend void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1269
static size_t parseElement(const std::vector< char > &in, std::unique_ptr< libhtmlpp::Element > &el, size_t start, bool &termination)
Definition html.cpp:1651
const std::string getText()
Definition html.cpp:1643
#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:734
void loadString(libhtmlpp::HtmlElement &html, const libhtmlpp::HtmlString *node)
Definition html.cpp:2199
Public declarations for libhtmlpp HTML element types and utilities.
Core namespace for the libhtmlpp HTML parsing and printing library.
Definition css.h:37
@ 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:2328
void HtmlEncode(const std::string &input, std::string &output)
Encodes special HTML characters in a string and writes into std::string.
Definition html.cpp:739
const std::array< std::string_view, 100 > ContainerTypes
Definition html.cpp:62
void _copy(libhtmlpp::Element *dest, const libhtmlpp::Element *src)
Definition html.cpp:1269
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:773
const std::string getValue() const
Definition html.cpp:2697
Attributes * nextAttribute() const
Definition html.cpp:2701
const std::string getKey() const
Definition html.cpp:2693