diff --git a/imagekit/templatetags/imagekit_tags.py b/imagekit/templatetags/imagekit_tags.py index 1d4cf82..b092ca4 100644 --- a/imagekit/templatetags/imagekit_tags.py +++ b/imagekit/templatetags/imagekit_tags.py @@ -1,5 +1,6 @@ from django import template from django.utils.safestring import mark_safe +import re from ..files import ImageSpecCacheFile from .. import specs @@ -7,27 +8,86 @@ from .. import specs register = template.Library() -class SpecNode(template.Node): - def __init__(self, spec_id, source_image, variable_name=None): - self.spec_id = spec_id - self.source_image = source_image - self.variable_name = variable_name +html_attr_pattern = r""" + (?P\w+) # The attribute name + ( + \s*=\s* # an equals sign, that may or may not have spaces around it + (?P + ("[^"]*") # a double-quoted value + | # or + ('[^']*') # a single-quoted value + | # or + ([^"'<>=\s]+) # an unquoted value + ) + )? +""" - def render(self, context): +html_attr_re = re.compile(html_attr_pattern, re.VERBOSE) + + +class SpecResultNodeMixin(object): + def __init__(self, spec_id, source_file): + self._spec_id = spec_id + self._source_file = source_file + + def get_spec(self, context): from ..utils import autodiscover autodiscover() - source_image = self.source_image.resolve(context) - spec_id = self.spec_id.resolve(context) + spec_id = self._spec_id.resolve(context) spec = specs.registry.get_spec(spec_id) if callable(spec): spec = spec() - file = ImageSpecCacheFile(spec, source_image) - if self.variable_name is not None: - variable_name = str(self.variable_name) - context[variable_name] = file - return '' + return spec - return mark_safe(u'' % file.url) + def get_source_file(self, context): + return self._source_file.resolve(context) + + def get_file(self, context): + spec = self.get_spec(context) + source_file = self.get_source_file(context) + return ImageSpecCacheFile(spec, source_file) + + +class SpecResultAssignmentNode(template.Node, SpecResultNodeMixin): + def __init__(self, spec_id, source_file, variable_name): + super(SpecResultAssignmentNode, self).__init__(spec_id, source_file) + self._variable_name = variable_name + + def get_variable_name(self, context): + return unicode(self._variable_name) + + def render(self, context): + variable_name = self.get_variable_name(context) + context[variable_name] = self.get_file(context) + return '' + + +class SpecResultImgTagNode(template.Node, SpecResultNodeMixin): + def __init__(self, spec_id, source_file, html_attrs): + super(SpecResultImgTagNode, self).__init__(spec_id, source_file) + self._html_attrs = html_attrs + + def get_html_attrs(self, context): + attrs = [] + for attr in self._html_attrs: + match = html_attr_re.search(attr) + if match: + attrs.append((match.group('name'), match.group('value'))) + return attrs + + def get_attr_str(self, k, v): + return k if v is None else '%s=%s' % (k, v) + + def render(self, context): + file = self.get_file(context) + attrs = self.get_html_attrs(context) + attr_dict = dict(attrs) + if not 'width' in attr_dict and not 'height' in attr_dict: + attrs = attrs + [('width', '"%s"' % file.width), + ('height', '"%s"' % file.height)] + attrs = [('src', '"%s"' % file.url)] + attrs + attr_str = ' '.join(self.get_attr_str(k, v) for k, v in attrs) + return mark_safe(u'' % attr_str) #@register.tag @@ -51,21 +111,26 @@ def spec(parser, token): """ - args = token.split_contents() - arg_count = len(args) + bits = token.split_contents() + tag_name = bits.pop(0) - if (arg_count < 3 or arg_count > 5 - or (arg_count > 3 and arg_count < 5) - or (args == 5 and args[3] != 'as')): + if len(bits) == 4 and bits[2] == 'as': + return SpecResultAssignmentNode( + parser.compile_filter(bits[0]), # spec id + parser.compile_filter(bits[1]), # source file + parser.compile_filter(bits[3]), # var name + ) + elif len(bits) > 1: + return SpecResultImgTagNode( + parser.compile_filter(bits[0]), # spec id + parser.compile_filter(bits[1]), # source file + bits[2:], # html attributes + ) + else: raise template.TemplateSyntaxError('\'spec\' tags must be in the form' ' "{% spec spec_id image %}" or' ' "{% spec spec_id image' ' as varname %}"') - spec_id = parser.compile_filter(args[1]) - source_image = parser.compile_filter(args[2]) - variable_name = arg_count > 3 and args[4] or None - return SpecNode(spec_id, source_image, variable_name) - spec = spec_tag = register.tag(spec)