1local WATCH_CYCLE_CHECK_MS = 50
2local OFICIAL_HTML_CSS = {}
3
4local parseCss, parseAndSetDisplayAttr, parseAndSetFloatStyle = dofile('ext/style')
5local parseStyle, parseLayout, parseExpression = dofile('ext/parse')
6local parseEvents, onCreateWidget, setText, createRadioGroup, afterLoadElement = dofile('ext/parseevent')
7local translateStyleName, translateAttribute = dofile('ext/translator')
8
9local function readNode(el, parent, controller, watchList)
10 local styleName = g_ui.getStyleName(translateStyleName(el.name, el))
11 local widget = g_ui.createWidget(styleName ~= '' and styleName or 'UIWidget', parent or rootWidget)
12 widget:setOnHtml(true)
13
14 el.widget = widget
15
16 onCreateWidget(el, widget, controller)
17
18 for attr, v in pairs(el.attributes) do
19 local attr = translateAttribute(styleName, el.name, attr)
20 if attr:starts('on') then
21 parseEvents(el, widget, attr:lower(), v, controller)
22 elseif attr == 'anchor' then
23 -- ignore
24 elseif attr == 'style' then
25 parseStyle(widget, el)
26 elseif attr == 'layout' then
27 parseLayout(widget, el)
28 elseif attr == 'class' then
29 for _, className in pairs(v:split(' ')) do
30 local css = g_ui.getStyle(className)
31 if css then
32 widget:mergeStyle(css)
33 end
34 end
35 else
36 local methodName = ''
37 for _, _name in pairs(attr:trim():split('-')) do
38 methodName = methodName .. _name:gsub("^%l", string.upper)
39 end
40
41 local watchObj = nil
42
43 local isExp = methodName:starts('*')
44 if isExp then
45 methodName = methodName:sub(2):gsub("^%l", string.upper)
46
47 local vStr = v
48 local f = loadstring('return function(self, target) return ' .. vStr .. ' end')
49 local fnc = f()
50 v = fnc(controller)
51
52 watchObj = {
53 widget = widget,
54 res = v,
55 el = el,
56 method = nil,
57 fnc = function(self)
58 local value = fnc(controller, self.widget)
59 if value ~= self.res then
60 self.method(self.widget, value)
61 self.res = value
62 end
63 end
64 }
65 elseif v == '' or v == methodName then
66 v = true
67 end
68
69 v = tonumber(v) or toboolean(v) or v
70
71 local method = nil
72 if methodName == 'Text' then
73 method = function(widget, v) setText(el, v) end
74 else
75 method = widget['set' .. methodName]
76 end
77
78 if method then
79 method(widget, v)
80
81 if watchObj then
82 watchObj.method = method
83 table.insert(watchList, watchObj)
84 end
85 else
86 local _name = string.sub(methodName, 1, 1):lower() .. string.sub(methodName, 2, -1)
87 widget[_name] = v
88 end
89 end
90 end
91
92 if #el.nodes > 0 then
93 if not widget.HTML_onReadNodes or widget:HTML_onReadNodes(el.nodes) then
94 local prevEl = nil
95 for _, chield in pairs(el.nodes) do
96 chield.prev = prevEl
97 readNode(chield, widget, controller, watchList)
98 prevEl = chield
99 end
100 end
101 end
102
103 return widget
104end
105
106parseCss(io.content('modulelib/html.css'), OFICIAL_HTML_CSS, false)
107
108function HtmlLoader(path, parent, controller)
109 HTML_PATH = path
110
111 local cssList = {}
112 table.insertall(cssList, OFICIAL_HTML_CSS)
113
114 local root = HtmlParser.parse(parseExpression(io.content(path), controller))
115 root.widget = nil
116 root.path = path
117
118 local watchList = {}
119 local prevEl = nil
120 for _, el in pairs(root.nodes) do
121 el.prev = prevEl
122 if el.name == 'style' then
123 parseCss(el:getcontent(), cssList, true)
124 elseif el.name == 'link' then
125 local href = el.attributes.href
126 if href then
127 parseCss(io.content(href), cssList, true)
128 end
129 else
130 root.widget = readNode(el, parent, controller, watchList)
131 end
132 prevEl = el
133 end
134
135 for _, css in pairs(cssList) do
136 local els = root:find(css.selector)
137
138 if css.checkExist and #els == 0 then
139 pwarning('[' .. path .. '][style] selector(' .. css.selector .. ') no element was found.')
140 end
141
142 for _, el in pairs(els) do
143 if not el.style then
144 el.style = {};
145 end
146
147 table.merge(el.style, css.attrs)
148
149 if el.widget then
150 el.widget:mergeStyle(el.style)
151 end
152 end
153 end
154
155 local radioGroups = {}
156 local all = root:find('*')
157 for i = #all, 1, -1 do
158 local el = all[i]
159 if el.widget then
160 parseAndSetDisplayAttr(el)
161 parseAndSetFloatStyle(el)
162
163 if el.name == 'input' and el.attributes.type == 'radio' then
164 createRadioGroup(el, radioGroups, controller)
165 end
166
167 afterLoadElement(el)
168 end
169 end
170
171 if #watchList > 0 then
172 local event = nil
173 event = controller:cycleEvent(function()
174 table.remove_if(watchList, function(i, obj)
175 local isDestroyed = obj.widget:isDestroyed()
176 if isDestroyed then
177 obj.widget = nil
178 obj.el.widget = nil
179 else
180 obj.fnc(obj)
181 end
182
183 return isDestroyed
184 end)
185
186 if #watchList == 0 then
187 removeEvent(event)
188 end
189 end, WATCH_CYCLE_CHECK_MS)
190 end
191
192 return root
193end
194