Diff
diff --git a/docs/cstyle/index.html b/docs/cstyle/index.html
index a417aef..4bc18aa 100644
--- a/docs/cstyle/index.html
+++ b/docs/cstyle/index.html
@@ -50,414 +50,414 @@ The utils.ChildrenHaveText function is called here instead of checking if the no
- 5 "gui/border"
- 6 "gui/color"
- 7 "gui/element"
- 8 "gui/font"
- 9 "gui/parser"
- 10 "gui/utils"
- 11 "os"
- 12 "sort"
- 13 "strconv"
- 14 "strings"
- 15
- 16 imgFont "golang.org/x/image/font"
- 17)
- 18
- 19// !TODO: Make a fine selector to target tags and if it has children or not etc
- 20// + could copy the transformers but idk
- 21type Plugin struct {
- 22 Selector func(*element.Node) bool
- 23 Level int
- 24 Handler func(*element.Node, *map[string]element.State)
- 25}
- 26
- 27type Transformer struct {
- 28 Selector func(*element.Node) bool
- 29 Handler func(*element.Node, *CSS) *element.Node
- 30}
- 31
- 32type CSS struct {
- 33 Width float32
- 34 Height float32
- 35 StyleSheets []map[string]map[string]string
- 36 Plugins []Plugin
- 37 Transformers []Transformer
- 38 Document *element.Node
- 39 Fonts map[string]imgFont.Face
- 40}
- 41
- 42func (c *CSS) Transform(n *element.Node) *element.Node {
- 43 for _, v := range c.Transformers {
- 44 if v.Selector(n) {
- 45 n = v.Handler(n, c)
- 46 }
- 47 }
- 48
- 49 for i := 0; i < len(n.Children); i++ {
- 50 tc := c.Transform(n.Children[i])
- 51 // Removing this causes text to break, what does this do??????
- 52 // its because we are using the dom methods to inject on text, not like ulol, prob need to get those working bc they effect user dom as well
- 53 // todo: dom fix, inline-block, text align vert (poss by tgting parent node instead), scroll
- 54 // n = tc.Parent
- 55 n.Children[i] = tc
- 56 }
- 57
- 58 return n
- 59}
- 60
- 61func (c *CSS) StyleSheet(path string) {
- 62 // Parse the CSS file
- 63 dat, _ := os.ReadFile(path)
- 64 styles := parser.ParseCSS(string(dat))
- 65
- 66 c.StyleSheets = append(c.StyleSheets, styles)
- 67}
- 68
- 69func (c *CSS) StyleTag(css string) {
- 70 styles := parser.ParseCSS(css)
- 71 c.StyleSheets = append(c.StyleSheets, styles)
- 72}
- 73
- 74var inheritedProps = []string{
- 75 "color",
- 76 "cursor",
- 77 "font",
- 78 "font-family",
- 79 "font-size",
- 80 "font-style",
- 81 "font-weight",
- 82 "letter-spacing",
- 83 "line-height",
- 84 // "text-align",
- 85 "text-indent",
- 86 "text-justify",
- 87 "text-shadow",
- 88 "text-transform",
- 89 "text-decoration",
- 90 "visibility",
- 91 "word-spacing",
- 92 "display",
- 93}
- 94
- 95func (c *CSS) QuickStyles(n *element.Node) map[string]string {
- 96 styles := make(map[string]string)
- 97
- 98 // Inherit styles from parent
- 99 if n.Parent != nil {
-100 ps := n.Parent.Style
-101 for _, prop := range inheritedProps {
-102 if value, ok := ps[prop]; ok && value != "" {
-103 styles[prop] = value
-104 }
-105 }
-106 }
-107
-108 // Add node's own styles
-109 for k, v := range n.Style {
-110 styles[k] = v
-111 }
-112
-113 return styles
-114}
-115
-116func (c *CSS) GetStyles(n *element.Node) map[string]string {
-117 styles := make(map[string]string)
-118
-119 // Inherit styles from parent
-120 if n.Parent != nil {
-121 ps := n.Parent.Style
-122 for _, prop := range inheritedProps {
-123 if value, ok := ps[prop]; ok && value != "" {
-124 styles[prop] = value
-125 }
-126 }
-127 }
-128
-129 // Add node's own styles
-130 for k, v := range n.Style {
-131 styles[k] = v
-132 }
-133
-134 // Check if node is hovered
-135 hovered := false
-136 for _, class := range n.ClassList.Classes {
-137 if class == ":hover" {
-138 hovered = true
-139 break
-140 }
-141 }
-142
-143 // Apply styles from style sheets
-144 for _, styleSheet := range c.StyleSheets {
-145 for selector, rules := range styleSheet {
-146 originalSelector := selector
-147
-148 if hovered && strings.Contains(selector, ":hover") {
-149 selector = strings.Replace(selector, ":hover", "", -1)
-150 }
-151
-152 if element.TestSelector(selector, n) {
-153 for k, v := range rules {
-154 styles[k] = v
-155 }
-156 }
-157
-158 selector = originalSelector // Restore original selector
-159 }
-160 }
-161
-162 // Parse inline styles
-163 inlineStyles := parser.ParseStyleAttribute(n.GetAttribute("style"))
-164 for k, v := range inlineStyles {
-165 styles[k] = v
-166 }
-167
-168 // Handle z-index inheritance
-169 if n.Parent != nil && styles["z-index"] == "" {
-170 if parentZIndex, ok := n.Parent.Style["z-index"]; ok && parentZIndex != "" {
-171 z, _ := strconv.Atoi(parentZIndex)
-172 z += 1
-173 styles["z-index"] = strconv.Itoa(z)
-174 }
-175 }
-176
-177 return styles
-178}
-179
-180func (c *CSS) AddPlugin(plugin Plugin) {
-181 c.Plugins = append(c.Plugins, plugin)
-182}
-183
-184func (c *CSS) AddTransformer(transformer Transformer) {
-185 c.Transformers = append(c.Transformers, transformer)
-186}
-187
-188func CheckNode(n *element.Node, state *map[string]element.State) {
-189 s := *state
-190 self := s[n.Properties.Id]
-191
-192 fmt.Println(n.TagName, n.Properties.Id)
-193 fmt.Printf("ID: %v\n", n.Id)
-194 fmt.Printf("EM: %v\n", self.EM)
-195 fmt.Printf("Parent: %v\n", n.Parent.TagName)
-196 fmt.Printf("Classes: %v\n", n.ClassList.Classes)
-197 fmt.Printf("Text: %v\n", n.InnerText)
-198 fmt.Printf("X: %v, Y: %v, Z: %v\n", self.X, self.Y, self.Z)
-199 fmt.Printf("Width: %v, Height: %v\n", self.Width, self.Height)
-200 fmt.Printf("Styles: %v\n", n.Style)
-201 fmt.Printf("Margin: %v\n", self.Margin)
-202 fmt.Printf("Padding: %v\n", self.Padding)
-203 // fmt.Printf("Background: %v\n", self.Background)
-204 // fmt.Printf("Border: %v\n\n\n", self.Border)
-205}
-206
-207func (c *CSS) ComputeNodeStyle(n *element.Node, state *map[string]element.State) *element.Node {
-208
-209 // Head is not renderable
-210 if utils.IsParent(*n, "head") {
-211 return n
-212 }
-213
-214 plugins := c.Plugins
-215
-216 s := *state
-217 self := s[n.Properties.Id]
-218 parent := s[n.Parent.Properties.Id]
-219
-220 self.Background = color.Parse(n.Style, "background")
-221 self.Border, _ = border.Parse(n.Style, self, parent)
-222
-223 fs := utils.ConvertToPixels(n.Style["font-size"], parent.EM, parent.Width)
-224 self.EM = fs
-225
-226 if n.Style["display"] == "none" {
-227 self.X = 0
-228 self.Y = 0
-229 self.Width = 0
-230 self.Height = 0
-231 return n
-232 }
-233
-234 // Set Z index value to be sorted in window
-235 if n.Style["z-index"] != "" {
-236 z, _ := strconv.Atoi(n.Style["z-index"])
-237 self.Z = float32(z)
-238 }
-239
-240 if parent.Z > 0 {
-241 self.Z = parent.Z + 1
-242 }
-243
-244 (*state)[n.Properties.Id] = self
-245
-246 wh := utils.GetWH(*n, state)
-247 width := wh.Width
-248 height := wh.Height
-249
-250 x, y := parent.X, parent.Y
-251 // !NOTE: Would like to consolidate all XY function into this function like WH
-252 offsetX, offsetY := utils.GetXY(n, state)
-253 x += offsetX
-254 y += offsetY
-255
-256 var top, left, right, bottom bool = false, false, false, false
-257
-258 m := utils.GetMP(*n, wh, state, "margin")
-259 p := utils.GetMP(*n, wh, state, "padding")
-260
-261 self.Margin = m
-262 self.Padding = p
-263
-264 if n.Style["position"] == "absolute" {
-265 bas := utils.GetPositionOffsetNode(n)
-266 base := s[bas.Properties.Id]
-267 if n.Style["top"] != "" {
-268 v := utils.ConvertToPixels(n.Style["top"], self.EM, parent.Width)
-269 y = v + base.Y
-270 top = true
-271 }
-272 if n.Style["left"] != "" {
-273 v := utils.ConvertToPixels(n.Style["left"], self.EM, parent.Width)
-274 x = v + base.X
-275 left = true
-276 }
-277 if n.Style["right"] != "" {
-278 v := utils.ConvertToPixels(n.Style["right"], self.EM, parent.Width)
-279 x = (base.Width - width) - v
-280 right = true
-281 }
-282 if n.Style["bottom"] != "" {
-283 v := utils.ConvertToPixels(n.Style["bottom"], self.EM, parent.Width)
-284 y = (base.Height - height) - v
-285 bottom = true
-286 }
-287
-288 } else {
-289 for i, v := range n.Parent.Children {
-290 if v.Style["position"] != "absolute" {
-291 if v.Properties.Id == n.Properties.Id {
-292 if i > 0 {
-293 sib := n.Parent.Children[i-1]
-294 sibling := s[sib.Properties.Id]
-295 if sib.Style["position"] != "absolute" {
-296 if n.Style["display"] == "inline" {
-297 if sib.Style["display"] == "inline" {
-298 y = sibling.Y
-299 } else {
-300 y = sibling.Y + sibling.Height
-301 }
-302 } else {
-303 y = sibling.Y + sibling.Height + (sibling.Border.Top.Width + sibling.Border.Bottom.Width) + sibling.Margin.Bottom
-304 }
-305 }
-306 }
-307 break
-308 } else if n.Style["display"] != "inline" {
-309 vState := s[v.Properties.Id]
-310 y += vState.Margin.Top + vState.Margin.Bottom + vState.Padding.Top + vState.Padding.Bottom + vState.Height + (self.Border.Top.Width)
-311 }
-312 }
-313 }
-314 }
-315
-316 // Display modes need to be calculated here
-317
-318 relPos := !top && !left && !right && !bottom
-319
-320 if left || relPos {
-321 x += m.Left
-322 }
-323 if top || relPos {
-324 y += m.Top
-325 }
-326 if right {
-327 x -= m.Right
-328 }
-329 if bottom {
-330 y -= m.Bottom
-331 }
-332
-333 self.X = x
-334 self.Y = y
-335 self.Width = width
-336 self.Height = height
-337 (*state)[n.Properties.Id] = self
-338
-339 if !utils.ChildrenHaveText(n) && len(n.InnerText) > 0 {
-340 // Confirm text exists
-341 n.InnerText = strings.TrimSpace(n.InnerText)
-342 self = genTextNode(n, state, c)
-343 }
-344
-345 (*state)[n.Properties.Id] = self
-346 (*state)[n.Parent.Properties.Id] = parent
-347 // Call children here
-348
-349 var childYOffset float32
-350 for i := 0; i < len(n.Children); i++ {
-351 v := n.Children[i]
-352 v.Parent = n
-353 // This is were the tainting comes from
-354 n.Children[i] = c.ComputeNodeStyle(v, state)
-355
-356 cState := (*state)[n.Children[i].Properties.Id]
-357 if n.Style["height"] == "" && n.Style["min-height"] == "" {
-358 if v.Style["position"] != "absolute" && cState.Y+cState.Height > childYOffset {
-359 childYOffset = cState.Y + cState.Height
-360 self.Height = (cState.Y - self.Border.Top.Width) - (self.Y) + cState.Height
-361 self.Height += cState.Margin.Top
-362 self.Height += cState.Margin.Bottom
-363 self.Height += cState.Padding.Top
-364 self.Height += cState.Padding.Bottom
-365 self.Height += cState.Border.Top.Width + cState.Border.Bottom.Width
-366 }
-367 }
-368 if cState.Width > self.Width {
-369 self.Width = cState.Width
-370 }
-371 }
-372
-373 if n.Style["height"] == "" {
-374 self.Height += self.Padding.Bottom
-375 }
-376
-377 (*state)[n.Properties.Id] = self
-378
-379 // Sorting the array by the Level field
-380 sort.Slice(plugins, func(i, j int) bool {
-381 return plugins[i].Level < plugins[j].Level
-382 })
-383
-384 for _, v := range plugins {
-385 if v.Selector(n) {
-386 v.Handler(n, state)
-387 }
-388 }
-389
-390 // CheckNode(n, state)
-391 return n
-392}
-393
-394func genTextNode(n *element.Node, state *map[string]element.State, css *CSS) element.State {
-395 s := *state
-396 self := s[n.Properties.Id]
-397 parent := s[n.Parent.Properties.Id]
-398
-399 text := element.Text{}
-400
-401 bold, italic := false, false
-402 // !ISSUE: needs bolder and the 100 -> 900
-403 if n.Style["font-weight"] == "bold" {
-404 bold = true
-405 }
-406
-407 if n.Style["font-style"] == "italic" {
-408 italic = true
-409 }
-410
-411 if text.Font == nil {
-412 if css.Fonts == nil {
-413 css.Fonts = map[string]imgFont.Face{}
-414 }
-415 fid := n.Style["font-family"] + fmt.Sprint(self.EM, bold, italic)
-416 if css.Fonts[fid] == nil {
-417 f, _ := font.LoadFont(n.Style["font-family"], int(self.EM), bold, italic)
-418 css.Fonts[fid] = f
+ 5 "gui/color"
+ 6 "gui/element"
+ 7 "gui/font"
+ 8 "gui/parser"
+ 9 "gui/utils"
+ 10 "os"
+ 11 "sort"
+ 12 "strconv"
+ 13 "strings"
+ 14
+ 15 imgFont "golang.org/x/image/font"
+ 16)
+ 17
+ 18// !TODO: Make a fine selector to target tags and if it has children or not etc
+ 19// + could copy the transformers but idk
+ 20type Plugin struct {
+ 21 Selector func(*element.Node) bool
+ 22 Level int
+ 23 Handler func(*element.Node, *map[string]element.State)
+ 24}
+ 25
+ 26type Transformer struct {
+ 27 Selector func(*element.Node) bool
+ 28 Handler func(*element.Node, *CSS) *element.Node
+ 29}
+ 30
+ 31type CSS struct {
+ 32 Width float32
+ 33 Height float32
+ 34 StyleSheets []map[string]map[string]string
+ 35 Plugins []Plugin
+ 36 Transformers []Transformer
+ 37 Document *element.Node
+ 38 Fonts map[string]imgFont.Face
+ 39}
+ 40
+ 41func (c *CSS) Transform(n *element.Node) *element.Node {
+ 42 for _, v := range c.Transformers {
+ 43 if v.Selector(n) {
+ 44 n = v.Handler(n, c)
+ 45 }
+ 46 }
+ 47
+ 48 for i := 0; i < len(n.Children); i++ {
+ 49 tc := c.Transform(n.Children[i])
+ 50 // Removing this causes text to break, what does this do??????
+ 51 // its because we are using the dom methods to inject on text, not like ulol, prob need to get those working bc they effect user dom as well
+ 52 // todo: dom fix, inline-block, text align vert (poss by tgting parent node instead), scroll
+ 53 // n = tc.Parent
+ 54 n.Children[i] = tc
+ 55 }
+ 56
+ 57 return n
+ 58}
+ 59
+ 60func (c *CSS) StyleSheet(path string) {
+ 61 // Parse the CSS file
+ 62 dat, _ := os.ReadFile(path)
+ 63 styles := parser.ParseCSS(string(dat))
+ 64
+ 65 c.StyleSheets = append(c.StyleSheets, styles)
+ 66}
+ 67
+ 68func (c *CSS) StyleTag(css string) {
+ 69 styles := parser.ParseCSS(css)
+ 70 c.StyleSheets = append(c.StyleSheets, styles)
+ 71}
+ 72
+ 73var inheritedProps = []string{
+ 74 "color",
+ 75 "cursor",
+ 76 "font",
+ 77 "font-family",
+ 78 "font-size",
+ 79 "font-style",
+ 80 "font-weight",
+ 81 "letter-spacing",
+ 82 "line-height",
+ 83 // "text-align",
+ 84 "text-indent",
+ 85 "text-justify",
+ 86 "text-shadow",
+ 87 "text-transform",
+ 88 "text-decoration",
+ 89 "visibility",
+ 90 "word-spacing",
+ 91 "display",
+ 92}
+ 93
+ 94func (c *CSS) QuickStyles(n *element.Node) map[string]string {
+ 95 styles := make(map[string]string)
+ 96
+ 97 // Inherit styles from parent
+ 98 if n.Parent != nil {
+ 99 ps := n.Parent.Style
+100 for _, prop := range inheritedProps {
+101 if value, ok := ps[prop]; ok && value != "" {
+102 styles[prop] = value
+103 }
+104 }
+105 }
+106
+107 // Add node's own styles
+108 for k, v := range n.Style {
+109 styles[k] = v
+110 }
+111
+112 return styles
+113}
+114
+115func (c *CSS) GetStyles(n *element.Node) map[string]string {
+116 styles := make(map[string]string)
+117
+118 // Inherit styles from parent
+119 if n.Parent != nil {
+120 ps := n.Parent.Style
+121 for _, prop := range inheritedProps {
+122 if value, ok := ps[prop]; ok && value != "" {
+123 styles[prop] = value
+124 }
+125 }
+126 }
+127
+128 // Add node's own styles
+129 for k, v := range n.Style {
+130 styles[k] = v
+131 }
+132
+133 // Check if node is hovered
+134 hovered := false
+135 for _, class := range n.ClassList.Classes {
+136 if class == ":hover" {
+137 hovered = true
+138 break
+139 }
+140 }
+141
+142 // Apply styles from style sheets
+143 for _, styleSheet := range c.StyleSheets {
+144 for selector, rules := range styleSheet {
+145 originalSelector := selector
+146
+147 if hovered && strings.Contains(selector, ":hover") {
+148 selector = strings.Replace(selector, ":hover", "", -1)
+149 }
+150
+151 if element.TestSelector(selector, n) {
+152 for k, v := range rules {
+153 styles[k] = v
+154 }
+155 }
+156
+157 selector = originalSelector // Restore original selector
+158 }
+159 }
+160
+161 // Parse inline styles
+162 inlineStyles := parser.ParseStyleAttribute(n.GetAttribute("style"))
+163 for k, v := range inlineStyles {
+164 styles[k] = v
+165 }
+166
+167 // Handle z-index inheritance
+168 if n.Parent != nil && styles["z-index"] == "" {
+169 if parentZIndex, ok := n.Parent.Style["z-index"]; ok && parentZIndex != "" {
+170 z, _ := strconv.Atoi(parentZIndex)
+171 z += 1
+172 styles["z-index"] = strconv.Itoa(z)
+173 }
+174 }
+175
+176 return styles
+177}
+178
+179func (c *CSS) AddPlugin(plugin Plugin) {
+180 c.Plugins = append(c.Plugins, plugin)
+181}
+182
+183func (c *CSS) AddTransformer(transformer Transformer) {
+184 c.Transformers = append(c.Transformers, transformer)
+185}
+186
+187func CheckNode(n *element.Node, state *map[string]element.State) {
+188 s := *state
+189 self := s[n.Properties.Id]
+190
+191 fmt.Println(n.TagName, n.Properties.Id)
+192 fmt.Printf("ID: %v\n", n.Id)
+193 fmt.Printf("EM: %v\n", self.EM)
+194 fmt.Printf("Parent: %v\n", n.Parent.TagName)
+195 fmt.Printf("Classes: %v\n", n.ClassList.Classes)
+196 fmt.Printf("Text: %v\n", n.InnerText)
+197 fmt.Printf("X: %v, Y: %v, Z: %v\n", self.X, self.Y, self.Z)
+198 fmt.Printf("Width: %v, Height: %v\n", self.Width, self.Height)
+199 fmt.Printf("Styles: %v\n", n.Style)
+200 fmt.Printf("Margin: %v\n", self.Margin)
+201 fmt.Printf("Padding: %v\n", self.Padding)
+202 // fmt.Printf("Background: %v\n", self.Background)
+203 // fmt.Printf("Border: %v\n\n\n", self.Border)
+204}
+205
+206func (c *CSS) ComputeNodeStyle(n *element.Node, state *map[string]element.State) *element.Node {
+207
+208 // Head is not renderable
+209 if utils.IsParent(*n, "head") {
+210 return n
+211 }
+212
+213 plugins := c.Plugins
+214
+215 s := *state
+216 self := s[n.Properties.Id]
+217 parent := s[n.Parent.Properties.Id]
+218
+219 self.Background = color.Parse(n.Style, "background")
+220 self.Border, _ = CompleteBorder(n.Style, self, parent)
+221
+222 fs := utils.ConvertToPixels(n.Style["font-size"], parent.EM, parent.Width)
+223 self.EM = fs
+224
+225 if n.Style["display"] == "none" {
+226 self.X = 0
+227 self.Y = 0
+228 self.Width = 0
+229 self.Height = 0
+230 return n
+231 }
+232
+233 // Set Z index value to be sorted in window
+234 if n.Style["z-index"] != "" {
+235 z, _ := strconv.Atoi(n.Style["z-index"])
+236 self.Z = float32(z)
+237 }
+238
+239 if parent.Z > 0 {
+240 self.Z = parent.Z + 1
+241 }
+242
+243 (*state)[n.Properties.Id] = self
+244
+245 wh := utils.GetWH(*n, state)
+246 width := wh.Width
+247 height := wh.Height
+248
+249 x, y := parent.X, parent.Y
+250 // !NOTE: Would like to consolidate all XY function into this function like WH
+251 offsetX, offsetY := utils.GetXY(n, state)
+252 x += offsetX
+253 y += offsetY
+254
+255 var top, left, right, bottom bool = false, false, false, false
+256
+257 m := utils.GetMP(*n, wh, state, "margin")
+258 p := utils.GetMP(*n, wh, state, "padding")
+259
+260 self.Margin = m
+261 self.Padding = p
+262
+263 if n.Style["position"] == "absolute" {
+264 bas := utils.GetPositionOffsetNode(n)
+265 base := s[bas.Properties.Id]
+266 if n.Style["top"] != "" {
+267 v := utils.ConvertToPixels(n.Style["top"], self.EM, parent.Width)
+268 y = v + base.Y
+269 top = true
+270 }
+271 if n.Style["left"] != "" {
+272 v := utils.ConvertToPixels(n.Style["left"], self.EM, parent.Width)
+273 x = v + base.X
+274 left = true
+275 }
+276 if n.Style["right"] != "" {
+277 v := utils.ConvertToPixels(n.Style["right"], self.EM, parent.Width)
+278 x = (base.Width - width) - v
+279 right = true
+280 }
+281 if n.Style["bottom"] != "" {
+282 v := utils.ConvertToPixels(n.Style["bottom"], self.EM, parent.Width)
+283 y = (base.Height - height) - v
+284 bottom = true
+285 }
+286
+287 } else {
+288 for i, v := range n.Parent.Children {
+289 if v.Style["position"] != "absolute" {
+290 if v.Properties.Id == n.Properties.Id {
+291 if i > 0 {
+292 sib := n.Parent.Children[i-1]
+293 sibling := s[sib.Properties.Id]
+294 if sib.Style["position"] != "absolute" {
+295 if n.Style["display"] == "inline" {
+296 if sib.Style["display"] == "inline" {
+297 y = sibling.Y
+298 } else {
+299 y = sibling.Y + sibling.Height
+300 }
+301 } else {
+302 y = sibling.Y + sibling.Height + (sibling.Border.Width * 2) + sibling.Margin.Bottom
+303 }
+304 }
+305 }
+306 break
+307 } else if n.Style["display"] != "inline" {
+308 vState := s[v.Properties.Id]
+309 y += vState.Margin.Top + vState.Margin.Bottom + vState.Padding.Top + vState.Padding.Bottom + vState.Height + (self.Border.Width)
+310 }
+311 }
+312 }
+313 }
+314
+315 // Display modes need to be calculated here
+316
+317 relPos := !top && !left && !right && !bottom
+318
+319 if left || relPos {
+320 x += m.Left
+321 }
+322 if top || relPos {
+323 y += m.Top
+324 }
+325 if right {
+326 x -= m.Right
+327 }
+328 if bottom {
+329 y -= m.Bottom
+330 }
+331
+332 self.X = x
+333 self.Y = y
+334 self.Width = width
+335 self.Height = height
+336 (*state)[n.Properties.Id] = self
+337
+338 if !utils.ChildrenHaveText(n) && len(n.InnerText) > 0 {
+339 // Confirm text exists
+340 n.InnerText = strings.TrimSpace(n.InnerText)
+341 self = genTextNode(n, state, c)
+342 }
+343
+344 (*state)[n.Properties.Id] = self
+345 (*state)[n.Parent.Properties.Id] = parent
+346 // Call children here
+347
+348 var childYOffset float32
+349 for i := 0; i < len(n.Children); i++ {
+350 v := n.Children[i]
+351 v.Parent = n
+352 // This is were the tainting comes from
+353 n.Children[i] = c.ComputeNodeStyle(v, state)
+354
+355 cState := (*state)[n.Children[i].Properties.Id]
+356 if n.Style["height"] == "" && n.Style["min-height"] == "" {
+357 if v.Style["position"] != "absolute" && cState.Y+cState.Height > childYOffset {
+358 childYOffset = cState.Y + cState.Height
+359 self.Height = (cState.Y - self.Border.Width) - (self.Y) + cState.Height
+360 self.Height += cState.Margin.Top
+361 self.Height += cState.Margin.Bottom
+362 self.Height += cState.Padding.Top
+363 self.Height += cState.Padding.Bottom
+364 self.Height += cState.Border.Width * 2
+365 }
+366 }
+367 if cState.Width > self.Width {
+368 self.Width = cState.Width
+369 }
+370 }
+371
+372 if n.Style["height"] == "" {
+373 self.Height += self.Padding.Bottom
+374 }
+375
+376 (*state)[n.Properties.Id] = self
+377
+378 // Sorting the array by the Level field
+379 sort.Slice(plugins, func(i, j int) bool {
+380 return plugins[i].Level < plugins[j].Level
+381 })
+382
+383 for _, v := range plugins {
+384 if v.Selector(n) {
+385 v.Handler(n, state)
+386 }
+387 }
+388
+389 // CheckNode(n, state)
+390 return n
+391}
+392
+393func CompleteBorder(cssProperties map[string]string, self, parent element.State) (element.Border, error) {
+394 // Split the shorthand into components
+395 borderComponents := strings.Fields(cssProperties["border"])
+396
+397 // Default values
+398 width := "0px" // Default width
+399 style := "solid"
+400 borderColor := "#000000" // Default color
+401
+402 // Suffixes for width properties
+403 widthSuffixes := []string{"px", "em", "pt", "pc", "%", "vw", "vh", "cm", "in"}
+404
+405 // Identify each component regardless of order
+406 for _, component := range borderComponents {
+407 if isWidthComponent(component, widthSuffixes) {
+408 width = component
+409 } else {
+410 switch component {
+411 case "thin", "medium", "thick":
+412 width = component
+413 case "none", "hidden", "dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset":
+414 style = component
+415 default:
+416 // Handle colors
+417 borderColor = component
+418 }
@@ -465,35 +465,35 @@ The utils.ChildrenHaveText function is called here instead of checking if the no
-420 fnt := css.Fonts[fid]
-421 text.Font = &fnt
-422 }
-423
-424 letterSpacing := utils.ConvertToPixels(n.Style["letter-spacing"], self.EM, parent.Width)
-425 wordSpacing := utils.ConvertToPixels(n.Style["word-spacing"], self.EM, parent.Width)
-426 lineHeight := utils.ConvertToPixels(n.Style["line-height"], self.EM, parent.Width)
-427 if lineHeight == 0 {
-428 lineHeight = self.EM + 3
-429 }
-430
-431 text.LineHeight = int(lineHeight)
-432 text.WordSpacing = int(wordSpacing)
-433 text.LetterSpacing = int(letterSpacing)
-434 wb := " "
-435
-436 if n.Style["word-wrap"] == "break-word" {
-437 wb = ""
-438 }
-439
-440 if n.Style["text-wrap"] == "wrap" || n.Style["text-wrap"] == "balance" {
-441 wb = ""
-442 }
-443
-444 var dt float32
-445
-446 if n.Style["text-decoration-thickness"] == "auto" || n.Style["text-decoration-thickness"] == "" {
-447 dt = self.EM / 7
-448 } else {
-449 dt = utils.ConvertToPixels(n.Style["text-decoration-thickness"], self.EM, parent.Width)
-450 }
-451
-452 col := color.Parse(n.Style, "font")
-453
-454 self.Color = col
+420 }
+421
+422 parsedColor, _ := color.Color(borderColor)
+423 w := utils.ConvertToPixels(width, self.EM, parent.Width)
+424
+425 return element.Border{
+426 Width: w,
+427 Style: style,
+428 Color: parsedColor,
+429 Radius: cssProperties["border-radius"],
+430 }, nil
+431}
+432
+433// Helper function to determine if a component is a width value
+434func isWidthComponent(component string, suffixes []string) bool {
+435 for _, suffix := range suffixes {
+436 if strings.HasSuffix(component, suffix) {
+437 return true
+438 }
+439 }
+440 return false
+441}
+442
+443func genTextNode(n *element.Node, state *map[string]element.State, css *CSS) element.State {
+444 s := *state
+445 self := s[n.Properties.Id]
+446 parent := s[n.Parent.Properties.Id]
+447
+448 text := element.Text{}
+449
+450 bold, italic := false, false
+451 // !ISSUE: needs bolder and the 100 -> 900
+452 if n.Style["font-weight"] == "bold" {
+453 bold = true
+454 }
@@ -501,33 +501,82 @@ The utils.ChildrenHaveText function is called here instead of checking if the no
-456 text.Color = col
-457 text.DecorationColor = color.Parse(n.Style, "decoration")
-458 text.Align = n.Style["text-align"]
-459 text.WordBreak = wb
-460 text.WordSpacing = int(wordSpacing)
-461 text.LetterSpacing = int(letterSpacing)
-462 text.WhiteSpace = n.Style["white-space"]
-463 text.DecorationThickness = int(dt)
-464 text.Overlined = n.Style["text-decoration"] == "overline"
-465 text.Underlined = n.Style["text-decoration"] == "underline"
-466 text.LineThrough = n.Style["text-decoration"] == "linethrough"
-467 text.EM = int(self.EM)
-468 text.Width = int(parent.Width)
-469 text.Text = n.InnerText
-470 text.Last = n.GetAttribute("last") == "true"
-471
-472 if n.Style["word-spacing"] == "" {
-473 text.WordSpacing = font.MeasureSpace(&text)
-474 }
-475
-476 img, width := font.Render(&text)
-477 self.Texture = img
-478
-479 if n.Style["height"] == "" && n.Style["min-height"] == "" {
-480 self.Height = float32(text.LineHeight)
-481 }
-482
-483 if n.Style["width"] == "" && n.Style["min-width"] == "" {
-484 self.Width = float32(width)
-485 }
-486
-487 return self
-488}
+456 if n.Style["font-style"] == "italic" {
+457 italic = true
+458 }
+459
+460 if text.Font == nil {
+461 if css.Fonts == nil {
+462 css.Fonts = map[string]imgFont.Face{}
+463 }
+464 fid := n.Style["font-family"] + fmt.Sprint(self.EM, bold, italic)
+465 if css.Fonts[fid] == nil {
+466 f, _ := font.LoadFont(n.Style["font-family"], int(self.EM), bold, italic)
+467 css.Fonts[fid] = f
+468 }
+469 fnt := css.Fonts[fid]
+470 text.Font = &fnt
+471 }
+472
+473 letterSpacing := utils.ConvertToPixels(n.Style["letter-spacing"], self.EM, parent.Width)
+474 wordSpacing := utils.ConvertToPixels(n.Style["word-spacing"], self.EM, parent.Width)
+475 lineHeight := utils.ConvertToPixels(n.Style["line-height"], self.EM, parent.Width)
+476 if lineHeight == 0 {
+477 lineHeight = self.EM + 3
+478 }
+479
+480 text.LineHeight = int(lineHeight)
+481 text.WordSpacing = int(wordSpacing)
+482 text.LetterSpacing = int(letterSpacing)
+483 wb := " "
+484
+485 if n.Style["word-wrap"] == "break-word" {
+486 wb = ""
+487 }
+488
+489 if n.Style["text-wrap"] == "wrap" || n.Style["text-wrap"] == "balance" {
+490 wb = ""
+491 }
+492
+493 var dt float32
+494
+495 if n.Style["text-decoration-thickness"] == "auto" || n.Style["text-decoration-thickness"] == "" {
+496 dt = self.EM / 7
+497 } else {
+498 dt = utils.ConvertToPixels(n.Style["text-decoration-thickness"], self.EM, parent.Width)
+499 }
+500
+501 col := color.Parse(n.Style, "font")
+502
+503 self.Color = col
+504
+505 text.Color = col
+506 text.DecorationColor = color.Parse(n.Style, "decoration")
+507 text.Align = n.Style["text-align"]
+508 text.WordBreak = wb
+509 text.WordSpacing = int(wordSpacing)
+510 text.LetterSpacing = int(letterSpacing)
+511 text.WhiteSpace = n.Style["white-space"]
+512 text.DecorationThickness = int(dt)
+513 text.Overlined = n.Style["text-decoration"] == "overline"
+514 text.Underlined = n.Style["text-decoration"] == "underline"
+515 text.LineThrough = n.Style["text-decoration"] == "linethrough"
+516 text.EM = int(self.EM)
+517 text.Width = int(parent.Width)
+518 text.Text = n.InnerText
+519 text.Last = n.GetAttribute("last") == "true"
+520
+521 if n.Style["word-spacing"] == "" {
+522 text.WordSpacing = font.MeasureSpace(&text)
+523 }
+524
+525 img, width := font.Render(&text)
+526 self.Texture = img
+527
+528 if n.Style["height"] == "" && n.Style["min-height"] == "" {
+529 self.Height = float32(text.LineHeight)
+530 }
+531
+532 if n.Style["width"] == "" && n.Style["min-width"] == "" {
+533 self.Width = float32(width)
+534 }
+535
+536 return self
+537}