mirror of
https://github.com/wrabit/django-cotton.git
synced 2025-09-03 13:27:33 +00:00
handle attributes containing quoted spaces
This commit is contained in:
parent
072342fae0
commit
12d64ede25
4 changed files with 90 additions and 5 deletions
|
@ -14,6 +14,4 @@ def index_view(request):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [path("", index_view)]
|
||||||
path("", index_view),
|
|
||||||
]
|
|
||||||
|
|
|
@ -7,7 +7,22 @@ class Tag:
|
||||||
r"<(/?)c-([^\s/>]+)((?:\s+[^\s/>\"'=<>`]+(?:\s*=\s*(?:\"[^\"]*\"|'[^']*'|\S+))?)*)\s*(/?)\s*>",
|
r"<(/?)c-([^\s/>]+)((?:\s+[^\s/>\"'=<>`]+(?:\s*=\s*(?:\"[^\"]*\"|'[^']*'|\S+))?)*)\s*(/?)\s*>",
|
||||||
re.DOTALL,
|
re.DOTALL,
|
||||||
)
|
)
|
||||||
attr_pattern = re.compile(r'([^\s/>\"\'=<>`]+)(?:\s*=\s*(?:(["\'])(.*?)\2|(\S+)))?', re.DOTALL)
|
# attr_pattern = re.compile(r'([^\s/>\"\'=<>`]+)(?:\s*=\s*(?:(["\'])(.*?)\2|(\S+)))?', re.DOTALL)
|
||||||
|
|
||||||
|
attr_pattern = re.compile(
|
||||||
|
r"""([^\s/>\"\'=<>`]+) # Attribute name
|
||||||
|
(?: # Optional group for value
|
||||||
|
\s*=\s* # Equals with optional whitespace
|
||||||
|
(?:
|
||||||
|
(["\']) # Quote character
|
||||||
|
((?:(?!\2)|.)*?) # Any character that's not the quote character
|
||||||
|
\2 # Matching quote
|
||||||
|
| # OR
|
||||||
|
(\S+) # Non-quoted value without spaces
|
||||||
|
)
|
||||||
|
)?""",
|
||||||
|
re.VERBOSE | re.DOTALL,
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, match: re.Match):
|
def __init__(self, match: re.Match):
|
||||||
self.html = match.group(0)
|
self.html = match.group(0)
|
||||||
|
@ -130,7 +145,7 @@ class CottonCompiler:
|
||||||
|
|
||||||
if len(matches) > 1:
|
if len(matches) > 1:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Multiple c-vars tags found in component template. Only one c-vars tag is allowed per template."
|
"Multiple c-vars tags found in component template. Only one c-vars tag is allowed per template."
|
||||||
)
|
)
|
||||||
|
|
||||||
# Process single c-vars tag if present
|
# Process single c-vars tag if present
|
||||||
|
|
|
@ -127,6 +127,57 @@ def cotton_component(parser, token):
|
||||||
attrs = {}
|
attrs = {}
|
||||||
only = False
|
only = False
|
||||||
|
|
||||||
|
current_key = None
|
||||||
|
current_value = []
|
||||||
|
|
||||||
|
for bit in bits[1:]:
|
||||||
|
if bit == "only":
|
||||||
|
only = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
if "=" in bit:
|
||||||
|
# If we were building a previous value, store it
|
||||||
|
if current_key:
|
||||||
|
attrs[current_key] = " ".join(current_value)
|
||||||
|
current_value = []
|
||||||
|
|
||||||
|
# Start new key-value pair
|
||||||
|
key, value = bit.split("=", 1)
|
||||||
|
if value.startswith(("'", '"')):
|
||||||
|
if value.endswith(("'", '"')) and value[0] == value[-1]:
|
||||||
|
# Complete quoted value
|
||||||
|
attrs[key] = value
|
||||||
|
else:
|
||||||
|
# Start of quoted value
|
||||||
|
current_key = key
|
||||||
|
current_value = [value]
|
||||||
|
else:
|
||||||
|
# Simple unquoted value
|
||||||
|
attrs[key] = value
|
||||||
|
else:
|
||||||
|
if current_key:
|
||||||
|
# Continue building quoted value
|
||||||
|
current_value.append(bit)
|
||||||
|
else:
|
||||||
|
# Boolean attribute
|
||||||
|
attrs[bit] = True
|
||||||
|
|
||||||
|
# Store any final value being built
|
||||||
|
if current_key:
|
||||||
|
attrs[current_key] = " ".join(current_value)
|
||||||
|
|
||||||
|
nodelist = parser.parse(("endc",))
|
||||||
|
parser.delete_first_token()
|
||||||
|
|
||||||
|
return CottonComponentNode(component_name, nodelist, attrs, only)
|
||||||
|
|
||||||
|
|
||||||
|
def cotton_component_legacy(parser, token):
|
||||||
|
bits = token.split_contents()[1:]
|
||||||
|
component_name = bits[0]
|
||||||
|
attrs = {}
|
||||||
|
only = False
|
||||||
|
|
||||||
node_class = CottonComponentNode
|
node_class = CottonComponentNode
|
||||||
|
|
||||||
for bit in bits[1:]:
|
for bit in bits[1:]:
|
||||||
|
|
|
@ -511,3 +511,24 @@ class AttributeHandlingTests(CottonTestCase):
|
||||||
response,
|
response,
|
||||||
"""attrs: ':string="variable" dynamic="Ive been resolved!" :complex-string="{'something': 1}" complex-dynamic="{'something': 1}"'""",
|
"""attrs: ':string="variable" dynamic="Ive been resolved!" :complex-string="{'something': 1}" complex-dynamic="{'something': 1}"'""",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_attributes_can_contain_valid_json(self):
|
||||||
|
self.create_template(
|
||||||
|
"cotton/json_attrs.html",
|
||||||
|
"""
|
||||||
|
<button {{ attrs }}>{{ slot }}</button>
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.create_template(
|
||||||
|
"json_attrs_view.html",
|
||||||
|
"""
|
||||||
|
<c-json-attrs data-something='{"key=dd": "the value with= spaces"}' />
|
||||||
|
""",
|
||||||
|
"view/",
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||||
|
response = self.client.get("/view/")
|
||||||
|
|
||||||
|
self.assertContains(response, """{"key=dd": "the value with= spaces"}""")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue