# -*- coding: utf-8 -*
########################################################################################################################
# DOM-access functions and variables
########################################################################################################################
try:
# Pyodide
from js import window, eval as jseval
document = window.document
except:
print("Emulation mode")
from xml.dom.minidom import parseString
jseval = None
window = None
document = parseString("
" + str(txt), "text/html")
return dom.body.textContent
def scanWhite(l):
"""
Scan and return whitespace.
"""
ret = ""
while l and l[0] in " \t\r\n":
ret += l.pop(0)
return ret
def scanWord(l):
"""
Scan and return a word.
"""
ret = ""
while l and l[0] not in " \t\r\n" + "<>=\"'":
ret += l.pop(0)
return ret
stack = []
# Obtain tag descriptions, if not already done!
global __tags
if __tags is None:
_buildTags(debug=debug)
# Prepare stack and input
stack.append((None, None, HtmlAst()))
html = [ch for ch in html]
# Parse
while html:
tag = None
text = ""
# Auto-close void elements (_isVoid), e.g.
,
, etc.
while stack and stack[-1][0] and issubclass(__tags[stack[-1][0]][0], _isVoid):
stack.pop()
if not stack:
break
parent = stack[-1][2]
while html:
ch = html.pop(0)
# Comment
if html and ch == "<" and "".join(html[:3]) == "!--":
html = html[3:]
while html and "".join(html[:3]) != "-->":
html.pop(0)
html = html[3:]
# Opening tag
elif html and ch == "<" and html[0] != "/":
tag = scanWord(html)
if tag.lower() in __tags:
break
text += ch + tag
# Closing tag
elif html and stack[-1][0] and ch == "<" and html[0] == "/":
junk = ch
junk += html.pop(0)
tag = scanWord(html)
junk += tag
if stack[-1][0] == tag.lower():
junk += scanWhite(html)
if html and html[0] == ">":
html.pop(0)
stack.pop()
tag = None
break
text += junk
tag = None
else:
text += ch
# Append plain text (if not only whitespace)
if (text and ((len(text) == 1 and text in ["\t "])
or not all([ch in " \t\r\n" for ch in text]))):
# print("text", text)
parent.append(convertEncodedText(text))
# Create tag
if tag:
tag = tag.lower()
# print("tag", tag)
elem = (tag, {}, HtmlAst())
stack.append(elem)
parent.append(elem)
while html:
scanWhite(html)
if not html:
break
# End of tag >
if html[0] == ">":
html.pop(0)
break
# Closing tag at end />
elif html[0] == "/":
html.pop(0)
scanWhite(html)
if html[0] == ">":
stack.pop()
html.pop(0)
break
val = att = scanWord(html).lower()
if not att:
html.pop(0)
continue
if att in __tags[tag][1] or att in ["[name]", "style", "disabled", "hidden"] or att.startswith("data-"):
scanWhite(html)
if html[0] == "=":
html.pop(0)
scanWhite(html)
if html[0] in "\"'":
ch = html.pop(0)
val = ""
while html and html[0] != ch:
val += html.pop(0)
html.pop(0)
if att not in elem[1]:
elem[1][att] = val
else:
elem[1][att] += " " + val
continue
while stack and stack[-1][0]:
stack.pop()
return stack[0][2]
def fromHTML(html, appendTo=None, bindTo=None, debug=False, vars=None, **kwargs):
"""
Parses the provided HTML code according to the objects defined in the html5-library.
html can also be pre-compiled by `parseHTML()` so that it executes faster.
Constructs all objects as DOM nodes. The first level is chained into appendTo.
If no appendTo is provided, appendTo will be set to html5.Body().
If bindTo is provided, objects are bound to this widget.
```python
from vi import html5
div = html5.Div()
html5.parse.fromHTML('''
''', div)
div.myLink.appendChild("appended!")
```
"""
# Handle defaults
if bindTo is None:
bindTo = appendTo
if isinstance(html, str):
html = parseHTML(html, debug=debug)
assert isinstance(html, HtmlAst)
if isinstance(vars, dict):
kwargs.update(vars)
def replaceVars(txt):
for var, val in kwargs.items():
txt = txt.replace("{{%s}}" % var, str(val) if val is not None else "")
return txt
def interpret(parent, items):
ret = []
for item in items:
if isinstance(item, str):
txt = TextNode(replaceVars(item))
if parent:
parent.appendChild(txt)
ret.append(txt)
continue
tag = item[0]
atts = item[1]
children = item[2]
# Special handling for tables: A "thead" and "tbody" are already part of table!
if tag in ["thead", "tbody"] and isinstance(parent, Table):
wdg = getattr(parent, tag[1:])
# Usual way: Construct new element and chain it into the parent.
else:
wdg = __tags[tag][0]()
for att, val in atts.items():
val = replaceVars(val)
if att == "[name]":
# Allow disable binding!
if not bindTo:
continue
if getattr(bindTo, val, None):
print("Cannot assign name '{}' because it already exists in {}".format(val, bindTo))
elif not (any([val.startswith(x) for x in
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "_"])
and all(
[x in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789" + "_"
for x in val[1:]])):
print("Cannot assign name '{}' because it contains invalid characters".format(val))
else:
setattr(bindTo, val, wdg)
wdg.onBind(bindTo, val)
if debug:
print("name '{}' assigned to {}".format(val, bindTo))
elif att == "class":
# print(tag, att, val.split())
wdg.addClass(*val.split())
elif att == "disabled":
# print(tag, att, val)
if val == "disabled":
wdg.disable()
elif att == "hidden":
# print(tag, att, val)
if val == "hidden":
wdg.hide()
elif att == "style":
for dfn in val.split(";"):
if ":" not in dfn:
continue
att, val = dfn.split(":", 1)
# print(tag, "style", att.strip(), val.strip())
wdg["style"][att.strip()] = val.strip()
elif att.startswith("data-"):
wdg["data"][att[5:]] = val
else:
wdg[att] = parseInt(val, val)
interpret(wdg, children)
if parent and not wdg.parent():
parent.appendChild(wdg)
ret.append(wdg)
return ret
return interpret(appendTo, html)
if __name__ == '__main__':
print(globals())