djangocms-render-context 1.1.0__py3-none-any.whl → 1.2.1__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.
Files changed (27) hide show
  1. djangocms_render_context/__init__.py +1 -1
  2. djangocms_render_context/cms_plugins.py +8 -0
  3. djangocms_render_context/encoders.py +8 -0
  4. djangocms_render_context/forms.py +46 -0
  5. djangocms_render_context/locale/cs/LC_MESSAGES/django.mo +0 -0
  6. djangocms_render_context/locale/cs/LC_MESSAGES/django.po +30 -2
  7. djangocms_render_context/locale/cs/LC_MESSAGES/djangojs.mo +0 -0
  8. djangocms_render_context/locale/cs/LC_MESSAGES/djangojs.po +130 -0
  9. djangocms_render_context/migrations/0003_rendercontext_config_and_path.py +46 -0
  10. djangocms_render_context/models.py +42 -5
  11. djangocms_render_context/static/djangocms_render_context/css/spreadsheet.css +15 -0
  12. djangocms_render_context/static/djangocms_render_context/css-dist/icon-family-Material-Icons.css +20 -0
  13. djangocms_render_context/static/djangocms_render_context/css-dist/jspreadsheet.min.css +8 -0
  14. djangocms_render_context/static/djangocms_render_context/css-dist/jsuites.min.css +8 -0
  15. djangocms_render_context/static/djangocms_render_context/js/spreadsheet.js +711 -0
  16. djangocms_render_context/static/djangocms_render_context/js-dist/csv.min.js +1 -0
  17. djangocms_render_context/static/djangocms_render_context/js-dist/index.min.js +8 -0
  18. djangocms_render_context/static/djangocms_render_context/js-dist/index.min.js.map +1 -0
  19. djangocms_render_context/static/djangocms_render_context/js-dist/js-yaml.min.js +2 -0
  20. djangocms_render_context/static/djangocms_render_context/js-dist/jsuites.min.js +8 -0
  21. djangocms_render_context/static/djangocms_render_context/js-dist/jsuites.min.js.map +1 -0
  22. djangocms_render_context/utils.py +16 -0
  23. {djangocms_render_context-1.1.0.dist-info → djangocms_render_context-1.2.1.dist-info}/METADATA +16 -1
  24. djangocms_render_context-1.2.1.dist-info/RECORD +32 -0
  25. {djangocms_render_context-1.1.0.dist-info → djangocms_render_context-1.2.1.dist-info}/WHEEL +1 -1
  26. djangocms_render_context-1.1.0.dist-info/RECORD +0 -17
  27. {djangocms_render_context-1.1.0.dist-info → djangocms_render_context-1.2.1.dist-info}/top_level.txt +0 -0
@@ -1 +1 @@
1
- __version__ = "1.1.0"
1
+ __version__ = "1.2.1"
@@ -31,6 +31,13 @@ class RenderContextPlugin(CMSPluginBase):
31
31
  ),
32
32
  },
33
33
  ),
34
+ (
35
+ _("Source settings"),
36
+ {
37
+ "classes": ["collapse"],
38
+ "fields": ("config",),
39
+ },
40
+ ),
34
41
  (
35
42
  _("Templates"),
36
43
  {
@@ -38,6 +45,7 @@ class RenderContextPlugin(CMSPluginBase):
38
45
  "fields": (
39
46
  "template",
40
47
  "template_list",
48
+ "path",
41
49
  ),
42
50
  },
43
51
  ),
@@ -0,0 +1,8 @@
1
+ from django.core.serializers.json import DjangoJSONEncoder
2
+
3
+
4
+ class PrettyJsonEncoder(DjangoJSONEncoder):
5
+ def __init__(self, *args, **kwargs):
6
+ kwargs["indent"] = 2
7
+ kwargs["sort_keys"] = False
8
+ super().__init__(*args, **kwargs)
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  from collections.abc import Callable
2
3
  from typing import Union
3
4
 
@@ -9,6 +10,8 @@ from django.forms import ALL_FIELDS, ModelForm
9
10
  from django.template import Context, Template, TemplateDoesNotExist
10
11
  from django.template.exceptions import TemplateSyntaxError
11
12
  from django.template.loader import get_template
13
+ from django.urls import reverse_lazy
14
+ from django.urls.exceptions import NoReverseMatch
12
15
  from django.utils.translation import gettext_lazy as _
13
16
  from filer.models.filemodels import File
14
17
  from sekizai.context import SekizaiContext
@@ -23,11 +26,14 @@ from .loaders import (
23
26
  get_data_from_url,
24
27
  value_to_bytes,
25
28
  )
29
+ from .utils import get_context_from_path
26
30
 
27
31
  TEMPLATES = (("", ""),)
28
32
 
29
33
  sourceType = Union[str, File] # noqa: UP007
30
34
 
35
+ logger = logging.getLogger(__name__)
36
+
31
37
 
