home readme diff tree note docs

0commit 0ac66915186c657ebd3b635167edeacc0448bec6
1Author: Mason Wright <mason@lakefox.net>
2Date:   Sun Jul 13 16:22:08 2025 -0600
3
4    Started StyleHandler
5
6diff --git a/src/grim.cc b/src/grim.cc
7index 39afe4f..a6975b6 100755
8--- a/src/grim.cc
9+++ b/src/grim.cc
10@@ -261,23 +261,93 @@ void Node::ClassList::toggle(std::string value) {
11 	}
12 }
13 
14-std::string Node::ClassList::value() const {
15-	return self->Attributes["class"];
16+// +---------------------------------------------------+
17+// | (lhujn) StyleList                                 |
18+// +---------------------------------------------------+
19+// StyleList provides the JS .style API with a few changes
20+// to get/set values use .get or .set. To get the entire
21+// value call .getAttribute("style"). The StyleList stores
22+// a index of the positions of the first charactor, the ':',
23+// and the position of the ';'. On every read/write the index
24+// is verified then the change or read is made. If the value
25+// changes the positions of the index it will be updated on the
26+// next read/write. This allows the attribute "style" to always
27+// contain the true value of the inline style and still have an
28+// easy to use API for .style. To get all styles, call .length()
29+// and itterate through all items using .item(<size_t>).
30+
31+void Node::StyleList::createIndex() {
32+	std::string styles = self->attributes["style"];
33+	// Update the stored length
34+	len = styles.length();
35+	indexes.clear();
36+
37+	// "  background-color: white;"
38+	//    ^
39+	
40+	int firstLetter = 0;
41+	bool firstNonWhiteSpace = false;
42+	// "  background-color: white;"
43+	//                    ^
44+	int splitPoint = 0;
45+	bool keyFound = false;
46+	// "  background-color: white;"
47+	//                      ^
48+	int end = 0;
49+	bool secondNonWhiteSpace = false;
50+	// "  background-color: white;"
51+	//                           ^
52+	// Reset all to false and add the indexs
53+
54+
55+	for (size_t i = 0; i < len; i++) {
56+		char s = styles[i];
57+		if (firstNonWhiteSpace) {
58+			if (keyFound) {
59+				if (secondNonWhiteSpace) {
60+					if (s == ';' || i >= len-1) {
61+						end = i;
62+						indexes.push_back({firstLetter, splitPoint, end});
63+						firstNonWhiteSpace = false;
64+						secondNonWhiteSpace = false;
65+						keyFound = false;
66+					}
67+				} else if (!std::isspace(s)) {
68+					secondNonWhiteSpace = true;
69+				}
70+			} else if (s == ':') {
71+				splitPoint = i;
72+				keyFound = true;
73+			}
74+		} else if (!std::isspace(s)) {
75+			firstLetter = i;
76+			firstNonWhiteSpace = true;
77+		}
78+	}
79 }
80 
81 
82-const std::unordered_map<std::string, std::string>& Node::getAttributes() const {
83-    return Attributes;
84+bool Node::StyleList::checkIndex() {
85+	std::string styles = self->attributes["style"];
86+	if (styles.length() != len) {
87+		return false;
88+	} else {
89+		for (size_t i = 0; i < indexes.size(); i++) {
90+			if (std::isspace(styles[std::get<0>(indexes[i])]) ||
91+					styles[std::get<1>(indexes[i])] != ':' || 
92+					styles[std::get<2>(indexes[i])] != ';'
93+					) {
94+				return false;
95+			}
96+		}
97+		return true;
98+	}
99 }
100 
101-// Implement the getter/setter methods declared using the macro
102-#define IMPLEMENT_ATTRIBUTE_ACCESSORS(_Type, _FuncNameSuffix, _AttrKeyString) \
103-    _Type Node::get##_FuncNameSuffix() const { \
104-        return getAttribute<_Type>(_AttrKeyString); \
105-    } \
106-    void Node::set##_FuncNameSuffix(_Type value) { \
107-        setAttribute(_AttrKeyString, value); \
108-    }
109+std::pair<std::string, std::string> Node::StyleList::item(size_t index) {
110+	if (!checkIndex()) {
111+		createIndex();
112+	}
113 
114 	std::pair<std::string, std::string> property;
115 	std::string styles = self->attributes["style"];
116@@ -301,48 +371,42 @@ size_t Node::StyleList::length() {
117 	return indexes.size();
118 }
119 
120-template<typename T>
121-T Node::getAttribute(const std::string& name) const {
122-    auto it = Attributes.find(name);
123-    if (it != Attributes.end()) {
124-        const std::string& s = it->second;
125-
126-        if constexpr (std::is_same_v<T, int>) {
127-            try {
128-                return std::stoi(s);
129-            } catch (const std::invalid_argument& e) {
130-                return 0;
131-            } catch (const std::out_of_range& e) {
132-                return 0;
133-            }
134-        } else if constexpr (std::is_same_v<T, bool>) {
135-            std::string lower_s = s;
136-            std::transform(lower_s.begin(), lower_s.end(), lower_s.begin(),
137-                [](unsigned char c){ return std::tolower(c); });
138-            return (!lower_s.empty() && lower_s != "false");
139-        } else if constexpr (std::is_same_v<T, double>) {
140-            try {
141-                return std::stod(s);
142-            } catch (const std::invalid_argument& e) {
143-                return 0.0;
144-            } catch (const std::out_of_range& e) {
145-                return 0.0;
146-            }
147-        } else {
148-            static_assert(std::is_convertible_v<std::string, T>,
149-                "getAttribute: Type conversion from std::string not implemented for this type.");
150-            return static_cast<T>(s);
151-        }
152-    }
153-    return T();
154+std::string Node::StyleList::get(std::string key) {
155+	if (!checkIndex()) {
156+		createIndex();
157+	}
158+	std::string styles = self->attributes["style"];
159+	for (size_t i = 0; i < indexes.size(); i++) {
160+		int f = std::get<0>(indexes[i]);
161+		int s = std::get<1>(indexes[i]);
162+		std::string slicedKey = styles.substr(f, s-f);
163+		trimSpace(slicedKey);
164+		if (slicedKey == key) {
165+			int t = std::get<2>(indexes[i]);
166+			std::string value = styles.substr(s+1, t-(s+1));
167+			trimSpace(value);
168+			return value;
169+		}
170+	}
171+	return "";
172 }
173 
174-std::string Node::getAttribute(const std::string& name) const {
175-    auto it = Attributes.find(name);
176-    if (it != Attributes.end()) {
177-        return it->second;
178-    }
179-    return "";
180+void Node::StyleList::set(std::string key, std::string value) {
181+	if (!checkIndex()) {
182+		createIndex();
183+	}
184+	std::string styles = self->attributes["style"];
185+	for (size_t i = 0; i < indexes.size(); i++) {
186+		int f = std::get<0>(indexes[i]);
187+		int s = std::get<1>(indexes[i]);
188+		std::string slicedKey = styles.substr(f, f-s);
189+		trimSpace(slicedKey);
190+		if (slicedKey == key) {
191+			int t = std::get<2>(indexes[i]);
192+			self->attributes["style"] = styles.substr(0, f) +" "+key+":"+value+";"+styles.substr(t);
193+			break;
194+		}
195+	}
196 }
197 
198 // +---------------------------------------------------+
199diff --git a/src/parser.cc b/src/parser.cc
200index 5d8d984..dfc5a68 100644
201--- a/src/parser.cc
202+++ b/src/parser.cc
203@@ -314,7 +314,7 @@ std::unique_ptr<Node> parseHTML(std::istream& inputStream) {
204 			// + Will need something simular for innerHTML
205 			if (hasText) {
206 				auto node = currentNode->createElement("text");
207-				node->setInnerText(token);
208+				node->setAttribute("innerText", token);
209 			}
210 
211 
212diff --git a/tests/html_node.cc b/tests/html_node.cc
213index ae1f346..bcddce2 100644
214--- a/tests/html_node.cc
215+++ b/tests/html_node.cc
216@@ -64,3 +64,22 @@ TEST_CASE("ClassList", "[html]") {
217 		};
218 	}
219 }
220+
221+TEST_CASE("StyleList", "[html]") {
222+	std::string html = "<div style=\"background: white; font-size: 12px;\"></div>";
223+
224+	std::stringstream ss(html);
225+	std::unique_ptr<Node> document = parseHTML(ss);
226+	auto div = document->children[0].get();
227+
228+	SECTION("StyleList can parse a inline style") {
229+		REQUIRE(div->style.get("background") == "white");
230+		REQUIRE(div->style.get("font-size") == "12px");
231+	}
232+
233+	SECTION("StyleList can parse a inline style without a ending ;") {
234+		div->setAttribute("style", "background: white; font-size: 12px");
235+		REQUIRE(div->style.get("background") == "white");
236+		REQUIRE(div->style.get("font-size") == "12px");
237+	}
238+}