cherrypy-foundation 1.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.
- cherrypy_foundation/__init__.py +0 -0
- cherrypy_foundation/components/ColorModes.jinja +70 -0
- cherrypy_foundation/components/Datatable.css +47 -0
- cherrypy_foundation/components/Datatable.jinja +63 -0
- cherrypy_foundation/components/Datatable.js +358 -0
- cherrypy_foundation/components/Field.css +10 -0
- cherrypy_foundation/components/Field.jinja +66 -0
- cherrypy_foundation/components/Field.js +56 -0
- cherrypy_foundation/components/Fields.jinja +4 -0
- cherrypy_foundation/components/Flash.jinja +13 -0
- cherrypy_foundation/components/Icon.jinja +3 -0
- cherrypy_foundation/components/LocaleSelection.jinja +13 -0
- cherrypy_foundation/components/LocaleSelection.js +26 -0
- cherrypy_foundation/components/SideBySideMultiSelect.css +25 -0
- cherrypy_foundation/components/SideBySideMultiSelect.jinja +9 -0
- cherrypy_foundation/components/SideBySideMultiSelect.js +9 -0
- cherrypy_foundation/components/Typeahead.css +55 -0
- cherrypy_foundation/components/Typeahead.jinja +106 -0
- cherrypy_foundation/components/Typeahead.js +8 -0
- cherrypy_foundation/components/__init__.py +51 -0
- cherrypy_foundation/components/tests/__init__.py +0 -0
- cherrypy_foundation/components/tests/test_static.py +90 -0
- cherrypy_foundation/components/vendor/bootstrap-icons/bootstrap-icons.css +2106 -0
- cherrypy_foundation/components/vendor/bootstrap-icons/bootstrap-icons.min.css +5 -0
- cherrypy_foundation/components/vendor/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
- cherrypy_foundation/components/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
- cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.css +9262 -0
- cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.css.map +95 -0
- cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.min.css +6 -0
- cherrypy_foundation/components/vendor/bootstrap5/css/bootstrap.min.css.map +7 -0
- cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.js +4846 -0
- cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.js.map +1 -0
- cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.min.js +7 -0
- cherrypy_foundation/components/vendor/bootstrap5/js/bootstrap.min.js.map +7 -0
- cherrypy_foundation/components/vendor/bootstrap5/js/color-modes.js +80 -0
- cherrypy_foundation/components/vendor/datatables/css/dataTables.dataTables.css +849 -0
- cherrypy_foundation/components/vendor/datatables/css/dataTables.dataTables.min.css +1 -0
- cherrypy_foundation/components/vendor/datatables/images/sort_asc.png +0 -0
- cherrypy_foundation/components/vendor/datatables/images/sort_asc_disabled.png +0 -0
- cherrypy_foundation/components/vendor/datatables/images/sort_both.png +0 -0
- cherrypy_foundation/components/vendor/datatables/images/sort_desc.png +0 -0
- cherrypy_foundation/components/vendor/datatables/images/sort_desc_disabled.png +0 -0
- cherrypy_foundation/components/vendor/datatables/js/dataTables.js +14073 -0
- cherrypy_foundation/components/vendor/datatables/js/dataTables.min.js +4 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Buttons/css/buttons.dataTables.css +556 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Buttons/css/buttons.dataTables.min.css +1 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/buttons.html5.js +1700 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/buttons.html5.min.js +8 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/dataTables.buttons.js +2944 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Buttons/js/dataTables.buttons.min.js +4 -0
- cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/css/fixedHeader.dataTables.css +13 -0
- cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/css/fixedHeader.dataTables.min.css +1 -0
- cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/js/dataTables.fixedHeader.js +1202 -0
- cherrypy_foundation/components/vendor/datatables-extensions/FixedHeader/js/dataTables.fixedHeader.min.js +4 -0
- cherrypy_foundation/components/vendor/datatables-extensions/JSZip/jszip.js +11577 -0
- cherrypy_foundation/components/vendor/datatables-extensions/JSZip/jszip.min.js +13 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Responsive/css/responsive.dataTables.css +194 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Responsive/css/responsive.dataTables.min.css +1 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Responsive/js/dataTables.responsive.js +1861 -0
- cherrypy_foundation/components/vendor/datatables-extensions/Responsive/js/dataTables.responsive.min.js +4 -0
- cherrypy_foundation/components/vendor/datatables-extensions/pdfmake/build/pdfmake.js +75023 -0
- cherrypy_foundation/components/vendor/datatables-extensions/pdfmake/build/pdfmake.min.js +3 -0
- cherrypy_foundation/components/vendor/datatables-extensions/pdfmake/build/vfs_fonts.js +6 -0
- cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/css/rowGroup.dataTables.css +53 -0
- cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/css/rowGroup.dataTables.min.css +1 -0
- cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/js/dataTables.rowGroup.js +485 -0
- cherrypy_foundation/components/vendor/datatables-extensions/rowgroup/js/dataTables.rowGroup.min.js +4 -0
- cherrypy_foundation/components/vendor/jquery/jquery.min.js +2 -0
- cherrypy_foundation/components/vendor/multi/LICENSE +7 -0
- cherrypy_foundation/components/vendor/multi/README.md +109 -0
- cherrypy_foundation/components/vendor/multi/multi.css +95 -0
- cherrypy_foundation/components/vendor/multi/multi.js +328 -0
- cherrypy_foundation/components/vendor/popper/popper.js +1825 -0
- cherrypy_foundation/components/vendor/popper/popper.min.js +6 -0
- cherrypy_foundation/components/vendor/typeahead/jquery.typeahead.min.css +1 -0
- cherrypy_foundation/components/vendor/typeahead/jquery.typeahead.min.js +10 -0
- cherrypy_foundation/error_page.py +94 -0
- cherrypy_foundation/flash.py +50 -0
- cherrypy_foundation/form.py +119 -0
- cherrypy_foundation/logging.py +103 -0
- cherrypy_foundation/passwd.py +65 -0
- cherrypy_foundation/plugins/__init__.py +0 -0
- cherrypy_foundation/plugins/db.py +286 -0
- cherrypy_foundation/plugins/ldap.py +257 -0
- cherrypy_foundation/plugins/restapi.py +74 -0
- cherrypy_foundation/plugins/scheduler.py +287 -0
- cherrypy_foundation/plugins/smtp.py +223 -0
- cherrypy_foundation/plugins/tests/__init__.py +0 -0
- cherrypy_foundation/plugins/tests/test_db.py +118 -0
- cherrypy_foundation/plugins/tests/test_ldap.py +451 -0
- cherrypy_foundation/plugins/tests/test_scheduler.py +100 -0
- cherrypy_foundation/plugins/tests/test_scheduler_db.py +107 -0
- cherrypy_foundation/plugins/tests/test_smtp.py +140 -0
- cherrypy_foundation/sessions.py +93 -0
- cherrypy_foundation/tests/__init__.py +72 -0
- cherrypy_foundation/tests/templates/test_flash.html +9 -0
- cherrypy_foundation/tests/templates/test_form.html +16 -0
- cherrypy_foundation/tests/templates/test_url.html +15 -0
- cherrypy_foundation/tests/test_error_page.py +78 -0
- cherrypy_foundation/tests/test_flash.py +61 -0
- cherrypy_foundation/tests/test_form.py +148 -0
- cherrypy_foundation/tests/test_logging.py +78 -0
- cherrypy_foundation/tests/test_passwd.py +51 -0
- cherrypy_foundation/tests/test_sessions.py +89 -0
- cherrypy_foundation/tests/test_url.py +161 -0
- cherrypy_foundation/tools/__init__.py +0 -0
- cherrypy_foundation/tools/auth.py +263 -0
- cherrypy_foundation/tools/auth_mfa.py +249 -0
- cherrypy_foundation/tools/i18n.py +529 -0
- cherrypy_foundation/tools/jinja2.py +158 -0
- cherrypy_foundation/tools/ratelimit.py +265 -0
- cherrypy_foundation/tools/secure_headers.py +119 -0
- cherrypy_foundation/tools/sessions_timeout.py +167 -0
- cherrypy_foundation/tools/tests/__init__.py +0 -0
- cherrypy_foundation/tools/tests/components/Button.jinja +2 -0
- cherrypy_foundation/tools/tests/locales/de/LC_MESSAGES/messages.mo +0 -0
- cherrypy_foundation/tools/tests/locales/de/LC_MESSAGES/messages.po +15 -0
- cherrypy_foundation/tools/tests/locales/fr/LC_MESSAGES/messages.mo +0 -0
- cherrypy_foundation/tools/tests/locales/fr/LC_MESSAGES/messages.po +15 -0
- cherrypy_foundation/tools/tests/locales/messages.pot +2 -0
- cherrypy_foundation/tools/tests/templates/test_jinja2.html +11 -0
- cherrypy_foundation/tools/tests/templates/test_jinjax.html +9 -0
- cherrypy_foundation/tools/tests/templates/test_jinjax_i18n.html +22 -0
- cherrypy_foundation/tools/tests/test_auth.py +110 -0
- cherrypy_foundation/tools/tests/test_auth_mfa.py +369 -0
- cherrypy_foundation/tools/tests/test_i18n.py +247 -0
- cherrypy_foundation/tools/tests/test_jinja2.py +153 -0
- cherrypy_foundation/tools/tests/test_ratelimit.py +109 -0
- cherrypy_foundation/tools/tests/test_secure_headers.py +200 -0
- cherrypy_foundation/url.py +66 -0
- cherrypy_foundation/widgets.py +48 -0
- cherrypy_foundation-1.0.0.dist-info/METADATA +71 -0
- cherrypy_foundation-1.0.0.dist-info/RECORD +136 -0
- cherrypy_foundation-1.0.0.dist-info/WHEEL +5 -0
- cherrypy_foundation-1.0.0.dist-info/licenses/LICENSE.md +674 -0
- cherrypy_foundation-1.0.0.dist-info/top_level.txt +1 -0
|
File without changes
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{# def header="Toggle theme", light_label="Light", dark_label="Dark", auto_label="Auto" #}
|
|
2
|
+
{#css vendor/bootstrap5/css/bootstrap.min.css #}
|
|
3
|
+
{#js vendor/popper/popper.min.js, vendor/bootstrap5/js/bootstrap.min.js, vendor/bootstrap5/js/color-modes.js #}
|
|
4
|
+
<svg class="d-none" xmlns="http://www.w3.org/2000/svg">
|
|
5
|
+
<symbol id="check2" viewbox="0 0 16 16">
|
|
6
|
+
<path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z">
|
|
7
|
+
</path>
|
|
8
|
+
</symbol>
|
|
9
|
+
<symbol id="circle-half" viewbox="0 0 16 16">
|
|
10
|
+
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"></path>
|
|
11
|
+
</symbol>
|
|
12
|
+
<symbol id="moon-stars-fill" viewbox="0 0 16 16">
|
|
13
|
+
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z">
|
|
14
|
+
</path>
|
|
15
|
+
<path d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z">
|
|
16
|
+
</path>
|
|
17
|
+
</symbol>
|
|
18
|
+
<symbol id="sun-fill" viewbox="0 0 16 16">
|
|
19
|
+
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z">
|
|
20
|
+
</path>
|
|
21
|
+
</symbol>
|
|
22
|
+
</svg>
|
|
23
|
+
<li>
|
|
24
|
+
<span id="bd-theme" class="dropdown-item disabled">
|
|
25
|
+
<span id="bd-theme-text">{{ header }}</span>
|
|
26
|
+
<svg aria-hidden="true"
|
|
27
|
+
class="theme-icon-active visually-hidden"
|
|
28
|
+
width="16"
|
|
29
|
+
height="16">
|
|
30
|
+
<use href="#circle-half">
|
|
31
|
+
</use>
|
|
32
|
+
</svg>
|
|
33
|
+
</span>
|
|
34
|
+
</li>
|
|
35
|
+
<li>
|
|
36
|
+
<button aria-pressed="false"
|
|
37
|
+
class="dropdown-item"
|
|
38
|
+
data-bs-theme-value="light"
|
|
39
|
+
type="button">
|
|
40
|
+
<svg aria-hidden="true" class="me-2" width="16" height="16">
|
|
41
|
+
<use href="#sun-fill">
|
|
42
|
+
</use>
|
|
43
|
+
</svg>
|
|
44
|
+
{{ light_label }}
|
|
45
|
+
</button>
|
|
46
|
+
</li>
|
|
47
|
+
<li>
|
|
48
|
+
<button aria-pressed="false"
|
|
49
|
+
class="dropdown-item"
|
|
50
|
+
data-bs-theme-value="dark"
|
|
51
|
+
type="button">
|
|
52
|
+
<svg aria-hidden="true" class="me-2" width="16" height="16">
|
|
53
|
+
<use href="#moon-stars-fill">
|
|
54
|
+
</use>
|
|
55
|
+
</svg>
|
|
56
|
+
{{ dark_label }}
|
|
57
|
+
</button>
|
|
58
|
+
</li>
|
|
59
|
+
<li>
|
|
60
|
+
<button aria-pressed="true"
|
|
61
|
+
class="dropdown-item"
|
|
62
|
+
data-bs-theme-value="auto"
|
|
63
|
+
type="button">
|
|
64
|
+
<svg aria-hidden="true" class="me-2 " width="16" height="16">
|
|
65
|
+
<use href="#circle-half">
|
|
66
|
+
</use>
|
|
67
|
+
</svg>
|
|
68
|
+
{{ auto_label }}
|
|
69
|
+
</button>
|
|
70
|
+
</li>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/* Remove default margin from layout */
|
|
2
|
+
div.dt-container div.dt-layout-row {
|
|
3
|
+
margin: 0;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/* Remove default seperator */
|
|
7
|
+
div.dt-container.dt-empty-footer .dt-scroll-body {
|
|
8
|
+
border-bottom: 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* Show hide Action button */
|
|
12
|
+
.dt-container tr .btn-hover {
|
|
13
|
+
opacity: 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.dt-container tr:hover .btn-hover {
|
|
17
|
+
opacity: 1;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* Hide responsive column "none" during loading.
|
|
22
|
+
*/
|
|
23
|
+
.dt-container th.none {
|
|
24
|
+
display: none;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Fix background color of button collection in DarkMode */
|
|
28
|
+
div.dt-button-collection {
|
|
29
|
+
background-color: var(--bs-body-bg);
|
|
30
|
+
color: var(--bs-body-color);
|
|
31
|
+
border-color: var(--bs-border-color-translucent);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* Css for responsive details render tableHidden */
|
|
35
|
+
table.dtr-details .dtr-title-cell {
|
|
36
|
+
width: 15%;
|
|
37
|
+
white-space: nowrap;
|
|
38
|
+
vertical-align: top;
|
|
39
|
+
padding: 8px 10px;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
table.dtr-details .dtr-data-cell {
|
|
43
|
+
width: 85%;
|
|
44
|
+
white-space: normal;
|
|
45
|
+
word-break: break-word;
|
|
46
|
+
padding: 8px 10px;
|
|
47
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{# def data, columns=[], order=[], empty_message=None, info_message=None, searching=True, buttons=[], paging=True, page_length=25, length_change=True, buttons_cfg={}, layout={}, server_side=False, state_save=True, auto_width=False, responsive=False, rowgroup=False, fixed_header=False, scroll_y=False #}
|
|
2
|
+
{#css vendor/bootstrap5/css/bootstrap.min.css, vendor/datatables/css/dataTables.dataTables.css, vendor/datatables-extensions/Buttons/css/buttons.dataTables.min.css, vendor/datatables-extensions/FixedHeader/css/fixedHeader.dataTables.css, vendor/datatables-extensions/Responsive/css/responsive.dataTables.min.css, vendor/datatables-extensions/rowgroup/css/rowGroup.dataTables.min.css #}
|
|
3
|
+
{#js vendor/jquery/jquery.min.js, vendor/popper/popper.min.js, vendor/bootstrap5/js/bootstrap.min.js, vendor/datatables-extensions/JSZip/jszip.min.js, vendor/datatables-extensions/pdfmake/build/pdfmake.min.js, vendor/datatables-extensions/pdfmake/build/vfs_fonts.js, vendor/datatables/js/dataTables.min.js, vendor/datatables-extensions/Responsive/js/dataTables.responsive.min.js, vendor/datatables-extensions/Buttons/js/dataTables.buttons.min.js, vendor/datatables-extensions/Buttons/js/buttons.html5.min.js, vendor/datatables-extensions/FixedHeader/js/dataTables.fixedHeader.min.js, vendor/datatables-extensions/rowgroup/js/dataTables.rowGroup.js #}
|
|
4
|
+
{% set classes = {
|
|
5
|
+
"sPaging": "d-flex justify-content-center",
|
|
6
|
+
"sPageButton": "btn btn-outline-primary ms-1 me-1",
|
|
7
|
+
"sPageButtonActive": "active"
|
|
8
|
+
} %}
|
|
9
|
+
{% set language = {
|
|
10
|
+
"url": url_for('language', _('en')),
|
|
11
|
+
"info": info_message or (_(' from _START_ to _END_ of _TOTAL_ total') if paging else _('Showing total of _TOTAL_')),
|
|
12
|
+
"emptyTable": empty_message or _('List is empty'),
|
|
13
|
+
} %}
|
|
14
|
+
{# Use jinja recursive loop to merge buttons config. #}
|
|
15
|
+
{% set buttons_cfg_default = {
|
|
16
|
+
"dom": {
|
|
17
|
+
"button": {
|
|
18
|
+
"className": "btn btn-sm ms-1 mb-2 mb-sm-0",
|
|
19
|
+
"active": "active"
|
|
20
|
+
},
|
|
21
|
+
"collection": {
|
|
22
|
+
"tag": "div",
|
|
23
|
+
"button": {
|
|
24
|
+
"tag": "a",
|
|
25
|
+
"className": "btn btn-sm btn-link mb-2 mb-sm-0 w-100 text-start",
|
|
26
|
+
"active": "active",
|
|
27
|
+
"disabled": "disabled"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
} %}
|
|
32
|
+
{% set ns = namespace(buttons_cfg = dict(buttons_cfg_default), stack_value=[]) %}
|
|
33
|
+
{% set ns.stack_value = [ns.buttons_cfg] %}
|
|
34
|
+
{% for key, value in buttons_cfg.items() recursive %}
|
|
35
|
+
{% if value is mapping %}
|
|
36
|
+
{% do ns.stack_value.append( ns.stack_value[-1].get(key, {}) ) %}
|
|
37
|
+
{{ loop(value.items() ) }}
|
|
38
|
+
{% set value = ns.stack_value.pop() %}
|
|
39
|
+
{% endif %}
|
|
40
|
+
{% do ns.stack_value[-1].__setitem__(key, value) %}
|
|
41
|
+
{% endfor %}
|
|
42
|
+
{% do ns.buttons_cfg.__setitem__('buttons', buttons) %}
|
|
43
|
+
<table {{ attrs.render(class="table table-hover",
|
|
44
|
+
width="100%",
|
|
45
|
+
data_ajax=data,
|
|
46
|
+
data_auto_width=auto_width | tojson,
|
|
47
|
+
data_buttons=ns.buttons_cfg | tojson,
|
|
48
|
+
data_classes=classes | tojson,
|
|
49
|
+
data_columns=columns | tojson,
|
|
50
|
+
data_layout=layout | tojson,
|
|
51
|
+
data_fixed_header=fixed_header | tojson,
|
|
52
|
+
data_language=language | tojson,
|
|
53
|
+
data_length_change=length_change | tojson,
|
|
54
|
+
data_order=order | tojson | e,
|
|
55
|
+
data_page_length=page_length | tojson,
|
|
56
|
+
data_paging=paging | tojson,
|
|
57
|
+
data_responsive=responsive | tojson,
|
|
58
|
+
data_row_group=rowgroup | tojson,
|
|
59
|
+
data_searching=searching | tojson,
|
|
60
|
+
data_server_side=server_side | tojson,
|
|
61
|
+
data_state_save=state_save | tojson,
|
|
62
|
+
data_scroll_y=scroll_y) }}>
|
|
63
|
+
</table>
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cherrypy-foundation
|
|
3
|
+
* Copyright (C) 2026 IKUS Software
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU General Public License as published by
|
|
7
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU General Public License
|
|
16
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/* Throw a JavaScript error. */
|
|
20
|
+
$.fn.dataTable.ext.errMode = 'throw';
|
|
21
|
+
|
|
22
|
+
$.fn.dataTable.rowGroupRender = $.fn.dataTable.rowGroupRender || {};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Buttons to filter content of datatable.
|
|
26
|
+
*
|
|
27
|
+
* Options:
|
|
28
|
+
* - search: Define the search criteria when filter is active
|
|
29
|
+
* - search_off: Define the search criteria when filter is not active (optional)
|
|
30
|
+
* - regex: True to enable regex lookup (optional)
|
|
31
|
+
* - multi: True to enablemultiple selection for the same column.
|
|
32
|
+
*/
|
|
33
|
+
$.fn.dataTable.ext.buttons.filter = {
|
|
34
|
+
init: function(dt, node, config) {
|
|
35
|
+
if (config.search_off && config.multi) {
|
|
36
|
+
console.error('search_off and multi are not supported together');
|
|
37
|
+
}
|
|
38
|
+
const that = this;
|
|
39
|
+
dt.on('search.dt', function() {
|
|
40
|
+
let activate;
|
|
41
|
+
const curSearch = dt.column(config.column).search();
|
|
42
|
+
if (config.multi) {
|
|
43
|
+
const terms = curSearch.replace(/^\(/, '').replace(/\)$/, '').split('|');
|
|
44
|
+
activate = terms.includes(config.search);
|
|
45
|
+
} else {
|
|
46
|
+
activate = dt.column(config.column).search() === config.search;
|
|
47
|
+
}
|
|
48
|
+
that.active(activate);
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
action: function(e, dt, node, config) {
|
|
52
|
+
const curSearch = dt.column(config.column).search();
|
|
53
|
+
let terms = curSearch.replace(/^\(/, '').replace(/\)$/, '').split('|').filter(item => item !== '');
|
|
54
|
+
if (node.hasClass('active')) {
|
|
55
|
+
if (config.search_off) {
|
|
56
|
+
// Disable - replace by our search_off pattern
|
|
57
|
+
terms = [config.search_off];
|
|
58
|
+
} else {
|
|
59
|
+
// Disable - remove from term.
|
|
60
|
+
terms = terms.filter(item => item != config.search)
|
|
61
|
+
}
|
|
62
|
+
} else if (config.multi) {
|
|
63
|
+
// Enable - add new terms
|
|
64
|
+
terms.push(config.search)
|
|
65
|
+
} else {
|
|
66
|
+
// Enable - replace all terms
|
|
67
|
+
terms = [config.search];
|
|
68
|
+
}
|
|
69
|
+
let search;
|
|
70
|
+
if (terms.length == 0) {
|
|
71
|
+
search = '';
|
|
72
|
+
} else if (terms.length == 1) {
|
|
73
|
+
search = terms[0];
|
|
74
|
+
} else {
|
|
75
|
+
search = '(' + terms.join('|') + ')';
|
|
76
|
+
}
|
|
77
|
+
dt.column(config.column).search(search, true);
|
|
78
|
+
dt.draw(true);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
$.fn.dataTable.ext.buttons.btnfilter = {
|
|
82
|
+
extend: 'filter',
|
|
83
|
+
className: 'cdt-btn-filter'
|
|
84
|
+
};
|
|
85
|
+
$.fn.dataTable.ext.buttons.collectionfilter = {
|
|
86
|
+
align: 'button-right',
|
|
87
|
+
autoClose: true,
|
|
88
|
+
background: false,
|
|
89
|
+
extend: 'collection',
|
|
90
|
+
className: 'cdt-btn-collectionfilter',
|
|
91
|
+
init: function(dt, node, config) {
|
|
92
|
+
const that = this;
|
|
93
|
+
dt.on('search.dt', function() {
|
|
94
|
+
const activate = dt.column(config.column).search() !== '';
|
|
95
|
+
that.active(activate);
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Button to reset the filters of datatable.
|
|
101
|
+
* Default settings are restored using init() API.
|
|
102
|
+
*/
|
|
103
|
+
$.fn.dataTable.ext.buttons.reset = {
|
|
104
|
+
text: 'Reset',
|
|
105
|
+
action: function(e, dt, node, config) {
|
|
106
|
+
dt.search('');
|
|
107
|
+
if (dt.init().aoSearchCols) {
|
|
108
|
+
const searchCols = dt.init().aoSearchCols;
|
|
109
|
+
for (let i = 0; i < searchCols.length; i++) {
|
|
110
|
+
const search = searchCols[i].search || "";
|
|
111
|
+
dt.column(i).search(search);
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
dt.columns().search('');
|
|
115
|
+
}
|
|
116
|
+
dt.draw(true);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Default render
|
|
121
|
+
*/
|
|
122
|
+
$.fn.dataTable.render.button = function ({
|
|
123
|
+
label = 'changeme',
|
|
124
|
+
className = 'btn btn-sm btn-primary btn-hover text-nowrap',
|
|
125
|
+
...attrs
|
|
126
|
+
} = {}) {
|
|
127
|
+
const { escapeHtml } = DataTable.util;
|
|
128
|
+
|
|
129
|
+
const attr = (name, value) => {
|
|
130
|
+
if (value == null || value === false) return '';
|
|
131
|
+
if (value === true) return ` ${name}`;
|
|
132
|
+
return ` ${name}="${escapeHtml(String(value))}"`;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
display: function (data, type, row, meta) {
|
|
137
|
+
if (!data) return '';
|
|
138
|
+
|
|
139
|
+
const href = encodeURI(String(data));
|
|
140
|
+
|
|
141
|
+
// If caller supplies `class`, prefer it over className
|
|
142
|
+
const { class: clsFromAttrs, ...rest } = attrs;
|
|
143
|
+
const classValue = clsFromAttrs ?? className;
|
|
144
|
+
|
|
145
|
+
const known = attr('class', classValue);
|
|
146
|
+
|
|
147
|
+
const extra = Object.entries(rest)
|
|
148
|
+
.map(([k, v]) => attr(k, v))
|
|
149
|
+
.join('');
|
|
150
|
+
|
|
151
|
+
return `<a${known}${extra} href="${escapeHtml(href)}">${escapeHtml(label)}</a>`;
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
$.fn.dataTable.render.choices = function(choices) {
|
|
156
|
+
let lookup = null;
|
|
157
|
+
if (Array.isArray(choices)) {
|
|
158
|
+
// Convert array of tuples to a lookup object
|
|
159
|
+
lookup = Object.fromEntries(choices);
|
|
160
|
+
} else if (typeof choices === "object" && choices !== null) {
|
|
161
|
+
// Already a dictionary
|
|
162
|
+
lookup = choices;
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
display: function(data, type, row, meta) {
|
|
166
|
+
return (lookup && data in lookup) ? lookup[data] : data;
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Build a child-row table showing only the hidden columns
|
|
172
|
+
$.fn.dataTable.Responsive.renderer.tableHidden = function (options) {
|
|
173
|
+
options = $.extend(
|
|
174
|
+
{
|
|
175
|
+
tableClass: '',
|
|
176
|
+
empty: '—' // placeholder when a hidden cell is empty
|
|
177
|
+
},
|
|
178
|
+
options
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
return function (api, rowIdx, columns) {
|
|
182
|
+
const data = $.map(columns, function (col) {
|
|
183
|
+
if (!col.hidden) {
|
|
184
|
+
return '';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const klass = col.className
|
|
188
|
+
? 'class="' + col.className + '"'
|
|
189
|
+
: '';
|
|
190
|
+
|
|
191
|
+
const title =
|
|
192
|
+
'' !== col.title
|
|
193
|
+
? col.title + ':'
|
|
194
|
+
: '';
|
|
195
|
+
|
|
196
|
+
// Treat null/undefined/empty-string as empty
|
|
197
|
+
const cell =
|
|
198
|
+
col.data !== null &&
|
|
199
|
+
col.data !== undefined &&
|
|
200
|
+
col.data !== ''
|
|
201
|
+
? col.data
|
|
202
|
+
: options.empty;
|
|
203
|
+
|
|
204
|
+
return (
|
|
205
|
+
'<tr ' +
|
|
206
|
+
klass +
|
|
207
|
+
' data-dt-row="' +
|
|
208
|
+
col.rowIndex +
|
|
209
|
+
'" data-dt-column="' +
|
|
210
|
+
col.columnIndex +
|
|
211
|
+
'">' +
|
|
212
|
+
'<th class="dtr-title-cell">' +
|
|
213
|
+
title +
|
|
214
|
+
'</th> ' +
|
|
215
|
+
'<td class="dtr-data-cell">' +
|
|
216
|
+
cell +
|
|
217
|
+
'</td>' +
|
|
218
|
+
'</tr>'
|
|
219
|
+
);
|
|
220
|
+
}).join('');
|
|
221
|
+
|
|
222
|
+
// If there are no hidden columns, return false so no child row is shown
|
|
223
|
+
return data
|
|
224
|
+
? $(
|
|
225
|
+
'<table class="' +
|
|
226
|
+
options.tableClass +
|
|
227
|
+
' dtr-details" width="100%"/>'
|
|
228
|
+
).append(data)
|
|
229
|
+
: false;
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Resolve one spec (render/startRender/endRender) into a callable
|
|
234
|
+
function resolveRenderSpec(source, key) {
|
|
235
|
+
if (!source || !key || source[key] == null) return;
|
|
236
|
+
|
|
237
|
+
const value = source[key];
|
|
238
|
+
let fn;
|
|
239
|
+
|
|
240
|
+
if (typeof value === 'function') {
|
|
241
|
+
// Case A: already a function — use as-is
|
|
242
|
+
fn = value;
|
|
243
|
+
} else {
|
|
244
|
+
// Case B: value is a string naming a render factory, e.g. 'number', 'text', 'ellipsis', 'myPlugin'
|
|
245
|
+
const factoryName = value;
|
|
246
|
+
let renderNS;
|
|
247
|
+
if(key == 'render') {
|
|
248
|
+
renderNS = $.fn.dataTable?.render;
|
|
249
|
+
} else if(key == 'startRender' || key == 'endRender') {
|
|
250
|
+
renderNS = $.fn.dataTable?.rowGroupRender;
|
|
251
|
+
} else if(key == 'renderer') {
|
|
252
|
+
renderNS = $.fn.dataTable?.Responsive?.renderer;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const factory = renderNS?.[factoryName];
|
|
256
|
+
if (typeof factory !== 'function') {
|
|
257
|
+
console.warn(`DataTables render factory '${factoryName}' not found`);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Support kwargs | args | arg
|
|
262
|
+
if (source[`${key}_kwargs`]) {
|
|
263
|
+
fn = factory({...source[`${key}_kwargs`]});
|
|
264
|
+
} else if (source[`${key}_args`]) {
|
|
265
|
+
fn = factory(...source[`${key}_args`]);
|
|
266
|
+
} else if (Object.hasOwn(source, `${key}_arg`)) {
|
|
267
|
+
fn = factory(source[`${key}_arg`]);
|
|
268
|
+
} else {
|
|
269
|
+
fn = factory();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return fn;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
jQuery(function() {
|
|
277
|
+
$('table[data-ajax]').each(function(_idx) {
|
|
278
|
+
/* Load column properties */
|
|
279
|
+
let columns = $(this).attr('data-columns');
|
|
280
|
+
$(this).removeAttr('data-columns');
|
|
281
|
+
columns = JSON.parse(columns);
|
|
282
|
+
$.each(columns, function(_index, item) {
|
|
283
|
+
item.render = resolveRenderSpec(item, 'render');
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
/* Process rowGroup render */
|
|
287
|
+
let rowGroup = $(this).attr('data-row-group');
|
|
288
|
+
$(this).removeAttr('data-row-group');
|
|
289
|
+
rowGroup = JSON.parse(rowGroup);
|
|
290
|
+
if(rowGroup && typeof rowGroup === 'object') {
|
|
291
|
+
rowGroup.startRender = resolveRenderSpec(rowGroup, 'startRender');
|
|
292
|
+
rowGroup.endRender = resolveRenderSpec(rowGroup, 'endRender');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/* Process responsive details render */
|
|
296
|
+
let responsive = $(this).attr('data-responsive');
|
|
297
|
+
$(this).removeAttr('data-responsive');
|
|
298
|
+
responsive = JSON.parse(responsive);
|
|
299
|
+
if(responsive && typeof responsive === 'object') {
|
|
300
|
+
responsive.renderer = resolveRenderSpec(responsive, 'renderer');
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
let searchCols = columns.map(function(item, _index) {
|
|
304
|
+
if (item.search !== undefined) {
|
|
305
|
+
return {
|
|
306
|
+
"search": item.search,
|
|
307
|
+
"regex": item.regex || false
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
return null;
|
|
311
|
+
});
|
|
312
|
+
let dt = $(this).DataTable({
|
|
313
|
+
columns: columns,
|
|
314
|
+
rowGroup: rowGroup,
|
|
315
|
+
responsive: responsive,
|
|
316
|
+
searchCols: searchCols,
|
|
317
|
+
drawCallback: function(_settings) {
|
|
318
|
+
// This callback show or hide the pagination when required
|
|
319
|
+
if (_settings.aanFeatures.p) {
|
|
320
|
+
if (_settings._iDisplayLength > _settings.fnRecordsDisplay()) {
|
|
321
|
+
$(_settings.aanFeatures.p[0]).parent().hide();
|
|
322
|
+
} else {
|
|
323
|
+
$(_settings.aanFeatures.p[0]).parent().show();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
// This callback is responsible to add and remove 'sorting-x-x' class
|
|
327
|
+
// to allow CSS customization of the table based on the sorted column
|
|
328
|
+
this.removeClass(function(_index, className) {
|
|
329
|
+
return className.split(/\s+/).filter(function(c) {
|
|
330
|
+
return c.startsWith('sorted-');
|
|
331
|
+
}).join(' ');
|
|
332
|
+
});
|
|
333
|
+
// Add sorting class when sorting without filter
|
|
334
|
+
if (this.api().order() && this.api().order()[0] && this.api().order()[0][0] >= 0 && this.api().search() === '') {
|
|
335
|
+
const colIdx = this.api().order()[0][0];
|
|
336
|
+
const direction = this.api().order()[0][1]
|
|
337
|
+
this.addClass('sorted-' + colIdx + '-' + direction);
|
|
338
|
+
const colName = _settings.aoColumns[colIdx].name;
|
|
339
|
+
if (colName) {
|
|
340
|
+
this.addClass('sorted-' + colName + '-' + direction);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
initComplete: function() {
|
|
345
|
+
// Remove no-footer class to fix CSS display with bootstrap5
|
|
346
|
+
$(this).removeClass("no-footer");
|
|
347
|
+
// If searching is enabled, focus on search field.
|
|
348
|
+
$("div.dataTables_filter input").focus();
|
|
349
|
+
// Trigger responsive recalculation on window resize
|
|
350
|
+
$(window).on('resize', function() {
|
|
351
|
+
dt.columns.adjust().responsive.recalc();
|
|
352
|
+
});
|
|
353
|
+
},
|
|
354
|
+
processing: true,
|
|
355
|
+
deferRender: true,
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{# def field, floating=False #}
|
|
2
|
+
{#css vendor/bootstrap5/css/bootstrap.min.css #}
|
|
3
|
+
{#js vendor/jquery/jquery.min.js, vendor/popper/popper.min.js, vendor/bootstrap5/js/bootstrap.min.js #}
|
|
4
|
+
{# djlint:off #}
|
|
5
|
+
{% set bootstrap_class_table = {
|
|
6
|
+
"CheckboxInput": ("form-check-input", "form-check-label", False, True),
|
|
7
|
+
"EmailInput": ("form-control", "form-label", True, False),
|
|
8
|
+
"NumberInput": ("form-control", "form-label", True, False),
|
|
9
|
+
"PasswordInput": ("form-control", "form-label", True, False),
|
|
10
|
+
"Select": ("form-select", "form-label", True, False),
|
|
11
|
+
"SubmitInput": ("btn", "d-none", False, False),
|
|
12
|
+
"TextArea": ("form-control", "form-label", True, False),
|
|
13
|
+
"TextInput": ("form-control", "form-label", True, False),
|
|
14
|
+
"RadioInput": ("form-check", "form-label", False, False),
|
|
15
|
+
"ListWidget": ("form-list-widget", "form-label", False, False)
|
|
16
|
+
} %}
|
|
17
|
+
{# djlint:on #}
|
|
18
|
+
{# Add proper class if field is invalid. #}
|
|
19
|
+
{% if field.errors %}
|
|
20
|
+
{% do attrs.add_class('is-invalid') %}
|
|
21
|
+
{% endif %}
|
|
22
|
+
{# Also include render_kw if any #}
|
|
23
|
+
{% if field.render_kw %}
|
|
24
|
+
{% do attrs.set(**field.render_kw) %}
|
|
25
|
+
{% endif %}
|
|
26
|
+
{% if field.widget.input_type is defined and field.widget.input_type == 'hidden' %}
|
|
27
|
+
{{ field.widget(field, **attrs.as_dict) }}
|
|
28
|
+
{% else %}
|
|
29
|
+
{# For each widget list supported options. #}
|
|
30
|
+
{# Assign propert bootstrap class based on wtform widget #}
|
|
31
|
+
{% set input_class, label_class, floating_supported, label_always_last = bootstrap_class_table.get(field.widget.__class__.__name__) or ('', 'form-label', False, False) %}
|
|
32
|
+
{% set label_last = (floating and floating_supported) or label_always_last %}
|
|
33
|
+
{% do attrs.add_class(input_class) %}
|
|
34
|
+
{# Used for input-group #}
|
|
35
|
+
{% set prepend = attrs.get('prepend', None) %}
|
|
36
|
+
{% set append = attrs.get('append', None) %}
|
|
37
|
+
{% set input_group = prepend or append %}
|
|
38
|
+
{# Build container & label specific attributes #}
|
|
39
|
+
{% set container_attrs = attrs.__class__({'class': 'mb-2 form-field'}) %}
|
|
40
|
+
{% set label_attrs = attrs.__class__({'class': label_class}) %}
|
|
41
|
+
{% for key, value in attrs.as_dict.items() %}
|
|
42
|
+
{% if key.startswith('container-') or key.startswith('container_') %}
|
|
43
|
+
{% do container_attrs.set(**{key[10:]: value|string}) %}
|
|
44
|
+
{% endif %}
|
|
45
|
+
{% if key.startswith('label-') or key.startswith('label_') %}
|
|
46
|
+
{% do label_attrs.set(**{key[6:]: value|string}) %}
|
|
47
|
+
{% endif %}
|
|
48
|
+
{% endfor %}
|
|
49
|
+
<div {{ container_attrs.as_dict | xmlattr }}>
|
|
50
|
+
{% if floating and floating_supported %}<div class="form-floating">{% endif %}
|
|
51
|
+
{% if not label_last %}{{ field.label(**label_attrs.as_dict) }}{% endif %}
|
|
52
|
+
{% if input_group %}
|
|
53
|
+
<div class="input-group">
|
|
54
|
+
{% if prepend %}<span class="input-group-text">{{ prepend }}</span>{% endif %}
|
|
55
|
+
{% endif %}
|
|
56
|
+
{{ field.widget(field, **attrs.as_dict) }}
|
|
57
|
+
{% if input_group %}
|
|
58
|
+
{% if append %}<span class="input-group-text">{{ append }}</span>{% endif %}
|
|
59
|
+
</div>
|
|
60
|
+
{% endif %}
|
|
61
|
+
{% if label_last %}{{ field.label(**label_attrs.as_dict) }}{% endif %}
|
|
62
|
+
{% for error in field.errors %}<div class="invalid-feedback">{{ error }}</div>{% endfor %}
|
|
63
|
+
{% if field.description %}<div class="form-text small test-secondary">{{ field.description }}</div>{% endif %}
|
|
64
|
+
{% if floating and floating_supported %}</div>{% endif %}
|
|
65
|
+
</div>
|
|
66
|
+
{% endif %}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Control showif
|
|
3
|
+
*/
|
|
4
|
+
jQuery(function() {
|
|
5
|
+
function escape(v) {
|
|
6
|
+
return v.replace(/(:|\.|\[|\]|,|=)/g, "\\$1");
|
|
7
|
+
}
|
|
8
|
+
$('[data-showif-field]').each(function() {
|
|
9
|
+
const elem = $(this);
|
|
10
|
+
const field = $(this).data('showif-field');
|
|
11
|
+
const operator = $(this).data('showif-operator');
|
|
12
|
+
const value = $(this).data('showif-value');
|
|
13
|
+
// Lookup field
|
|
14
|
+
if (!field) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const fieldElem = $("[name='" + escape(field) + "']");
|
|
18
|
+
if (fieldElem.length > 0) {
|
|
19
|
+
function updateShowIf() {
|
|
20
|
+
const curValue = fieldElem.val();
|
|
21
|
+
let visible = false;
|
|
22
|
+
if (operator == 'eq') {
|
|
23
|
+
visible = curValue == value;
|
|
24
|
+
} else if (operator == 'ne') {
|
|
25
|
+
visible = curValue != value;
|
|
26
|
+
} else if (operator == 'in' && Array.isArray(value)) {
|
|
27
|
+
visible = $.inArray(curValue, value) >= 0;
|
|
28
|
+
}
|
|
29
|
+
// To handle the initial state, manually add the collapse class before creating the collapsable class.
|
|
30
|
+
const parent = elem.closest('.form-field');
|
|
31
|
+
if (!parent.hasClass('collapse')) {
|
|
32
|
+
parent.addClass('collapse');
|
|
33
|
+
if (visible) {
|
|
34
|
+
parent.addClass('show');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Update widget visibility accordingly.
|
|
38
|
+
let collapsible = bootstrap.Collapse.getOrCreateInstance(parent, {
|
|
39
|
+
toggle: false
|
|
40
|
+
});
|
|
41
|
+
if (visible) {
|
|
42
|
+
collapsible.show();
|
|
43
|
+
elem.removeAttr('disabled');
|
|
44
|
+
} else {
|
|
45
|
+
collapsible.hide();
|
|
46
|
+
elem.attr('disabled', '1');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Attach event to field.
|
|
50
|
+
fieldElem.change(function() {
|
|
51
|
+
updateShowIf();
|
|
52
|
+
})
|
|
53
|
+
updateShowIf();
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|