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+}