home
readme
diff
tree
note
docs
0commit 8cb68b3a6e02329a09a5c4065b6722c85b4b4b7a
1Author: lakefox <mason@lakefox.net>
2Date: Tue Dec 30 12:14:30 2025 -0700
3
4 WindowUnitLengthValue calculates correctly needs to be finished
5
6diff --git a/include/grim.h b/include/grim.h
7index 77af12c..9496863 100755
8--- a/include/grim.h
9+++ b/include/grim.h
10@@ -87,7 +87,9 @@ enum class UnitType : short {
11
12
13 // General Function
14- CALC, MIN, BLUR, BRIGHTNESS, CONTRAST, DROP_SHADOW, GRAYSCALE, HUE_ROTATE, INVERT, OPACITY, SEPIA, SATURATE,
15+ CALC, MIN, MAX, CLAMP, ROUND, MOD, PROGRESS, REMD, POW, SQRT, HYPOT, LOG, EXP, ABS,
16+ SIGN, SIN, COS, TAN, ASIN, ACOS, ATAN, ATAN2, VAR,
17+ BLUR, BRIGHTNESS, CONTRAST, DROP_SHADOW, GRAYSCALE, HUE_ROTATE, INVERT, OPACITY, SEPIA, SATURATE,
18 // Easing Functions
19 EASING_FUNCTION, LINEAR_EASING_FUNCTION, LINEAR, EASE, EASE_IN, EASE_OUT, EASE_IN_OUT, CUBIC_BEZIER_FUNCTION,
20 STEP_START, STEP_END, STEPS_FUNCTION, JUMP_START, JUMP_END, JUMP_NONE, JUMP_BOTH,
21@@ -110,10 +112,11 @@ union UnitValue {
22 char CHAR;
23 };
24
25-// 6 bytes
26+// 7 bytes
27 struct Unit {
28 UnitType type; // short
29 UnitValue value;
30+ bool isFunc;
31 };
32
33 class Window;
34@@ -168,8 +171,7 @@ class Node {
35
36 StyleList style;
37
38- Node() : parent(nullptr), classList(this), style(this) {
39- }
40+ Node() : parent(nullptr), classList(this), style(this) {}
41
42 std::string getTagName() const;
43 void setTagName(const std::string& name);
44@@ -267,7 +269,6 @@ enum class KeyType : short {
45
46 class Style {
47 public:
48- Window* window;
49 std::unordered_map<std::string, std::vector<size_t>> basemap{};
50
51 std::string name;
52@@ -284,65 +285,47 @@ class Style {
53 std::unordered_map<KeyType, UnitType> getStyles(Node* n);
54 };
55
56-enum class AnyHover : char {
57- None,
58- Hover
59-};
60-
61-enum class AnyPointer : char {
62+enum class Media : char {
63+ All,
64+ Print,
65+ Screen,
66 None,
67+ Hover,
68 Fine,
69- Coarse
70-};
71-
72-enum class Orientation : char {
73+ Coarse,
74 Landscape,
75- Portrait
76-};
77-
78-enum class ColorScheme : char {
79+ Portrait,
80 Light,
81- Dark
82-};
83-
84-enum class Contrast : char {
85+ Dark,
86 NoPreference,
87 More,
88- Less
89-};
90-
91-enum class ReducedMotion : char {
92- NoPreference,
93- Reduce
94-};
95-
96-enum class ReducedTransparency : char {
97- NoPreference,
98- Reduce
99-};
100-
101-enum class Update : char {
102+ Less,
103+ Reduce,
104 Fast,
105 Slow
106 };
107
108 class Window {
109 private:
110- int width = 0;
111- int height = 0;
112- int resolution = 0;
113- int em = 16;
114- AnyHover anyHover = AnyHover::Hover;
115- AnyPointer anyPointer = AnyPointer::Fine;
116- Orientation orientation = Orientation::Landscape;
117- ColorScheme colorScheme = ColorScheme::Light;
118- Contrast contrast = Contrast::NoPreference;
119- ReducedMotion reducedMotion = ReducedMotion::NoPreference;
120- ReducedTransparency reducedTransparency = ReducedTransparency::NoPreference;
121- Update update = Update::Fast;
122-
123+ float width = 0;
124+ float height = 0;
125+ float resolution = 0;
126+ float em = 16;
127 public:
128
129+ std::unordered_map<std::string, Media> MediaFeatures {
130+ {"any-hover", Media::Hover},
131+ {"any-pointer", Media::Fine},
132+ {"orientation", Media::Landscape},
133+ {"pefers-color-scheme", Media::Light},
134+ {"contrast", Media::NoPreference},
135+ {"prefers-reduced-motion", Media::NoPreference},
136+ {"prefers-reduced-transparency", Media::NoPreference},
137+ {"update", Media::Fast},
138+ {"media", Media::All},
139+ };
140+
141+
142 void parseHTML(std::istream& inputStream);
143
144 Node document;
145@@ -352,70 +335,115 @@ class Window {
146 // | (hldfk) Window Properties |
147 // +---------------------------------------------------+
148
149- void setWidth(int value) {
150+ void setWidth(float value) {
151 width = value;
152 }
153- void setHeight(int value) {
154+ void setHeight(float value) {
155 height = value;
156 }
157- void setResolution(int value) {
158+ void setResolution(float value) {
159 resolution = value;
160 }
161
162- void setEM(int value) {
163+ void setEM(float value) {
164 em = value;
165 }
166
167- void setMouse(AnyPointer pointer) {
168- if (pointer == AnyPointer::None) {
169- anyHover = AnyHover::None;
170- } else {
171- anyHover = AnyHover::Hover;
172- }
173- anyPointer = pointer;
174+ void setMouse(Media value) {
175+ // Accepts: Media::Hover && Media::None
176+ MediaFeatures.at("any-hover") = value;
177+ MediaFeatures.at("any-pointer") = value;
178+ }
179+
180+ void setOrientation(Media value) {
181+ MediaFeatures.at("orientation") = value;
182 }
183
184- void setOrientation(Orientation value) {
185- orientation = value;
186+ void setColorScheme(Media value) {
187+ MediaFeatures.at("prefers-color-scheme") = value;
188 }
189
190- void setColorScheme(ColorScheme value) {
191- colorScheme = value;
192+ void setContrast(Media value) {
193+ MediaFeatures.at("contrast") = value;
194 }
195
196- void setContrast(Contrast value) {
197- contrast = value;
198+ void setReducedMotion(Media value) {
199+ MediaFeatures.at("prefers-reduced-motion") = value;
200 }
201
202- void setReducedMotion(ReducedMotion value) {
203- reducedMotion = value;
204+ void setReducedTransparency(Media value) {
205+ MediaFeatures.at("prefers-reduced-transparency") = value;
206 }
207
208- void setReducedTransparency(ReducedTransparency value) {
209- reducedTransparency = value;
210+ void setUpdate(Media value) {
211+ MediaFeatures.at("update") = value;
212 }
213
214- void setUpdate(Update value) {
215- update = value;
216+ void setMedia(Media value) {
217+ MediaFeatures.at("media") = value;
218 }
219
220 // Getters
221- int getWidth() const {
222+ float getWidth() const {
223 return width;
224 }
225- int getHeight() const {
226+ float getHeight() const {
227 return height;
228 }
229- int getResolution() const {
230+ float getResolution() const {
231 return resolution;
232 }
233- int getEM() const {
234+ float getEM() const {
235 return em;
236 }
237- double getAspectRatio() const {
238+ float getAspectRatio() const {
239 if (height == 0) return 0.0;
240 return static_cast<double>(width) / height;
241 }
242+ Media getMedia() const {
243+ auto it = MediaFeatures.find("media");
244+ return it->second;
245+ }
246+
247+ Media getAnyHover() const {
248+ auto it = MediaFeatures.find("any-hover");
249+ return it->second;
250+ }
251+
252+ Media getAnyPointer() const {
253+ auto it = MediaFeatures.find("any-pointer");
254+ return it->second;
255+ }
256+
257+ Media getOrientation() const {
258+ auto it = MediaFeatures.find("orientation");
259+ return it->second;
260+ }
261+
262+ Media getColorScheme() const {
263+ auto it = MediaFeatures.find("prefers-color-scheme");
264+ return it->second;
265+ }
266+
267+ Media getContrast() const {
268+ auto it = MediaFeatures.find("contrast");
269+ return it->second;
270+ }
271+
272+ Media getReducedMotion() const {
273+ auto it = MediaFeatures.find("prefers-reduced-motion");
274+ return it->second;
275+ }
276+
277+ Media getReducedTransparency() const {
278+ auto it = MediaFeatures.find("prefers-reduced-motion");
279+ return it->second;
280+ }
281+
282+ Media getUpdate() const {
283+ auto it = MediaFeatures.find("update");
284+ return it->second;
285+ }
286 };
287
288 // Will need to require an adapter later
289@@ -426,4 +454,7 @@ For the adapter, it will take a window as a argument to set the adapters capabil
290 */
291 Window createWindow();
292
293+
294+float WindowUnitLengthValue(Window*, const std::vector<Unit>&, size_t, size_t);
295+
296 #endif // NODE_H
297diff --git a/playground/css_unit.cc b/playground/css_unit.cc
298index 48da6ac..148320a 100644
299--- a/playground/css_unit.cc
300+++ b/playground/css_unit.cc
301@@ -4,11 +4,7 @@
302 #include <fstream>
303
304 int main() {
305- std::vector<Unit> test = getUnit("first baseline");
306+ std::vector<Unit> test = getUnit("min(100px, 12rem)");
307
308- UnitType t = test[0].type;
309-
310- bool et = t == UnitType::FIRST_BASELINE;
311-
312- std::cout << et << std::endl;
313+ std::cout << "Done" << std::endl;
314 }
315diff --git a/playground/parseselectorparts.cc b/playground/parseselectorparts.cc
316index e0e9299..ca566fe 100644
317--- a/playground/parseselectorparts.cc
318+++ b/playground/parseselectorparts.cc
319@@ -5,8 +5,17 @@
320 #include <iostream>
321
322 int main() {
323- std::vector<std::vector<std::string>> parts = parseSelectorParts("h1:test, h1[name='asd']");
324+ std::vector<std::vector<std::string>> parts = parseSelectorParts("@media (min-width: 60px)");
325
326- std::cout << parts[1][1] << std::endl;
327+
328+ std::cout << "[" << std::endl;
329+ for (size_t i = 0; i < parts.size(); i++) {
330+ std::cout << "[";
331+ for (size_t j = 0; j < parts[i].size(); j++) {
332+ std::cout << "\"" << parts[i][j] << "\", ";
333+ }
334+ std::cout << "]," << std::endl;
335+ }
336+ std::cout << "]" << std::endl;
337 }
338
339diff --git a/playground/window.cc b/playground/window.cc
340index bbf274a..a76ef6e 100644
341--- a/playground/window.cc
342+++ b/playground/window.cc
343@@ -10,7 +10,7 @@ int main() {
344 "<html>"
345 "<head>"
346 "<title>This is a simple test</title>"
347- "<!--<link rel=\"stylesheet\" href=\'/style.css\'/>-->"
348+ "<link rel=\"stylesheet\" href=\'tests/style.css\'/>"
349 "<style>h1 {background: red;}</style>"
350 "</head>"
351 "<body>"
352@@ -41,5 +41,14 @@ int main() {
353 Window window = createWindow();
354 window.parseHTML(ss);
355
356- Node document = window.document;
357+// Node document = window.document;
358+
359+ window.setWidth(1000);
360+ window.setHeight(700);
361+ window.setEM(16);
362+
363+ // std::vector<Unit> units = getUnit("clamp(100px, calc(30% / 2rem + 10px), 900px)");
364+ std::vector<Unit> units = getUnit("clamp(100px, calc(30% / 2rem + 10px), 900px)");
365+
366+ std::cout << WindowUnitLengthValue(&window, units, 0, units.size()) << std::endl;
367 }
368diff --git a/src/grim.cc b/src/grim.cc
369index fd316a0..6cde02b 100755
370--- a/src/grim.cc
371+++ b/src/grim.cc
372@@ -11,6 +11,7 @@
373 #include <tuple>
374 #include <optional>
375 #include <stdexcept>
376+#include <limits>
377
378 // +------------------------------------------------------+
379 // |(TOC) Table of contents |
380@@ -727,9 +728,6 @@ std::vector<std::string> parseNodeParts(Node* node) {
381 void Style::addChild(Style s) {
382 size_t index = children.size();
383
384- // Add the window
385- s.window = window;
386-
387 children.push_back(s);
388
389 // Get the last selector group from the parsed selector parts
390@@ -803,15 +801,351 @@ std::unordered_map<KeyType, UnitType> Style::getStyles(Node* node) {
391 // testQuery tries to calculate if the query (ie a media query or
392 // container query) applies to the node passed
393
394-bool testQuery(Node* node, std::vector<std::string> parts) {
395+bool testQuery(Node* node, std::vector<std::vector<std::string>> parts) {
396+ // Takes a fully parsed selector. If it is a at-rule even multiple,
397+ // the at-rule tag must but the first.
398+
399+/*
400+Just a ref
401+bool result = false;
402+for (auto& condition : conditions) {
403+ result = result || evaluateSingleQuery(condition, windowProps);
404+}
405+
406+
407+also not can only be at the start
408+*/
409+
410+ Window* w = node->window;
411+
412+ std::string tag = parts[0][0];
413+ parts[0].erase(parts[0].begin());
414+
415+ if (tag == "@media") {
416+ for (size_t i = 0; i < parts.size(); i++) {
417+ bool prev = false;
418+ bool match = false;
419+ for (size_t j = 0; j < parts[i].size(); j++) {
420+ if (parts[i][j] == "and") {
421+
422+ }
423+ }
424+ }
425+ }
426+
427 return true;
428 }
429
430+/*
431+ at-rules consist of "query's" the are tre or false given the stat of the window.
432+ executeQuery runs the non-logic (and, or etc..) and returns the value of the
433+ executed query.
434+*/
435+
436+std::unordered_map<std::string, Media> MediaLookup {
437+ {"all", Media::All},
438+ {"print", Media::Print},
439+ {"screen", Media::Screen},
440+ {"none", Media::None},
441+ {"hover", Media::Hover},
442+ {"fine", Media::Fine},
443+ {"coarse", Media::Coarse},
444+ {"landscape", Media::Landscape},
445+ {"portrait", Media::Portrait},
446+ {"light", Media::Light},
447+ {"dark", Media::Dark},
448+ {"no-preference", Media::NoPreference},
449+ {"more", Media::More},
450+ {"less", Media::Less},
451+ {"reduce", Media::Reduce},
452+ {"fast", Media::Fast},
453+ {"slow", Media::Slow},
454+};
455+
456+bool executeQuery(Window* w, std::string p) {
457+ bool matches = false;
458+ if (p != " ") {
459+ // in parenthesis
460+ if (p[0] == '(') {
461+ std::string key;
462+ std::string value;
463+ bool colonFound = false;
464+
465+ // if colonFound is false then key has the parsed substr
466+ // and we know what type of query we are dealing with
467+ for (size_t j = 1; j < p.length()-1; j++) {
468+ if (!colonFound) {
469+ if (p[j] == ':') {
470+ colonFound = true;
471+ } else {
472+ key.push_back(p[j]);
473+ }
474+ } else {
475+ value.push_back(p[j]);
476+ }
477+ }
478+
479+ if (colonFound) {
480+ auto k = w->MediaFeatures.find(key);
481+ if (k != w->MediaFeatures.end()) {
482+ auto v = MediaLookup.find(value);
483+
484+ if (v != MediaLookup.end()) {
485+ matches = k->second == v->second;
486+ }
487+ }/* else {
488+ if (k == "min-width") {
489+
490+ matches = w->getWidth() >
491+ }
492+ }*/
493+ }
494+ } else {
495+ // Lookup keyword
496+ if (p == "all") {
497+ matches = w->getMedia() == Media::All;
498+ } else if (p == "print") {
499+ matches = w->getMedia() == Media::Print;
500+ } else if (p == "screen") {
501+ matches = w->getMedia() == Media::Screen;
502+ }
503+ }
504+ }
505+ return matches;
506+}
507+
508+struct Range {
509+ size_t start;
510+ size_t end;
511+};
512+
513+std::vector<Range> extractUnits(const std::vector<Unit>& units, size_t start, size_t end) {
514+ std::vector<Range> u;
515+ for (size_t i = start; i < end; i++) {
516+ Unit unit = units[i];
517+
518+ if (unit.isFunc) {
519+ u.push_back({i,i+unit.value.INT});
520+ i += unit.value.INT;
521+ } else {
522+ std::vector<Unit> s{unit};
523+ u.push_back({i, i+1});
524+ }
525+ }
526+ return u;
527+}
528+
529+/*
530+ CAUTION: These values are only correct for top level media queries
531+*/
532+float WindowUnitLengthValue(Window* window, const std::vector<Unit>& units, size_t start, size_t end) {
533+ float sum = 0;
534+ UnitType op = UnitType::ADD;
535+
536+ for (size_t i = start; i < end; i++) {
537+ Unit unit = units[i];
538+ UnitType type = unit.type;
539+
540+ float value = 0;
541+ float w = 0;
542+ float h = 0;
543+ float v1 = 0;
544+ float v2 = 0;
545+ float v3 = 0;
546+ std::vector<Range> extracted;
547+
548+ switch (type) {
549+ case UnitType::ADD:
550+ case UnitType::SUB:
551+ case UnitType::DIV:
552+ case UnitType::MULT:
553+ op = type;
554+ break;
555+ case UnitType::PX:
556+ std::cout << "PX: " << unit.value.FLOAT << std::endl;
557+ value = unit.value.FLOAT;
558+ break;
559+ case UnitType::CM:
560+ value = unit.value.FLOAT*37.8f;
561+ break;
562+ case UnitType::MM:
563+ value = unit.value.FLOAT*3.78f;
564+ break;
565+ case UnitType::Q:
566+ value = unit.value.FLOAT*0.94;
567+ break;
568+ case UnitType::IN:
569+ value = unit.value.FLOAT*96;
570+ break;
571+ case UnitType::PC:
572+ value = unit.value.FLOAT*16;
573+ break;
574+ case UnitType::PT:
575+ value = unit.value.FLOAT*1.3;
576+ break;
577+ case UnitType::EM:
578+ case UnitType::REM:
579+ case UnitType::CAP:
580+ // Technically wrong but fonts aren't loaded before this is ran
581+ case UnitType::CH:
582+ case UnitType::IC:
583+ case UnitType::LH:
584+ case UnitType::RCAP:
585+ case UnitType::RCH:
586+ case UnitType::RIC:
587+ case UnitType::RLH:
588+ value = unit.value.FLOAT*window->getEM();
589+ break;
590+ case UnitType::EX:
591+ case UnitType::REX:
592+ value = unit.value.FLOAT*(window->getEM()/2);
593+ break;
594+ case UnitType::CQH:
595+ case UnitType::VH:
596+ value = window->getHeight()*(unit.value.FLOAT/100);
597+ break;
598+ case UnitType::CQW:
599+ case UnitType::CQI:
600+ case UnitType::CQB:
601+ case UnitType::PERCENT:
602+ case UnitType::VW:
603+ value = window->getWidth()*(unit.value.FLOAT/100);
604+ break;
605+ case UnitType::VMAX:
606+ case UnitType::CQMAX:
607+ w = window->getWidth();
608+ h = window->getHeight();
609+ if (w > h) {
610+ value = unit.value.FLOAT*w;
611+ } else {
612+ value = unit.value.FLOAT*h;
613+ }
614+ break;
615+ case UnitType::VMIN:
616+ case UnitType::CQMIN:
617+ w = window->getWidth();
618+ h = window->getHeight();
619+ if (w < h) {
620+ value = unit.value.FLOAT*w;
621+ } else {
622+ value = unit.value.FLOAT*h;
623+ }
624+ break;
625+ case UnitType::VB:
626+ case UnitType::VI:
627+ value = unit.value.FLOAT*window->getWidth();
628+ break;
629+ case UnitType::CALC:
630+ value = WindowUnitLengthValue(window, units, i+1, i+1+unit.value.INT);
631+ i += unit.value.INT;
632+ break;
633+ case UnitType::MIN:
634+ extracted = extractUnits(units, i+1, i+1+unit.value.INT);
635+
636+ v1 = std::numeric_limits<float>::max(); // Store of the min value
637+
638+ for (size_t j = 0; j < extracted.size(); j++) {
639+ v2 = WindowUnitLengthValue(window, units, extracted[j].start, extracted[j].end);
640+
641+ if (v2 < v1) {
642+ v1 = v2;
643+ }
644+ }
645+
646+ value = v1;
647+
648+ i += unit.value.INT;
649+ break;
650+ case UnitType::MAX:
651+ extracted = extractUnits(units, i+1, i+1+unit.value.INT);
652+
653+ v1 = std::numeric_limits<float>::min(); // Store of the max value
654+
655+ for (size_t j = 0; j < extracted.size(); j++) {
656+ v2 = WindowUnitLengthValue(window, units, extracted[j].start, extracted[j].end);
657+
658+ if (v2 > v1) {
659+ v1 = v2;
660+ }
661+ }
662+
663+ value = v1;
664+
665+ i += unit.value.INT;
666+ break;
667+ case UnitType::CLAMP:
668+ extracted = extractUnits(units, i+1, i+1+unit.value.INT);
669+
670+ if (extracted.size() >= 3) {
671+ v1 = WindowUnitLengthValue(window, units, extracted[0].start, extracted[0].end);
672+ v2 = WindowUnitLengthValue(window, units, extracted[1].start, extracted[1].end);
673+ v3 = WindowUnitLengthValue(window, units, extracted[2].start, extracted[2].end);
674+
675+ std::cout << "V1: " << v1 << std::endl;
676+ std::cout << "V2: " << v2 << std::endl;
677+ std::cout << "V3: " << v3 << std::endl;
678+
679+ if (v2 < v1) {
680+ v2 = v1;
681+ }
682+ if (v2 > v3) {
683+ v2 = v3;
684+ }
685+ }
686+
687+ value = v2;
688+
689+
690+ i += unit.value.INT;
691+ break;
692+ case UnitType::ROUND:
693+
694+ // Need to add variable injection
695+ }
696+
697+ if (op != type) {
698+ switch (op) {
699+ case UnitType::ADD:
700+ sum += value;
701+ break;
702+ case UnitType::SUB:
703+ sum -= value;
704+ break;
705+ case UnitType::DIV:
706+ sum /= value;
707+ break;
708+ case UnitType::MULT:
709+ sum *= value;
710+ break;
711+ }
712+ }
713+ }
714+
715+ return sum;
716+}
717+
718 // Used for root node without a parent
719 std::vector<Node> EMPTY_NODE_CHILDREN_VECTOR;
720
721 // node: The node you are checking the selector against
722 // selector: output of parseSelector
723+
724+
725+
726+
727+
728+
729+
730+// todo: make testSelector use indices
731+
732+
733+
734+
735+
736+
737+
738+
739+
740 bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
741 // By the time we get here, we know the right most selector some what matches
742 // so now if it is a compound selector we should see if the parent('s) some what match
743@@ -979,9 +1313,6 @@ bool testSelector(Node* node, std::vector<std::vector<std::string>> selector) {
744 matched = true;
745 } else if (i == 0 && p[0] != '#' && p[0] != '.' && p[0] != '[' && p[0] != ':') {
746 matched = tagName == p;
747- } else if (p[0] == '@') {
748- // This is a at-rule we will process it in a different function
749- return testQuery(node, parts);
750 } else if (p[0] == '#') {
751 matched = "#"+attributes["id"] == p;
752 } else if (p[0] == '.') {
753diff --git a/src/parser.cc b/src/parser.cc
754index c1dbd83..2c156df 100755
755--- a/src/parser.cc
756+++ b/src/parser.cc
757@@ -951,6 +951,27 @@ static const std::unordered_map<std::string_view, UnitType> kMatch{
758 static const std::unordered_map<std::string_view, UnitType> kFunc{
759 {"calc", UnitType::CALC},
760 {"min", UnitType::MIN},
761+ {"max", UnitType::MAX},
762+ {"clamp", UnitType::CLAMP},
763+ {"round", UnitType::ROUND},
764+ {"mod", UnitType::MOD},
765+ {"progress", UnitType::PROGRESS},
766+ {"rem", UnitType::REMD},
767+ {"pow", UnitType::POW},
768+ {"sqrt", UnitType::SQRT},
769+ {"hypot", UnitType::HYPOT},
770+ {"log", UnitType::LOG},
771+ {"exp", UnitType::EXP},
772+ {"ABS", UnitType::ABS},
773+ {"sin", UnitType::SIN},
774+ {"cos", UnitType::COS},
775+ {"tan", UnitType::TAN},
776+ {"asin", UnitType::ASIN},
777+ {"acos", UnitType::ACOS},
778+ {"atan", UnitType::ATAN},
779+ {"atan2", UnitType::ATAN2},
780+ {"sign", UnitType::SIGN},
781+ {"var", UnitType::VAR},
782 {"rgb", UnitType::RGB},
783 {"rgba", UnitType::RGBA},
784 {"hsl", UnitType::HSL},
785@@ -1054,6 +1075,7 @@ std::vector<Unit> getUnit(std::string value) {
786 std::vector<Unit> children = getUnit(value);
787
788 Unit unit;
789+ unit.isFunc = true;
790
791 if (auto it = kFunc.find(name); it != kFunc.end()) {
792 unit.type = it->second;
793@@ -1071,6 +1093,7 @@ std::vector<Unit> getUnit(std::string value) {
794 if (v != ',' && !std::isspace(v)) buffer.push_back(v);
795
796 Unit unit;
797+ unit.isFunc = false;
798
799 // Split by spaces into parts
800
801@@ -1145,6 +1168,7 @@ std::vector<Unit> getUnit(std::string value) {
802 } else if (inQuotes) {
803 if (v == '"') continue;
804 Unit u;
805+ u.isFunc = false;
806 u.type = UnitType::STRING;
807 UnitValue uv;
808 uv.CHAR = v;
809@@ -1334,6 +1358,52 @@ Style parseCSS(std::istream& inputStream) {
810 return root;
811 }
812
813+std::string get_working_path() {
814+ char temp [ PATH_MAX ];
815+
816+ if ( getcwd(temp, PATH_MAX) != 0)
817+ return std::string ( temp );
818+
819+ int error = errno;
820+
821+ switch ( error ) {
822+ // EINVAL can't happen - size argument > 0
823+
824+ // PATH_MAX includes the terminating nul,
825+ // so ERANGE should not be returned
826+
827+ case EACCES:
828+ throw std::runtime_error("Access denied");
829+
830+ case ENOMEM:
831+ // I'm not sure whether this can happen or not
832+ throw std::runtime_error("Insufficient storage");
833+
834+ default: {
835+ std::ostringstream str;
836+ str << "Unrecognised error" << error;
837+ throw std::runtime_error(str.str());
838+ }
839+ }
840+}
841+
842+std::string pathJoin(const std::string& p1, const std::string& p2) {
843+ char sep = '/';
844+ std::string tmp = p1;
845+
846+#ifdef _WIN32
847+ sep = '\\';
848+#endif
849+
850+ // Add separator if it is not included in the first path:
851+ if (p1[p1.length() - 1] != sep) {
852+ tmp += sep;
853+ return tmp + p2;
854+ } else {
855+ return p1 + p2;
856+ }
857+}
858+
859 struct ParsedDocument {
860 Node document;
861 Style CSS;
862@@ -1355,7 +1425,8 @@ ParsedDocument parseDocument(std::istream& inputStream) {
863 std::string token;
864
865 // CSS stuff
866- std::vector<Node*> cssElements;
867+ std::vector<std::string> cssLinks;
868+ std::vector<std::string> cssTags;
869
870 char current;
871 while (inputStream.get(current)) {
872@@ -1478,8 +1549,10 @@ ParsedDocument parseDocument(std::istream& inputStream) {
873 // All attributes are stored as strings so we can just throw them in
874 node->setAttribute(pair.first, pair.second);
875 }
876- if (attrs["tagName"] == "link" || attrs["tagName"] == "style") {
877- cssElements.push_back(node);
878+ if (attrs["tagName"] == "link" && attrs["rel"] == "stylesheet") {
879+ cssLinks.push_back(node->getAttribute("href"));
880+ } else if (attrs["tagName"] == "style") {
881+ cssTags.push_back(node->getAttribute("innerText"));
882 }
883 } else if (closingTag) {
884 // Checksum and tree move
885@@ -1494,7 +1567,14 @@ ParsedDocument parseDocument(std::istream& inputStream) {
886 break;
887 }
888 }
889+
890 if (currentNode->getTagName() == tagName) {
891+ if (tagName == "link" && currentNode->getAttribute("rel") == "stylesheet") {
892+ cssLinks.push_back(currentNode->getAttribute("href"));
893+ } else if (tagName == "style") {
894+ cssTags.push_back(currentNode->children[0].getAttribute("innerText"));
895+ }
896+
897 currentNode = currentNode->parent;
898 } else {
899 std::cerr << "malformed html: closing tag (</" << tagName << ">) found for <" << currentNode->getTagName() << ">" << std::endl;
900@@ -1508,10 +1588,7 @@ ParsedDocument parseDocument(std::istream& inputStream) {
901 for (auto const& pair : attrs) {
902 // All attributes are stored as strings so we can just throw them in
903 currentNode->setAttribute(pair.first, pair.second);
904- }
905- if (attrs["tagName"] == "link" || attrs["tagName"] == "style") {
906- cssElements.push_back(currentNode);
907- }
908+ }
909 }
910
911 }
912@@ -1584,22 +1661,33 @@ ParsedDocument parseDocument(std::istream& inputStream) {
913
914 Style style;
915
916- for (size_t i = 0; i < cssElements.size(); i++) {
917- Node* el = cssElements[i];
918+ std::string workingPath = get_working_path();
919
920- if (el->getTagName() == "link" && el->getAttribute("rel") == "stylesheet") {
921- std::ifstream file(el->getAttribute("href"));
922- if (!file.is_open()) {
923- std::cerr << "Failed to open file!" << std::endl;
924- } else {
925- Style s = parseCSS(file);
926- style.addChild(s);
927+ for (size_t i = 0; i < cssLinks.size(); i++) {
928+ std::string link = cssLinks[i];
929+
930+ std::ifstream file(pathJoin(workingPath, link));
931+
932+ if (!file.is_open()) {
933+ std::cerr << "Failed to open file!" << std::endl;
934+ } else {
935+ Style s = parseCSS(file);
936+
937+ for (size_t j = 0; j < s.children.size(); j++) {
938+ style.addChild(s.children[j]);
939 }
940- file.close();
941- } else if (el->getTagName() == "style") {
942- std::istringstream iss(el->children[0].getAttribute("innerText"));
943- Style s = parseCSS(iss);
944- style.addChild(s);
945+ }
946+ file.close();
947+ }
948+
949+ for (size_t i = 0; i < cssTags.size(); i++) {
950+ std::string tag = cssTags[i];
951+
952+ std::istringstream iss(tag);
953+ Style s = parseCSS(iss);
954+
955+ for (size_t j = 0; j < s.children.size(); j++) {
956+ style.addChild(s.children[j]);
957 }
958 }
959