├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── __init__.py
├── core.py
├── ext.py
├── ignite.py
├── svg.py
└── utils.py
/.gitignore:
--------------------------------------------------------------------------------
1 | __target__
2 | __pycache__
3 | *.pyc
4 | .idea
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | This file documents any relevant changes done to ViUR html5 since version 2.
4 |
5 | ## 3.0.0 [develop]
6 |
7 | - Feature: Changed LGPLv3 licensing terms into MIT
8 | - Feature: Ported framework to Python 3.7 using [Pyodide](https://github.com/iodide-project/pyodide), with a full source code and library cleanup
9 | - Feature: `html5.Widget.__init__()` now allows for parameters equal to `Widget.appendChild()` to directly stack widgets together.
10 | Additionally, the following parameters can be used:
11 | - `appendTo`: Directly append the newly created widget to another widget.
12 | - `style`: Provide class attributes for styling added to the new Widget, using `Widget.addClass()`.
13 | - Feature: `html5.Widget.appendChild()` and `html5.Widget.prependChild()` can handle arbitrary input now, including HTML, lists of widgets or just text, in any order. `html5.Widget.insertChild()` runs slightly different, but shares same features. This change mostly supersedes `html5.Widget.fromHTML()`.
14 | - Feature: New `replace`-parameter for `html5.Widget.appendChild()` and `html5.Widget.prependChild()` which clears the content.
15 | - Feature: `html5.ext.InputDialog` refactored & disables OK-Button when no value is present.
16 | - Feature: `html5.utils.doesEventHitWidgetOrChildren()` and `html5.utils.doesEventHitWidgetOrParent()` now return the Widget or None instead of a boolean, to avoid creating loops and directly work with the recognized Widget.
17 | - Feature: New function `html5.Widget.onBind()` enables widgets to react when bound to other widgets using the HTML parser.
18 | - Feature: Replace HTML-parsing-related `vars`-parameter generally by `**kwargs`, with backward-compatibility.
19 | - Feature: Splitting SVG-Widgets into separate module.
20 | - Feature: Replaced `_isVoid`-class by class-attribute `Widget._leafTag` for easier leaf-widget-/tag configuration outside of html5.
21 | - Feature: HTML-parser improvements
22 | - Direct attribute assignments: Store attributes which are not HTML-element attributes directly, e.g. `
` becomes `self.kind = "internal"` on the created `html5.Div()` instance
23 | - ':'-notation on attributes to transfer objects from binder to its children
24 | - Feature: Unified usage of "hidden" and "disabled" attributes on Widgets
25 | - Speed-improvement: Hold static `_WidgetClassWrapper` per `html5.Widget` instead of creating one each time on the fly.
26 |
27 | ## [2.5.1] Vesuv
28 |
29 | - Feature: Allow to bind multiple [name] attributes to one widget in HTML parser
30 | - Feature: `html5.utils.textToHtml()` extended to `clear`-parameter
31 |
32 | ## [2.5.0] Vesuv
33 |
34 | Release date: Jul 26, 2019
35 |
36 | - Bugfix: `Widget.Th()` now supporting full col-/rowspan getting and setting.
37 | - Bugfix: HTML-parser accepts tags in upper-/camel-case order now.
38 | - Bugfix: HTML-parser handles table tags with tbody/thead tags inside more gracefully.
39 | - Feature: Split HTML-parser into separate stages to compile and run; This allows to pre-compile HTML into a list/dict-structure and render it later on without parsing it again. `parseHTML()` is the new function, `fromHTML()` works like before and handles pre-compiled or raw HTML as parameter.
40 | - Feature: `fromHTML()` extended to `vars` parameter to replace key-value pairs in text-nodes and attribute values expressed as `{{key}}`.
41 | - Feature: HTML-parser dynamically reconizes void elements
42 | - Feature: `html5.registerTag()` can be used to define new or override existing HTML elements in the HTML parser by custom implementations based on `html5.Widget()`
43 | - Feature: New function `Widget.isVisible()` as counterpart for `Widget.isHidden()`.
44 |
45 | ## [2.4.0] Agung
46 |
47 | Release date: May 17, 2019
48 |
49 | - Bugfix: Fixed bug with disabling of input widgets.
50 | - Feature: Fully refactored the librarys source base into just two single files, to reduce number of required files to download and make the library easier to access.
51 | - Feature: New function `Widget.isHidden()` to check if a widget is currently shown.
52 | - Feature: Improved handling of key-events.
53 | - Feature: Allow to close popups by pressing `ESC`.
54 | - Feature: Improvements for SVG and TextNode.
55 |
56 | ## [2.3.0] Kilauea
57 |
58 | Release date: Oct 2, 2018
59 |
60 | - Refactored `html5.ext.SelectDialog`
61 | - Extended html parser to apply data-attributes
62 | - Switching event handling to newer JavaScript event listener API
63 | - Added `onFocusIn` and `onFocusOut` events
64 |
65 | ## [2.2.0] Etna
66 |
67 | Release date: Apr 23, 2018
68 |
69 | - Implemented `html5.Head()` to access the document's head object within the library.
70 | - Directly append text in construction of Li().
71 |
72 | ## [2.1.0]
73 |
74 | Release date: Nov 2, 2017
75 |
76 | - Introduced a build-in HTML parser (`Widget.fromHTML()`) that is capable to compile HTML-code into DOM-objects of the html5 library, and an extra-feature to bind them to their root node for further access. This attempt makes it possible to create PyJS apps using the HTML5 library without creating every single element by hand.
77 | - A more distinct way for `Widget.hide()` and `Widget.show()` that cannot be overridden by styling. (setting "hidden" does not work when another display value is set).
78 | - Utility functions `Widget.enable() and `Widget.disable()`.
79 | - Directly append text in construction of Div() and Span().
80 | - Allow for tuple and list processing in table cell assignments.
81 | - Adding `utils.parseFloat()` and `utils.parseInt()` utility functions.
82 | - Implemented `colspan` attribute for Th()
83 | - New README.md and CHANGELOG.md.
84 |
85 | ## 2.0
86 |
87 | Release date: Dec 22, 2016
88 |
89 | - v[2.0.1]: Directly append text in construction of Option().
90 | - v[2.0.1]: Anything added to Widget.appendChild() or Widget.prependChild() which is not a widget is handled as text (TextNode() is automatically created).
91 | - New functions `Widget.prependChild()`, `Widget.insertBefore()`, `Widget.children()`, `Widget.removeAllChildren()`,
92 | `Widget.addClass()`, `Widget.removeClass()`, `Widget.toggleClass()`
93 | - Utility functions `utils.doesEventHitWidgetOrParents()`, `utils.doesEventHitWidgetOrChildren()` taken from vi77
94 | - Insert text blocks easier with `utils.textToHtml()`
95 | - Several bugfixes
96 |
97 | [develop]: https://github.com/viur-framework/html5/compare/v2.5.1...develop
98 | [2.5.1]: https://github.com/viur-framework/html5/compare/v2.5.0...v2.5.1
99 | [2.5.0]: https://github.com/viur-framework/html5/compare/v2.4.0...v2.5.0
100 | [2.4.0]: https://github.com/viur-framework/html5/compare/v2.3.0...v2.4.0
101 | [2.3.0]: https://github.com/viur-framework/html5/compare/v2.2.0...v2.3.0
102 | [2.2.0]: https://github.com/viur-framework/html5/compare/v2.1.0...v2.2.0
103 | [2.1.0]: https://github.com/viur-framework/html5/compare/v2.0.0...v2.1.0
104 | [2.0.1]: https://github.com/viur-framework/html5/compare/v2.0.0...v2.0.1
105 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) 2014-2020 by Mausbrand Informationssysteme GmbH
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ViUR html5
2 |
3 | **html5** is a DOM-abstraction layer and API that is used to create client-side Web-Apps running in the browser and written in Python.
4 |
5 | ## About
6 |
7 | This API and framework is used to implement HTML5 web-apps using the Python programming language. The framework is an abstraction layer for a DOM running in [Pyodide](https://github.com/iodide-project/pyodide), a Python 3 interpreter compiled to web-assembly.
8 |
9 | It provides
10 |
11 | - class abstraction for all HTML5-DOM-elements, e.g. `html5.Div()`
12 | - a built-in HTML parser and executor to generate DOM objects from HTML-code
13 | - helpers for adding/removing classes, arrange children, handling events etc.
14 |
15 | The most prominent software completely established on this library is [ViUR-vi](https://github.com/viur-framework/viur-vi/), the visual administration interface for ViUR-based applications.
16 |
17 | Look [here](https://www.viur.dev/blog/html5-library) for a short introduction about features and usage.
18 |
19 | [flare](https://github.com/mausbrand/flare) is now available, which supersedes this library and provides a self-contained version of its core components, but with the aspect to provide a full web-app development framework.
20 | Both libraries, flare & html5, will be held synchronous, except the dialogs and ignite-related stuff.
21 |
22 | ## Contributing
23 |
24 | We take great interest in your opinion about ViUR. We appreciate your feedback and are looking forward to hear about your ideas. Share your vision or questions with us and participate in ongoing discussions.
25 |
26 | - [ViUR website](https://www.viur.dev)
27 | - [#ViUR on freenode IRC](https://webchat.freenode.net/?channels=viur)
28 | - [ViUR on GitHub](https://github.com/viur-framework)
29 | - [ViUR on Twitter](https://twitter.com/weloveViUR)
30 |
31 | ## Credits
32 |
33 | ViUR is developed and maintained by [Mausbrand Informationssysteme GmbH](https://www.mausbrand.de/en), from Dortmund in Germany. We are a software company consisting of young, enthusiastic software developers, designers and social media experts, working on exciting projects for different kinds of customers. All of our newer projects are implemented with ViUR, from tiny web-pages to huge company intranets with hundreds of users.
34 |
35 | Help of any kind to extend and improve or enhance this project in any kind or way is always appreciated.
36 |
37 | ## License
38 |
39 | Copyright (C) 2012-2020 by Mausbrand Informationssysteme GmbH.
40 |
41 | Mausbrand and ViUR are registered trademarks of Mausbrand Informationssysteme GmbH.
42 |
43 | You may use, modify and distribute this software under the terms and conditions of the MIT license. See the file LICENSE provided within this package for more information.
44 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
1 | #-*- coding: utf-8 -*-
2 |
3 | from .core import *
4 | from . import ext, ignite, svg, utils
5 |
--------------------------------------------------------------------------------
/core.py:
--------------------------------------------------------------------------------
1 | import logging, string
2 |
3 | ########################################################################################################################
4 | # DOM-access functions and variables
5 | ########################################################################################################################
6 |
7 | try:
8 | # Pyodide
9 | from js import window, eval as jseval
10 | document = window.document
11 |
12 | except:
13 | print("Emulation mode")
14 | from xml.dom.minidom import parseString
15 |
16 | jseval = None
17 | window = None
18 | document = parseString("
" + str(txt), "text/html")
2667 | return dom.body.textContent
2668 |
2669 | def scanWhite(l):
2670 | """
2671 | Scan and return whitespace.
2672 | """
2673 |
2674 | ret = ""
2675 | while l and l[0] in " \t\r\n":
2676 | ret += l.pop(0)
2677 |
2678 | return ret
2679 |
2680 | def scanWord(l):
2681 | """
2682 | Scan and return a word.
2683 | """
2684 |
2685 | ret = ""
2686 | while l and l[0] not in " \t\r\n" + "<>=\"'":
2687 | ret += l.pop(0)
2688 |
2689 | return ret
2690 |
2691 | stack = []
2692 |
2693 | # Obtain tag descriptions, if not already done!
2694 | global __tags
2695 |
2696 | if __tags is None:
2697 | _buildTags(debug=debug)
2698 |
2699 | # Prepare stack and input
2700 | stack.append((None, None, HtmlAst()))
2701 | html = [ch for ch in html]
2702 |
2703 | # Parse
2704 | while html:
2705 | tag = None
2706 | text = ""
2707 |
2708 | # Auto-close leaf elements, e.g. like , , etc.
2709 | while stack and stack[-1][0] and __tags[stack[-1][0]][0]._leafTag:
2710 | stack.pop()
2711 |
2712 | if not stack:
2713 | break
2714 |
2715 | parent = stack[-1][2]
2716 |
2717 | while html:
2718 | ch = html.pop(0)
2719 |
2720 | # Comment
2721 | if html and ch == "<" and "".join(html[:3]) == "!--":
2722 | html = html[3:]
2723 | while html and "".join(html[:3]) != "-->":
2724 | html.pop(0)
2725 |
2726 | html = html[3:]
2727 |
2728 | # Opening tag
2729 | elif html and ch == "<" and html[0] != "/":
2730 | tag = scanWord(html)
2731 | if tag.lower() in __tags:
2732 | break
2733 |
2734 | text += ch + tag
2735 |
2736 | # Closing tag
2737 | elif html and stack[-1][0] and ch == "<" and html[0] == "/":
2738 | junk = ch
2739 | junk += html.pop(0)
2740 |
2741 | tag = scanWord(html)
2742 | junk += tag
2743 |
2744 | if stack[-1][0] == tag.lower():
2745 | junk += scanWhite(html)
2746 | if html and html[0] == ">":
2747 | html.pop(0)
2748 | stack.pop()
2749 | tag = None
2750 | break
2751 |
2752 | text += junk
2753 | tag = None
2754 |
2755 | else:
2756 | text += ch
2757 |
2758 | # Append plain text (if not only whitespace)
2759 | if (text and ((len(text) == 1 and text in ["\t "])
2760 | or not all([ch in " \t\r\n" for ch in text]))):
2761 | # print("text", text)
2762 | parent.append(convertEncodedText(text))
2763 |
2764 | # Create tag
2765 | if tag:
2766 | tag = tag.lower()
2767 | # print("tag", tag)
2768 |
2769 | elem = (tag, {}, HtmlAst())
2770 |
2771 | stack.append(elem)
2772 | parent.append(elem)
2773 |
2774 | while html:
2775 | scanWhite(html)
2776 | if not html:
2777 | break
2778 |
2779 | # End of tag >
2780 | if html[0] == ">":
2781 | html.pop(0)
2782 | break
2783 |
2784 | # Closing tag at end />
2785 | elif html[0] == "/":
2786 | html.pop(0)
2787 | scanWhite(html)
2788 |
2789 | if html[0] == ">":
2790 | stack.pop()
2791 | html.pop(0)
2792 | break
2793 |
2794 | val = att = scanWord(html).lower()
2795 |
2796 | if not att:
2797 | html.pop(0)
2798 | continue
2799 |
2800 | scanWhite(html)
2801 | if html[0] == "=":
2802 | html.pop(0)
2803 | scanWhite(html)
2804 |
2805 | if html[0] in "\"'":
2806 | ch = html.pop(0)
2807 |
2808 | val = ""
2809 | while html and html[0] != ch:
2810 | val += html.pop(0)
2811 |
2812 | html.pop(0)
2813 |
2814 | if att not in elem[1]:
2815 | elem[1][att] = val
2816 | else:
2817 | elem[1][att] += " " + val
2818 |
2819 | continue
2820 |
2821 | while stack and stack[-1][0]:
2822 | stack.pop()
2823 |
2824 | return stack[0][2]
2825 |
2826 | def fromHTML(html, appendTo=None, bindTo=None, debug=False, vars=None, **kwargs):
2827 | """
2828 | Parses the provided HTML code according to the objects defined in the html5-library.
2829 | html can also be pre-compiled by `parseHTML()` so that it executes faster.
2830 |
2831 | Constructs all objects as DOM nodes. The first level is chained into appendTo.
2832 | If no appendTo is provided, appendTo will be set to html5.Body().
2833 |
2834 | If bindTo is provided, objects are bound to this widget.
2835 |
2836 | ```python
2837 | from vi import html5
2838 |
2839 | div = html5.Div()
2840 | html5.parse.fromHTML('''
2841 |