home
readme
diff
tree
note
docs
0commit 48f371d7b8f789ed2a4563054643fc868cb3c3fb
1Author: Mason Wright <mason@lakefox.net>
2Date: Fri Nov 28 19:32:56 2025 -0700
3
4 Window object and remove complex attributes
5
6diff --git a/Makefile b/Makefile
7index 715686b..435bf3b 100755
8--- a/Makefile
9+++ b/Makefile
10@@ -61,6 +61,9 @@ build/css_parser_playground: playground/css_parser.cc build/grim.o build/parser.
11 build/css_unit_playground: playground/css_unit.cc build/grim.o build/parser.o | build
12 ${CC} ${CCFLAGS} -Iinclude/ playground/css_unit.cc build/grim.o build/parser.o -o build/css_unit_playground
13
14+build/window_playground: playground/window.cc build/grim.o build/parser.o | build
15+ ${CC} ${CCFLAGS} -Iinclude/ playground/window.cc build/grim.o build/parser.o -o build/window_playground
16+
17 # --- Prebuild ---
18
19 # use to prebuild the master.css
20diff --git a/README.txt b/README.txt
21index dc70472..d48b986 100644
22--- a/README.txt
23+++ b/README.txt
24@@ -1,4 +1,4 @@
25 Grim is a HTML/CSS render with a 'JS like' DOM API.
26
27 Installation:
28-git submodule add http://decode.sh/grim.git
29+git submodule add http://git.ws/repo/grim.git
30diff --git a/include/grim.h b/include/grim.h
31index d61c21a..77af12c 100755
32--- a/include/grim.h
33+++ b/include/grim.h
34@@ -13,62 +13,107 @@
35 #include <stdexcept>
36
37 // TODO:
38-// * Document object
39 // * templates
40
41-class Attribute {
42- private:
43- std::string VALUE;
44- public:
45- Attribute(const std::string& value) : VALUE(value) {}
46+enum class UnitType : short {
47+ // Color
48+ TEST, HEX, RGB, RGBA, HSL,
49+ // Logic
50+ AND, OR, NOT, ONLY, COMMA,
51+ // Length
52+ PX, EM, PERCENT, CM, CAP, CH, EX, IC, LH, RCAP, RCH, REM, REX, RIC, RLH, VH,
53+ VW, VMAX, VMIN, VB, VI, CQW, CQH, CQI, CQB, CQMIN, CQMAX, MM, Q, IN, PC, PT,
54+ // Diminsion
55+ KHZ, HZ, SECOND, MS, FRACTION,
56+ // Angle
57+ DEG, RAD, GRAD, TURN,
58+ // Non-unit numbers
59+ NUMBER, INF, NEGINF, NaN, E, PI,
60
61- std::optional<std::string> String() const {return VALUE;}
62- std::optional<int> Int() const {
63- int iVal = 0;
64- auto [ptr, ec] = std::from_chars(VALUE.data(), VALUE.data() + VALUE.size(), iVal);
65
66- if (ec == std::errc()) {
67- return iVal;
68- } else {
69- return std::nullopt;
70- }
71- }
72
73- std::optional<double> Double() const {
74- int dVal = 0.0;
75- auto [ptr, ec] = std::from_chars(VALUE.data(), VALUE.data() + VALUE.size(), dVal);
76+ // Global
77+ INHERIT, INITIAL, REVERT, REVERT_LAYER, UNSET, AUTO, NONE,
78+ // Operator
79+ EQ, LT, GT, LE, GE, ADD, SUB, MULT, DIV,
80+ // Baseline Position
81+ FIRST_BASELINE, LAST_BASELINE,
82+ // Box Edge
83+ CONTENT, PADDING, BORDER, MARGIN, FILL, STROKE, VIEW,
84+ // Distribution
85+ NORMAL, STRETCH, BETWEEN, AROUND, EVENLY,
86+ // Position
87+ CENTER, START, END, TOP, LEFT, BOTTOM, RIGHT, INLINE_START, INLINE_END, BLOCK_START, BLOCK_END, SELF_START, SELF_END, FLEX_START, FLEX_END,
88+ // Overflow
89+ CLIP, SCROLL,
90+ SAFE, UNSAFE,
91+ // Display
92+ HIDDEN, VISIBLE,
93+ // Display Box
94+ CONTENTS,
95+ // Display Inside
96+ FLOW, FLOW_ROOT, TABLE, FLEX, GRID, RUBY,
97+ // Display Internal
98+ TABLE_ROW_GROUP, TABLE_HEADER_GROUP, TABLE_FOOTER_GROUP, TABLE_COLUMN_GROUP,
99+ TABLE_ROW, TABLE_CELL, TABLE_COLUMN, TABLE_CAPTION, RUBY_BASE, RUBY_TEXT, RUBY_BASE_CONTAINER,
100+ RUBY_TEXT_CONTAINER,
101+ // Display Legacy
102+ INLINE_BLOCK, INLINE_TABLE, INLINE_GRID, INLINE_FLEX,
103+ // Display List Item
104+ LISTITEM,
105+ // Display Outside
106+ BLOCK, INLINE, RUN_IN,
107+ // Shape
108+ RADIAL_SHAPE, CIRCLE, ELLIPSE,
109+ // Generic Family
110+ SERIF, SANS_SERIF, MONOSPACE, CURSIVE, FANTASY, SYSTEM_UI, UI_SERIF, UI_SANS_SERIF, UI_MONOSPACE,
111+ UI_ROUNDED, MATH, FANGSONG,
112+ // Hue Interpolation
113+ SHORTER_HUE, LONGER_HUE, INCREASING_HUE, DECREASING_HUE, HUE,
114+ // Line Style
115+ DOTTED, DASHED, SOLID, DOUBLE, GROOVE, RIDGE, INSET, OUTSET,
116+ // Image
117+ URL, IMAGE_SET, CROSS_FADE, ELEMENT, GRADIENT,
118+ // Appearance *Not added*
119+ MENU_LIST, TEXTFIELD, BASE_SELECT, BUTTON, CHECKBOX,
120+ // Space
121+ SPACE_BETWEEN, SPACE_AROUND, SPACE_EVENLY,
122+ // Background Attachment
123+ LOCAL, FIXED,
124+ // Background Blend Mode
125+ MULTIPLY, OVERLAY, DARKEN, LIGHTEN, COLOR_DODGE, COLOR_BURN, HARD_LIGHT, SOFT_LIGHT, DIFFERENCE, EXCLUSION, SATURATION, COLOR, LUMINOSITY,
126+ // Background Clip
127+ BORDER_BOX, PADDING_BOX, CONTENT_BOX, TEXT, BORDER_AREA,
128+
129
130- if (ec == std::errc()) {
131- return dVal;
132- } else {
133- return std::nullopt;
134- }
135- }
136+ // General Function
137+ CALC, MIN, BLUR, BRIGHTNESS, CONTRAST, DROP_SHADOW, GRAYSCALE, HUE_ROTATE, INVERT, OPACITY, SEPIA, SATURATE,
138+ // Easing Functions
139+ EASING_FUNCTION, LINEAR_EASING_FUNCTION, LINEAR, EASE, EASE_IN, EASE_OUT, EASE_IN_OUT, CUBIC_BEZIER_FUNCTION,
140+ STEP_START, STEP_END, STEPS_FUNCTION, JUMP_START, JUMP_END, JUMP_NONE, JUMP_BOTH,
141+ // Transform Functions
142+ MATRIX, MATRIX3D, PERSPECTIVE, ROTATE, ROTATE3D, ROTATEX, ROTATEY, ROTATEZ, SCALE, SCALE3D, SCALEX, SCALEY, SCALEZ, TRANSLATE, TRANSLATE3D, TRANSLATEX, TRANSLATEY, TRANSLATEZ,
143+ SKEW, SKEWX, SKEWY,
144
145- std::optional<bool> Bool() const {
146- if (VALUE == "true") {
147- return true;
148- } else
149- if (VALUE == "false") {
150- return false;
151- } else {
152- return std::nullopt;
153- }
154- }
155+ // Keyword
156+ PRINT, SCREEN, TOKEN, FUNC, GROUP, NO_SUPPORT, IMAGE, STRING
157+};
158
159- operator std::string() const { return VALUE; }
160- operator int() const {
161- if (auto val = Int()) { return *val; }
162- throw std::runtime_error("Cannot convert '" + VALUE + "' to int.");
163- }
164- operator double() const {
165- if (auto val = Double()) { return *val; }
166- throw std::runtime_error("Cannot convert '" + VALUE + "' to double.");
167- }
168- operator bool() const {
169- if (auto val = Bool()) { return *val; }
170- throw std::runtime_error("Cannot convert '" + VALUE + "' to bool.");
171- };
172+
173+// 4 bytes
174+union UnitValue {
175+ UnitType ENUM;
176+ int INT;
177+ unsigned int UINT;
178+ float FLOAT;
179+ bool BOOL;
180+ char CHAR;
181+};
182+
183+// 6 bytes
184+struct Unit {
185+ UnitType type; // short
186+ UnitValue value;
187 };
188
189 class Window;
190@@ -77,10 +122,11 @@ class Node {
191 private:
192 std::string TagName;
193 std::unordered_map<std::string, std::string> attributes;
194- Window* window;
195 public:
196+ Window* window;
197+
198 Node* parent;
199- std::vector<std::unique_ptr<Node>> children;
200+ std::vector<Node> children;
201 struct ClassList {
202 private:
203 Node* self;
204@@ -113,6 +159,7 @@ class Node {
205 public:
206 StyleList(Node* node) : self(node) {}
207 std::string get(std::string key);
208+ std::vector<Unit> getUnit(std::string key);
209 void set(std::string key, std::string value);
210 void remove(std::string key);
211 std::pair<std::string, std::string> item(size_t index);
212@@ -131,7 +178,7 @@ class Node {
213
214 void setAttribute(const std::string& name, const std::string& value);
215 void deleteAttribute(const std::string& name);
216- std::optional<Attribute> getAttribute(const std::string& name) const;
217+ std::string getAttribute(const std::string& name) const;
218
219 const std::unordered_map<std::string, std::string>& getAttributes() const;
220 std::vector<std::string> getAttributeKeys();
221@@ -217,118 +264,12 @@ enum class KeyType : short {
222 Z_INDEX, ZOOM
223 };
224
225-enum class UnitType : short {
226- // Color
227- TEST, HEX, RGB, RGBA, HSL,
228- // Logic
229- AND, OR, NOT, ONLY, COMMA,
230- // Length
231- PX, EM, PERCENT, CM, CAP, CH, EX, IC, LH, RCAP, RCH, REM, REX, RIC, RLH, VH,
232- VW, VMAX, VMIN, VB, VI, CQW, CQH, CQI, CQB, CQMIN, CQMAX, MM, Q, IN, PC, PT,
233- // Diminsion
234- KHZ, HZ, SECOND, MS, FRACTION,
235- // Angle
236- DEG, RAD, GRAD, TURN,
237- // Non-unit numbers
238- NUMBER, INF, NEGINF, NaN, E, PI,
239-
240-
241-
242- // Global
243- INHERIT, INITIAL, REVERT, REVERT_LAYER, UNSET, AUTO, NONE,
244- // Operator
245- EQ, LT, GT, LE, GE, ADD, SUB, MULT, DIV,
246- // Baseline Position
247- FIRST_BASELINE, LAST_BASELINE,
248- // Box Edge
249- CONTENT, PADDING, BORDER, MARGIN, FILL, STROKE, VIEW,
250- // Distribution
251- NORMAL, STRETCH, BETWEEN, AROUND, EVENLY,
252- // Position
253- CENTER, START, END, TOP, LEFT, BOTTOM, RIGHT, INLINE_START, INLINE_END, BLOCK_START, BLOCK_END, SELF_START, SELF_END, FLEX_START, FLEX_END,
254- // Overflow
255- CLIP, SCROLL,
256- SAFE, UNSAFE,
257- // Display
258- HIDDEN, VISIBLE,
259- // Display Box
260- CONTENTS,
261- // Display Inside
262- FLOW, FLOW_ROOT, TABLE, FLEX, GRID, RUBY,
263- // Display Internal
264- TABLE_ROW_GROUP, TABLE_HEADER_GROUP, TABLE_FOOTER_GROUP, TABLE_COLUMN_GROUP,
265- TABLE_ROW, TABLE_CELL, TABLE_COLUMN, TABLE_CAPTION, RUBY_BASE, RUBY_TEXT, RUBY_BASE_CONTAINER,
266- RUBY_TEXT_CONTAINER,
267- // Display Legacy
268- INLINE_BLOCK, INLINE_TABLE, INLINE_GRID, INLINE_FLEX,
269- // Display List Item
270- LISTITEM,
271- // Display Outside
272- BLOCK, INLINE, RUN_IN,
273- // Shape
274- RADIAL_SHAPE, CIRCLE, ELLIPSE,
275- // Generic Family
276- SERIF, SANS_SERIF, MONOSPACE, CURSIVE, FANTASY, SYSTEM_UI, UI_SERIF, UI_SANS_SERIF, UI_MONOSPACE,
277- UI_ROUNDED, MATH, FANGSONG,
278- // Hue Interpolation
279- SHORTER_HUE, LONGER_HUE, INCREASING_HUE, DECREASING_HUE, HUE,
280- // Line Style
281- DOTTED, DASHED, SOLID, DOUBLE, GROOVE, RIDGE, INSET, OUTSET,
282- // Image
283- URL, IMAGE_SET, CROSS_FADE, ELEMENT, GRADIENT,
284- // Appearance *Not added*
285- MENU_LIST, TEXTFIELD, BASE_SELECT, BUTTON, CHECKBOX,
286- // Space
287- SPACE_BETWEEN, SPACE_AROUND, SPACE_EVENLY,
288- // Background Attachment
289- LOCAL, FIXED,
290- // Background Blend Mode
291- MULTIPLY, OVERLAY, DARKEN, LIGHTEN, COLOR_DODGE, COLOR_BURN, HARD_LIGHT, SOFT_LIGHT, DIFFERENCE, EXCLUSION, SATURATION, COLOR, LUMINOSITY,
292- // Background Clip
293- BORDER_BOX, PADDING_BOX, CONTENT_BOX, TEXT, BORDER_AREA,
294-
295-
296- // General Function
297- CALC, MIN, BLUR, BRIGHTNESS, CONTRAST, DROP_SHADOW, GRAYSCALE, HUE_ROTATE, INVERT, OPACITY, SEPIA, SATURATE,
298- // Easing Functions
299- EASING_FUNCTION, LINEAR_EASING_FUNCTION, LINEAR, EASE, EASE_IN, EASE_OUT, EASE_IN_OUT, CUBIC_BEZIER_FUNCTION,
300- STEP_START, STEP_END, STEPS_FUNCTION, JUMP_START, JUMP_END, JUMP_NONE, JUMP_BOTH,
301- // Transform Functions
302- MATRIX, MATRIX3D, PERSPECTIVE, ROTATE, ROTATE3D, ROTATEX, ROTATEY, ROTATEZ, SCALE, SCALE3D, SCALEX, SCALEY, SCALEZ, TRANSLATE, TRANSLATE3D, TRANSLATEX, TRANSLATEY, TRANSLATEZ,
303- SKEW, SKEWX, SKEWY,
304-
305- // Keyword
306- PRINT, SCREEN, TOKEN, FUNC, GROUP, NO_SUPPORT, IMAGE, STRING
307-};
308-
309-// 4 bytes
310-union UnitValue {
311- UnitType ENUM;
312- int INT;
313- unsigned int UINT;
314- float FLOAT;
315- bool BOOL;
316- char CHAR;
317-};
318-
319-// 6 bytes
320-struct Unit {
321- UnitType type; // short
322- UnitValue value;
323-};
324-
325-enum class StyleType : char {
326- CONTAINER, COUNTER, FONTFACE, FONTFEATURE, SWASH, ANNOTATION, ORNAMENTS, STYLISTIC, STYLESET, CHARACTERVARIANT, FONTPALETTEVALUES,
327- IMPORT, KEYFRAMES, LAYER, NAMESPACE, PAGE, PROPERTY, ROOT, SHEET, MEDIA, STARTINGSTYLE, STYLE
328-};
329
330 class Style {
331 public:
332 Window* window;
333-
334 std::unordered_map<std::string, std::vector<size_t>> basemap{};
335
336- StyleType type;
337 std::string name;
338 std::vector<std::vector<std::string>> selector{};
339
340@@ -402,8 +343,7 @@ class Window {
341
342 public:
343
344- void loadHTML(std::istream& inputStream);
345- void loadCSS(std::istream& inputStream);
346+ void parseHTML(std::istream& inputStream);
347
348 Node document;
349 Style CSS;
350@@ -478,4 +418,12 @@ class Window {
351 }
352 };
353
354+// Will need to require an adapter later
355+// for now I just want to work on at rules
356+
357+/*
358+For the adapter, it will take a window as a argument to set the adapters capabilties
359+*/
360+Window createWindow();
361+
362 #endif // NODE_H
363diff --git a/include/parser.h b/include/parser.h
364index aca9526..ba33470 100755
365--- a/include/parser.h
366+++ b/include/parser.h
367@@ -11,11 +11,15 @@
368 class Node;
369 class Style;
370
371+struct ParsedDocument {
372+ Node document;
373+ Style CSS;
374+};
375+
376 // Declare the parsing functions
377 std::unordered_map<std::string, std::string> parseAttributes(std::string_view& token);
378-Node parseHTML(std::istream& inputStream);
379+ParsedDocument parseDocument(std::istream& inputStream);
380 Style parseCSS(std::istream& inputStream);
381-std::unordered_map<KeyType, std::vector<Unit>> parseCSSInline(std::string inlineStyles);
382 std::vector<Unit> getUnit(std::string);
383
384 #endif
385diff --git a/playground/css_parser.cc b/playground/css_parser.cc
386index 57bde4c..8adeeec 100644
387--- a/playground/css_parser.cc
388+++ b/playground/css_parser.cc
389@@ -6,56 +6,6 @@
390
391 int main() {
392 std::string cssContent = R"~~~(
393-h1 {
394- color: red;
395-}
396-
397-h1 > #id {
398- background: purple;
399-}
400-
401-div:checked {
402- display: flex;
403- margin: 1px 100% 20vh;
404-}
405-
406-
407-html#nested {
408- background: url("https://example.com");
409-
410- h1[value="nested"] {
411- color: red;
412- }
413-
414- &.class {
415- border:
416- none;
417- }
418-}
419-.notice {
420- width: 90%;
421- justify-content: center;
422- border-radius: 1rem;
423- border: black solid 2px;
424- background-color: #ffc107;
425- color: black;
426- padding: 1rem;
427- .notice-heading::before {
428- /* equivalent to `.notice .notice-heading::before` */
429- content: "i ";
430- }
431- font-size: 10px;
432- &.warning {
433- /* equivalent to `.notice.warning` */
434- background-color: #d81b60;
435- border-color: #d81b60;
436- color: white;
437- .warning-heading::before {
438- /* equivalent to `.notice.warning .warning-heading::before` */
439- content: "! ";
440- }
441- }
442-}
443 @media (hover: hover) {
444 abbr:hover {
445 color: #001ca8;
446@@ -68,5 +18,5 @@ html#nested {
447
448 Style style = parseCSS(inputFile);
449
450- Style s = style.children[4];
451+ Style s = style.children[0];
452 }
453diff --git a/playground/parseselectorparts.cc b/playground/parseselectorparts.cc
454index ba0065d..e0e9299 100644
455--- a/playground/parseselectorparts.cc
456+++ b/playground/parseselectorparts.cc
457@@ -5,8 +5,8 @@
458 #include <iostream>
459
460 int main() {
461- std::vector<std::vector<std::string>> parts = parseSelectorParts("2n + 3");
462+ std::vector<std::vector<std::string>> parts = parseSelectorParts("h1:test, h1[name='asd']");
463
464- std::cout << std::stoi(parts[0][0].substr(0,parts[0][0].length()-1)) << std::endl;
465+ std::cout << parts[1][1] << std::endl;
466 }
467
468diff --git a/playground/window.cc b/playground/window.cc
469new file mode 100644
470index 0000000..bbf274a
471--- /dev/null
472+++ b/playground/window.cc
473@@ -0,0 +1,45 @@
474+#include "../include/grim.h"
475+#include "../include/parser.h"
476+#include <string>
477+#include <sstream>
478+#include <vector>
479+#include <iostream>
480+
481+int main() {
482+ std::string html = "<!DOCTYPE html>"
483+ "<html>"
484+ "<head>"
485+ "<title>This is a simple test</title>"
486+ "<!--<link rel=\"stylesheet\" href=\'/style.css\'/>-->"
487+ "<style>h1 {background: red;}</style>"
488+ "</head>"
489+ "<body>"
490+ "<h1 id=\"h1Tag\">Header</h1>"
491+ "<p id=\"paragraph\" class=\"class1 class2\">This is some text for a simple test</p>"
492+ "<div contenteditable=\"true\">"
493+ "<p>this is a only child</p>"
494+ "</div>"
495+ "<input type=\"text\" checked required/>"
496+ "<ul>"
497+ "<li>one</li>"
498+ "<li>two</li>"
499+ "<li>three</li>"
500+ "<li>four</li>"
501+ "</ul>"
502+ "<input type=\"radio\" checked required/>"
503+ "<input type=\"text\" readonly/>"
504+ "<input type=\"text\" disabled/>"
505+ "<textarea></textarea>"
506+ "<textarea readonly></textarea>"
507+ "<textarea disabled></textarea>"
508+ "</body>"
509+ "</html>";
510+
511+
512+ std::stringstream ss(html);
513+
514+ Window window = createWindow();
515+ window.parseHTML(ss);
516+
517+ Node document = window.document;
518+}
519diff --git a/src/grim.cc b/src/grim.cc
520index 070f139..fd316a0 100755
521--- a/src/grim.cc
522+++ b/src/grim.cc
523@@ -16,7 +16,7 @@
524 // |(TOC) Table of contents |
525 // +------------------------------------------------------+
526 // |(p98ym) Utils |
527-// | (hldfk) Document Parsing |
528+// |(hldfk) Window |
529 // |(tigfn) Attribute Handling |
530 // |(f98y0) ClassList |
531 // |(lhujn) StyleList |
532@@ -66,18 +66,21 @@ void trimSpace(std::string& str) {
533 }
534 }
535 // +---------------------------------------------------+
536-// | (hldfk) Document Parsing |
537+// | (hldfk) Window |
538 // +---------------------------------------------------+
539-
540-void Window::loadHTML(std::istream& inputStream) {
541- document = parseHTML(inputStream);
542+// All of windows props will come from the adapter setting
543+// them
544+Window createWindow() {
545+ Window window;
546+ return window;
547 }
548
549-void Window::loadCSS(std::istream& inputStream) {
550- CSS.addChild(parseCSS(inputStream));
551+void Window::parseHTML(std::istream& inputStream) {
552+ ParsedDocument doc = parseDocument(inputStream);
553+ document = doc.document;
554+ CSS = doc.CSS;
555 }
556
557-
558 // +---------------------------------------------------+
559 // | (tigfn) Attribute Handling |
560 // +---------------------------------------------------+
561@@ -110,20 +113,20 @@ void Node::deleteAttribute(const std::string& name) {
562 attributes.erase(name);
563 }
564
565-std::optional<Attribute> Node::getAttribute(const std::string& name) const {
566+std::string Node::getAttribute(const std::string& name) const {
567 // First check in the user set attributes
568 auto it = attributes.find(name);
569 if (it != attributes.end()) {
570 // return the value wrapped in the Attribute class
571- return Attribute(it->second);
572+ return it->second;
573 } else {
574 // If its not set by the user then find it in the
575 // default values map
576 it = DEFAULT_ATTRIBUTE_VALUES.find(name);
577 if (it != DEFAULT_ATTRIBUTE_VALUES.end()) {
578- return Attribute(it->second);
579+ return it->second;
580 } else {
581- return std::nullopt;
582+ return "";
583 }
584 }
585 }
586@@ -294,6 +297,9 @@ void Node::ClassList::toggle(std::string value) {
587 // contain the true value of the inline style and still have an
588 // easy to use API for .style. To get all styles, call .length()
589 // and itterate through all items using .item(<size_t>).
590+//
591+// getUnit is a interal method to get styles from the stylelist
592+// as parsed units for rendering
593
594 void Node::StyleList::createIndex() {
595 std::string styles = self->attributes["style"];
596@@ -414,6 +420,27 @@ std::string Node::StyleList::get(std::string key) {
597 return "";
598 }
599
600+std::vector<Unit> Node::StyleList::getUnit(std::string key) {
601+ if (!checkIndex()) {
602+ createIndex();
603+ }
604+ std::string styles = self->attributes["style"];
605+ for (size_t i = 0; i < indexes.size(); i++) {
606+ int f = std::get<0>(indexes[i]);
607+ int s = std::get<1>(indexes[i]);
608+ std::string slicedKey = styles.substr(f, s-f);
609+ trimSpace(slicedKey);
610+ if (slicedKey == key) {
611+ int t = std::get<2>(indexes[i]);
612+ std::string value = styles.substr(s+1, t-(s+1));
613+ trimSpace(value);
614+ return getUnit(value);
615+ }
616+ }
617+ std::vector<Unit> empty;
618+ return empty;
619+}
620+
621 void Node::StyleList::set(std::string key, std::string value) {
622 if (!checkIndex()) {
623 createIndex();
624@@ -470,49 +497,49 @@ void Node::StyleList::remove(std::string key) {
625 // +---------------------------------------------------+
626
627 Node* Node::createElement(std::string name) {
628- std::unique_ptr<Node> newNode = std::make_unique<Node>();
629- newNode->setTagName(name);
630- newNode->parent = this;
631- children.push_back(std::move(newNode));
632- return children.back().get();
633+ Node newNode;
634+ newNode.setTagName(name);
635+ newNode.parent = this;
636+ children.push_back(newNode);
637+ return &children.back();
638 }
639
640 std::string Node::print(int indent) {
641 std::string out = "";
642- for (int i = 0; i < indent; ++i) {
643- out += " ";
644- }
645+ for (int i = 0; i < indent; ++i) {
646+ out += " ";
647+ }
648
649- out += "<" + getTagName();
650- for (const auto& attr_pair : getAttributes()) {
651- if (attr_pair.first == "innerText" || attr_pair.first == "tagName") {
652- continue;
653- }
654- out += " " + attr_pair.first;
655- if (!attr_pair.second.empty()) {
656- out += "=\"" + attr_pair.second + "\"";
657- }
658- }
659- out += ">";
660+ out += "<" + getTagName();
661+ for (const auto& attr_pair : getAttributes()) {
662+ if (attr_pair.first == "innerText" || attr_pair.first == "tagName") {
663+ continue;
664+ }
665+ out += " " + attr_pair.first;
666+ if (!attr_pair.second.empty()) {
667+ out += "=\"" + attr_pair.second + "\"";
668+ }
669+ }
670+ out += ">";
671
672- if (auto innerText = getAttribute("innerText")) {
673- if (innerText != std::nullopt) {
674- out += "\n" + *innerText->String();
675- }
676- }
677+ std::string innerText = getAttribute("innerText");
678
679- out += "\n";
680+ if (innerText != "") {
681+ out += "\n" + innerText;
682+ }
683
684- for (const auto& child : children) {
685- out += child->print(indent + 1)+"\n";
686- }
687+ out += "\n";
688
689- for (int i = 0; i < indent; ++i) {
690- out += " ";
691- }
692- out += "</" + getTagName() + ">\n";
693+ for (auto& child : children) {
694+ out += child.print(indent + 1)+"\n";
695+ }
696+
697+ for (int i = 0; i < indent; ++i) {
698+ out += " ";
699+ }
700+ out += "</" + getTagName() + ">\n";
701
702- return out;
703+ return out;
704 }
705
706 // +---------------------------------------------------+
707@@ -665,8 +692,8 @@ std::vector<std::string> parseNodeParts(Node* node) {
708 parts.push_back(node->getTagName());
709 auto id = node->getAttribute("id");
710
711- if (id != std::nullopt) {
712- parts.push_back("#"+*id->String());
713+ if (id != "") {
714+ parts.push_back("#"+id);
715 }
716
717 // classes and attributes
718@@ -677,8 +704,8 @@ std::vector<std::string> parseNodeParts(Node* node) {
719 std::string v;
720 auto av = node->getAttribute(k);
721
722- if (av != std::nullopt) {
723- v = "=\""+*av->String()+"\"";
724+ if (av != "") {
725+ v = "=\""+av+"\"";
726
727 }
728 parts.push_back('['+k+v+']');
729@@ -708,13 +735,15 @@ void Style::addChild(Style s) {
730 // Get the last selector group from the parsed selector parts
731 // because that is the selector that applies to an element
732 // and map them into basemap
733- std::vector<std::string> targetParts = s.selector[s.selector.size()-1];
734- for (auto p : targetParts) {
735- if (basemap.find(p) != basemap.end()) {
736- basemap[p].push_back(index);
737- } else {
738- std::vector<size_t> bm = {index};
739- basemap[p] = bm;
740+ if (s.selector.size() > 0) {
741+ std::vector<std::string> targetParts = s.selector[s.selector.size()-1];
742+ for (auto p : targetParts) {
743+ if (basemap.find(p) != basemap.end()) {
744+ basemap[p].push_back(index);
745+ } else {
746+ std::vector<size_t> bm = {index};
747+ basemap[p] = bm;
748+ }
749 }
750 }
751 }
752@@ -751,35 +780,35 @@ std::unordered_map<KeyType, UnitType> Style::getStyles(Node* node) {
753
754 for (size_t c : canidates) {
755 // need to check the type of sheet it is
756- SheetType t = sheet->type;
757 std::vector<std::vector<std::string>> selector = sheet->children[c].selector;
758
759- if (t == SheetType::STYLE) {
760- // Run testSelector with the preparsed parts
761- if (testSelector(node, selector)) {
762- std::unordered_map<std::string, std::string> props = sheet->children[c].properties;
763- for (auto p : props) {
764- styles->insert(p);
765- }
766-
767- // If the Sheet matches then we will repeat the same process with that sheet
768- if (sheet->children[c].children.size() > 0) {
769- getstyles(node, node_parts, &sheet->children[c], styles);
770- }
771+ // Run testSelector with the preparsed parts
772+ if (testSelector(node, selector)) {
773+ std::unordered_map<std::string, std::string> props = sheet->children[c].properties;
774+ for (auto p : props) {
775+ styles->insert(p);
776 }
777- } else if (t == SheetType::CONTAINER) {
778
779+ // If the Sheet matches then we will repeat the same process with that sheet
780+ if (sheet->children[c].children.size() > 0) {
781+ getstyles(node, node_parts, &sheet->children[c], styles);
782+ }
783 }
784 }*/
785 }
786
787-// Used for root node without a parent
788-const std::vector<std::unique_ptr<Node>> EMPTY_NODE_CHILDREN_VECTOR;
789+// +------------------------------------------------------+
790+// |(7tygl) Query matching |
791+// +------------------------------------------------------+
792+// testQuery tries to calculate if the query (ie a media query or
793+// container query) applies to the node passed
794+
795+bool testQuery(Node* node, std::vector<std::string> parts) {
796+ return true;
797+}
798
799-/*
800-at-rules need to be calculated at getstyles
801-make a function to testAtRules
802-*/
803+// Used for root node without a parent
804+std::vector<Node> EMPTY_NODE_CHILDREN_VECTOR;
805
806 // node: The node you are checking the selector against
807 // selector: output of parseSelector
808@@ -869,19 +898,19 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
809 // the children variable is the PARENTS CHILDREN not the current node's children
810 // aka siblings
811 // if you want those, node->children
812- const std::vector<std::unique_ptr<Node>>& children = (node->parent != nullptr) ? node->parent->children : EMPTY_NODE_CHILDREN_VECTOR;
813+ std::vector<Node>& children = (node->parent != nullptr) ? node->parent->children : EMPTY_NODE_CHILDREN_VECTOR;
814 size_t childIndex = 0;
815 size_t childTypeIndex = 0;
816 size_t childTypeCount = 0;
817 if (children.size() > 1) {
818 for (size_t i = 0; i < children.size(); i++) {
819- Node* child_ptr = children[i].get();
820+ Node child = children[i];
821
822- if (child_ptr == node) {
823+ if (&child == node) {
824 childIndex = i;
825 childTypeIndex = childTypeCount;
826 }
827- if (child_ptr->getTagName() == tagName) {
828+ if (child.getTagName() == tagName) {
829 childTypeCount++;
830 }
831 }
832@@ -948,9 +977,11 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
833 for (auto p : parts) {
834 if (p[0] == '*') {
835 matched = true;
836- } else
837- if (i == 0 && p[0] != '#' && p[0] != '.' && p[0] != '[' && p[0] != ':') {
838+ } else if (i == 0 && p[0] != '#' && p[0] != '.' && p[0] != '[' && p[0] != ':') {
839 matched = tagName == p;
840+ } else if (p[0] == '@') {
841+ // This is a at-rule we will process it in a different function
842+ return testQuery(node, parts);
843 } else if (p[0] == '#') {
844 matched = "#"+attributes["id"] == p;
845 } else if (p[0] == '.') {
846@@ -1027,8 +1058,8 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
847 // The child combinator only checks direct children so we
848 // can loop through and if we find one that matches ip[1:]
849 // (remove the ">") we can stop and return
850- for (const auto& c : node->children) {
851- found = testSelector(c.get(), slicedParts);
852+ for (Node c : node->children) {
853+ found = testSelector(&c, slicedParts);
854 if (found) {
855 break;
856 }
857@@ -1038,7 +1069,7 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
858 // If there isn't a prevous sibling then it cannot match
859 if (childIndex < children.size()-1) {
860 // In this selector we just need to get the direct sibling and test it
861- found = testSelector(children[childIndex+1].get(), slicedParts);
862+ found = testSelector(&children[childIndex+1], slicedParts);
863 } else {
864 // if the element is the last there is no way it could have one
865 found = false;
866@@ -1049,7 +1080,7 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
867 for (size_t i = childIndex+1; i < children.size(); i++) {
868 // We have to test every selector after the current element,
869 // if any match then it matches
870- found = testSelector(children[i].get(), slicedParts);
871+ found = testSelector(&children[i], slicedParts);
872 if (found) {
873 break;
874 }
875@@ -1076,11 +1107,10 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
876 break;
877 }
878
879- for (const auto& child_ptr : n->children) {
880- Node* child = child_ptr.get();
881- if (visited.find(child) == visited.end()) {
882- visited.insert(child);
883- queue.push(child);
884+ for (Node child : n->children) {
885+ if (visited.find(&child) == visited.end()) {
886+ visited.insert(&child);
887+ queue.push(&child);
888 }
889 }
890 }
891@@ -1236,9 +1266,9 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
892 if (last) {
893 // Reverse through the siblings
894 for (size_t i = children.size() - 1; i >= 0; i--) {
895- Node* child_ptr = children[i].get();
896- if (child_ptr != node) {
897- if (testSelector(child_ptr, pOS)) {
898+ Node child = children[i];
899+ if (&child != node) {
900+ if (testSelector(&child, pOS)) {
901 // if the selector matches the ofSelector add one
902 index++;
903 }
904@@ -1252,9 +1282,9 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
905 }
906 } else {
907 for (size_t i = 0; i < children.size(); i++) {
908- Node* child_ptr = children[i].get();
909- if (child_ptr != node) {
910- if (testSelector(child_ptr, pOS)) {
911+ Node child = children[i];
912+ if (&child != node) {
913+ if (testSelector(&child, pOS)) {
914 // if the selector matches the ofSelector add one
915 index++;
916 }
917@@ -1352,7 +1382,7 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
918 return false;
919 } else {
920 // In this selector we just need to get the direct sibling and test it
921- matched = testSelector(children[childIndex-1].get(), {trimmed});
922+ matched = testSelector(&children[childIndex-1], {trimmed});
923 }
924 }
925 } else if (symbol == "~") {
926@@ -1361,7 +1391,7 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
927 for (size_t i = 0; i < childIndex; i++) {
928 // We have to test every selector before the current element,
929 // if any match then it matches
930- matched = testSelector(children[i].get(), {trimmed});
931+ matched = testSelector(&children[i], {trimmed});
932 if (matched) {
933 break;
934 }
935@@ -1387,24 +1417,3 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
936 return matched;
937 }
938
939-// +------------------------------------------------------+
940-// |(7tygl) Query matching |
941-// +------------------------------------------------------+
942-// testQuery takes any sheet class that isn't a SheetType::STYLE
943-// and tries to calculate if the query (ie a media query or
944-// container query) applies to the node passed
945-/*
946-bool testQuery(Sheet* query, Node* node, Window* window) {
947- // First step is to understand the query
948- // it is parsed from parseSelector parts
949- // @container scroll-state(scrollable: top)
950- // {{" ", "scroll-state(scrollable: top)"}}
951-
952- //I feel like having string content is going to make things slow
953-
954-
955- if (query->type == SheetType::CONTAINER) {
956-
957- }
958-}
959-*/
960diff --git a/src/parser.cc b/src/parser.cc
961index 487e591..c1dbd83 100755
962--- a/src/parser.cc
963+++ b/src/parser.cc
964@@ -107,239 +107,6 @@ std::unordered_map<std::string, std::string> parseAttributes(std::string_view to
965 return attrs;
966 }
967
968-Node parseHTML(std::istream& inputStream) {
969- Node root;
970- root.setTagName("root");
971-
972- Node* currentNode = &root;
973-
974- bool inTag = false;
975- bool escaped = false;
976- bool inQuote = false;
977- bool inComment = false;
978- bool waitToClose = false;
979- char quoteType = '"';
980-
981- std::string token;
982-
983- char current;
984- while (inputStream.get(current)) {
985- // Finds the --> and removes it then resets inComment
986- if (inComment) {
987- // added the peek to prevent hitting on every -
988- if (current == '-' && inputStream.peek() == '-') {
989- char a,b;
990- // load the next two
991- if (inputStream.get(a) && inputStream.get(b)) {
992- // We know a == -
993- if (b == '>') {
994- // Close the comment
995- inComment = false;
996- }
997- }
998-
999- if (!inComment) {
1000- // we don't put anything back and that puts us where we need to be
1001- continue;
1002- } else {
1003- // Not the end
1004- inputStream.putback(b);
1005- inputStream.putback(a);
1006- }
1007- }
1008- continue;
1009- }
1010-
1011- std::string tagName = currentNode->getTagName();
1012- if (!waitToClose && (
1013- tagName == "style" ||
1014- tagName == "script" ||
1015- tagName == "textarea"
1016- ) && current == '<' && inputStream.peek() == '/' ) {
1017- // If its the starting of a closing tag and we are inside of a special tag
1018- // we need to see if the upcoming closing tag is the correct closing tag
1019- // if it is then we put the read ahead back and waitToClose. This will let normal parsing
1020- // continue
1021- const size_t readTo = tagName.length()+2;
1022- std::string buffer(readTo, '\0');
1023-
1024- inputStream.read(&buffer[0], readTo);
1025-
1026- if (buffer == "/"+tagName+">") {
1027- waitToClose = true;
1028- } else {
1029- token += current;
1030- }
1031-
1032- for (int i = readTo-1; i>= 0; i--) {
1033- inputStream.putback(buffer[i]);
1034- }
1035- if (!waitToClose) {
1036- continue;
1037- }
1038- } else if (!waitToClose && (
1039- tagName == "style" ||
1040- tagName == "script" ||
1041- tagName == "textarea"
1042- )) {
1043- token += current;
1044- continue;
1045- }
1046-
1047- if (inTag) {
1048- if ((current == '"' || current == '\'') && !escaped) {
1049- if (inQuote && current == quoteType) {
1050- inQuote = false;
1051- } else if (!inQuote) {
1052- inQuote = true;
1053- quoteType = current;
1054- }
1055- }
1056-
1057- if (current == '>' && !inQuote && !escaped) {
1058- inTag = false;
1059- waitToClose = false;
1060- // Even if the next tag is next still add a text tag as we can just check if its empty and remove it
1061- // this check would still have to be done either way so we aren't wasting anything
1062- if (token.length() > 0) {
1063- bool empty = true;
1064- bool closingTag = false;
1065- for (size_t i = 0; i < token.length(); i++) {
1066- auto c = token[i];
1067- if (std::isspace(c)) {
1068- continue;
1069- } else if (c == '/') {
1070- closingTag = true;
1071- } else {
1072- empty = false;
1073- break;
1074- }
1075- }
1076-
1077- bool selfClosing = false;
1078- int selfClosingPosition = token.length()-1;
1079- if (!closingTag) {
1080- for (int i = token.length()-1; i >= 0; i--) {
1081- auto c = token[i];
1082- if (std::isspace(c)) {
1083- continue;
1084- } else if (c == '/') {
1085- selfClosingPosition = i;
1086- selfClosing = true;
1087- break;
1088- } else {
1089- break;
1090- }
1091- }
1092- }
1093-
1094- if (!empty) {
1095- if (selfClosing) {
1096- //Create element and don't jump inside
1097- // Use node instead of currentNode because we do not need to jump into the created element
1098- auto attrs = parseAttributes(token.substr(0, selfClosingPosition));
1099- auto node = currentNode->createElement(attrs["tagName"]);
1100- for (auto const& pair : attrs) {
1101- // All attributes are stored as strings so we can just throw them in
1102- node->setAttribute(pair.first, pair.second);
1103- }
1104- } else if (closingTag) {
1105- // Checksum and tree move
1106- std::string tagName = "";
1107- for (auto t : token) {
1108- if (!std::isspace(t) && t != '/') {
1109- tagName += t;
1110- } else if (t == '/') {
1111- // For closing tags we just want the name
1112- continue;
1113- } else if (tagName.length() > 0) {
1114- break;
1115- }
1116- }
1117- if (currentNode->getTagName() == tagName) {
1118- currentNode = currentNode->parent;
1119- } else {
1120- std::cerr << "malformed html: closing tag (</" << tagName << ">) found for <" << currentNode->getTagName() << ">" << std::endl;
1121- }
1122- } else {
1123- // Create a element and jump inside
1124- auto attrs = parseAttributes(token);
1125- // Don't add elements like <!DOCTYPE>
1126- if (attrs["tagName"][0] != '!') {
1127- currentNode = currentNode->createElement(attrs["tagName"]);
1128- for (auto const& pair : attrs) {
1129- // All attributes are stored as strings so we can just throw them in
1130- currentNode->setAttribute(pair.first, pair.second);
1131- }
1132- }
1133- }
1134- }
1135- }
1136-
1137- token = "";
1138- continue;
1139- }
1140- } else if (!escaped && current == '<') {
1141- // if the next character is a !
1142- if (inputStream.peek() == '!') {
1143- char a,b,c;
1144- // load the next three characters (includes !)
1145- if (inputStream.get(a) && inputStream.get(b) && inputStream.get(c)) {
1146- // We know a == !
1147- if (b == '-' && c == '-') {
1148- inComment = true;
1149- }
1150- }
1151-
1152- if (inComment) {
1153- continue;
1154- } else {
1155- // Not a comment add all back
1156- inputStream.putback(c);
1157- inputStream.putback(b);
1158- inputStream.putback(a);
1159- }
1160- }
1161- inTag = true;
1162-
1163- // Here's where you actually make the text node and above the real nodes
1164- // can also prob make the vector of tokens a single variable
1165- // it really just needs to be a data string bc we know the type based on inTag
1166-
1167-
1168- bool hasText = false;
1169- for (auto t : token) {
1170- if (!std::isspace(t)) {
1171- hasText = true;
1172- break;
1173- }
1174- }
1175-
1176- // TODO: Add a vector that stores inner text and when elements start to close out pop them but add all to their innerText
1177- // | Will need something similar for innerHTML
1178- if (hasText) {
1179- auto node = currentNode->createElement("text");
1180- node->setAttribute("innerText", token);
1181- }
1182-
1183-
1184- token = "";
1185- continue;
1186- }
1187-
1188- escaped = false;
1189-
1190- if (current == '\\') { //'
1191- escaped = true;
1192- continue;
1193- }
1194-
1195- token += current;
1196- }
1197-
1198- return root;
1199-}
1200-
1201 static const std::unordered_map<std::string, KeyType> keySearch {
1202 {"accent-color", KeyType::ACCENT_COLOR},
1203 {"align-content", KeyType::ALIGN_CONTENT},
1204@@ -1488,71 +1255,6 @@ Style parseCSS(std::istream& inputStream) {
1205
1206 current = ¤t->children.back();
1207
1208- // here we need to take the selector (buffer)
1209- // and find out what type it is. Also need to manage the nesting
1210- if (parts[0][0][0] == '@') {
1211- if (parts[0][0] == "@container") {
1212- current->type = StyleType::CONTAINER;
1213- bool hasName = false;
1214- if (parts[0].size() >= 3) {
1215- // @container not (width < 400px)
1216- if (parts[0][2] != "not") {
1217- // @container (width > 400px) or|and (height > 400px)
1218- if (parts[0].size() >= 5) {
1219- if (parts[0][5] != "and" || parts[0][5] != "or") {
1220- current->name = parts[0][2];
1221- hasName = true;
1222- }
1223- }
1224- }
1225- }
1226- // If a name is present then remove the name and tag from the selector
1227- // else just remove the @container
1228- if (hasName) {
1229- current->selector[0] = std::vector<std::string>(current->selector[0].begin() + 3, current->selector[0].end());
1230- } else {
1231- current->selector[0] = std::vector<std::string>(current->selector[0].begin() + 1, current->selector[0].end());
1232- }
1233- } else if (parts[0][0] == "@counter-style") {
1234- current->name = parts[0][2];
1235- current->type = StyleType::COUNTER;
1236- } else if (parts[0][0] == "@font-face") {
1237- current->type = StyleType::FONTFACE;
1238- } else if (parts[0][0] == "@font-feature") {
1239- current->type = StyleType::FONTFEATURE;
1240- } else if (parts[0][0] == "@swash") {
1241- current->type = StyleType::SWASH;
1242- } else if (parts[0][0] == "@annotation") {
1243- current->type = StyleType::ANNOTATION;
1244- } else if (parts[0][0] == "@ornaments") {
1245- current->type = StyleType::ORNAMENTS;
1246- } else if (parts[0][0] == "@stylistic") {
1247- current->type = StyleType::STYLISTIC;
1248- } else if (parts[0][0] == "@styleset") {
1249- current->type = StyleType::STYLESET;
1250- } else if (parts[0][0] == "@character-varient") {
1251- current->type = StyleType::CHARACTERVARIANT;
1252- } else if (parts[0][0] == "@font-palette-values") {
1253- current->type = StyleType::FONTPALETTEVALUES;
1254- current->name = parts[0][2];
1255- } else if (parts[0][0] == "@keyframes") {
1256- current->type = StyleType::KEYFRAMES;
1257- current->name = parts[0][2];
1258- } else if (parts[0][0] == "@layer") {
1259- current->type = StyleType::LAYER;
1260- } else if (parts[0][0] == "@media") {
1261- current->type = StyleType::MEDIA;
1262- } else if (parts[0][0] == "@property") {
1263- current->type = StyleType::PROPERTY;
1264- current->name = parts[0][2];
1265- } else if (parts[0][0] == "@starting-style") {
1266- current->type = StyleType::STARTINGSTYLE;
1267- }
1268- } else {
1269- current->type = StyleType::STYLE;
1270- }
1271-
1272-
1273 buffer.clear();
1274 } else if (ch == '}') {
1275
1276@@ -1632,54 +1334,276 @@ Style parseCSS(std::istream& inputStream) {
1277 return root;
1278 }
1279
1280-std::unordered_map<KeyType, std::vector<Unit>> parseCSSInline(std::string inlineStyles) {
1281- std::unordered_map<KeyType, std::vector<Unit>> props;
1282+struct ParsedDocument {
1283+ Node document;
1284+ Style CSS;
1285+};
1286
1287- std::string buffer;
1288+ParsedDocument parseDocument(std::istream& inputStream) {
1289+ Node root;
1290+ root.setTagName("root");
1291
1292- for (size_t i = 0; i < inlineStyles.length(); i++) {
1293- char ch = inlineStyles[i];
1294+ Node* currentNode = &root;
1295
1296- if (ch == ';' || i >= inlineStyles.length()-1) {
1297- // If this is a ';' then it will be removed in the last else if statement
1298- buffer.push_back(ch);
1299- std::string key;
1300- std::string value;
1301- bool keyFound = false;
1302- for (size_t i = 0; i < buffer.length(); i++) {
1303- char c = buffer[i];
1304- if (!keyFound && c == ':') {
1305- keyFound = true;
1306- } else if (!std::isspace(c) && !keyFound) {
1307- key += c;
1308- } else if (keyFound &&
1309- !(std::isspace(c) && i < buffer.length()-1 && std::isspace(buffer[i+1])) &&
1310- c != ';'
1311- ) {
1312- // if the key is found and the character isn't a ':'
1313- // also (if c is a space and the next c will be a space)
1314- // this is to auto trim duplicate spaces from the value
1315- value += c;
1316-
1317+ bool inTag = false;
1318+ bool escaped = false;
1319+ bool inQuote = false;
1320+ bool inComment = false;
1321+ bool waitToClose = false;
1322+ char quoteType = '"';
1323+
1324+ std::string token;
1325+
1326+ // CSS stuff
1327+ std::vector<Node*> cssElements;
1328+
1329+ char current;
1330+ while (inputStream.get(current)) {
1331+ // Finds the --> and removes it then resets inComment
1332+ if (inComment) {
1333+ // added the peek to prevent hitting on every -
1334+ if (current == '-' && inputStream.peek() == '-') {
1335+ char a,b;
1336+ // load the next two
1337+ if (inputStream.get(a) && inputStream.get(b)) {
1338+ // We know a == -
1339+ if (b == '>') {
1340+ // Close the comment
1341+ inComment = false;
1342+ }
1343+ }
1344+
1345+ if (!inComment) {
1346+ // we don't put anything back and that puts us where we need to be
1347+ continue;
1348+ } else {
1349+ // Not the end
1350+ inputStream.putback(b);
1351+ inputStream.putback(a);
1352 }
1353 }
1354+ continue;
1355+ }
1356
1357- KeyType keyENUM;
1358+ std::string tagName = currentNode->getTagName();
1359+ if (!waitToClose && (
1360+ tagName == "style" ||
1361+ tagName == "script" ||
1362+ tagName == "textarea"
1363+ ) && current == '<' && inputStream.peek() == '/' ) {
1364+ // If its the starting of a closing tag and we are inside of a special tag
1365+ // we need to see if the upcoming closing tag is the correct closing tag
1366+ // if it is then we put the read ahead back and waitToClose. This will let normal parsing
1367+ // continue
1368+ const size_t readTo = tagName.length()+2;
1369+ std::string buffer(readTo, '\0');
1370
1371- if (auto it = keySearch.find(key); it != keySearch.end()) {
1372- keyENUM = it->second;
1373+ inputStream.read(&buffer[0], readTo);
1374+
1375+ if (buffer == "/"+tagName+">") {
1376+ waitToClose = true;
1377 } else {
1378- std::cout << "Invalid property name: " << key << std::endl;
1379+ token += current;
1380 }
1381- props[keyENUM] = getUnit(value);
1382
1383- buffer.clear();
1384- } else {
1385- buffer.push_back(ch);
1386+ for (int i = readTo-1; i>= 0; i--) {
1387+ inputStream.putback(buffer[i]);
1388+ }
1389+ if (!waitToClose) {
1390+ continue;
1391+ }
1392+ } else if (!waitToClose && (
1393+ tagName == "style" ||
1394+ tagName == "script" ||
1395+ tagName == "textarea"
1396+ )) {
1397+ token += current;
1398+ continue;
1399 }
1400+
1401+ if (inTag) {
1402+ if ((current == '"' || current == '\'') && !escaped) {
1403+ if (inQuote && current == quoteType) {
1404+ inQuote = false;
1405+ } else if (!inQuote) {
1406+ inQuote = true;
1407+ quoteType = current;
1408+ }
1409+ }
1410+
1411+ if (current == '>' && !inQuote && !escaped) {
1412+ inTag = false;
1413+ waitToClose = false;
1414+ // Even if the next tag is next still add a text tag as we can just check if its empty and remove it
1415+ // this check would still have to be done either way so we aren't wasting anything
1416+ if (token.length() > 0) {
1417+ bool empty = true;
1418+ bool closingTag = false;
1419+ for (size_t i = 0; i < token.length(); i++) {
1420+ auto c = token[i];
1421+ if (std::isspace(c)) {
1422+ continue;
1423+ } else if (c == '/') {
1424+ closingTag = true;
1425+ } else {
1426+ empty = false;
1427+ break;
1428+ }
1429+ }
1430+
1431+ bool selfClosing = false;
1432+ int selfClosingPosition = token.length()-1;
1433+ if (!closingTag) {
1434+ for (int i = token.length()-1; i >= 0; i--) {
1435+ auto c = token[i];
1436+ if (std::isspace(c)) {
1437+ continue;
1438+ } else if (c == '/') {
1439+ selfClosingPosition = i;
1440+ selfClosing = true;
1441+ break;
1442+ } else {
1443+ break;
1444+ }
1445+ }
1446+ }
1447+
1448+ if (!empty) {
1449+ if (selfClosing) {
1450+ //Create element and don't jump inside
1451+ // Use node instead of currentNode because we do not need to jump into the created element
1452+ auto attrs = parseAttributes(token.substr(0, selfClosingPosition));
1453+ auto node = currentNode->createElement(attrs["tagName"]);
1454+ for (auto const& pair : attrs) {
1455+ // All attributes are stored as strings so we can just throw them in
1456+ node->setAttribute(pair.first, pair.second);
1457+ }
1458+ if (attrs["tagName"] == "link" || attrs["tagName"] == "style") {
1459+ cssElements.push_back(node);
1460+ }
1461+ } else if (closingTag) {
1462+ // Checksum and tree move
1463+ std::string tagName = "";
1464+ for (auto t : token) {
1465+ if (!std::isspace(t) && t != '/') {
1466+ tagName += t;
1467+ } else if (t == '/') {
1468+ // For closing tags we just want the name
1469+ continue;
1470+ } else if (tagName.length() > 0) {
1471+ break;
1472+ }
1473+ }
1474+ if (currentNode->getTagName() == tagName) {
1475+ currentNode = currentNode->parent;
1476+ } else {
1477+ std::cerr << "malformed html: closing tag (</" << tagName << ">) found for <" << currentNode->getTagName() << ">" << std::endl;
1478+ }
1479+ } else {
1480+ // Create a element and jump inside
1481+ auto attrs = parseAttributes(token);
1482+ // Don't add elements like <!DOCTYPE>
1483+ if (attrs["tagName"][0] != '!') {
1484+ currentNode = currentNode->createElement(attrs["tagName"]);
1485+ for (auto const& pair : attrs) {
1486+ // All attributes are stored as strings so we can just throw them in
1487+ currentNode->setAttribute(pair.first, pair.second);
1488+ }
1489+ if (attrs["tagName"] == "link" || attrs["tagName"] == "style") {
1490+ cssElements.push_back(currentNode);
1491+ }
1492+ }
1493+
1494+ }
1495+ }
1496+ }
1497+
1498+ token = "";
1499+ continue;
1500+ }
1501+ } else if (!escaped && current == '<') {
1502+ // if the next character is a !
1503+ if (inputStream.peek() == '!') {
1504+ char a,b,c;
1505+ // load the next three characters (includes !)
1506+ if (inputStream.get(a) && inputStream.get(b) && inputStream.get(c)) {
1507+ // We know a == !
1508+ if (b == '-' && c == '-') {
1509+ inComment = true;
1510+ }
1511+ }
1512+
1513+ if (inComment) {
1514+ continue;
1515+ } else {
1516+ // Not a comment add all back
1517+ inputStream.putback(c);
1518+ inputStream.putback(b);
1519+ inputStream.putback(a);
1520+ }
1521+ }
1522+ inTag = true;
1523+
1524+ // Here's where you actually make the text node and above the real nodes
1525+ // can also prob make the vector of tokens a single variable
1526+ // it really just needs to be a data string bc we know the type based on inTag
1527+
1528+
1529+ bool hasText = false;
1530+ for (auto t : token) {
1531+ if (!std::isspace(t)) {
1532+ hasText = true;
1533+ break;
1534+ }
1535+ }
1536+
1537+ // TODO: Add a vector that stores inner text and when elements start to close out pop them but add all to their innerText
1538+ // | Will need something similar for innerHTML
1539+ if (hasText) {
1540+ auto node = currentNode->createElement("text");
1541+ node->setAttribute("innerText", token);
1542+ }
1543+
1544+
1545+ token = "";
1546+ continue;
1547+ }
1548+
1549+ escaped = false;
1550+
1551+ if (current == '\\') { //'
1552+ escaped = true;
1553+ continue;
1554+ }
1555+
1556+ token += current;
1557 }
1558
1559- return props;
1560-}
1561+ ParsedDocument doc;
1562+ doc.document = root;
1563+
1564+ Style style;
1565
1566+ for (size_t i = 0; i < cssElements.size(); i++) {
1567+ Node* el = cssElements[i];
1568
1569+ if (el->getTagName() == "link" && el->getAttribute("rel") == "stylesheet") {
1570+ std::ifstream file(el->getAttribute("href"));
1571+ if (!file.is_open()) {
1572+ std::cerr << "Failed to open file!" << std::endl;
1573+ } else {
1574+ Style s = parseCSS(file);
1575+ style.addChild(s);
1576+ }
1577+ file.close();
1578+ } else if (el->getTagName() == "style") {
1579+ std::istringstream iss(el->children[0].getAttribute("innerText"));
1580+ Style s = parseCSS(iss);
1581+ style.addChild(s);
1582+ }
1583+ }
1584+
1585+ doc.CSS = style;
1586+
1587+ return doc;
1588+}
1589diff --git a/tests/css_parser.cc b/tests/css_parser.cc
1590index c5a1524..2bb474b 100755
1591--- a/tests/css_parser.cc
1592+++ b/tests/css_parser.cc
1593@@ -281,36 +281,4 @@ html#nested {
1594 }
1595
1596 }
1597-/*
1598-TEST_CASE("parseCSSInline can parse inline styles") {
1599- SECTION("parseCSSInline can parse a single property with a ending ;") {
1600- std::string inlineStyles = "background: yellow;";
1601- compareInlineStyles(inlineStyles, {
1602- {"background", "yellow"}
1603- });
1604- }
1605
1606- SECTION("parseCSSInline can parse a single property without a ending ;") {
1607- std::string inlineStyles = "background: red";
1608- compareInlineStyles(inlineStyles, {
1609- {"background", "red"}
1610- });
1611- }
1612- SECTION("parseCSSInline can parse multiple properties with a ending ;") {
1613- std::string inlineStyles = "background: yellow; color: red; font-size: 10px;";
1614- compareInlineStyles(inlineStyles, {
1615- {"background", "yellow"},
1616- {"color", "red"},
1617- {"font-size", "10px"}
1618- });
1619- }
1620-
1621- SECTION("parseCSSInline can parse multiple properties without a ending ;") {
1622- std::string inlineStyles = "background: red; color: yellow; font-size: 10px";
1623- compareInlineStyles(inlineStyles, {
1624- {"background", "red"},
1625- {"color", "yellow"},
1626- {"font-size", "10px"},
1627- });
1628- }
1629-}*/