@pyscript/core 0.4.42 → 0.4.44

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.
Files changed (34) hide show
  1. package/dist/core-By0QRQ2R.js +3 -0
  2. package/dist/{core-DmXwjHtp.js.map → core-By0QRQ2R.js.map} +1 -1
  3. package/dist/core.js +1 -1
  4. package/dist/{deprecations-manager-_UxKHXZB.js → deprecations-manager-fi4TlTxy.js} +2 -2
  5. package/dist/{deprecations-manager-_UxKHXZB.js.map → deprecations-manager-fi4TlTxy.js.map} +1 -1
  6. package/dist/{error-DbRphA3T.js → error-CyeTN0Zs.js} +2 -2
  7. package/dist/{error-DbRphA3T.js.map → error-CyeTN0Zs.js.map} +1 -1
  8. package/dist/{mpy-BzefGwGL.js → mpy-imctvuTW.js} +2 -2
  9. package/dist/{mpy-BzefGwGL.js.map → mpy-imctvuTW.js.map} +1 -1
  10. package/dist/{py-CdfTwlyw.js → py-CeGQ10Xz.js} +2 -2
  11. package/dist/{py-CdfTwlyw.js.map → py-CeGQ10Xz.js.map} +1 -1
  12. package/dist/py-editor-B1bte20d.js +2 -0
  13. package/dist/py-editor-B1bte20d.js.map +1 -0
  14. package/dist/{py-terminal-CnFb82OC.js → py-terminal-Dhz_Ni1G.js} +2 -2
  15. package/dist/{py-terminal-CnFb82OC.js.map → py-terminal-Dhz_Ni1G.js.map} +1 -1
  16. package/dist.zip +0 -0
  17. package/package.json +4 -4
  18. package/src/core.js +1 -1
  19. package/src/plugins/py-editor.js +45 -13
  20. package/src/stdlib/pyscript/__init__.py +1 -1
  21. package/src/stdlib/pyscript/event_handling.py +9 -7
  22. package/src/stdlib/pyscript/magic_js.py +4 -1
  23. package/src/stdlib/pyscript/web/__init__.py +5 -0
  24. package/src/stdlib/pyscript/web/dom.py +21 -0
  25. package/src/stdlib/pyscript/web/elements.py +1484 -0
  26. package/src/stdlib/pyscript.js +9 -8
  27. package/src/stdlib.js +1 -1
  28. package/types/stdlib/pyscript.d.ts +6 -5
  29. package/dist/core-DmXwjHtp.js +0 -3
  30. package/dist/py-editor-z9mZdLa8.js +0 -2
  31. package/dist/py-editor-z9mZdLa8.js.map +0 -1
  32. package/src/stdlib/pyweb/__init__.py +0 -1
  33. package/src/stdlib/pyweb/pydom.py +0 -550
  34. /package/src/stdlib/{pyweb → pyscript/web}/media.py +0 -0
@@ -0,0 +1,1484 @@
1
+ import inspect
2
+ import sys
3
+
4
+ try:
5
+ from typing import Any
6
+ except ImportError:
7
+ Any = "Any"
8
+
9
+ try:
10
+ import warnings
11
+ except ImportError:
12
+ # TODO: For now it probably means we are in MicroPython. We should figure
13
+ # out the "right" way to handle this. For now we just ignore the warning
14
+ # and logging to console
15
+ class warnings:
16
+ @staticmethod
17
+ def warn(*args, **kwargs):
18
+ print("WARNING: ", *args, **kwargs)
19
+
20
+
21
+ try:
22
+ from functools import cached_property
23
+ except ImportError:
24
+ # TODO: same comment about micropython as above
25
+ cached_property = property
26
+
27
+ try:
28
+ from pyodide.ffi import JsProxy
29
+ except ImportError:
30
+ # TODO: same comment about micropython as above
31
+ def JsProxy(obj):
32
+ return obj
33
+
34
+
35
+ from pyscript import document, window
36
+
37
+ # from pyscript.web import dom as pydom
38
+
39
+ #: A flag to show if MicroPython is the current Python interpreter.
40
+ is_micropython = "MicroPython" in sys.version
41
+
42
+
43
+ def getmembers_static(cls):
44
+ """Cross-interpreter implementation of inspect.getmembers_static."""
45
+
46
+ if is_micropython: # pragma: no cover
47
+ return [(name, getattr(cls, name)) for name, _ in inspect.getmembers(cls)]
48
+
49
+ return inspect.getmembers_static(cls)
50
+
51
+
52
+ class JSProperty:
53
+ """JS property descriptor that directly maps to the property with the same
54
+ name in the underlying JS component."""
55
+
56
+ def __init__(self, name: str, allow_nones: bool = False):
57
+ self.name = name
58
+ self.allow_nones = allow_nones
59
+
60
+ def __get__(self, obj, objtype=None):
61
+ return getattr(obj._js, self.name)
62
+
63
+ def __set__(self, obj, value):
64
+ if not self.allow_nones and value is None:
65
+ return
66
+ setattr(obj._js, self.name, value)
67
+
68
+
69
+ # ------ TODO: REMOVE!!!! pydom elements
70
+
71
+
72
+ class BaseElement:
73
+ def __init__(self, js_element):
74
+ self._js = js_element
75
+ self._parent = None
76
+ self.style = StyleProxy(self)
77
+ self._proxies = {}
78
+
79
+ def __eq__(self, obj):
80
+ """Check if the element is the same as the other element by comparing
81
+ the underlying JS element"""
82
+ return isinstance(obj, BaseElement) and obj._js == self._js
83
+
84
+ @property
85
+ def parent(self):
86
+ if self._parent:
87
+ return self._parent
88
+
89
+ if self._js.parentElement:
90
+ # TODO: This should actually return the correct class (== to tagName)
91
+ self._parent = Element(self._js.parentElement)
92
+
93
+ return self._parent
94
+
95
+ # @property
96
+ # def __class(self):
97
+ # return self.__class__ if self.__class__ != PyDom else Element
98
+
99
+ def create(self, type_, is_child=True, classes=None, html=None, label=None):
100
+ js_el = document.createElement(type_)
101
+ element = self.__class(js_el)
102
+
103
+ if classes:
104
+ for class_ in classes:
105
+ element.add_class(class_)
106
+
107
+ if html is not None:
108
+ element.html = html
109
+
110
+ if label is not None:
111
+ element.label = label
112
+
113
+ if is_child:
114
+ self.append(element)
115
+
116
+ return element
117
+
118
+ def find(self, selector):
119
+ """Return an ElementCollection representing all the child elements that
120
+ match the specified selector.
121
+
122
+ Args:
123
+ selector (str): A string containing a selector expression
124
+
125
+ Returns:
126
+ ElementCollection: A collection of elements matching the selector
127
+ """
128
+ elements = self._js.querySelectorAll(selector)
129
+ if not elements:
130
+ return None
131
+ return ElementCollection([Element(el) for el in elements])
132
+
133
+
134
+ class Element(BaseElement):
135
+ @property
136
+ def children(self):
137
+ return [self.__class__(el) for el in self._js.children]
138
+
139
+ def append(self, child):
140
+ # TODO: this is Pyodide specific for now!!!!!!
141
+ # if we get passed a JSProxy Element directly we just map it to the
142
+ # higher level Python element
143
+ if inspect.isclass(JsProxy) and isinstance(child, JsProxy):
144
+ return self.append(Element(child))
145
+
146
+ elif isinstance(child, Element):
147
+ self._js.appendChild(child._js)
148
+
149
+ return child
150
+
151
+ elif isinstance(child, ElementCollection):
152
+ for el in child:
153
+ self.append(el)
154
+
155
+ # -------- Pythonic Interface to Element -------- #
156
+ @property
157
+ def html(self):
158
+ return self._js.innerHTML
159
+
160
+ @html.setter
161
+ def html(self, value):
162
+ self._js.innerHTML = value
163
+
164
+ @property
165
+ def text(self):
166
+ return self._js.textContent
167
+
168
+ @text.setter
169
+ def text(self, value):
170
+ self._js.textContent = value
171
+
172
+ @property
173
+ def content(self):
174
+ # TODO: This breaks with with standard template elements. Define how to best
175
+ # handle this specifica use case. Just not support for now?
176
+ if self._js.tagName == "TEMPLATE":
177
+ warnings.warn(
178
+ "Content attribute not supported for template elements.", stacklevel=2
179
+ )
180
+ return None
181
+ return self._js.innerHTML
182
+
183
+ @content.setter
184
+ def content(self, value):
185
+ # TODO: (same comment as above)
186
+ if self._js.tagName == "TEMPLATE":
187
+ warnings.warn(
188
+ "Content attribute not supported for template elements.", stacklevel=2
189
+ )
190
+ return
191
+
192
+ display(value, target=self.id)
193
+
194
+ @property
195
+ def id(self):
196
+ return self._js.id
197
+
198
+ @id.setter
199
+ def id(self, value):
200
+ self._js.id = value
201
+
202
+ @property
203
+ def options(self):
204
+ if "options" in self._proxies:
205
+ return self._proxies["options"]
206
+
207
+ if not self._js.tagName.lower() in {"select", "datalist", "optgroup"}:
208
+ raise AttributeError(
209
+ f"Element {self._js.tagName} has no options attribute."
210
+ )
211
+ self._proxies["options"] = OptionsProxy(self)
212
+ return self._proxies["options"]
213
+
214
+ @property
215
+ def value(self):
216
+ return self._js.value
217
+
218
+ @value.setter
219
+ def value(self, value):
220
+ # in order to avoid confusion to the user, we don't allow setting the
221
+ # value of elements that don't have a value attribute
222
+ if not hasattr(self._js, "value"):
223
+ raise AttributeError(
224
+ f"Element {self._js.tagName} has no value attribute. If you want to "
225
+ "force a value attribute, set it directly using the `_js.value = <value>` "
226
+ "javascript API attribute instead."
227
+ )
228
+ self._js.value = value
229
+
230
+ @property
231
+ def selected(self):
232
+ return self._js.selected
233
+
234
+ @selected.setter
235
+ def selected(self, value):
236
+ # in order to avoid confusion to the user, we don't allow setting the
237
+ # value of elements that don't have a value attribute
238
+ if not hasattr(self._js, "selected"):
239
+ raise AttributeError(
240
+ f"Element {self._js.tagName} has no value attribute. If you want to "
241
+ "force a value attribute, set it directly using the `_js.value = <value>` "
242
+ "javascript API attribute instead."
243
+ )
244
+ self._js.selected = value
245
+
246
+ def clone(self, new_id=None):
247
+ clone = Element(self._js.cloneNode(True))
248
+ clone.id = new_id
249
+
250
+ return clone
251
+
252
+ def remove_class(self, classname):
253
+ classList = self._js.classList
254
+ if isinstance(classname, list):
255
+ classList.remove(*classname)
256
+ else:
257
+ classList.remove(classname)
258
+ return self
259
+
260
+ def add_class(self, classname):
261
+ classList = self._js.classList
262
+ if isinstance(classname, list):
263
+ classList.add(*classname)
264
+ else:
265
+ self._js.classList.add(classname)
266
+ return self
267
+
268
+ @property
269
+ def classes(self):
270
+ classes = self._js.classList.values()
271
+ return [x for x in classes]
272
+
273
+ def show_me(self):
274
+ self._js.scrollIntoView()
275
+
276
+ def snap(
277
+ self,
278
+ to: BaseElement | str = None,
279
+ width: int | None = None,
280
+ height: int | None = None,
281
+ ):
282
+ """
283
+ Captures a snapshot of a video element. (Only available for video elements)
284
+
285
+ Inputs:
286
+
287
+ * to: element where to save the snapshot of the video frame to
288
+ * width: width of the image
289
+ * height: height of the image
290
+
291
+ Output:
292
+ (Element) canvas element where the video frame snapshot was drawn into
293
+ """
294
+ if self._js.tagName != "VIDEO":
295
+ raise AttributeError("Snap method is only available for video Elements")
296
+
297
+ if to is None:
298
+ canvas = self.create("canvas")
299
+ if width is None:
300
+ width = self._js.width
301
+ if height is None:
302
+ height = self._js.height
303
+ canvas._js.width = width
304
+ canvas._js.height = height
305
+
306
+ elif isinstance(to, Element):
307
+ if to._js.tagName != "CANVAS":
308
+ raise TypeError("Element to snap to must a canvas.")
309
+ canvas = to
310
+ elif getattr(to, "tagName", "") == "CANVAS":
311
+ canvas = Element(to)
312
+ elif isinstance(to, str):
313
+ # TODO (fpliger): This needs a better fix but doing a local import here for a quick fix
314
+ from pyscript.web import dom
315
+
316
+ canvas = dom[to][0]
317
+ if canvas._js.tagName != "CANVAS":
318
+ raise TypeError("Element to snap to must a be canvas.")
319
+
320
+ canvas.draw(self, width, height)
321
+
322
+ return canvas
323
+
324
+ def download(self, filename: str = "snapped.png") -> None:
325
+ """Download the current element (only available for canvas elements) with the filename
326
+ provided in input.
327
+
328
+ Inputs:
329
+ * filename (str): name of the file being downloaded
330
+
331
+ Output:
332
+ None
333
+ """
334
+ if self._js.tagName != "CANVAS":
335
+ raise AttributeError(
336
+ "The download method is only available for canvas Elements"
337
+ )
338
+
339
+ link = self.create("a")
340
+ link._js.download = filename
341
+ link._js.href = self._js.toDataURL()
342
+ link._js.click()
343
+
344
+ def draw(self, what, width, height):
345
+ """Draw `what` on the current element (only available for canvas elements).
346
+
347
+ Inputs:
348
+
349
+ * what (canvas image source): An element to draw into the context. The specification permits any canvas
350
+ image source, specifically, an HTMLImageElement, an SVGImageElement, an HTMLVideoElement,
351
+ an HTMLCanvasElement, an ImageBitmap, an OffscreenCanvas, or a VideoFrame.
352
+ """
353
+ if self._js.tagName != "CANVAS":
354
+ raise AttributeError(
355
+ "The draw method is only available for canvas Elements"
356
+ )
357
+
358
+ if isinstance(what, Element):
359
+ what = what._js
360
+
361
+ # https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage
362
+ self._js.getContext("2d").drawImage(what, 0, 0, width, height)
363
+
364
+
365
+ class OptionsProxy:
366
+ """This class represents the options of a select element. It
367
+ allows to access to add and remove options by using the `add` and `remove` methods.
368
+ """
369
+
370
+ def __init__(self, element: Element) -> None:
371
+ self._element = element
372
+ if self._element._js.tagName.lower() != "select":
373
+ raise AttributeError(
374
+ f"Element {self._element._js.tagName} has no options attribute."
375
+ )
376
+
377
+ def add(
378
+ self,
379
+ value: Any = None,
380
+ html: str = None,
381
+ text: str = None,
382
+ before: Element | int = None,
383
+ **kws,
384
+ ) -> None:
385
+ """Add a new option to the select element"""
386
+ # create the option element and set the attributes
387
+ option = document.createElement("option")
388
+ if value is not None:
389
+ kws["value"] = value
390
+ if html is not None:
391
+ option.innerHTML = html
392
+ if text is not None:
393
+ kws["text"] = text
394
+
395
+ for key, value in kws.items():
396
+ option.setAttribute(key, value)
397
+
398
+ if before:
399
+ if isinstance(before, Element):
400
+ before = before._js
401
+
402
+ self._element._js.add(option, before)
403
+
404
+ def remove(self, item: int) -> None:
405
+ """Remove the option at the specified index"""
406
+ self._element._js.remove(item)
407
+
408
+ def clear(self) -> None:
409
+ """Remove all the options"""
410
+ for i in range(len(self)):
411
+ self.remove(0)
412
+
413
+ @property
414
+ def options(self):
415
+ """Return the list of options"""
416
+ return [Element(opt) for opt in self._element._js.options]
417
+
418
+ @property
419
+ def selected(self):
420
+ """Return the selected option"""
421
+ return self.options[self._element._js.selectedIndex]
422
+
423
+ def __iter__(self):
424
+ yield from self.options
425
+
426
+ def __len__(self):
427
+ return len(self.options)
428
+
429
+ def __repr__(self):
430
+ return f"{self.__class__.__name__} (length: {len(self)}) {self.options}"
431
+
432
+ def __getitem__(self, key):
433
+ return self.options[key]
434
+
435
+
436
+ class StyleProxy: # (dict):
437
+ def __init__(self, element: Element) -> None:
438
+ self._element = element
439
+
440
+ @cached_property
441
+ def _style(self):
442
+ return self._element._js.style
443
+
444
+ def __getitem__(self, key):
445
+ return self._style.getPropertyValue(key)
446
+
447
+ def __setitem__(self, key, value):
448
+ self._style.setProperty(key, value)
449
+
450
+ def remove(self, key):
451
+ self._style.removeProperty(key)
452
+
453
+ def set(self, **kws):
454
+ for k, v in kws.items():
455
+ self._element._js.style.setProperty(k, v)
456
+
457
+ # CSS Properties
458
+ # Reference: https://github.com/microsoft/TypeScript/blob/main/src/lib/dom.generated.d.ts#L3799C1-L5005C2
459
+ # Following prperties automatically generated from the above reference using
460
+ # tools/codegen_css_proxy.py
461
+ @property
462
+ def visible(self):
463
+ return self._element._js.style.visibility
464
+
465
+ @visible.setter
466
+ def visible(self, value):
467
+ self._element._js.style.visibility = value
468
+
469
+
470
+ # --------- END OF PYDOM STUFF ------
471
+
472
+
473
+ class ElementBase(Element):
474
+ tag = "div"
475
+
476
+ # GLOBAL ATTRIBUTES
477
+ # These are attribute that all elements have (this list is a subset of the official one)
478
+ # We are trying to capture the most used ones
479
+ accesskey = JSProperty("accesskey")
480
+ autofocus = JSProperty("autofocus")
481
+ autocapitalize = JSProperty("autocapitalize")
482
+ className = JSProperty("className")
483
+ contenteditable = JSProperty("contenteditable")
484
+ draggable = JSProperty("draggable")
485
+ enterkeyhint = JSProperty("enterkeyhint")
486
+ hidden = JSProperty("hidden")
487
+ id = JSProperty("id")
488
+ lang = JSProperty("lang")
489
+ nonce = JSProperty("nonce")
490
+ part = JSProperty("part")
491
+ popover = JSProperty("popover")
492
+ slot = JSProperty("slot")
493
+ spellcheck = JSProperty("spellcheck")
494
+ tabindex = JSProperty("tabindex")
495
+ title = JSProperty("title")
496
+ translate = JSProperty("translate")
497
+ virtualkeyboardpolicy = JSProperty("virtualkeyboardpolicy")
498
+
499
+ def __init__(self, style=None, **kwargs):
500
+ super().__init__(document.createElement(self.tag))
501
+
502
+ # set all the style properties provided in input
503
+ if isinstance(style, dict):
504
+ for key, value in style.items():
505
+ self.style[key] = value
506
+ elif style is None:
507
+ pass
508
+ else:
509
+ raise ValueError(
510
+ f"Style should be a dictionary, received {style} (type {type(style)}) instead."
511
+ )
512
+
513
+ # IMPORTANT!!! This is used to auto-harvest all input arguments and set them as properties
514
+ self._init_properties(**kwargs)
515
+
516
+ def _init_properties(self, **kwargs):
517
+ """Set all the properties (of type JSProperties) provided in input as properties
518
+ of the class instance.
519
+
520
+ Args:
521
+ **kwargs: The properties to set
522
+ """
523
+ # Look at all the properties of the class and see if they were provided in kwargs
524
+ for attr_name, attr in getmembers_static(self.__class__):
525
+ # For each one, actually check if it is a property of the class and set it
526
+ if isinstance(attr, JSProperty) and attr_name in kwargs:
527
+ try:
528
+ setattr(self, attr_name, kwargs[attr_name])
529
+ except Exception as e:
530
+ print(f"Error setting {attr_name} to {kwargs[attr_name]}: {e}")
531
+ raise
532
+
533
+
534
+ class TextElementBase(ElementBase):
535
+ def __init__(self, content=None, style=None, **kwargs):
536
+ super().__init__(style=style, **kwargs)
537
+
538
+ # If it's an element, append the element
539
+ if isinstance(content, Element):
540
+ self.append(content)
541
+ # If it's a list of elements
542
+ elif isinstance(content, list):
543
+ for item in content:
544
+ self.append(item)
545
+ # If the content wasn't set just ignore
546
+ elif content is None:
547
+ pass
548
+ else:
549
+ # Otherwise, set content as the html of the element
550
+ self.html = content
551
+
552
+
553
+ # IMPORTANT: For all HTML components defined below, we are not mapping all
554
+ # available attributes, just the global and the most common ones.
555
+ # If you need to access a specific attribute, you can always use the `_js.<attribute>`
556
+ class a(TextElementBase):
557
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a"""
558
+
559
+ tag = "a"
560
+
561
+ download = JSProperty("download")
562
+ href = JSProperty("href")
563
+ referrerpolicy = JSProperty("referrerpolicy")
564
+ rel = JSProperty("rel")
565
+ target = JSProperty("target")
566
+ type = JSProperty("type")
567
+
568
+
569
+ class abbr(TextElementBase):
570
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/abbr"""
571
+
572
+ tag = "abbr"
573
+
574
+
575
+ class address(TextElementBase):
576
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/address"""
577
+
578
+ tag = "address"
579
+
580
+
581
+ class area(ElementBase):
582
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area"""
583
+
584
+ tag = "area"
585
+
586
+ alt = JSProperty("alt")
587
+ coords = JSProperty("coords")
588
+ download = JSProperty("download")
589
+ href = JSProperty("href")
590
+ ping = JSProperty("ping")
591
+ referrerpolicy = JSProperty("referrerpolicy")
592
+ rel = JSProperty("rel")
593
+ shape = JSProperty("shape")
594
+ target = JSProperty("target")
595
+
596
+
597
+ class article(TextElementBase):
598
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/article"""
599
+
600
+ tag = "article"
601
+
602
+
603
+ class aside(TextElementBase):
604
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/aside"""
605
+
606
+ tag = "aside"
607
+
608
+
609
+ class audio(ElementBase):
610
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio"""
611
+
612
+ tag = "audio"
613
+
614
+ autoplay = JSProperty("autoplay")
615
+ controls = JSProperty("controls")
616
+ controlslist = JSProperty("controlslist")
617
+ crossorigin = JSProperty("crossorigin")
618
+ disableremoteplayback = JSProperty("disableremoteplayback")
619
+ loop = JSProperty("loop")
620
+ muted = JSProperty("muted")
621
+ preload = JSProperty("preload")
622
+ src = JSProperty("src")
623
+
624
+
625
+ class b(TextElementBase):
626
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/b"""
627
+
628
+ tag = "b"
629
+
630
+
631
+ class blockquote(TextElementBase):
632
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/blockquote"""
633
+
634
+ tag = "blockquote"
635
+
636
+ cite = JSProperty("cite")
637
+
638
+
639
+ class br(ElementBase):
640
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/br"""
641
+
642
+ tag = "br"
643
+
644
+
645
+ class button(TextElementBase):
646
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button"""
647
+
648
+ tag = "button"
649
+
650
+ autofocus = JSProperty("autofocus")
651
+ disabled = JSProperty("disabled")
652
+ form = JSProperty("form")
653
+ formaction = JSProperty("formaction")
654
+ formenctype = JSProperty("formenctype")
655
+ formmethod = JSProperty("formmethod")
656
+ formnovalidate = JSProperty("formnovalidate")
657
+ formtarget = JSProperty("formtarget")
658
+ name = JSProperty("name")
659
+ type = JSProperty("type")
660
+ value = JSProperty("value")
661
+
662
+
663
+ class canvas(TextElementBase):
664
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas"""
665
+
666
+ tag = "canvas"
667
+
668
+ height = JSProperty("height")
669
+ width = JSProperty("width")
670
+
671
+
672
+ class caption(TextElementBase):
673
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption"""
674
+
675
+ tag = "caption"
676
+
677
+
678
+ class cite(TextElementBase):
679
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/cite"""
680
+
681
+ tag = "cite"
682
+
683
+
684
+ class code(TextElementBase):
685
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/code"""
686
+
687
+ tag = "code"
688
+
689
+
690
+ class data(TextElementBase):
691
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/data"""
692
+
693
+ tag = "data"
694
+
695
+ value = JSProperty("value")
696
+
697
+
698
+ class datalist(TextElementBase):
699
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist"""
700
+
701
+ tag = "datalist"
702
+
703
+
704
+ class dd(TextElementBase):
705
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd"""
706
+
707
+ tag = "dd"
708
+
709
+
710
+ class del_(TextElementBase):
711
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/del"""
712
+
713
+ tag = "del"
714
+
715
+ cite = JSProperty("cite")
716
+ datetime = JSProperty("datetime")
717
+
718
+
719
+ class details(TextElementBase):
720
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details"""
721
+
722
+ tag = "details"
723
+
724
+ open = JSProperty("open")
725
+
726
+
727
+ class dialog(TextElementBase):
728
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog"""
729
+
730
+ tag = "dialog"
731
+
732
+ open = JSProperty("open")
733
+
734
+
735
+ class div(TextElementBase):
736
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div"""
737
+
738
+ tag = "div"
739
+
740
+
741
+ class dl(TextElementBase):
742
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl"""
743
+
744
+ tag = "dl"
745
+
746
+ value = JSProperty("value")
747
+
748
+
749
+ class dt(TextElementBase):
750
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dt"""
751
+
752
+ tag = "dt"
753
+
754
+
755
+ class em(TextElementBase):
756
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/em"""
757
+
758
+ tag = "em"
759
+
760
+
761
+ class embed(TextElementBase):
762
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed"""
763
+
764
+ tag = "embed"
765
+
766
+ height = JSProperty("height")
767
+ src = JSProperty("src")
768
+ type = JSProperty("type")
769
+ width = JSProperty("width")
770
+
771
+
772
+ class fieldset(TextElementBase):
773
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset"""
774
+
775
+ tag = "fieldset"
776
+
777
+ disabled = JSProperty("disabled")
778
+ form = JSProperty("form")
779
+ name = JSProperty("name")
780
+
781
+
782
+ class figcaption(TextElementBase):
783
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption"""
784
+
785
+ tag = "figcaption"
786
+
787
+
788
+ class figure(TextElementBase):
789
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure"""
790
+
791
+ tag = "figure"
792
+
793
+
794
+ class footer(TextElementBase):
795
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/footer"""
796
+
797
+ tag = "footer"
798
+
799
+
800
+ class form(TextElementBase):
801
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form"""
802
+
803
+ tag = "form"
804
+
805
+ accept_charset = JSProperty("accept-charset")
806
+ action = JSProperty("action")
807
+ autocapitalize = JSProperty("autocapitalize")
808
+ autocomplete = JSProperty("autocomplete")
809
+ enctype = JSProperty("enctype")
810
+ name = JSProperty("name")
811
+ method = JSProperty("method")
812
+ nonvalidate = JSProperty("nonvalidate")
813
+ rel = JSProperty("rel")
814
+ target = JSProperty("target")
815
+
816
+
817
+ class h1(TextElementBase):
818
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h1"""
819
+
820
+ tag = "h1"
821
+
822
+
823
+ class h2(TextElementBase):
824
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h2"""
825
+
826
+ tag = "h2"
827
+
828
+
829
+ class h3(TextElementBase):
830
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h3"""
831
+
832
+ tag = "h3"
833
+
834
+
835
+ class h4(TextElementBase):
836
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h4"""
837
+
838
+ tag = "h4"
839
+
840
+
841
+ class h5(TextElementBase):
842
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h5"""
843
+
844
+ tag = "h5"
845
+
846
+
847
+ class h6(TextElementBase):
848
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/h6"""
849
+
850
+ tag = "h6"
851
+
852
+
853
+ class header(TextElementBase):
854
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header"""
855
+
856
+ tag = "header"
857
+
858
+
859
+ class hgroup(TextElementBase):
860
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hgroup"""
861
+
862
+ tag = "hgroup"
863
+
864
+
865
+ class hr(TextElementBase):
866
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr"""
867
+
868
+ tag = "hr"
869
+
870
+
871
+ class i(TextElementBase):
872
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/i"""
873
+
874
+ tag = "i"
875
+
876
+
877
+ class iframe(TextElementBase):
878
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe"""
879
+
880
+ tag = "iframe"
881
+
882
+ allow = JSProperty("allow")
883
+ allowfullscreen = JSProperty("allowfullscreen")
884
+ height = JSProperty("height")
885
+ loading = JSProperty("loading")
886
+ name = JSProperty("name")
887
+ referrerpolicy = JSProperty("referrerpolicy")
888
+ sandbox = JSProperty("sandbox")
889
+ src = JSProperty("src")
890
+ srcdoc = JSProperty("srcdoc")
891
+ width = JSProperty("width")
892
+
893
+
894
+ class img(ElementBase):
895
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img"""
896
+
897
+ tag = "img"
898
+
899
+ alt = JSProperty("alt")
900
+ crossorigin = JSProperty("crossorigin")
901
+ decoding = JSProperty("decoding")
902
+ fetchpriority = JSProperty("fetchpriority")
903
+ height = JSProperty("height")
904
+ ismap = JSProperty("ismap")
905
+ loading = JSProperty("loading")
906
+ referrerpolicy = JSProperty("referrerpolicy")
907
+ sizes = JSProperty("sizes")
908
+ src = JSProperty("src")
909
+ width = JSProperty("width")
910
+
911
+
912
+ # NOTE: Input is a reserved keyword in Python, so we use input_ instead
913
+ class input_(ElementBase):
914
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input"""
915
+
916
+ tag = "input"
917
+
918
+ accept = JSProperty("accept")
919
+ alt = JSProperty("alt")
920
+ autofocus = JSProperty("autofocus")
921
+ capture = JSProperty("capture")
922
+ checked = JSProperty("checked")
923
+ dirname = JSProperty("dirname")
924
+ disabled = JSProperty("disabled")
925
+ form = JSProperty("form")
926
+ formaction = JSProperty("formaction")
927
+ formenctype = JSProperty("formenctype")
928
+ formmethod = JSProperty("formmethod")
929
+ formnovalidate = JSProperty("formnovalidate")
930
+ formtarget = JSProperty("formtarget")
931
+ height = JSProperty("height")
932
+ list = JSProperty("list")
933
+ max = JSProperty("max")
934
+ maxlength = JSProperty("maxlength")
935
+ min = JSProperty("min")
936
+ minlength = JSProperty("minlength")
937
+ multiple = JSProperty("multiple")
938
+ name = JSProperty("name")
939
+ pattern = JSProperty("pattern")
940
+ placeholder = JSProperty("placeholder")
941
+ popovertarget = JSProperty("popovertarget")
942
+ popovertargetaction = JSProperty("popovertargetaction")
943
+ readonly = JSProperty("readonly")
944
+ required = JSProperty("required")
945
+ size = JSProperty("size")
946
+ src = JSProperty("src")
947
+ step = JSProperty("step")
948
+ type = JSProperty("type")
949
+ value = JSProperty("value")
950
+ width = JSProperty("width")
951
+
952
+
953
+ class ins(TextElementBase):
954
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ins"""
955
+
956
+ tag = "ins"
957
+
958
+ cite = JSProperty("cite")
959
+ datetime = JSProperty("datetime")
960
+
961
+
962
+ class kbd(TextElementBase):
963
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/kbd"""
964
+
965
+ tag = "kbd"
966
+
967
+
968
+ class label(TextElementBase):
969
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label"""
970
+
971
+ tag = "label"
972
+
973
+ for_ = JSProperty("for")
974
+
975
+
976
+ class legend(TextElementBase):
977
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/legend"""
978
+
979
+ tag = "legend"
980
+
981
+
982
+ class li(TextElementBase):
983
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li"""
984
+
985
+ tag = "li"
986
+
987
+ value = JSProperty("value")
988
+
989
+
990
+ class link(TextElementBase):
991
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link"""
992
+
993
+ tag = "link"
994
+
995
+ as_ = JSProperty("as")
996
+ crossorigin = JSProperty("crossorigin")
997
+ disabled = JSProperty("disabled")
998
+ fetchpriority = JSProperty("fetchpriority")
999
+ href = JSProperty("href")
1000
+ imagesizes = JSProperty("imagesizes")
1001
+ imagesrcset = JSProperty("imagesrcset")
1002
+ integrity = JSProperty("integrity")
1003
+ media = JSProperty("media")
1004
+ rel = JSProperty("rel")
1005
+ referrerpolicy = JSProperty("referrerpolicy")
1006
+ sizes = JSProperty("sizes")
1007
+ title = JSProperty("title")
1008
+ type = JSProperty("type")
1009
+
1010
+
1011
+ class main(TextElementBase):
1012
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/main"""
1013
+
1014
+ tag = "main"
1015
+
1016
+
1017
+ class map_(TextElementBase):
1018
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/map"""
1019
+
1020
+ tag = "map"
1021
+
1022
+ name = JSProperty("name")
1023
+
1024
+
1025
+ class mark(TextElementBase):
1026
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/mark"""
1027
+
1028
+ tag = "mark"
1029
+
1030
+
1031
+ class menu(TextElementBase):
1032
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/menu"""
1033
+
1034
+ tag = "menu"
1035
+
1036
+
1037
+ class meter(TextElementBase):
1038
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meter"""
1039
+
1040
+ tag = "meter"
1041
+
1042
+ form = JSProperty("form")
1043
+ high = JSProperty("high")
1044
+ low = JSProperty("low")
1045
+ max = JSProperty("max")
1046
+ min = JSProperty("min")
1047
+ optimum = JSProperty("optimum")
1048
+ value = JSProperty("value")
1049
+
1050
+
1051
+ class nav(TextElementBase):
1052
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav"""
1053
+
1054
+ tag = "nav"
1055
+
1056
+
1057
+ class object_(TextElementBase):
1058
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object"""
1059
+
1060
+ tag = "object"
1061
+
1062
+ data = JSProperty("data")
1063
+ form = JSProperty("form")
1064
+ height = JSProperty("height")
1065
+ name = JSProperty("name")
1066
+ type = JSProperty("type")
1067
+ usemap = JSProperty("usemap")
1068
+ width = JSProperty("width")
1069
+
1070
+
1071
+ class ol(TextElementBase):
1072
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol"""
1073
+
1074
+ tag = "ol"
1075
+
1076
+ reversed = JSProperty("reversed")
1077
+ start = JSProperty("start")
1078
+ type = JSProperty("type")
1079
+
1080
+
1081
+ class optgroup(TextElementBase):
1082
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/optgroup"""
1083
+
1084
+ tag = "optgroup"
1085
+
1086
+ disabled = JSProperty("disabled")
1087
+ label = JSProperty("label")
1088
+
1089
+
1090
+ class option(TextElementBase):
1091
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option"""
1092
+
1093
+ tag = "option"
1094
+
1095
+ disabled = JSProperty("value")
1096
+ label = JSProperty("label")
1097
+ selected = JSProperty("selected")
1098
+ value = JSProperty("value")
1099
+
1100
+
1101
+ class output(TextElementBase):
1102
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/output"""
1103
+
1104
+ tag = "output"
1105
+
1106
+ for_ = JSProperty("for")
1107
+ form = JSProperty("form")
1108
+ name = JSProperty("name")
1109
+
1110
+
1111
+ class p(TextElementBase):
1112
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/p"""
1113
+
1114
+ tag = "p"
1115
+
1116
+
1117
+ class picture(TextElementBase):
1118
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture"""
1119
+
1120
+ tag = "picture"
1121
+
1122
+
1123
+ class pre(TextElementBase):
1124
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/pre"""
1125
+
1126
+ tag = "pre"
1127
+
1128
+
1129
+ class progress(TextElementBase):
1130
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/progress"""
1131
+
1132
+ tag = "progress"
1133
+
1134
+ max = JSProperty("max")
1135
+ value = JSProperty("value")
1136
+
1137
+
1138
+ class q(TextElementBase):
1139
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/q"""
1140
+
1141
+ tag = "q"
1142
+
1143
+ cite = JSProperty("cite")
1144
+
1145
+
1146
+ class s(TextElementBase):
1147
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/s"""
1148
+
1149
+ tag = "s"
1150
+
1151
+
1152
+ class script(TextElementBase):
1153
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script"""
1154
+
1155
+ tag = "script"
1156
+
1157
+ # Let's add async manually since it's a reserved keyword in Python
1158
+ async_ = JSProperty("async")
1159
+ blocking = JSProperty("blocking")
1160
+ crossorigin = JSProperty("crossorigin")
1161
+ defer = JSProperty("defer")
1162
+ fetchpriority = JSProperty("fetchpriority")
1163
+ integrity = JSProperty("integrity")
1164
+ nomodule = JSProperty("nomodule")
1165
+ nonce = JSProperty("nonce")
1166
+ referrerpolicy = JSProperty("referrerpolicy")
1167
+ src = JSProperty("src")
1168
+ type = JSProperty("type")
1169
+
1170
+
1171
+ class section(TextElementBase):
1172
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section"""
1173
+
1174
+ tag = "section"
1175
+
1176
+
1177
+ class select(TextElementBase):
1178
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select"""
1179
+
1180
+ tag = "select"
1181
+
1182
+
1183
+ class small(TextElementBase):
1184
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/small"""
1185
+
1186
+ tag = "small"
1187
+
1188
+
1189
+ class source(TextElementBase):
1190
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/source"""
1191
+
1192
+ tag = "source"
1193
+
1194
+ media = JSProperty("media")
1195
+ sizes = JSProperty("sizes")
1196
+ src = JSProperty("src")
1197
+ srcset = JSProperty("srcset")
1198
+ type = JSProperty("type")
1199
+
1200
+
1201
+ class span(TextElementBase):
1202
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span"""
1203
+
1204
+ tag = "span"
1205
+
1206
+
1207
+ class strong(TextElementBase):
1208
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/strong"""
1209
+
1210
+ tag = "strong"
1211
+
1212
+
1213
+ class style(TextElementBase):
1214
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style"""
1215
+
1216
+ tag = "style"
1217
+
1218
+ blocking = JSProperty("blocking")
1219
+ media = JSProperty("media")
1220
+ nonce = JSProperty("nonce")
1221
+ title = JSProperty("title")
1222
+
1223
+
1224
+ class sub(TextElementBase):
1225
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sub"""
1226
+
1227
+ tag = "sub"
1228
+
1229
+
1230
+ class summary(TextElementBase):
1231
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary"""
1232
+
1233
+ tag = "summary"
1234
+
1235
+
1236
+ class sup(TextElementBase):
1237
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/sup"""
1238
+
1239
+ tag = "sup"
1240
+
1241
+
1242
+ class table(TextElementBase):
1243
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table"""
1244
+
1245
+ tag = "table"
1246
+
1247
+
1248
+ class tbody(TextElementBase):
1249
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tbody"""
1250
+
1251
+ tag = "tbody"
1252
+
1253
+
1254
+ class td(TextElementBase):
1255
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td"""
1256
+
1257
+ tag = "td"
1258
+
1259
+ colspan = JSProperty("colspan")
1260
+ headers = JSProperty("headers")
1261
+ rowspan = JSProperty("rowspan")
1262
+
1263
+
1264
+ class template(TextElementBase):
1265
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template"""
1266
+
1267
+ tag = "template"
1268
+
1269
+ shadowrootmode = JSProperty("shadowrootmode")
1270
+
1271
+
1272
+ class textarea(TextElementBase):
1273
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea"""
1274
+
1275
+ tag = "textarea"
1276
+
1277
+ autocapitalize = JSProperty("autocapitalize")
1278
+ autocomplete = JSProperty("autocomplete")
1279
+ autofocus = JSProperty("autofocus")
1280
+ cols = JSProperty("cols")
1281
+ dirname = JSProperty("dirname")
1282
+ disabled = JSProperty("disabled")
1283
+ form = JSProperty("form")
1284
+ maxlength = JSProperty("maxlength")
1285
+ minlength = JSProperty("minlength")
1286
+ name = JSProperty("name")
1287
+ placeholder = JSProperty("placeholder")
1288
+ readonly = JSProperty("readonly")
1289
+ required = JSProperty("required")
1290
+ rows = JSProperty("rows")
1291
+ spellcheck = JSProperty("spellcheck")
1292
+ wrap = JSProperty("wrap")
1293
+
1294
+
1295
+ class tfoot(TextElementBase):
1296
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tfoot"""
1297
+
1298
+ tag = "tfoot"
1299
+
1300
+
1301
+ class th(TextElementBase):
1302
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/th"""
1303
+
1304
+ tag = "th"
1305
+
1306
+
1307
+ class thead(TextElementBase):
1308
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/thead"""
1309
+
1310
+ tag = "thead"
1311
+
1312
+
1313
+ class time(TextElementBase):
1314
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time"""
1315
+
1316
+ tag = "time"
1317
+
1318
+ datetime = JSProperty("datetime")
1319
+
1320
+
1321
+ class title(TextElementBase):
1322
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/title"""
1323
+
1324
+ tag = "title"
1325
+
1326
+
1327
+ class tr(TextElementBase):
1328
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/tr"""
1329
+
1330
+ tag = "tr"
1331
+
1332
+ abbr = JSProperty("abbr")
1333
+ colspan = JSProperty("colspan")
1334
+ headers = JSProperty("headers")
1335
+ rowspan = JSProperty("rowspan")
1336
+ scope = JSProperty("scope")
1337
+
1338
+
1339
+ class track(TextElementBase):
1340
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/track"""
1341
+
1342
+ tag = "track"
1343
+
1344
+ default = JSProperty("default")
1345
+ kind = JSProperty("kind")
1346
+ label = JSProperty("label")
1347
+ src = JSProperty("src")
1348
+ srclang = JSProperty("srclang")
1349
+
1350
+
1351
+ class u(TextElementBase):
1352
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/u"""
1353
+
1354
+ tag = "u"
1355
+
1356
+
1357
+ class ul(TextElementBase):
1358
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul"""
1359
+
1360
+ tag = "ul"
1361
+
1362
+
1363
+ class var(TextElementBase):
1364
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/var"""
1365
+
1366
+ tag = "var"
1367
+
1368
+
1369
+ class video(TextElementBase):
1370
+ """Ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video"""
1371
+
1372
+ tag = "video"
1373
+
1374
+ autoplay = JSProperty("autoplay")
1375
+ controls = JSProperty("controls")
1376
+ crossorigin = JSProperty("crossorigin")
1377
+ disablepictureinpicture = JSProperty("disablepictureinpicture")
1378
+ disableremoteplayback = JSProperty("disableremoteplayback")
1379
+ height = JSProperty("height")
1380
+ loop = JSProperty("loop")
1381
+ muted = JSProperty("muted")
1382
+ playsinline = JSProperty("playsinline")
1383
+ poster = JSProperty("poster")
1384
+ preload = JSProperty("preload")
1385
+ src = JSProperty("src")
1386
+ width = JSProperty("width")
1387
+
1388
+
1389
+ # Custom Elements
1390
+ class grid(TextElementBase):
1391
+ tag = "div"
1392
+
1393
+ def __init__(self, layout, content=None, gap=None, **kwargs):
1394
+ super().__init__(content, **kwargs)
1395
+ self.style["display"] = "grid"
1396
+ self.style["grid-template-columns"] = layout
1397
+
1398
+ # TODO: This should be a property
1399
+ if not gap is None:
1400
+ self.style["gap"] = gap
1401
+
1402
+
1403
+ class StyleCollection:
1404
+ def __init__(self, collection: "ElementCollection") -> None:
1405
+ self._collection = collection
1406
+
1407
+ def __get__(self, obj, objtype=None):
1408
+ return obj._get_attribute("style")
1409
+
1410
+ def __getitem__(self, key):
1411
+ return self._collection._get_attribute("style")[key]
1412
+
1413
+ def __setitem__(self, key, value):
1414
+ for element in self._collection._elements:
1415
+ element.style[key] = value
1416
+
1417
+ def remove(self, key):
1418
+ for element in self._collection._elements:
1419
+ element.style.remove(key)
1420
+
1421
+
1422
+ class ElementCollection:
1423
+ def __init__(self, elements: [Element]) -> None:
1424
+ self._elements = elements
1425
+ self.style = StyleCollection(self)
1426
+
1427
+ def __getitem__(self, key):
1428
+ # If it's an integer we use it to access the elements in the collection
1429
+ if isinstance(key, int):
1430
+ return self._elements[key]
1431
+ # If it's a slice we use it to support slice operations over the elements
1432
+ # in the collection
1433
+ elif isinstance(key, slice):
1434
+ return ElementCollection(self._elements[key])
1435
+
1436
+ # If it's anything else (basically a string) we use it as a selector
1437
+ # TODO: Write tests!
1438
+ elements = self._element.querySelectorAll(key)
1439
+ return ElementCollection([Element(el) for el in elements])
1440
+
1441
+ def __len__(self):
1442
+ return len(self._elements)
1443
+
1444
+ def __eq__(self, obj):
1445
+ """Check if the element is the same as the other element by comparing
1446
+ the underlying JS element"""
1447
+ return isinstance(obj, ElementCollection) and obj._elements == self._elements
1448
+
1449
+ def _get_attribute(self, attr, index=None):
1450
+ if index is None:
1451
+ return [getattr(el, attr) for el in self._elements]
1452
+
1453
+ # As JQuery, when getting an attr, only return it for the first element
1454
+ return getattr(self._elements[index], attr)
1455
+
1456
+ def _set_attribute(self, attr, value):
1457
+ for el in self._elements:
1458
+ setattr(el, attr, value)
1459
+
1460
+ @property
1461
+ def html(self):
1462
+ return self._get_attribute("html")
1463
+
1464
+ @html.setter
1465
+ def html(self, value):
1466
+ self._set_attribute("html", value)
1467
+
1468
+ @property
1469
+ def value(self):
1470
+ return self._get_attribute("value")
1471
+
1472
+ @value.setter
1473
+ def value(self, value):
1474
+ self._set_attribute("value", value)
1475
+
1476
+ @property
1477
+ def children(self):
1478
+ return self._elements
1479
+
1480
+ def __iter__(self):
1481
+ yield from self._elements
1482
+
1483
+ def __repr__(self):
1484
+ return f"{self.__class__.__name__} (length: {len(self._elements)}) {self._elements}"