32
38
  class RenderContextForm(ModelForm):
33
39
  """Render Context Form."""
@@ -44,9 +50,39 @@ class RenderContextForm(ModelForm):
44
50
  help_text=_("List of templates. If Template is specified, the template set in the list will not be used."),
45
51
  )
46
52
 
53
+ class Media:
54
+ js = [
55
+ # "https://cdn.jsdelivr.net/npm/jspreadsheet-ce@5/dist/index.min.js",
56
+ "djangocms_render_context/js-dist/index.min.js",
57
+ # "https://cdn.jsdelivr.net/npm/jsuites@5/dist/jsuites.min.js",
58
+ "djangocms_render_context/js-dist/jsuites.min.js",
59
+ # https://github.com/nodeca/js-yaml
60
+ "djangocms_render_context/js-dist/js-yaml.min.js",
61
+ # https://github.com/rufuspollock/csv.js
62
+ "djangocms_render_context/js-dist/csv.min.js",
63
+ "djangocms_render_context/js/spreadsheet.js",
64
+ ]
65
+ css = {
66
+ "all": [
67
+ # "https://cdn.jsdelivr.net/npm/jspreadsheet-ce@5/dist/jspreadsheet.min.css",
68
+ "djangocms_render_context/css-dist/jspreadsheet.min.css",
69
+ # "https://cdn.jsdelivr.net/npm/jsuites@5/dist/jsuites.min.css",
70
+ "djangocms_render_context/css-dist/jsuites.min.css",
71
+ # "https://fonts.googleapis.com/icon?family=Material+Icons",
72
+ "djangocms_render_context/css-dist/icon-family-Material-Icons.css",
73
+ "djangocms_render_context/css/spreadsheet.css",
74
+ ],
75
+ }
76
+
47
77
  def __init__(self, *args, **kwargs):
48
78
  super().__init__(*args, **kwargs)
49
79
  self.fields["file"].help_text += " " + _("Supported formats are:") + " " + ", ".join(SUPPORTED_FILE_TYPES)
80
+ pathname = getattr(settings, "DJANGOCMS_RENDER_CONTEXT_JSI18N", "jsi18n")
81
+ try:
82
+ path = reverse_lazy(pathname, kwargs={"packages": "djangocms_render_context"})
83
+ self.media._js_lists[-1].append(path)
84
+ except NoReverseMatch as err:
85
+ logger.error(err)
50
86
 
51
87
  def clean_template_list(self) -> None:
52
88
  if self.cleaned_data["template_list"]:
@@ -101,11 +137,21 @@ class RenderContextForm(ModelForm):
101
137
  except (TemplateDoesNotExist, TemplateSyntaxError) as error:
102
138
  self.add_error(field_name, error)
103
139
 
140
+ def check_path(self, cleaned_data: dict, context: Context) -> Context:
141
+ """Check path."""
142
+ if cleaned_data["path"]:
143
+ try:
144
+ context = Context(get_context_from_path(context, cleaned_data["path"]))
145
+ except (IndexError, KeyError, ValueError, TypeError) as error:
146
+ self.add_error("path", _("The data path is not valid. Error: %s") % error)
147
+ return context
148
+
104
149
  def clean(self) -> None:
105
150
  """Clean form."""
106
151
  cleaned_data = super().clean()
107
152
  if self.is_valid():
108
153
  context = self.check_context(cleaned_data)
154
+ context = self.check_path(cleaned_data, context)
109
155
  self.check_template(cleaned_data, context)
110
156
 
111
157
  def save(self, *args, **kwargs):
@@ -17,6 +17,16 @@ msgstr ""
17
17
  "Content-Transfer-Encoding: 8bit\n"
18
18
  "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
19
19
 
20
+ msgid ""
21
+ "Additional configuration for spreadsheet with source data. The configuration "
22
+ "is automatically added or updated when the spreadsheet is saved."
23
+ msgstr ""
24
+ "Další konfigurace pro tabulku se zdrojovými daty. Konfigurace je automaticky "
25
+ "doplněna nebo aktualizována při ukládání tabulky."
26
+
27
+ msgid "Configuration"
28
+ msgstr "Konfigurace"
29
+
20
30
  msgid ""
21
31
  "Context data file. If Data is specified for the context, the source file is "
22
32
  "not used even though it is specified."
@@ -37,10 +47,15 @@ msgstr "Data pro kontext"
37
47
  msgid "Data not entered."
38
48
  msgstr "Data nejsou zadána."
39
49
 
50
+ msgid "Data path"
51
+ msgstr "Cesta k datům"
52
+
40
53
  msgid ""
41
54
  "Django template for the context. It takes precedence over the template "
42
- "selected from the list."
43
- msgstr "Django šablona. Má přednost přes šablonou vybranou ze seznamu."
55
+ "selected from the list. The context is available through the \"data\" value."
56
+ msgstr ""
57
+ "Django šablona. Má přednost přes šablonou vybranou ze seznamu. Kontext je "
58
+ "dostupný přes hodnotu \"data\"."
44
59
 
45
60
  msgid "Download period from source URL"
46
61
  msgstr "Perioda stahování ze URL zdroje"
@@ -70,6 +85,9 @@ msgstr "URL zdroje"
70
85
  msgid "Source file"
71
86
  msgstr "Zdrojový soubor."
72
87
 
88
+ msgid "Source settings"
89
+ msgstr "Nastavení zdroje"
90
+
73
91
  msgid "Sources"
74
92
  msgstr "Zdroje"
75
93
 
@@ -94,9 +112,19 @@ msgstr ""
94
112
  "kontext nebo Zdrojový soubor, tak se URL zdroje nepoužije i přesto, že je "
95
113
  "zadáno."
96
114
 
115
+ msgid "The data path is not valid. Error: %s"
116
+ msgstr "Cesta k datům není platná. Chyba: %s"
117
+
97
118
  msgid "The format must be consistent with the data in the context."
98
119
  msgstr "Formát musí odpovídat datům v kontextu."
99
120
 
121
+ msgid ""
122
+ "The path to the data in the form \"name.name.name\". The \"data\" context in "
123
+ "the template starts from this key."
124
+ msgstr ""
125
+ "Cesta k datům ve tvaru \"name.name.name\". Od tohoto klíče začíná kontext "
126
+ "\"data\" v šabloně."
127
+
100
128
  msgid ""
101
129
  "The time in minutes during which data will not be retrieved from the Source "
102
130
  "URL, but will remain in the cache. If set to zero, data from the source URL "
@@ -0,0 +1,130 @@
1
+ # SOME DESCRIPTIVE TITLE.
2
+ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3
+ # This file is distributed under the same license as the PACKAGE package.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
+ #
6
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: PACKAGE VERSION\n"
9
+ "Report-Msgid-Bugs-To: \n"
10
+ "POT-Creation-Date: 2026-01-20 08:21+0100\n"
11
+ "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12
+ "Last-Translator: Zdeněk Böhm <zdenek.bohm@nic.cz>\n"
13
+ "Language-Team: LANGUAGE <LL@li.org>\n"
14
+ "Language: cs\n"
15
+ "MIME-Version: 1.0\n"
16
+ "Content-Type: text/plain; charset=UTF-8\n"
17
+ "Content-Transfer-Encoding: 8bit\n"
18
+ "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
19
+
20
+ msgid "(Press Alt+Enter to keep changes. / Alt+Q to cancel.)"
21
+ msgstr "(Alt+Enter ponechat změny. / Alt+Q ztušit.)"
22
+
23
+ msgid "About"
24
+ msgstr "O aplikaci"
25
+
26
+ msgid "Add comments"
27
+ msgstr "Přidat poznámku"
28
+
29
+ msgid "Are you sure?"
30
+ msgstr "Jste si jistí?"
31
+
32
+ msgid "Clear comments"
33
+ msgstr "Smazat poznámku"
34
+
35
+ msgid "Close this window."
36
+ msgstr "Zavřít toto okno."
37
+
38
+ msgid "Copy"
39
+ msgstr "Kopírovat"
40
+
41
+ msgid "Cut"
42
+ msgstr "Vyjmout"
43
+
44
+ msgid "Data conversion error"
45
+ msgstr "Chyba v konverzi dat"
46
+
47
+ msgid "Data serialization error"
48
+ msgstr "Chyba při serializaci dat"
49
+
50
+ msgid "Delete selected columns"
51
+ msgstr "Smazat vybrané sloupce"
52
+
53
+ msgid "Delete selected rows"
54
+ msgstr "Smazat vybrané řádky"
55
+
56
+ msgid "Display"
57
+ msgstr "Zobrazit"
58
+
59
+ msgid "Edit CSV"
60
+ msgstr "Editovat CSV"
61
+
62
+ msgid "Edit comments"
63
+ msgstr "Upravit poznámku"
64
+
65
+ msgid "Enter a URL:"
66
+ msgstr "Zadejte URL:"
67
+
68
+ msgid "Error in Source settings"
69
+ msgstr "Chyba v Nastavení zdroje"
70
+
71
+ msgid "Error message"
72
+ msgstr "Chybové hlášení"
73
+
74
+ msgid "Insert a new column after"
75
+ msgstr "Vložit nový sloupec za tento"
76
+
77
+ msgid "Insert a new column before"
78
+ msgstr "Vložit nový sloupec před tento"
79
+
80
+ msgid "Insert a new row after"
81
+ msgstr "Vložit nový řádek za tento"
82
+
83
+ msgid "Insert a new row before"
84
+ msgstr "Vložit nový řádek před tento"
85
+
86
+ msgid "Order ascending"
87
+ msgstr "Setřídit vzestupně"
88
+
89
+ msgid "Order descending"
90
+ msgstr "Setřídit sestupně"
91
+
92
+ msgid "Parse Data Error"
93
+ msgstr "Chyba při parsování dat"
94
+
95
+ msgid "Paste"
96
+ msgstr "Vložit"
97
+
98
+ msgid "Rename this cell"
99
+ msgstr "Přejmenovat tuto buňku"
100
+
101
+ msgid "Rename this column"
102
+ msgstr "Přejmenovat tento sloupec"
103
+
104
+ msgid "Save as"
105
+ msgstr "Uložit jako"
106
+
107
+ msgid "Show data in spreadsheet."
108
+ msgstr "Zobrazit data v tabulce."
109
+
110
+ msgid "Source"
111
+ msgstr "Zdroj"
112
+
113
+ msgid "Spreadsheet"
114
+ msgstr "Tabulka"
115
+
116
+ msgid ""
117
+ "This data cannot be serialized in the selected format. Please select a "
118
+ "different format."
119
+ msgstr ""
120
+ "Tato data nelze to vybraného formátu serializovat. Vyberte prosím jiný "
121
+ "formát."
122
+
123
+ msgid "Toggle source"
124
+ msgstr "Přepnout na zdroj"
125
+
126
+ msgid "Toggle spreadsheet"
127
+ msgstr "Přepnout na tabulku"
128
+
129
+ msgid "View source data."
130
+ msgstr "Zobrazit zdrojová data."
@@ -0,0 +1,46 @@
1
+ # Generated by Django 5.2.9 on 2026-01-29 13:20
2
+
3
+ import djangocms_render_context.encoders
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ("djangocms_render_context", "0002_rendercontext_mimetype"),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AddField(
15
+ model_name="rendercontext",
16
+ name="config",
17
+ field=models.JSONField(
18
+ blank=True,
19
+ encoder=djangocms_render_context.encoders.PrettyJsonEncoder,
20
+ help_text="Additional configuration for spreadsheet with source data. The configuration is automatically added or updated when the spreadsheet is saved.",
21
+ null=True,
22
+ verbose_name="Configuration",
23
+ ),
24
+ ),
25
+ migrations.AddField(
26
+ model_name="rendercontext",
27
+ name="path",
28
+ field=models.CharField(
29
+ blank=True,
30
+ help_text='The path to the data in the form "name.name.name". The "data" context in the template starts from this key.',
31
+ max_length=255,
32
+ null=True,
33
+ verbose_name="Data path",
34
+ ),
35
+ ),
36
+ migrations.AlterField(
37
+ model_name="rendercontext",
38
+ name="template",
39
+ field=models.TextField(
40
+ blank=True,
41
+ help_text='Django template for the context. It takes precedence over the template selected from the list. The context is available through the "data" value.',
42
+ null=True,
43
+ verbose_name="Template",
44
+ ),
45
+ ),
46
+ ]
@@ -1,12 +1,17 @@
1
1
  import logging
2
+ from typing import Any, Optional, Union
2
3
 
3
4
  from cms.models.pluginmodel import CMSPlugin
4
5
  from django.db import models
5
6
  from django.utils.translation import gettext_lazy as _
6
7
  from filer.fields.file import FilerFileField
7
8
 
9
+ from .encoders import PrettyJsonEncoder
8
10
  from .exceptions import SourceParseFailure
9
11
  from .loaders import LOADERS, get_cached_data_from_url, get_data_from_file, value_to_bytes
12
+ from .utils import get_context_from_path
13
+
14
+ contentType = Optional[Union[dict[Any, Any], list[Any]]] # noqa: UP045, UP007
10
15
 
11
16
  LOGGER = logging.getLogger(__name__)
12
17
 
@@ -24,6 +29,16 @@ class RenderContext(CMSPlugin):
24
29
  blank=True,
25
30
  help_text=_("Context data in selected format. They take precedence over the source file and source URL."),
26
31
  )
32
+ config = models.JSONField(
33
+ verbose_name=_("Configuration"),
34
+ null=True,
35
+ blank=True,
36
+ encoder=PrettyJsonEncoder,
37
+ help_text=_(
38
+ "Additional configuration for spreadsheet with source data. "
39
+ "The configuration is automatically added or updated when the spreadsheet is saved."
40
+ ),
41
+ )
27
42
  file = FilerFileField(
28
43
  verbose_name=_("Source file"),
29
44
  null=True,
@@ -56,7 +71,10 @@ class RenderContext(CMSPlugin):
56
71
  verbose_name=_("Template"),
57
72
  null=True,
58
73
  blank=True,
59
- help_text=_("Django template for the context. It takes precedence over the template selected from the list."),
74
+ help_text=_(
75
+ "Django template for the context. It takes precedence over the template selected from the list."
76
+ ' The context is available through the "data" value.'
77
+ ),
60
78
  )
