reactpy_django 5.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- js/node_modules/@pyscript/core/src/stdlib/pyscript/__init__.py +61 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/display.py +177 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/event_handling.py +76 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/fetch.py +87 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/ffi.py +18 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/flatted.py +148 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/magic_js.py +79 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/storage.py +60 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/util.py +33 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/web.py +1176 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/websocket.py +71 -0
- js/node_modules/@pyscript/core/src/stdlib/pyscript/workers.py +43 -0
- js/node_modules/flatted/python/flatted.py +149 -0
- js/node_modules/flatted/python/test.py +63 -0
- js/node_modules/polyscript/python/mip.py +288 -0
- reactpy_django/__init__.py +32 -0
- reactpy_django/apps.py +11 -0
- reactpy_django/checks.py +558 -0
- reactpy_django/clean.py +141 -0
- reactpy_django/components.py +284 -0
- reactpy_django/config.py +128 -0
- reactpy_django/database.py +31 -0
- reactpy_django/decorators.py +65 -0
- reactpy_django/exceptions.py +34 -0
- reactpy_django/hooks.py +469 -0
- reactpy_django/html.py +3 -0
- reactpy_django/http/__init__.py +0 -0
- reactpy_django/http/urls.py +18 -0
- reactpy_django/http/views.py +62 -0
- reactpy_django/management/__init__.py +0 -0
- reactpy_django/management/commands/__init__.py +0 -0
- reactpy_django/management/commands/clean_reactpy.py +37 -0
- reactpy_django/migrations/0001_initial.py +25 -0
- reactpy_django/migrations/0002_rename_created_at_componentparams_last_accessed.py +17 -0
- reactpy_django/migrations/0003_componentsession_delete_componentparams.py +28 -0
- reactpy_django/migrations/0004_config.py +27 -0
- reactpy_django/migrations/0005_alter_componentsession_last_accessed.py +17 -0
- reactpy_django/migrations/0006_userdatamodel.py +28 -0
- reactpy_django/migrations/__init__.py +0 -0
- reactpy_django/models.py +48 -0
- reactpy_django/py.typed +1 -0
- reactpy_django/pyscript/__init__.py +0 -0
- reactpy_django/pyscript/component_template.py +26 -0
- reactpy_django/pyscript/layout_handler.py +139 -0
- reactpy_django/router/__init__.py +5 -0
- reactpy_django/router/converters.py +8 -0
- reactpy_django/router/resolvers.py +22 -0
- reactpy_django/static/reactpy_django/client.js +1 -0
- reactpy_django/static/reactpy_django/morphdom/morphdom-esm.js +769 -0
- reactpy_django/static/reactpy_django/morphdom/morphdom-factory.js +705 -0
- reactpy_django/static/reactpy_django/morphdom/morphdom-umd.js +777 -0
- reactpy_django/static/reactpy_django/morphdom/morphdom-umd.min.js +1 -0
- reactpy_django/static/reactpy_django/morphdom/morphdom.js +771 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror-BEtcgaoQ.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror-BEtcgaoQ.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_commands-DDxffOmd.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_commands-DDxffOmd.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_lang-python-CnWnFqxD.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_lang-python-CnWnFqxD.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_language-CjmvX4ix.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_language-CjmvX4ix.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_state-D1qTXrff.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_state-D1qTXrff.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_view-DVb8uYMr.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/codemirror_view-DVb8uYMr.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/core-CjO3FOKB.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/core-CjO3FOKB.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/core.css +1 -0
- reactpy_django/static/reactpy_django/pyscript/core.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/core.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/deprecations-manager-pFtn19mE.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/deprecations-manager-pFtn19mE.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/error-tq-z48YI.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/error-tq-z48YI.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/index-S1Do43bx.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/index-S1Do43bx.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/mpy-DovD7Qjy.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/mpy-DovD7Qjy.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/py-BUsUWVJg.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/py-BUsUWVJg.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/py-editor-CeySmmer.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/py-editor-CeySmmer.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/py-terminal-CH_wV7wQ.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/py-terminal-CH_wV7wQ.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/storage.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/storage.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/toml-CvAfdf9_.js +3 -0
- reactpy_django/static/reactpy_django/pyscript/toml-CvAfdf9_.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/toml-DiUM0_qs.js +3 -0
- reactpy_django/static/reactpy_django/pyscript/toml-DiUM0_qs.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/xterm-BY7uk_OU.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/xterm-BY7uk_OU.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/xterm-readline-CZfBw7ic.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/xterm-readline-CZfBw7ic.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/xterm.css +7 -0
- reactpy_django/static/reactpy_django/pyscript/xterm_addon-fit--gyF3PcZ.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/xterm_addon-fit--gyF3PcZ.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/xterm_addon-web-links-Cnej-nJ6.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/xterm_addon-web-links-Cnej-nJ6.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript/zip-DrwYHuF9.js +2 -0
- reactpy_django/static/reactpy_django/pyscript/zip-DrwYHuF9.js.map +1 -0
- reactpy_django/static/reactpy_django/pyscript-custom.css +3 -0
- reactpy_django/static/reactpy_django/pyscript-hide-debug.css +3 -0
- reactpy_django/templates/reactpy/component.html +27 -0
- reactpy_django/templates/reactpy/pyscript_component.html +2 -0
- reactpy_django/templates/reactpy/pyscript_setup.html +8 -0
- reactpy_django/templatetags/__init__.py +0 -0
- reactpy_django/templatetags/reactpy.py +250 -0
- reactpy_django/types.py +81 -0
- reactpy_django/utils.py +560 -0
- reactpy_django/websocket/__init__.py +0 -0
- reactpy_django/websocket/consumer.py +227 -0
- reactpy_django/websocket/paths.py +15 -0
- reactpy_django-5.0.0.dist-info/LICENSE.md +9 -0
- reactpy_django-5.0.0.dist-info/METADATA +152 -0
- reactpy_django-5.0.0.dist-info/RECORD +118 -0
- reactpy_django-5.0.0.dist-info/WHEEL +5 -0
- reactpy_django-5.0.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,1176 @@
|
|
|
1
|
+
"""Lightweight interface to the DOM and HTML elements."""
|
|
2
|
+
|
|
3
|
+
# `when` is not used in this module. It is imported here save the user an additional
|
|
4
|
+
# import (i.e. they can get what they need from `pyscript.web`).
|
|
5
|
+
from pyscript import document, when # NOQA
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def wrap_dom_element(dom_element):
|
|
9
|
+
"""Wrap an existing DOM element in an instance of a subclass of `Element`.
|
|
10
|
+
|
|
11
|
+
This is just a convenience function to avoid having to import the `Element` class
|
|
12
|
+
and use its class method.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
return Element.wrap_dom_element(dom_element)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Element:
|
|
19
|
+
# A lookup table to get an `Element` subclass by tag name. Used when wrapping an
|
|
20
|
+
# existing DOM element.
|
|
21
|
+
element_classes_by_tag_name = {}
|
|
22
|
+
|
|
23
|
+
@classmethod
|
|
24
|
+
def get_tag_name(cls):
|
|
25
|
+
"""Return the HTML tag name for the class.
|
|
26
|
+
|
|
27
|
+
For classes that have a trailing underscore (because they clash with a Python
|
|
28
|
+
keyword or built-in), we remove it to get the tag name. e.g. for the `input_`
|
|
29
|
+
class, the tag name is `input`.
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
return cls.__name__.replace("_", "")
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def register_element_classes(cls, element_classes):
|
|
36
|
+
"""Register an iterable of element classes."""
|
|
37
|
+
for element_class in element_classes:
|
|
38
|
+
tag_name = element_class.get_tag_name()
|
|
39
|
+
cls.element_classes_by_tag_name[tag_name] = element_class
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def unregister_element_classes(cls, element_classes):
|
|
43
|
+
"""Unregister an iterable of element classes."""
|
|
44
|
+
for element_class in element_classes:
|
|
45
|
+
tag_name = element_class.get_tag_name()
|
|
46
|
+
cls.element_classes_by_tag_name.pop(tag_name, None)
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def wrap_dom_element(cls, dom_element):
|
|
50
|
+
"""Wrap an existing DOM element in an instance of a subclass of `Element`.
|
|
51
|
+
|
|
52
|
+
We look up the `Element` subclass by the DOM element's tag name. For any unknown
|
|
53
|
+
elements (custom tags etc.) use *this* class (`Element`).
|
|
54
|
+
"""
|
|
55
|
+
element_cls = cls.element_classes_by_tag_name.get(
|
|
56
|
+
dom_element.tagName.lower(), cls
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
return element_cls(dom_element=dom_element)
|
|
60
|
+
|
|
61
|
+
def __init__(self, dom_element=None, classes=None, style=None, **kwargs):
|
|
62
|
+
"""Create a new, or wrap an existing DOM element.
|
|
63
|
+
|
|
64
|
+
If `dom_element` is None we are being called to *create* a new element.
|
|
65
|
+
Otherwise, we are being called to *wrap* an existing DOM element.
|
|
66
|
+
"""
|
|
67
|
+
self._dom_element = dom_element or document.createElement(
|
|
68
|
+
type(self).get_tag_name()
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# A set-like interface to the element's `classList`.
|
|
72
|
+
self._classes = Classes(self)
|
|
73
|
+
|
|
74
|
+
# A dict-like interface to the element's `style` attribute.
|
|
75
|
+
self._style = Style(self)
|
|
76
|
+
|
|
77
|
+
# Set any specified classes, styles, and DOM properties.
|
|
78
|
+
self.update(classes=classes, style=style, **kwargs)
|
|
79
|
+
|
|
80
|
+
def __eq__(self, obj):
|
|
81
|
+
"""Check for equality by comparing the underlying DOM element."""
|
|
82
|
+
return isinstance(obj, Element) and obj._dom_element == self._dom_element
|
|
83
|
+
|
|
84
|
+
def __getitem__(self, key):
|
|
85
|
+
"""Get an item within the element's children.
|
|
86
|
+
|
|
87
|
+
If `key` is an integer or a slice we use it to index/slice the element's
|
|
88
|
+
children. Otherwise, we use `key` as a query selector.
|
|
89
|
+
"""
|
|
90
|
+
if isinstance(key, int) or isinstance(key, slice):
|
|
91
|
+
return self.children[key]
|
|
92
|
+
|
|
93
|
+
return self.find(key)
|
|
94
|
+
|
|
95
|
+
def __getattr__(self, name):
|
|
96
|
+
# This allows us to get attributes on the underlying DOM element that clash
|
|
97
|
+
# with Python keywords or built-ins (e.g. the output element has an
|
|
98
|
+
# attribute `for` which is a Python keyword, so you can access it on the
|
|
99
|
+
# Element instance via `for_`).
|
|
100
|
+
if name.endswith("_"):
|
|
101
|
+
name = name[:-1]
|
|
102
|
+
|
|
103
|
+
return getattr(self._dom_element, name)
|
|
104
|
+
|
|
105
|
+
def __setattr__(self, name, value):
|
|
106
|
+
# This class overrides `__setattr__` to delegate "public" attributes to the
|
|
107
|
+
# underlying DOM element. BUT, we don't use the usual Python pattern where
|
|
108
|
+
# we set attributes on the element itself via `self.__dict__` as that is not
|
|
109
|
+
# yet supported in our build of MicroPython. Instead, we handle it here by
|
|
110
|
+
# using super for all "private" attributes (those starting with an underscore).
|
|
111
|
+
if name.startswith("_"):
|
|
112
|
+
super().__setattr__(name, value)
|
|
113
|
+
|
|
114
|
+
else:
|
|
115
|
+
# This allows us to set attributes on the underlying DOM element that clash
|
|
116
|
+
# with Python keywords or built-ins (e.g. the output element has an
|
|
117
|
+
# attribute `for` which is a Python keyword, so you can access it on the
|
|
118
|
+
# Element instance via `for_`).
|
|
119
|
+
if name.endswith("_"):
|
|
120
|
+
name = name[:-1]
|
|
121
|
+
|
|
122
|
+
setattr(self._dom_element, name, value)
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def children(self):
|
|
126
|
+
"""Return the element's children as an `ElementCollection`."""
|
|
127
|
+
return ElementCollection.wrap_dom_elements(self._dom_element.children)
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def classes(self):
|
|
131
|
+
"""Return the element's `classList` as a `Classes` instance."""
|
|
132
|
+
return self._classes
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def parent(self):
|
|
136
|
+
"""Return the element's `parent `Element`."""
|
|
137
|
+
if self._dom_element.parentElement is None:
|
|
138
|
+
return None
|
|
139
|
+
|
|
140
|
+
return Element.wrap_dom_element(self._dom_element.parentElement)
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def style(self):
|
|
144
|
+
"""Return the element's `style` attribute as a `Style` instance."""
|
|
145
|
+
return self._style
|
|
146
|
+
|
|
147
|
+
def append(self, *items):
|
|
148
|
+
"""Append the specified items to the element."""
|
|
149
|
+
for item in items:
|
|
150
|
+
if isinstance(item, Element):
|
|
151
|
+
self._dom_element.appendChild(item._dom_element)
|
|
152
|
+
|
|
153
|
+
elif isinstance(item, ElementCollection):
|
|
154
|
+
for element in item:
|
|
155
|
+
self._dom_element.appendChild(element._dom_element)
|
|
156
|
+
|
|
157
|
+
# We check for list/tuple here and NOT for any iterable as it will match
|
|
158
|
+
# a JS Nodelist which is handled explicitly below.
|
|
159
|
+
# NodeList.
|
|
160
|
+
elif isinstance(item, list) or isinstance(item, tuple):
|
|
161
|
+
for child in item:
|
|
162
|
+
self.append(child)
|
|
163
|
+
|
|
164
|
+
else:
|
|
165
|
+
# In this case we know it's not an Element or an ElementCollection, so
|
|
166
|
+
# we guess that it's either a DOM element or NodeList returned via the
|
|
167
|
+
# ffi.
|
|
168
|
+
try:
|
|
169
|
+
# First, we try to see if it's an element by accessing the 'tagName'
|
|
170
|
+
# attribute.
|
|
171
|
+
item.tagName
|
|
172
|
+
self._dom_element.appendChild(item)
|
|
173
|
+
|
|
174
|
+
except AttributeError:
|
|
175
|
+
try:
|
|
176
|
+
# Ok, it's not an element, so let's see if it's a NodeList by
|
|
177
|
+
# accessing the 'length' attribute.
|
|
178
|
+
item.length
|
|
179
|
+
for element_ in item:
|
|
180
|
+
self._dom_element.appendChild(element_)
|
|
181
|
+
|
|
182
|
+
except AttributeError:
|
|
183
|
+
# Nope! This is not an element or a NodeList.
|
|
184
|
+
raise TypeError(
|
|
185
|
+
f'Element "{item}" is a proxy object, "'
|
|
186
|
+
f"but not a valid element or a NodeList."
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
def clone(self, clone_id=None):
|
|
190
|
+
"""Make a clone of the element (clones the underlying DOM object too)."""
|
|
191
|
+
clone = Element.wrap_dom_element(self._dom_element.cloneNode(True))
|
|
192
|
+
clone.id = clone_id
|
|
193
|
+
return clone
|
|
194
|
+
|
|
195
|
+
def find(self, selector):
|
|
196
|
+
"""Find all elements that match the specified selector.
|
|
197
|
+
|
|
198
|
+
Return the results as a (possibly empty) `ElementCollection`.
|
|
199
|
+
"""
|
|
200
|
+
return ElementCollection.wrap_dom_elements(
|
|
201
|
+
self._dom_element.querySelectorAll(selector)
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
def show_me(self):
|
|
205
|
+
"""Convenience method for 'element.scrollIntoView()'."""
|
|
206
|
+
self._dom_element.scrollIntoView()
|
|
207
|
+
|
|
208
|
+
def update(self, classes=None, style=None, **kwargs):
|
|
209
|
+
"""Update the element with the specified classes, styles, and DOM properties."""
|
|
210
|
+
|
|
211
|
+
if classes:
|
|
212
|
+
self.classes.add(classes)
|
|
213
|
+
|
|
214
|
+
if style:
|
|
215
|
+
self.style.set(**style)
|
|
216
|
+
|
|
217
|
+
for name, value in kwargs.items():
|
|
218
|
+
setattr(self, name, value)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class Classes:
|
|
222
|
+
"""A set-like interface to an element's `classList`."""
|
|
223
|
+
|
|
224
|
+
def __init__(self, element: Element):
|
|
225
|
+
self._element = element
|
|
226
|
+
self._class_list = self._element._dom_element.classList
|
|
227
|
+
|
|
228
|
+
def __contains__(self, item):
|
|
229
|
+
return item in self._class_list
|
|
230
|
+
|
|
231
|
+
def __eq__(self, other):
|
|
232
|
+
# We allow comparison with either another `Classes` instance...
|
|
233
|
+
if isinstance(other, Classes):
|
|
234
|
+
compare_with = list(other._class_list)
|
|
235
|
+
|
|
236
|
+
# ...or iterables of strings.
|
|
237
|
+
else:
|
|
238
|
+
# TODO: Check MP for existence of better iterable test.
|
|
239
|
+
try:
|
|
240
|
+
compare_with = iter(other)
|
|
241
|
+
|
|
242
|
+
except TypeError:
|
|
243
|
+
return False
|
|
244
|
+
|
|
245
|
+
return set(self._class_list) == set(compare_with)
|
|
246
|
+
|
|
247
|
+
def __iter__(self):
|
|
248
|
+
return iter(self._class_list)
|
|
249
|
+
|
|
250
|
+
def __len__(self):
|
|
251
|
+
return self._class_list.length
|
|
252
|
+
|
|
253
|
+
def __repr__(self):
|
|
254
|
+
return f"Classes({', '.join(self._class_list)})"
|
|
255
|
+
|
|
256
|
+
def __str__(self):
|
|
257
|
+
return " ".join(self._class_list)
|
|
258
|
+
|
|
259
|
+
def add(self, *class_names):
|
|
260
|
+
"""Add one or more classes to the element."""
|
|
261
|
+
for class_name in class_names:
|
|
262
|
+
if isinstance(class_name, list):
|
|
263
|
+
for item in class_name:
|
|
264
|
+
self.add(item)
|
|
265
|
+
|
|
266
|
+
else:
|
|
267
|
+
self._class_list.add(class_name)
|
|
268
|
+
|
|
269
|
+
def contains(self, class_name):
|
|
270
|
+
"""Check if the element has the specified class."""
|
|
271
|
+
return class_name in self
|
|
272
|
+
|
|
273
|
+
def remove(self, *class_names):
|
|
274
|
+
"""Remove one or more classes from the element."""
|
|
275
|
+
for class_name in class_names:
|
|
276
|
+
if isinstance(class_name, list):
|
|
277
|
+
for item in class_name:
|
|
278
|
+
self.remove(item)
|
|
279
|
+
|
|
280
|
+
else:
|
|
281
|
+
self._class_list.remove(class_name)
|
|
282
|
+
|
|
283
|
+
def replace(self, old_class, new_class):
|
|
284
|
+
"""Replace one of the element's classes with another."""
|
|
285
|
+
self.remove(old_class)
|
|
286
|
+
self.add(new_class)
|
|
287
|
+
|
|
288
|
+
def toggle(self, *class_names):
|
|
289
|
+
"""Toggle one or more of the element's classes."""
|
|
290
|
+
for class_name in class_names:
|
|
291
|
+
if class_name in self:
|
|
292
|
+
self.remove(class_name)
|
|
293
|
+
|
|
294
|
+
else:
|
|
295
|
+
self.add(class_name)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class HasOptions:
|
|
299
|
+
"""Mix-in for elements that have an options attribute.
|
|
300
|
+
|
|
301
|
+
The elements that support options are: <datalist>, <optgroup>, and <select>.
|
|
302
|
+
"""
|
|
303
|
+
|
|
304
|
+
@property
|
|
305
|
+
def options(self):
|
|
306
|
+
"""Return the element's options as an `Options"""
|
|
307
|
+
if not hasattr(self, "_options"):
|
|
308
|
+
self._options = Options(self)
|
|
309
|
+
|
|
310
|
+
return self._options
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
class Options:
|
|
314
|
+
"""This class represents the <option>s of a <datalist>, <optgroup> or <select>.
|
|
315
|
+
|
|
316
|
+
It allows access to add and remove <option>s by using the `add`, `remove` and
|
|
317
|
+
`clear` methods.
|
|
318
|
+
"""
|
|
319
|
+
|
|
320
|
+
def __init__(self, element):
|
|
321
|
+
self._element = element
|
|
322
|
+
|
|
323
|
+
def __getitem__(self, key):
|
|
324
|
+
return self.options[key]
|
|
325
|
+
|
|
326
|
+
def __iter__(self):
|
|
327
|
+
yield from self.options
|
|
328
|
+
|
|
329
|
+
def __len__(self):
|
|
330
|
+
return len(self.options)
|
|
331
|
+
|
|
332
|
+
def __repr__(self):
|
|
333
|
+
return f"{self.__class__.__name__} (length: {len(self)}) {self.options}"
|
|
334
|
+
|
|
335
|
+
@property
|
|
336
|
+
def options(self):
|
|
337
|
+
"""Return the list of options."""
|
|
338
|
+
return [Element.wrap_dom_element(o) for o in self._element._dom_element.options]
|
|
339
|
+
|
|
340
|
+
@property
|
|
341
|
+
def selected(self):
|
|
342
|
+
"""Return the selected option."""
|
|
343
|
+
return self.options[self._element._dom_element.selectedIndex]
|
|
344
|
+
|
|
345
|
+
def add(self, value=None, html=None, text=None, before=None, **kwargs):
|
|
346
|
+
"""Add a new option to the element"""
|
|
347
|
+
if value is not None:
|
|
348
|
+
kwargs["value"] = value
|
|
349
|
+
|
|
350
|
+
if html is not None:
|
|
351
|
+
kwargs["innerHTML"] = html
|
|
352
|
+
|
|
353
|
+
if text is not None:
|
|
354
|
+
kwargs["text"] = text
|
|
355
|
+
|
|
356
|
+
new_option = option(**kwargs)
|
|
357
|
+
|
|
358
|
+
if before:
|
|
359
|
+
if isinstance(before, Element):
|
|
360
|
+
before = before._dom_element
|
|
361
|
+
|
|
362
|
+
self._element._dom_element.add(new_option._dom_element, before)
|
|
363
|
+
|
|
364
|
+
def clear(self):
|
|
365
|
+
"""Remove all options."""
|
|
366
|
+
while len(self) > 0:
|
|
367
|
+
self.remove(0)
|
|
368
|
+
|
|
369
|
+
def remove(self, index):
|
|
370
|
+
"""Remove the option at the specified index."""
|
|
371
|
+
self._element._dom_element.remove(index)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
class Style:
|
|
375
|
+
"""A dict-like interface to an element's `style` attribute."""
|
|
376
|
+
|
|
377
|
+
def __init__(self, element: Element):
|
|
378
|
+
self._element = element
|
|
379
|
+
self._style = self._element._dom_element.style
|
|
380
|
+
|
|
381
|
+
def __getitem__(self, key):
|
|
382
|
+
return self._style.getPropertyValue(key)
|
|
383
|
+
|
|
384
|
+
def __setitem__(self, key, value):
|
|
385
|
+
self._style.setProperty(key, value)
|
|
386
|
+
|
|
387
|
+
def remove(self, key):
|
|
388
|
+
"""Remove a CSS property from the element."""
|
|
389
|
+
self._style.removeProperty(key)
|
|
390
|
+
|
|
391
|
+
def set(self, **kwargs):
|
|
392
|
+
"""Set one or more CSS properties on the element."""
|
|
393
|
+
for key, value in kwargs.items():
|
|
394
|
+
self._element._dom_element.style.setProperty(key, value)
|
|
395
|
+
|
|
396
|
+
# CSS Properties
|
|
397
|
+
# Reference: https://github.com/microsoft/TypeScript/blob/main/src/lib/dom.generated.d.ts#L3799C1-L5005C2
|
|
398
|
+
# Following properties automatically generated from the above reference using
|
|
399
|
+
# tools/codegen_css_proxy.py
|
|
400
|
+
@property
|
|
401
|
+
def visible(self):
|
|
402
|
+
return self._element._dom_element.style.visibility
|
|
403
|
+
|
|
404
|
+
@visible.setter
|
|
405
|
+
def visible(self, value):
|
|
406
|
+
self._element._dom_element.style.visibility = value
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
class ContainerElement(Element):
|
|
410
|
+
"""Base class for elements that can contain other elements."""
|
|
411
|
+
|
|
412
|
+
def __init__(
|
|
413
|
+
self, *args, children=None, dom_element=None, style=None, classes=None, **kwargs
|
|
414
|
+
):
|
|
415
|
+
super().__init__(
|
|
416
|
+
dom_element=dom_element, style=style, classes=classes, **kwargs
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
for child in list(args) + (children or []):
|
|
420
|
+
if isinstance(child, Element) or isinstance(child, ElementCollection):
|
|
421
|
+
self.append(child)
|
|
422
|
+
|
|
423
|
+
else:
|
|
424
|
+
self._dom_element.insertAdjacentHTML("beforeend", child)
|
|
425
|
+
|
|
426
|
+
def __iter__(self):
|
|
427
|
+
yield from self.children
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
class ClassesCollection:
|
|
431
|
+
"""A set-like interface to the classes of the elements in a collection."""
|
|
432
|
+
|
|
433
|
+
def __init__(self, collection):
|
|
434
|
+
self._collection = collection
|
|
435
|
+
|
|
436
|
+
def __contains__(self, class_name):
|
|
437
|
+
for element in self._collection:
|
|
438
|
+
if class_name in element.classes:
|
|
439
|
+
return True
|
|
440
|
+
|
|
441
|
+
return False
|
|
442
|
+
|
|
443
|
+
def __eq__(self, other):
|
|
444
|
+
return (
|
|
445
|
+
isinstance(other, ClassesCollection)
|
|
446
|
+
and self._collection == other._collection
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
def __iter__(self):
|
|
450
|
+
for class_name in self._all_class_names():
|
|
451
|
+
yield class_name
|
|
452
|
+
|
|
453
|
+
def __len__(self):
|
|
454
|
+
return len(self._all_class_names())
|
|
455
|
+
|
|
456
|
+
def __repr__(self):
|
|
457
|
+
return f"ClassesCollection({repr(self._collection)})"
|
|
458
|
+
|
|
459
|
+
def __str__(self):
|
|
460
|
+
return " ".join(self._all_class_names())
|
|
461
|
+
|
|
462
|
+
def add(self, *class_names):
|
|
463
|
+
"""Add one or more classes to the elements in the collection."""
|
|
464
|
+
for element in self._collection:
|
|
465
|
+
element.classes.add(*class_names)
|
|
466
|
+
|
|
467
|
+
def contains(self, class_name):
|
|
468
|
+
"""Check if any element in the collection has the specified class."""
|
|
469
|
+
return class_name in self
|
|
470
|
+
|
|
471
|
+
def remove(self, *class_names):
|
|
472
|
+
"""Remove one or more classes from the elements in the collection."""
|
|
473
|
+
|
|
474
|
+
for element in self._collection:
|
|
475
|
+
element.classes.remove(*class_names)
|
|
476
|
+
|
|
477
|
+
def replace(self, old_class, new_class):
|
|
478
|
+
"""Replace one of the classes in the elements in the collection with another."""
|
|
479
|
+
for element in self._collection:
|
|
480
|
+
element.classes.replace(old_class, new_class)
|
|
481
|
+
|
|
482
|
+
def toggle(self, *class_names):
|
|
483
|
+
"""Toggle one or more classes on the elements in the collection."""
|
|
484
|
+
for element in self._collection:
|
|
485
|
+
element.classes.toggle(*class_names)
|
|
486
|
+
|
|
487
|
+
def _all_class_names(self):
|
|
488
|
+
all_class_names = set()
|
|
489
|
+
for element in self._collection:
|
|
490
|
+
for class_name in element.classes:
|
|
491
|
+
all_class_names.add(class_name)
|
|
492
|
+
|
|
493
|
+
return all_class_names
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
class StyleCollection:
|
|
497
|
+
"""A dict-like interface to the styles of the elements in a collection."""
|
|
498
|
+
|
|
499
|
+
def __init__(self, collection):
|
|
500
|
+
self._collection = collection
|
|
501
|
+
|
|
502
|
+
def __getitem__(self, key):
|
|
503
|
+
return [element.style[key] for element in self._collection._elements]
|
|
504
|
+
|
|
505
|
+
def __setitem__(self, key, value):
|
|
506
|
+
for element in self._collection._elements:
|
|
507
|
+
element.style[key] = value
|
|
508
|
+
|
|
509
|
+
def __repr__(self):
|
|
510
|
+
return f"StyleCollection({repr(self._collection)})"
|
|
511
|
+
|
|
512
|
+
def remove(self, key):
|
|
513
|
+
"""Remove a CSS property from the elements in the collection."""
|
|
514
|
+
for element in self._collection._elements:
|
|
515
|
+
element.style.remove(key)
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
class ElementCollection:
|
|
519
|
+
@classmethod
|
|
520
|
+
def wrap_dom_elements(cls, dom_elements):
|
|
521
|
+
"""Wrap an iterable of dom_elements in an `ElementCollection`."""
|
|
522
|
+
|
|
523
|
+
return cls(
|
|
524
|
+
[Element.wrap_dom_element(dom_element) for dom_element in dom_elements]
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
def __init__(self, elements: [Element]):
|
|
528
|
+
self._elements = elements
|
|
529
|
+
self._classes = ClassesCollection(self)
|
|
530
|
+
self._style = StyleCollection(self)
|
|
531
|
+
|
|
532
|
+
def __eq__(self, obj):
|
|
533
|
+
"""Check for equality by comparing the underlying DOM elements."""
|
|
534
|
+
return isinstance(obj, ElementCollection) and obj._elements == self._elements
|
|
535
|
+
|
|
536
|
+
def __getitem__(self, key):
|
|
537
|
+
"""Get an item in the collection.
|
|
538
|
+
|
|
539
|
+
If `key` is an integer or a slice we use it to index/slice the collection.
|
|
540
|
+
Otherwise, we use `key` as a query selector.
|
|
541
|
+
"""
|
|
542
|
+
if isinstance(key, int):
|
|
543
|
+
return self._elements[key]
|
|
544
|
+
|
|
545
|
+
elif isinstance(key, slice):
|
|
546
|
+
return ElementCollection(self._elements[key])
|
|
547
|
+
|
|
548
|
+
return self.find(key)
|
|
549
|
+
|
|
550
|
+
def __iter__(self):
|
|
551
|
+
yield from self._elements
|
|
552
|
+
|
|
553
|
+
def __len__(self):
|
|
554
|
+
return len(self._elements)
|
|
555
|
+
|
|
556
|
+
def __repr__(self):
|
|
557
|
+
return (
|
|
558
|
+
f"{self.__class__.__name__} (length: {len(self._elements)}) "
|
|
559
|
+
f"{self._elements}"
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
def __getattr__(self, name):
|
|
563
|
+
return [getattr(element, name) for element in self._elements]
|
|
564
|
+
|
|
565
|
+
def __setattr__(self, name, value):
|
|
566
|
+
# This class overrides `__setattr__` to delegate "public" attributes to the
|
|
567
|
+
# elements in the collection. BUT, we don't use the usual Python pattern where
|
|
568
|
+
# we set attributes on the collection itself via `self.__dict__` as that is not
|
|
569
|
+
# yet supported in our build of MicroPython. Instead, we handle it here by
|
|
570
|
+
# using super for all "private" attributes (those starting with an underscore).
|
|
571
|
+
if name.startswith("_"):
|
|
572
|
+
super().__setattr__(name, value)
|
|
573
|
+
|
|
574
|
+
else:
|
|
575
|
+
for element in self._elements:
|
|
576
|
+
setattr(element, name, value)
|
|
577
|
+
|
|
578
|
+
@property
|
|
579
|
+
def classes(self):
|
|
580
|
+
"""Return the classes of the elements in the collection as a `ClassesCollection`."""
|
|
581
|
+
return self._classes
|
|
582
|
+
|
|
583
|
+
@property
|
|
584
|
+
def elements(self):
|
|
585
|
+
"""Return the elements in the collection as a list."""
|
|
586
|
+
return self._elements
|
|
587
|
+
|
|
588
|
+
@property
|
|
589
|
+
def style(self):
|
|
590
|
+
""""""
|
|
591
|
+
return self._style
|
|
592
|
+
|
|
593
|
+
def find(self, selector):
|
|
594
|
+
"""Find all elements that match the specified selector.
|
|
595
|
+
|
|
596
|
+
Return the results as a (possibly empty) `ElementCollection`.
|
|
597
|
+
"""
|
|
598
|
+
elements = []
|
|
599
|
+
for element in self._elements:
|
|
600
|
+
elements.extend(element.find(selector))
|
|
601
|
+
|
|
602
|
+
return ElementCollection(elements)
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
# Classes for every HTML element. If the element tag name (e.g. "input") clashes with
|
|
606
|
+
# either a Python keyword or common symbol, then we suffix the class name with an "_"
|
|
607
|
+
# (e.g. the class for the "input" element is "input_").
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
class a(ContainerElement):
|
|
611
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a"""
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
class abbr(ContainerElement):
|
|
615
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/abbr"""
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
class address(ContainerElement):
|
|
619
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/address"""
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
class area(Element):
|
|
623
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area"""
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
class article(ContainerElement):
|
|
627
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/article"""
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
class aside(ContainerElement):
|
|
631
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside"""
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
class audio(ContainerElement):
|
|
635
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio"""
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
class b(ContainerElement):
|
|
639
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/b"""
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
class base(Element):
|
|
643
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base"""
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
class blockquote(ContainerElement):
|
|
647
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote"""
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
class body(ContainerElement):
|
|
651
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body"""
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
class br(Element):
|
|
655
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/br"""
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
class button(ContainerElement):
|
|
659
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button"""
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
class canvas(ContainerElement):
|
|
663
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas"""
|
|
664
|
+
|
|
665
|
+
def download(self, filename: str = "snapped.png"):
|
|
666
|
+
"""Download the current element with the filename provided in input.
|
|
667
|
+
|
|
668
|
+
Inputs:
|
|
669
|
+
* filename (str): name of the file being downloaded
|
|
670
|
+
|
|
671
|
+
Output:
|
|
672
|
+
None
|
|
673
|
+
"""
|
|
674
|
+
download_link = a(download=filename, href=self._dom_element.toDataURL())
|
|
675
|
+
|
|
676
|
+
# Adding the link to the DOM is recommended for browser compatibility to make
|
|
677
|
+
# sure that the click works.
|
|
678
|
+
self.append(download_link)
|
|
679
|
+
|
|
680
|
+
download_link._dom_element.click()
|
|
681
|
+
|
|
682
|
+
def draw(self, what, width=None, height=None):
|
|
683
|
+
"""Draw `what` on the current element
|
|
684
|
+
|
|
685
|
+
Inputs:
|
|
686
|
+
|
|
687
|
+
* what (canvas image source): An element to draw into the context. The
|
|
688
|
+
specification permits any canvas image source, specifically, an
|
|
689
|
+
HTMLImageElement, an SVGImageElement, an HTMLVideoElement,
|
|
690
|
+
an HTMLCanvasElement, an ImageBitmap, an OffscreenCanvas, or a
|
|
691
|
+
VideoFrame.
|
|
692
|
+
"""
|
|
693
|
+
if isinstance(what, Element):
|
|
694
|
+
what = what._dom_element
|
|
695
|
+
|
|
696
|
+
# https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
|
|
697
|
+
ctx = self._dom_element.getContext("2d")
|
|
698
|
+
if width or height:
|
|
699
|
+
ctx.drawImage(what, 0, 0, width, height)
|
|
700
|
+
|
|
701
|
+
else:
|
|
702
|
+
ctx.drawImage(what, 0, 0)
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
class caption(ContainerElement):
|
|
706
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption"""
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
class cite(ContainerElement):
|
|
710
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/cite"""
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
class code(ContainerElement):
|
|
714
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code"""
|
|
715
|
+
|
|
716
|
+
|
|
717
|
+
class col(Element):
|
|
718
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/col"""
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
class colgroup(ContainerElement):
|
|
722
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/colgroup"""
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
class data(ContainerElement):
|
|
726
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/data"""
|
|
727
|
+
|
|
728
|
+
|
|
729
|
+
class datalist(ContainerElement, HasOptions):
|
|
730
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist"""
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
class dd(ContainerElement):
|
|
734
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd"""
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
class del_(ContainerElement):
|
|
738
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/del"""
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
class details(ContainerElement):
|
|
742
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details"""
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
class dialog(ContainerElement):
|
|
746
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog"""
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
class div(ContainerElement):
|
|
750
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div"""
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
class dl(ContainerElement):
|
|
754
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl"""
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
class dt(ContainerElement):
|
|
758
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dt"""
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
class em(ContainerElement):
|
|
762
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/em"""
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
class embed(Element):
|
|
766
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed"""
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
class fieldset(ContainerElement):
|
|
770
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset"""
|
|
771
|
+
|
|
772
|
+
|
|
773
|
+
class figcaption(ContainerElement):
|
|
774
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption"""
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
class figure(ContainerElement):
|
|
778
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure"""
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
class footer(ContainerElement):
|
|
782
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/footer"""
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
class form(ContainerElement):
|
|
786
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form"""
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
class h1(ContainerElement):
|
|
790
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1"""
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
class h2(ContainerElement):
|
|
794
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h2"""
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
class h3(ContainerElement):
|
|
798
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h3"""
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
class h4(ContainerElement):
|
|
802
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h4"""
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
class h5(ContainerElement):
|
|
806
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h5"""
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
class h6(ContainerElement):
|
|
810
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h6"""
|
|
811
|
+
|
|
812
|
+
|
|
813
|
+
class head(ContainerElement):
|
|
814
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head"""
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
class header(ContainerElement):
|
|
818
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header"""
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
class hgroup(ContainerElement):
|
|
822
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hgroup"""
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
class hr(Element):
|
|
826
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr"""
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
class html(ContainerElement):
|
|
830
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/html"""
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
class i(ContainerElement):
|
|
834
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/i"""
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
class iframe(ContainerElement):
|
|
838
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe"""
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
class img(Element):
|
|
842
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img"""
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
class input_(Element):
|
|
846
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input"""
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
class ins(ContainerElement):
|
|
850
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ins"""
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
class kbd(ContainerElement):
|
|
854
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/kbd"""
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
class label(ContainerElement):
|
|
858
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label"""
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
class legend(ContainerElement):
|
|
862
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend"""
|
|
863
|
+
|
|
864
|
+
|
|
865
|
+
class li(ContainerElement):
|
|
866
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li"""
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
class link(Element):
|
|
870
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link"""
|
|
871
|
+
|
|
872
|
+
|
|
873
|
+
class main(ContainerElement):
|
|
874
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main"""
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
class map_(ContainerElement):
|
|
878
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/map"""
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
class mark(ContainerElement):
|
|
882
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark"""
|
|
883
|
+
|
|
884
|
+
|
|
885
|
+
class menu(ContainerElement):
|
|
886
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menu"""
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
class meta(ContainerElement):
|
|
890
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta"""
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
class meter(ContainerElement):
|
|
894
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meter"""
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
class nav(ContainerElement):
|
|
898
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav"""
|
|
899
|
+
|
|
900
|
+
|
|
901
|
+
class object_(ContainerElement):
|
|
902
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object"""
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
class ol(ContainerElement):
|
|
906
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol"""
|
|
907
|
+
|
|
908
|
+
|
|
909
|
+
class optgroup(ContainerElement, HasOptions):
|
|
910
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup"""
|
|
911
|
+
|
|
912
|
+
|
|
913
|
+
class option(ContainerElement):
|
|
914
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option"""
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
class output(ContainerElement):
|
|
918
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output"""
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
class p(ContainerElement):
|
|
922
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p"""
|
|
923
|
+
|
|
924
|
+
|
|
925
|
+
class param(ContainerElement):
|
|
926
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/param"""
|
|
927
|
+
|
|
928
|
+
|
|
929
|
+
class picture(ContainerElement):
|
|
930
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture"""
|
|
931
|
+
|
|
932
|
+
|
|
933
|
+
class pre(ContainerElement):
|
|
934
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre"""
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
class progress(ContainerElement):
|
|
938
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress"""
|
|
939
|
+
|
|
940
|
+
|
|
941
|
+
class q(ContainerElement):
|
|
942
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q"""
|
|
943
|
+
|
|
944
|
+
|
|
945
|
+
class s(ContainerElement):
|
|
946
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/s"""
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
class script(ContainerElement):
|
|
950
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script"""
|
|
951
|
+
|
|
952
|
+
|
|
953
|
+
class section(ContainerElement):
|
|
954
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section"""
|
|
955
|
+
|
|
956
|
+
|
|
957
|
+
class select(ContainerElement, HasOptions):
|
|
958
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select"""
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
class small(ContainerElement):
|
|
962
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/small"""
|
|
963
|
+
|
|
964
|
+
|
|
965
|
+
class source(Element):
|
|
966
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source"""
|
|
967
|
+
|
|
968
|
+
|
|
969
|
+
class span(ContainerElement):
|
|
970
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span"""
|
|
971
|
+
|
|
972
|
+
|
|
973
|
+
class strong(ContainerElement):
|
|
974
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong"""
|
|
975
|
+
|
|
976
|
+
|
|
977
|
+
class style(ContainerElement):
|
|
978
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style"""
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
class sub(ContainerElement):
|
|
982
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub"""
|
|
983
|
+
|
|
984
|
+
|
|
985
|
+
class summary(ContainerElement):
|
|
986
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary"""
|
|
987
|
+
|
|
988
|
+
|
|
989
|
+
class sup(ContainerElement):
|
|
990
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup"""
|
|
991
|
+
|
|
992
|
+
|
|
993
|
+
class table(ContainerElement):
|
|
994
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table"""
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
class tbody(ContainerElement):
|
|
998
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody"""
|
|
999
|
+
|
|
1000
|
+
|
|
1001
|
+
class td(ContainerElement):
|
|
1002
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td"""
|
|
1003
|
+
|
|
1004
|
+
|
|
1005
|
+
class template(ContainerElement):
|
|
1006
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template"""
|
|
1007
|
+
|
|
1008
|
+
|
|
1009
|
+
class textarea(ContainerElement):
|
|
1010
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea"""
|
|
1011
|
+
|
|
1012
|
+
|
|
1013
|
+
class tfoot(ContainerElement):
|
|
1014
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tfoot"""
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
class th(ContainerElement):
|
|
1018
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/th"""
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
class thead(ContainerElement):
|
|
1022
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead"""
|
|
1023
|
+
|
|
1024
|
+
|
|
1025
|
+
class time(ContainerElement):
|
|
1026
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time"""
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
class title(ContainerElement):
|
|
1030
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title"""
|
|
1031
|
+
|
|
1032
|
+
|
|
1033
|
+
class tr(ContainerElement):
|
|
1034
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr"""
|
|
1035
|
+
|
|
1036
|
+
|
|
1037
|
+
class track(Element):
|
|
1038
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/track"""
|
|
1039
|
+
|
|
1040
|
+
|
|
1041
|
+
class u(ContainerElement):
|
|
1042
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/u"""
|
|
1043
|
+
|
|
1044
|
+
|
|
1045
|
+
class ul(ContainerElement):
|
|
1046
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul"""
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
class var(ContainerElement):
|
|
1050
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/var"""
|
|
1051
|
+
|
|
1052
|
+
|
|
1053
|
+
class video(ContainerElement):
|
|
1054
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video"""
|
|
1055
|
+
|
|
1056
|
+
def snap(
|
|
1057
|
+
self,
|
|
1058
|
+
to: Element | str = None,
|
|
1059
|
+
width: int | None = None,
|
|
1060
|
+
height: int | None = None,
|
|
1061
|
+
):
|
|
1062
|
+
"""
|
|
1063
|
+
Capture a snapshot (i.e. a single frame) of a video to a canvas.
|
|
1064
|
+
|
|
1065
|
+
Inputs:
|
|
1066
|
+
|
|
1067
|
+
* to: the canvas to save the video frame to (if None, one is created).
|
|
1068
|
+
* width: width of the snapshot (defaults to the video width).
|
|
1069
|
+
* height: height of the snapshot (defaults to the video height).
|
|
1070
|
+
|
|
1071
|
+
Output:
|
|
1072
|
+
(Element) canvas element where the video frame snapshot was drawn into
|
|
1073
|
+
"""
|
|
1074
|
+
width = width if width is not None else self.videoWidth
|
|
1075
|
+
height = height if height is not None else self.videoHeight
|
|
1076
|
+
|
|
1077
|
+
if to is None:
|
|
1078
|
+
to = canvas(width=width, height=height)
|
|
1079
|
+
|
|
1080
|
+
elif isinstance(to, Element):
|
|
1081
|
+
if to.tag != "canvas":
|
|
1082
|
+
raise TypeError("Element to snap to must be a canvas.")
|
|
1083
|
+
|
|
1084
|
+
elif getattr(to, "tagName", "") == "CANVAS":
|
|
1085
|
+
to = canvas(dom_element=to)
|
|
1086
|
+
|
|
1087
|
+
# If 'to' is a string, then assume it is a query selector.
|
|
1088
|
+
elif isinstance(to, str):
|
|
1089
|
+
nodelist = document.querySelectorAll(to) # NOQA
|
|
1090
|
+
if nodelist.length == 0:
|
|
1091
|
+
raise TypeError("No element with selector {to} to snap to.")
|
|
1092
|
+
|
|
1093
|
+
if nodelist[0].tagName != "CANVAS":
|
|
1094
|
+
raise TypeError("Element to snap to must be a canvas.")
|
|
1095
|
+
|
|
1096
|
+
to = canvas(dom_element=nodelist[0])
|
|
1097
|
+
|
|
1098
|
+
to.draw(self, width, height)
|
|
1099
|
+
|
|
1100
|
+
return to
|
|
1101
|
+
|
|
1102
|
+
|
|
1103
|
+
class wbr(Element):
|
|
1104
|
+
"""Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/wbr"""
|
|
1105
|
+
|
|
1106
|
+
|
|
1107
|
+
# fmt: off
|
|
1108
|
+
ELEMENT_CLASSES = [
|
|
1109
|
+
a, abbr, address, area, article, aside, audio,
|
|
1110
|
+
b, base, blockquote, body, br, button,
|
|
1111
|
+
canvas, caption, cite, code, col, colgroup,
|
|
1112
|
+
data, datalist, dd, del_, details, dialog, div, dl, dt,
|
|
1113
|
+
em, embed,
|
|
1114
|
+
fieldset, figcaption, figure, footer, form,
|
|
1115
|
+
h1, h2, h3, h4, h5, h6, head, header, hgroup, hr, html,
|
|
1116
|
+
i, iframe, img, input_, ins,
|
|
1117
|
+
kbd,
|
|
1118
|
+
label, legend, li, link,
|
|
1119
|
+
main, map_, mark, menu, meta, meter,
|
|
1120
|
+
nav,
|
|
1121
|
+
object_, ol, optgroup, option, output,
|
|
1122
|
+
p, param, picture, pre, progress,
|
|
1123
|
+
q,
|
|
1124
|
+
s, script, section, select, small, source, span, strong, style, sub, summary, sup,
|
|
1125
|
+
table, tbody, td, template, textarea, tfoot, th, thead, time, title, tr, track,
|
|
1126
|
+
u, ul,
|
|
1127
|
+
var, video,
|
|
1128
|
+
wbr,
|
|
1129
|
+
]
|
|
1130
|
+
# fmt: on
|
|
1131
|
+
|
|
1132
|
+
|
|
1133
|
+
# Register all the default (aka "built-in") Element classes.
|
|
1134
|
+
Element.register_element_classes(ELEMENT_CLASSES)
|
|
1135
|
+
|
|
1136
|
+
|
|
1137
|
+
class Page:
|
|
1138
|
+
"""Represents the whole page."""
|
|
1139
|
+
|
|
1140
|
+
def __init__(self):
|
|
1141
|
+
self.html = Element.wrap_dom_element(document.documentElement)
|
|
1142
|
+
self.body = Element.wrap_dom_element(document.body)
|
|
1143
|
+
self.head = Element.wrap_dom_element(document.head)
|
|
1144
|
+
|
|
1145
|
+
def __getitem__(self, selector):
|
|
1146
|
+
"""Get an item on the page.
|
|
1147
|
+
|
|
1148
|
+
We don't index/slice the page like we do with `Element` and `ElementCollection`
|
|
1149
|
+
as it is a bit muddier what the ideal behavior should be. Instead, we simply
|
|
1150
|
+
use this as a convenience method to `find` elements on the page.
|
|
1151
|
+
"""
|
|
1152
|
+
return self.find(selector)
|
|
1153
|
+
|
|
1154
|
+
@property
|
|
1155
|
+
def title(self):
|
|
1156
|
+
"""Return the page title."""
|
|
1157
|
+
return document.title
|
|
1158
|
+
|
|
1159
|
+
@title.setter
|
|
1160
|
+
def title(self, value):
|
|
1161
|
+
"""Set the page title."""
|
|
1162
|
+
document.title = value
|
|
1163
|
+
|
|
1164
|
+
def append(self, *items):
|
|
1165
|
+
"""Shortcut for `page.body.append`."""
|
|
1166
|
+
self.body.append(*items)
|
|
1167
|
+
|
|
1168
|
+
def find(self, selector): # NOQA
|
|
1169
|
+
"""Find all elements that match the specified selector.
|
|
1170
|
+
|
|
1171
|
+
Return the results as a (possibly empty) `ElementCollection`.
|
|
1172
|
+
"""
|
|
1173
|
+
return ElementCollection.wrap_dom_elements(document.querySelectorAll(selector))
|
|
1174
|
+
|
|
1175
|
+
|
|
1176
|
+
page = Page()
|