document
document
is the main API interface for the library, it contains most of the methods provided in the JavaScript document object
flowchart LR; index.html-->open master.css-->open gui-->index.html; gui-->master.css; gui-->new; open-->new; new-->window; window-->document; document-->gui.display; script-->document;
# Open?(go)
The Open
methed is used to load a file from disk and display the file.
# CreateElement?(go)
# parse?(go)
# extractStylesheets?(go)
# extractStyleTags?(go)
# localizePath?(go)
# encapsulateText?(go)
# matchFactory?(go)
# removeWhitespace?(go)
# removeHTMLComments?(go)
# check?(go)
1package document
2
3import (
4 "bufio"
5 "gui/cstyle"
6 "gui/element"
7 "gui/events"
8 "gui/window"
9 "net/url"
10 "os"
11 "path/filepath"
12 "regexp"
13 "strings"
14
15 "gui/cstyle/plugins/block"
16 "gui/cstyle/plugins/flex"
17 "gui/cstyle/plugins/inline"
18
19 rl "github.com/gen2brain/raylib-go/raylib"
20 "golang.org/x/net/html"
21)
22
23type Window struct {
24 StyleSheets []string
25 StyleTags []string
26 DOM *html.Node
27 Title string
28}
29
30type Document struct {
31 CSS cstyle.CSS
32}
33
34func (doc Document) Open(index string, script func(*element.Node)) {
35 d := parse(index)
36
37 wm := window.NewWindowManager()
38 wm.FPS = true
39
40 // Initialization
41 var screenWidth int32 = 800
42 var screenHeight int32 = 450
43
44 // Open the window
45 wm.OpenWindow(screenWidth, screenHeight)
46 defer wm.CloseWindow()
47
48 doc.CSS = cstyle.CSS{
49 Width: 800,
50 Height: 450,
51 }
52 doc.CSS.StyleSheet("./master.css")
53 // css.AddPlugin(position.Init())
54 doc.CSS.AddPlugin(inline.Init())
55 doc.CSS.AddPlugin(block.Init())
56 doc.CSS.AddPlugin(flex.Init())
57
58 for _, v := range d.StyleSheets {
59 doc.CSS.StyleSheet(v)
60 }
61
62 for _, v := range d.StyleTags {
63 doc.CSS.StyleTag(v)
64 }
65
66 nodes := doc.CSS.CreateDocument(d.DOM)
67 root := &nodes
68
69 script(root)
70
71 // fmt.Println(nodes.Style)
72
73 evts := map[string]element.EventList{}
74
75 eventStore := &evts
76
77 // Main game loop
78 for !wm.WindowShouldClose() {
79 rl.BeginDrawing()
80
81 // Check if the window size has changed
82 newWidth := int32(rl.GetScreenWidth())
83 newHeight := int32(rl.GetScreenHeight())
84
85 if newWidth != screenWidth || newHeight != screenHeight {
86 rl.ClearBackground(rl.RayWhite)
87 // Window has been resized, handle the event
88 screenWidth = newWidth
89 screenHeight = newHeight
90
91 doc.CSS.Width = float32(screenWidth)
92 doc.CSS.Height = float32(screenHeight)
93
94 nodes = doc.CSS.CreateDocument(d.DOM)
95 root = &nodes
96 script(root)
97 }
98
99 eventStore = events.GetEvents(root, eventStore)
100 doc.CSS.ComputeNodeStyle(root)
101 rd := doc.CSS.Render(*root)
102 wm.LoadTextures(rd)
103 wm.Draw(rd)
104
105 events.RunEvents(eventStore)
106
107 rl.EndDrawing()
108 }
109}
110
111// func (doc Document) CreateElement(t string) element.Node {
112// n := element.Node{TagName: t, Properties: element.Properties{Node: &html.Node{}}}
113// cstyle.InitNode(&n, doc.CSS)
114// return n
115// }
116
117func parse(path string) Window {
118 file, err := os.Open(path)
119 check(err)
120 defer file.Close()
121
122 scanner := bufio.NewScanner(file)
123 var htmlContent string
124
125 for scanner.Scan() {
126 htmlContent += scanner.Text() + "\n"
127 }
128
129 check(scanner.Err())
130 // println(encapsulateText(htmlContent))
131 doc, err := html.Parse(strings.NewReader(encapsulateText(htmlContent)))
132 check(err)
133
134 // Extract stylesheet link tags and style tags
135 stylesheets := extractStylesheets(doc, filepath.Dir(path))
136 styleTags := extractStyleTags(doc)
137
138 d := Window{
139 StyleSheets: stylesheets,
140 StyleTags: styleTags,
141 DOM: doc,
142 }
143
144 return d
145}
146
147func extractStylesheets(n *html.Node, baseDir string) []string {
148 var stylesheets []string
149
150 var dfs func(*html.Node)
151 dfs = func(node *html.Node) {
152 if node.Type == html.ElementNode && node.Data == "link" {
153 var href string
154 isStylesheet := false
155
156 for _, attr := range node.Attr {
157 if attr.Key == "rel" && attr.Val == "stylesheet" {
158 isStylesheet = true
159 } else if attr.Key == "href" {
160 href = attr.Val
161 }
162 }
163
164 if isStylesheet {
165 resolvedHref := localizePath(baseDir, href)
166 stylesheets = append(stylesheets, resolvedHref)
167 }
168 }
169
170 for c := node.FirstChild; c != nil; c = c.NextSibling {
171 dfs(c)
172 }
173 }
174
175 dfs(n)
176 return stylesheets
177}
178
179func extractStyleTags(n *html.Node) []string {
180 var styleTags []string
181
182 var dfs func(*html.Node)
183 dfs = func(node *html.Node) {
184 if node.Type == html.ElementNode && node.Data == "style" {
185 var styleContent strings.Builder
186 for c := node.FirstChild; c != nil; c = c.NextSibling {
187 if c.Type == html.TextNode {
188 styleContent.WriteString(c.Data)
189 }
190 }
191 styleTags = append(styleTags, styleContent.String())
192 }
193
194 for c := node.FirstChild; c != nil; c = c.NextSibling {
195 dfs(c)
196 }
197 }
198
199 dfs(n)
200 return styleTags
201}
202
203func localizePath(rootPath, filePath string) string {
204 // Check if the file path has a scheme, indicating it's a URL
205 u, err := url.Parse(filePath)
206 if err == nil && u.Scheme != "" {
207 return filePath
208 }
209
210 // Join the root path and the file path to create an absolute path
211 absPath := filepath.Join(rootPath, filePath)
212
213 // If the absolute path is the same as the original path, return it
214 if absPath == filePath {
215 return filePath
216 }
217
218 return "./" + absPath
219}
220
221func encapsulateText(htmlString string) string {
222 htmlString = removeHTMLComments(htmlString)
223 openOpen := regexp.MustCompile(`(<\w+[^>]*>)([^<]+)(<\w+[^>]*>)`)
224 closeOpen := regexp.MustCompile(`(</\w+[^>]*>)([^<]+)(<\w+[^>]*>)`)
225 closeClose := regexp.MustCompile(`(</\w+[^>]*>)([^<]+)(</\w+[^>]*>)`)
226 a := matchFactory(openOpen)
227 t := openOpen.ReplaceAllStringFunc(htmlString, a)
228 b := matchFactory(closeOpen)
229 u := closeOpen.ReplaceAllStringFunc(t, b)
230 c := matchFactory(closeClose)
231 v := closeClose.ReplaceAllStringFunc(u, c)
232 return v
233}
234
235func matchFactory(re *regexp.Regexp) func(string) string {
236 return func(match string) string {
237 submatches := re.FindStringSubmatch(match)
238 if len(submatches) != 4 {
239 return match
240 }
241
242 // Process submatches
243 if len(removeWhitespace(submatches[2])) > 0 {
244 return submatches[1] + "<notaspan>" + submatches[2] + "</notaspan>" + submatches[3]
245 } else {
246 return match
247 }
248 }
249}
250func removeWhitespace(htmlString string) string {
251 // Remove extra white space
252 reSpaces := regexp.MustCompile(`\s+`)
253 htmlString = reSpaces.ReplaceAllString(htmlString, " ")
254
255 // Trim leading and trailing white space
256 htmlString = strings.TrimSpace(htmlString)
257
258 return htmlString
259}
260
261func removeHTMLComments(htmlString string) string {
262 re := regexp.MustCompile(`<!--(.*?)-->`)
263 return re.ReplaceAllString(htmlString, "")
264}
265
266func check(e error) {
267 if e != nil {
268 panic(e)
269 }
270}