61
79
  template_list = models.CharField(
62
80
  verbose_name=_("Template list"),
@@ -65,6 +83,16 @@ class RenderContext(CMSPlugin):
65
83
  max_length=255,
66
84
  help_text=_("List of templates. If Template is specified, the template set in the list will not be used."),
67
85
  )
86
+ path = models.CharField(
87
+ verbose_name=_("Data path"),
88
+ null=True,
89
+ blank=True,
90
+ max_length=255,
91
+ help_text=_(
92
+ 'The path to the data in the form "name.name.name".'
93
+ ' The "data" context in the template starts from this key.'
94
+ ),
95
+ )
68
96
 
69
97
  def __str__(self):
70
98
  text = []
@@ -84,17 +112,26 @@ class RenderContext(CMSPlugin):
84
112
  text.append(_("No template."))
85
113
  return " + ".join([str(t) for t in text])
86
114
 
87
- def get_data(self):
115
+ def get_data_by_path(self, data: contentType) -> contentType:
116
+ if self.path:
117
+ try:
118
+ data = get_context_from_path(data, self.path)
119
+ except (IndexError, KeyError, ValueError, TypeError) as err:
120
+ LOGGER.error(err)
121
+ return {}
122
+ return data
123
+
124
+ def get_data(self) -> contentType:
88
125
  if self.context:
89
- return LOADERS[self.mimetype](value_to_bytes(self.context, self.mimetype))
126
+ return self.get_data_by_path(LOADERS[self.mimetype](value_to_bytes(self.context, self.mimetype)))
90
127
  elif self.file:
91
128
  try:
92
- return get_data_from_file(self.file)
129
+ return self.get_data_by_path(get_data_from_file(self.file))
93
130
  except SourceParseFailure as err:
94
131
  LOGGER.error(err)
95
132
  elif self.source:
96
133
  try:
97
- return get_cached_data_from_url(self.pk, self.source, self.cached)
134
+ return self.get_data_by_path(get_cached_data_from_url(self.pk, self.source, self.cached))
98
135
  except SourceParseFailure as err:
99
136
  LOGGER.error(err)
100
137
  return None
@@ -0,0 +1,15 @@
1
+ table.jss_worksheet {
2
+ border-collapse: separate;
3
+ }
4
+
5
+ #id-toggle-spreadsheet {
6
+ display: flex;
7
+ gap: 1em;
8
+ margin-bottom: 1em;
9
+ }
10
+
11
+ #id-toggle-spreadsheet a.selected {
12
+ background-color: #00bbff !important;
13
+ color: #fff !important;
14
+ font-weight: bold;
15
+ }
@@ -0,0 +1,20 @@
1
+ @font-face {
2
+ font-family: 'Material Icons';
3
+ font-style: normal;
4
+ font-weight: 400;
5
+ src: url(https://fonts.gstatic.com/s/materialicons/v145/flUhRq6tzZclQEJ-Vdg-IuiaDsNZ.ttf) format('truetype');
6
+ }
7
+
8
+ .material-icons {
9
+ font-family: 'Material Icons';
10
+ font-weight: normal;
11
+ font-style: normal;
12
+ font-size: 24px;
13
+ line-height: 1;
14
+ letter-spacing: normal;
15
+ text-transform: none;
16
+ display: inline-block;
17
+ white-space: nowrap;
18
+ word-wrap: normal;
19
+ direction: ltr;
20
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Minified by jsDelivr using clean-css v5.3.3.
3
+ * Original file: /npm/jspreadsheet-ce@5.0.4/dist/jspreadsheet.css
4
+ *
5
+ * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
6
+ */
7
+ :root{--jss-border-color:#000}.jss_spreadsheet{outline:0}.jss_container{display:inline-block;padding-right:2px;box-sizing:border-box;overscroll-behavior:contain;outline:0}.fullscreen{position:fixed!important;top:0;left:0;width:100%;height:100%;z-index:21;display:flex;flex-direction:column;background-color:#fff}.fullscreen .jtabs-content{flex:1;overflow:hidden}.fullscreen .jss_content{overflow:auto;width:100%!important;height:100%;max-height:100%!important}.fullscreen .jss_container{height:100%}.jss_content{display:inline-block;box-sizing:border-box;padding-right:3px;padding-bottom:3px;position:relative;scrollbar-width:thin;scrollbar-color:#666 transparent}@supports (-moz-appearance:none){.jss_content{padding-right:10px}}.jss_content::-webkit-scrollbar{width:8px;height:8px}.jss_content::-webkit-scrollbar-track{background:#eee}.jss_content::-webkit-scrollbar-thumb{background:#666}.jss_worksheet{border-collapse:separate;table-layout:fixed;white-space:nowrap;empty-cells:show;border:0;background-color:#fff;width:0;border-top:1px solid transparent;border-left:1px solid transparent;border-right:1px solid #ccc;border-bottom:1px solid #ccc}.jss_worksheet>thead>tr>td{border-top:1px solid #ccc;border-left:1px solid #ccc;border-right:1px solid transparent;border-bottom:1px solid transparent;background-color:#f3f3f3;padding:2px;cursor:pointer;box-sizing:border-box;overflow:hidden;position:-webkit-sticky;position:sticky;top:0;z-index:2}.jss_worksheet>thead>tr>td.dragging{opacity:.5}.jss_worksheet>thead>tr>td.selected{background-color:#dcdcdc}.jss_worksheet>thead>tr>td.arrow-up{background-repeat:no-repeat;background-position:center right 5px;background-image:url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 14l5-5 5 5H7z' fill='gray'/%3E%3C/svg%3E");text-decoration:underline}.jss_worksheet>thead>tr>td.arrow-down{background-repeat:no-repeat;background-position:center right 5px;background-image:url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='gray'/%3E%3C/svg%3E");text-decoration:underline}.jss_worksheet>tbody>tr>td:first-child{position:relative;background-color:#f3f3f3;text-align:center}.jss_worksheet>tbody.resizable>tr>td:first-child::before{content:'\00a0';width:100%;height:3px;position:absolute;bottom:0;left:0;cursor:row-resize}.jss_worksheet>tbody.draggable>tr>td:first-child::after{content:'\00a0';width:3px;height:100%;position:absolute;top:0;right:0;cursor:move}.jss_worksheet>tbody>tr.dragging>td{background-color:#eee;opacity:.5}.jss_worksheet>tbody>tr>td{border-top:1px solid #ccc;border-left:1px solid #ccc;border-right:1px solid transparent;border-bottom:1px solid transparent;padding:4px;white-space:nowrap;box-sizing:border-box;line-height:1em}.jss_overflow>tbody>tr>td{overflow:hidden}.jss_worksheet>tbody>tr>td:last-child{overflow:hidden}.jss_worksheet>tbody>tr>td>img{display:inline-block;max-width:100px}.jss_worksheet>tbody>tr>td.readonly{color:rgba(0,0,0,.3)}.jss_worksheet>tbody>tr.selected>td:first-child{background-color:#dcdcdc}.jss_worksheet>tbody>tr>td>input,.jss_worksheet>tbody>tr>td>select,.jss_worksheet>tbody>tr>td>textarea{border:0;border-radius:0;outline:0;width:100%;margin:0;padding:0;padding-right:2px;background-color:transparent;box-sizing:border-box}.jss_worksheet>tbody>tr>td>textarea{resize:none;padding-top:6px!important}.jss_worksheet>tbody>tr>td>input[type=checkbox]{width:12px;margin-top:2px}.jss_worksheet>tbody>tr>td>input[type=radio]{width:12px;margin-top:2px}.jss_worksheet>tbody>tr>td>select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-repeat:no-repeat;background-position-x:100%;background-position-y:40%;background-image:url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSdibGFjaycgaGVpZ2h0PScyNCcgdmlld0JveD0nMCAwIDI0IDI0JyB3aWR0aD0nMjQnIHhtbG5zPSdodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2Zyc+PHBhdGggZD0nTTcgMTBsNSA1IDUtNXonLz48cGF0aCBkPSdNMCAwaDI0djI0SDB6JyBmaWxsPSdub25lJy8+PC9zdmc+)}.jss_worksheet>tbody>tr>td.jss_dropdown{background-repeat:no-repeat;background-position:top 50% right 5px;background-image:url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='lightgray'/%3E%3C/svg%3E");text-overflow:ellipsis;overflow-x:hidden}.jss_worksheet>tbody>tr>td.jss_dropdown.jss_comments{background:url("data:image/svg+xml,%0A%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' d='M0 0h24v24H0V0z'/%3E%3Cpath d='M7 10l5 5 5-5H7z' fill='lightgray'/%3E%3C/svg%3E") top 50% right 5px no-repeat,url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFuGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHhtcDpNb2RpZnlEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDphMTlhZDJmOC1kMDI2LTI1NDItODhjOS1iZTRkYjkyMmQ0MmQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpkOGI5NDUyMS00ZjEwLWQ5NDktYjUwNC0wZmU1N2I3Nzk1MDEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIHN0RXZ0OndoZW49IjIwMTktMDEtMzFUMTg6NTU6MDhaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmExOWFkMmY4LWQwMjYtMjU0Mi04OGM5LWJlNGRiOTIyZDQyZCIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4En6MDAAAAX0lEQVQYlX3KOw6AIBBAwS32RpJADXfx0pTET+ERZJ8F8RODFtONsG0QAoh0CSDM82dqodaBdQXnfoLZQM7gPai+wjNNE8R4pTuAYNZSKZASqL7CMy0LxNgJp30fKYUDi3+vIqb/+rUAAAAASUVORK5CYII=') top right no-repeat}.jss_worksheet>tbody>tr>td>.color{width:90%;height:10px;margin:auto}.jss_worksheet>tbody>tr>td>a{text-decoration:underline}.jss_worksheet>tbody>tr>td.highlight>a{color:#00f;cursor:pointer}.jss_worksheet>tfoot>tr>td{border-top:1px solid #ccc;border-left:1px solid #ccc;border-right:1px solid transparent;border-bottom:1px solid transparent;background-color:#f3f3f3;padding:2px;cursor:pointer;box-sizing:border-box;overflow:hidden}.jss_worksheet .highlight{background-color:rgba(0,0,0,.05)}.jss_worksheet .highlight-top{border-top:1px solid #000;box-shadow:0 -1px #ccc}.jss_worksheet .highlight-left{border-left:1px solid #000;box-shadow:-1px 0 #ccc}.jss_worksheet .highlight-right{border-right:1px solid #000}.jss_worksheet .highlight-bottom{border-bottom:1px solid #000}.jss_worksheet .highlight-top.highlight-left{box-shadow:-1px -1px #ccc;-webkit-box-shadow:-1px -1px #ccc;-moz-box-shadow:-1px -1px #ccc}.jss_worksheet .highlight-selected{background-color:rgba(0,0,0,0)}.jss_worksheet .selection{background-color:rgba(0,0,0,.05)}.jss_worksheet .selection-left{border-left:1px dotted #000}.jss_worksheet .selection-right{border-right:1px dotted #000}.jss_worksheet .selection-top{border-top:1px dotted #000}.jss_worksheet .selection-bottom{border-bottom:1px dotted #000}.jss_corner{position:absolute;background-color:#000;height:1px;width:1px;border:1px solid #fff;top:-2000px;left:-2000px;cursor:crosshair;box-sizing:initial;z-index:20;padding:2px}.jss_worksheet .editor{outline:0 solid transparent;overflow:visible;white-space:nowrap;text-align:left;padding:0;box-sizing:border-box;overflow:visible!important}.jss_worksheet .editor>input{padding-left:4px}.jss_worksheet .editor .jupload{position:fixed;top:100%;z-index:40;user-select:none;-webkit-font-smoothing:antialiased;font-size:.875rem;letter-spacing:.2px;-webkit-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.2);box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.2);padding:10px;background-color:#fff;width:300px;min-height:225px;margin-top:2px}.jss_worksheet .editor .jupload img{width:100%;height:auto}.jss_worksheet .editor .jss_richtext{position:fixed;top:100%;z-index:40;user-select:none;-webkit-font-smoothing:antialiased;font-size:.875rem;letter-spacing:.2px;-webkit-box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.2);box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.2);padding:10px;background-color:#fff;width:358px;margin-top:2px;text-align:left;white-space:initial}.jss_worksheet .editor .jclose:after{position:absolute;top:0;right:0;margin:10px;content:'close';font-family:'Material icons';font-size:24px;width:24px;height:24px;line-height:24px;cursor:pointer;text-shadow:0 0 5px #fff}.jss_corner,.jss_worksheet,.jss_worksheet td{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-user-drag:none;-khtml-user-drag:none;-moz-user-drag:none;-o-user-drag:none;user-drag:none}.jss_textarea{position:absolute;top:-999px;left:-999px;width:1px;height:1px}.jss_worksheet .dragline{position:absolute}.jss_worksheet .dragline div{position:relative;top:-6px;height:5px;width:22px}.jss_worksheet .dragline div:hover{cursor:move}.jss_worksheet .onDrag{background-color:rgba(0,0,0,.6)}.jss_worksheet .error{border:1px solid red}.jss_worksheet thead td.resizing{border-right-style:dotted!important;border-right-color:red!important}.jss_worksheet tbody tr.resizing>td{border-bottom-style:dotted!important;border-bottom-color:red!important}.jss_worksheet tbody td.resizing{border-right-style:dotted!important;border-right-color:red!important}.jss_worksheet .jdropdown-header{border:0!important;outline:0!important;width:100%!important;height:100%!important;padding:0!important;padding-left:8px!important}.jss_worksheet .jdropdown-container{margin-top:1px}.jss_worksheet .jdropdown-container-header{padding:0;margin:0;height:inherit}.jss_worksheet .jdropdown-picker{border:0!important;padding:0!important;width:inherit;height:inherit}.jss_worksheet .jss_comments{background:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFuGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdEV2dD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlRXZlbnQjIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnBob3Rvc2hvcD0iaHR0cDovL25zLmFkb2JlLmNvbS9waG90b3Nob3AvMS4wLyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHhtcDpNb2RpZnlEYXRlPSIyMDE5LTAxLTMxVDE4OjU1OjA4WiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDphMTlhZDJmOC1kMDI2LTI1NDItODhjOS1iZTRkYjkyMmQ0MmQiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDpkOGI5NDUyMS00ZjEwLWQ5NDktYjUwNC0wZmU1N2I3Nzk1MDEiIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIGRjOmZvcm1hdD0iaW1hZ2UvcG5nIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplMzdjYmE1ZS1hYTMwLWNkNDUtYTAyNS1lOWYxZjk2MzUzOGUiIHN0RXZ0OndoZW49IjIwMTktMDEtMzFUMTg6NTU6MDhaIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmExOWFkMmY4LWQwMjYtMjU0Mi04OGM5LWJlNGRiOTIyZDQyZCIgc3RFdnQ6d2hlbj0iMjAxOS0wMS0zMVQxODo1NTowOFoiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDQyAyMDE5IChXaW5kb3dzKSIgc3RFdnQ6Y2hhbmdlZD0iLyIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4En6MDAAAAX0lEQVQYlX3KOw6AIBBAwS32RpJADXfx0pTET+ERZJ8F8RODFtONsG0QAoh0CSDM82dqodaBdQXnfoLZQM7gPai+wjNNE8R4pTuAYNZSKZASqL7CMy0LxNgJp30fKYUDi3+vIqb/+rUAAAAASUVORK5CYII=');background-repeat:no-repeat;background-position:top right}.jss_worksheet .sp-replacer{margin:2px;border:0}.jss_worksheet>thead>tr.jss_filter>td>input{border:0;width:100%;outline:0}.jss_about{float:right;font-size:.7em;padding:2px;text-transform:uppercase;letter-spacing:1px;display:none}.jss_about a{color:#ccc;text-decoration:none}.jss_about img{display:none}.jss_filter{display:flex;justify-content:space-between;margin-bottom:4px}.jss_filter>div{padding:8px;align-items:center}.jss_pagination{display:flex;justify-content:space-between;align-items:center}.jss_pagination>div{display:flex;padding:10px}.jss_pagination>div:last-child{padding-right:10px;padding-top:10px}.jss_pagination>div>div{text-align:center;width:36px;height:36px;line-height:34px;border:1px solid #ccc;box-sizing:border-box;margin-left:2px;cursor:pointer}.jss_page{font-size:.8em}.jss_page_selected{font-weight:700;background-color:#f3f3f3}.jss_toolbar{display:flex;background-color:#f3f3f3;border:1px solid #ccc;padding:4px;margin:0 2px 4px 1px}.jss_toolbar:empty{display:none}.jss_worksheet .dragging-left{background-repeat:no-repeat;background-position:top 50% left 0;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M14 7l-5 5 5 5V7z'/%3E%3Cpath fill='none' d='M24 0v24H0V0h24z'/%3E%3C/svg%3E")}.jss_worksheet .dragging-right{background-repeat:no-repeat;background-position:top 50% right 0;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M10 17l5-5-5-5v10z'/%3E%3Cpath fill='none' d='M0 24V0h24v24H0z'/%3E%3C/svg%3E")}.jss_hidden_index>colgroup>col:first-child,.jss_hidden_index>tbody>tr>td:first-child,.jss_hidden_index>tfoot>tr>td:first-child,.jss_hidden_index>thead>tr>td:first-child{display:none}.jss_worksheet .jrating{display:inline-flex}.jss_worksheet .jrating>div{zoom:0.55}.jss_worksheet .copying-top{border-top:1px dashed #000}.jss_worksheet .copying-left{border-left:1px dashed #000}.jss_worksheet .copying-right{border-right:1px dashed #000}.jss_worksheet .copying-bottom{border-bottom:1px dashed #000}.jss_worksheet .jss_column_filter{background-repeat:no-repeat;background-position:top 50% right 5px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='gray' width='18px' height='18px'%3E%3Cpath d='M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E");text-overflow:ellipsis;overflow:hidden;padding:0;padding-left:6px;padding-right:20px}.jss_worksheet tfoot .jss_freezed,.jss_worksheet thead .jss_freezed{left:0;z-index:3!important;box-shadow:2px 0 2px .2px #ccc!important;-webkit-box-shadow:2px 0 2px .2px #ccc!important;-moz-box-shadow:2px 0 2px .2px #ccc!important}.jss_worksheet tbody .jss_freezed{position:relative;background-color:#fff;box-shadow:1px 1px 1px 1px #ccc!important;-webkit-box-shadow:2px 4px 4px .1px #ccc!important;-moz-box-shadow:2px 4px 4px .1px #ccc!important}.red{color:red}.jss_worksheet>tbody>tr>td.readonly>input[type=checkbox],.jss_worksheet>tbody>tr>td.readonly>input[type=radio]{pointer-events:none;opacity:.5}
8
+ /*# sourceMappingURL=/sm/696f99b125d5dcb276715f749ee2460590be8939ec3d578977f40370ddb8ed8a.map */